#c #curl
#c #curl
Вопрос:
Я пытаюсь написать функцию-оболочку для выполнения http get-запроса с использованием lib curl. Однако я не могу понять, как вернуть заголовки и тело ответа, используя curl_easy_setopt
функции с функциями обратного вызова.
Вот мой код моей функции-оболочки.
typedef struct {
char* key;
char* value;
} http_header_t;
typedef struct {
int status;
char *body;
http_header_t headers[MAX_HEADERS];
} http_response_t;
size_t http_response_write(void *ptr, size_t size, size_t nmemb, void* userdata) {
return size * nmemb;
}
size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
http_response_t *response = (http_response_t*)userdata;
printf("buf: %sn", buffer);
return nitems * size;
}
/**
* @brief HTTP GET request
*
* @param response The response buffer
* @param url The URL to request
*/
void http_get(http_response_t *response, const char *url) {
CURL *curl;
CURLcode res;
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEHEADER, header_callback);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_response_write);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
}
Я не понимаю, что означает параметр userstata в функции обратного CUROPT_WRITEHEADER
вызова и другие параметры. Вам нужно передавать вашу структуру таким образом?
size_t header_callback(char *buffer, size_t size, size_t nitems, void *userdata) {
http_response_t *response = (http_response_t) userdata;
response->headers = buffer; // lets pretend that the headers key type in my struct is a char
return (int) response; // return the pointer address of the response
}
Я ожидаю, что смогу записать содержимое моей структуры, чтобы она заполнила все ее поля.
Ответ №1:
Я не понимаю, что означает параметр userstata в функции обратного вызова CUROPT_WRITEHEADER и другие параметры.
CUROPT_WRITEHEADER
не устанавливает функцию обратного вызова; CURLOPT_HEADERFUNCTION
делает это, чтобы функция получала данные заголовка (вместо этого вы можете установить CURLOPT_HEADER
, что заставляет CURLOPT_WRITEFUNCTION
обратный вызов получать все данные, заголовки и тело, но это более запутанно).
CUROPT_WRITEHEADER
по сути, это псевдоним для CURLOPT_HEADERDATA
; оба задают данные для передачи в качестве userdata
параметра CURLOPT_HEADERFUNCTION
(curl не устанавливает это сам; он передаст NULL, если вы его не установите). Это аналогично CURLOPT_WRITEDATA
for CURLOPT_WRITEFUNCTION
. Используйте его так, как CURLOPT_WRITEDATA
используется в примере кода:
void http_get(http_response_t *response, const char *url) {
//...
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, http_response_write);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, response);
Каждый заголовок передается отдельно функции обратного вызова заголовка, поэтому вам нужно где-то отслеживать количество заголовков ( http_response_t
это естественное место).
typedef struct {
//...
size_t nHeaders;
} http_response_t;
Что касается обработки заголовков, у вас есть это прямо там header_callback
, где вы приводите userdata
к a http_response_t
. Присвоение buffer
функции response->headers
не совсем правильное, но я предполагаю, что вы вставили это в качестве заполнителя на основе комментария «давайте притворимся». Возвращаемое значение обоих обратных вызовов должно быть количеством обработанных байтов.
Поскольку buffer
curl принадлежит и управляется curl, при анализе функции обратного buffer
вызова ей необходимо будет создать поля заголовка и скопировать данные заголовка. Это может быть передано некоторым другим функциям, которые вы создаете для этой цели (например http_header_set_key
, and http_header_set_value
), которые взяли бы на себя эту ответственность. Еще лучше, передайте ответственность за синтаксический анализ строки заголовка другой функции (которая сама будет вызывать функции, ответственные за создание и настройку полей):
// In the "http_response" library
// return non-0 if error
int http_header_set_key(char *buffer, size_t nChars, http_header_t *header);
// return non-0 if error
int http_header_set_value(char *buffer, size_t nChars, http_header_t *header);
// return bytes processed (should be == bufSize)
size_t http_header_parse_line(char *buffer, size_t bufSize, http_header_t *header);
//...
// In the client library
size_t header_callback(char *buffer, size_t size, size_t nItems, void *userdata) {
http_response_t *response = (http_response_t) userdata;
int iHdr = response->nHeaders;
if (iHdr >= MAX_HEADERS - 1) {
// Error: ran out of space in response to store header; signal this
// condition somehow.
// ...
// If signaling doesn't interrupt this function, must stop here
return 0;
}
int nProcessed = http_header_parse_line(buffer, size * nItems, response->headers iHdr);
if (nProcessed) {
// A header was added to response->headers.
response->nHeaders;
}
return nProcessed;
}
Обратите внимание, что buffer
это не завершается нулем, поэтому его размер передается http_header_parse_line
. Если вместо этого вы хотите http_header_parse_line
работать со строкой C, вам нужно сначала скопировать и завершить строку заголовка null buffer
; это может http_header_parse_line
немного упростить, но усложнит header_callback
и будет менее эффективным.
Комментарии:
1. Какую опцию мне нужно использовать, чтобы я мог установить обратный вызов для http_header_init ?
2. @ViniDalvino:
http_header_init
это был не обратный вызов, а предлагаемая функция, которую вы бы написали и вызвали из обратных вызовов. Надеюсь, обновление более четкое (и более конкретное в отношении дизайна).