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. Любые дополнения и вопросы, как всегда, горячо приветствуются.

EnglishRussianUkrainian