libcurl で http レスポンス ボディを受け取る

curl はコマンドではよくお世話になっていたが、C から使ったことはなくこの度 libcurl を使うコードを書いてみた。
curl はいろんなプロトコルに対応しているが、そのほとんどが option や callback 関数を設定するだけでなんとかなりそう。

それと、--libcurl というコマンドラインオプションがあり、curl コマンドで実行した内容の C ソースコードを出力できる。
普段使い慣れているコマンドラインから、C ソースコードでの実装に入っていけるから、実装が少しだけ楽になりそう。

コード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "curl/curl.h"

typedef struct {
    char *m;
    size_t size;
} Memory;

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata);

int main(int argc, char **argv)
{
    CURL *curl;
    CURLcode ret;
    Memory body;

    curl_global_init(CURL_GLOBAL_ALL);

    curl = curl_easy_init();
    if (curl == NULL) {
        fprintf(stderr, "Error: curl_easy_init() failed\n");
        return 1;
    }

    body.m = (char *)malloc(1);
    body.m[0] = '\0';
    body.size = 0;

    curl_easy_setopt(curl, CURLOPT_URL, "http://localhost/");
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &body);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");

    ret = curl_easy_perform(curl);
    if (ret != CURLE_OK) {
        fprintf(stderr, "Error: curl_easy_perform() failed: %s\n", curl_easy_strerror(ret));
        return 1;
    }

    printf("response: \n%s\n", body.m);

    free(body.m);

    curl_easy_cleanup(curl);

    curl_global_cleanup();

    return 0;
}

size_t write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
    char *p;
    Memory *mem = (Memory *)userdata;
    size_t append_size = size * nmemb;

    p = (char *)realloc(mem->m, mem->size + append_size + 1);
    if (p == NULL) {
        return 0;
    }
    memcpy(p + mem->size, ptr, append_size);

    mem->m = p;
    mem->size += append_size;
    mem->m[mem->size] = '\0';

    return size * nmemb;
}