← На главную

Не самый тривиальный пример использования libcurl

Библиотека cURL, написанная на языке C, реализует ряд сетевых протоколов, включая HTTP, FTP, SMTP, POP3, Telnet, и другие. Если вам нужно поговорить о чем-то с сервером, где-то в 90% случаев вы можете сделать это, используя cURL. В рамках сей заметки мы разберемся, как при помощи libcurl написать не самый тривиальный HTTP-клиент.

Рассмотрим для примера задачу заливки картинок на ImageShack. Эту задачу мы уже многократно решали. В частности, решение на Python можно найти в заметке Как я выбирал скриптовый язык и остановился на Python. Приступим!

Подключаем заголовочный файл:

#include <curl/curl.h>

Инициализируем библиотеку cURL:

if(curl_global_init(CURL_GLOBAL_ALL) != 0) { fprintf(stderr, "curl_global_init() failed\n"); return 1; }

Создаем «объект» CURL, который, собственно, и будет далее посылать запросы:

CURL* curl = curl_easy_init(); if(curl == NULL) { fprintf(stderr, "curl_easy_init() failed\n"); return 1; } // потом его нужно освободить вот так: // curl_easy_cleanup(curl);

Указываем HTTP-заголовки:

struct curl_slist* curlHeaders = NULL; 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* formFirstItem = NULL; struct curl_httppost* formLastItem = NULL; curl_formadd(&formFirstItem, &formLastItem, CURLFORM_COPYNAME, "key", CURLFORM_COPYCONTENTS, "015EFMNVfe7f6f7e93cb4a7b0a41e19956ce59f8", CURLFORM_END); // форма освобождается таким образом: // curl_formfree(formFirstItem);

Чтобы в форме передать файл, говорим:

curl_formadd(&formFirstItem, &formLastItem, CURLFORM_COPYNAME, "Filedata", CURLFORM_FILE, fname, CURLFORM_END);

Связываем все это вместе – указываем URL, заголовки, форму, а также колбэк, в который прилетит ответ от сервера:

WriteFuncCtx ctx; 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 определены нами следующим образом:

typedef struct { 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.

Наконец, посылаем запрос:

CURLcode res = curl_easy_perform(curl); if(res != CURLE_OK) { // ... return 1; }

В конец буфера записываем нулевой байт:

if(!writeFuncCtxAppend(&ctx, "", 1)) { // ... return 1; }

После чего мы можем работать с ним, как с обычной строкой, найдя в ответе сервера URL загруженной картинки при помощи обыкновенного strstr.

Итак, мы научились отправлять формы, заливать файлы, проставлять HTTP заголовки – пожалуй, всему, что может потребоваться на практике при работе с HTTP. И все это на языке C. Согласитесь, это было совсем несложно!

Полную версию исходного кода вы найдете в этом репозитории. Более детальное описание API libcurl вы найдете либо здесь, либо в самом curl/curl.h. Любые дополнения и вопросы, как всегда, горячо приветствуются.