Не самый тривиальный пример использования libcurl
17 марта 2016
Библиотека cURL, написанная на языке C, реализует ряд сетевых протоколов, включая HTTP, FTP, SMTP, POP3, Telnet, и другие. Если вам нужно поговорить о чем-то с сервером, где-то в 90% случаев вы можете сделать это, используя cURL. В рамках сей заметки мы разберемся, как при помощи libcurl написать не самый тривиальный HTTP-клиент.
Рассмотрим для примера задачу заливки картинок на ImageShack. Эту задачу мы уже многократно решали. В частности, решение на Python можно найти в заметке Как я выбирал скриптовый язык и остановился на Python. Приступим!
Подключаем заголовочный файл:
Инициализируем библиотеку cURL:
{
fprintf(stderr, "curl_global_init() failed\n");
return 1;
}
Создаем «объект» CURL, который, собственно, и будет далее посылать запросы:
if(curl == NULL)
{
fprintf(stderr, "curl_easy_init() failed\n");
return 1;
}
// потом его нужно освободить вот так:
// curl_easy_cleanup(curl);
Указываем HTTP-заголовки:
curlHeaders = curl_slist_append(curlHeaders, USER_AGENT_STRING);
if(curlHeaders == NULL)
{
fprintf(stderr, "curl_slist_append() failed\n");
curl_easy_cleanup(curl);
return 1;
}
// потом их нужно освободить вот так:
// curl_slist_free_all(curlHeaders);
Заполняем форму:
struct curl_httppost* formLastItem = NULL;
curl_formadd(&formFirstItem,
&formLastItem,
CURLFORM_COPYNAME, "key",
CURLFORM_COPYCONTENTS, "015EFMNVfe7f6f7e93cb4a7b0a41e19956ce59f8",
CURLFORM_END);
// форма освобождается таким образом:
// curl_formfree(formFirstItem);
Чтобы в форме передать файл, говорим:
&formLastItem,
CURLFORM_COPYNAME, "Filedata",
CURLFORM_FILE, fname,
CURLFORM_END);
Связываем все это вместе — указываем URL, заголовки, форму, а также колбэк, в который прилетит ответ от сервера:
if(!writeFuncCtxInit(&ctx))
{
// ...
return 1;
}
curl_easy_setopt(curl, CURLOPT_URL, IMAGESHACK_UPLOAD_URL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ctx);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curlHeaders);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formFirstItem);
… где writeCallback и WriteFuncCtx определены нами следующим образом:
{
char* buff;
size_t buffSize;
size_t used;
} WriteFuncCtx;
bool
writeFuncCtxInit(WriteFuncCtx* ctx)
{
// ...
}
bool
writeFuncCtxAppend(WriteFuncCtx* ctx, const char* data, size_t size)
{
// ...
}
void
writeFuncCtxFree(WriteFuncCtx* ctx)
{
// ...
}
size_t
writeCallback(char *ptr, size_t size, size_t nmemb, void *userdata)
{
WriteFuncCtx* ctx = (WriteFuncCtx*)userdata;
size_t totalSize = size*nmemb;
if(writeFuncCtxAppend(ctx, ptr, totalSize))
return totalSize;
else
return 0;
}
Другими словами, все, что делает writeCallback — это записывает ответ сервера в буфер переменного размера, тот самый WriteFuncCtx. Если место в буфере заканчивается, выделяется буфер в два раза большего размера, в него копируются все данные, а старый буфер освобождается. С тем же успехом мы могли бы использовать любой готовый контейнер, похожий на Vector.
Наконец, посылаем запрос:
if(res != CURLE_OK)
{
// ...
return 1;
}
В конец буфера записываем нулевой байт:
{
// ...
return 1;
}
После чего мы можем работать с ним, как с обычной строкой, найдя в ответе сервера URL загруженной картинки при помощи обыкновенного strstr.
Итак, мы научились отправлять формы, заливать файлы, проставлять HTTP заголовки — пожалуй, всему, что может потребоваться на практике при работе с HTTP. И все это на языке C. Согласитесь, это было совсем несложно!
Полную версию исходного кода вы найдете в этом репозитории. Более детальное описание API libcurl вы найдете либо здесь, либо в самом curl/curl.h. Любые дополнения и вопросы, как всегда, горячо приветствуются.
Метки: C/C++.
Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.