#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "string-util.h" #include "auth.h" #define BUFFER_SIZE (256 * 1024) #define AUTH_HEADER_STR_PREFIX "Authorization: Bearer " int http_init() { return curl_global_init(CURL_GLOBAL_ALL); } void http_cleanup() { curl_global_cleanup(); return; } struct write_result { char *data; int pos; }; static size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) { struct write_result *result = (struct write_result *)stream; if (result->pos + size * nmemb >= BUFFER_SIZE - 1) { fprintf(stderr, "error: too small buffer\n"); return 0; } memcpy(result->data + result->pos, ptr, size * nmemb); result->pos += size * nmemb; return size * nmemb; } char *get_request(const char *url) { CURL *curl = NULL; CURLcode status; struct curl_slist *headers = NULL; char *data = NULL; long code; curl = curl_easy_init(); if (!curl) goto error; data = malloc(BUFFER_SIZE); if (!data) goto error; struct write_result write_result = {.data = data, .pos = 0}; if (auth_token) { char *auth_header_val = malloc(strlen(AUTH_HEADER_STR_PREFIX) + strlen(auth_token) + 1); strlcpy(auth_header_val, AUTH_HEADER_STR_PREFIX, sizeof(auth_header_val)); strlcat(auth_header_val, auth_token, sizeof(auth_header_val)); headers = curl_slist_append(headers, auth_header_val); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); status = curl_easy_perform(curl); if (status != 0) { fprintf(stderr, "get_request(): unable to request data from %s: %s\n", url, curl_easy_strerror(status)); goto error; } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if (code != 200) { fprintf(stderr, "error: server responded with code %ld\n", code); goto error; } data[write_result.pos] = '\0'; curl_slist_free_all(headers); curl_easy_cleanup(curl); return data; error: if (data) free(data); if (headers) curl_slist_free_all(headers); if (curl) curl_easy_cleanup(curl); return NULL; } char *post_request(const char *url, char *post_data) { CURL *curl = NULL; CURLcode status; struct curl_slist *headers = NULL; char *data = NULL; long code; curl = curl_easy_init(); if (!curl) goto error; data = malloc(BUFFER_SIZE); if (!data) goto error; struct write_result write_result = {.data = data, .pos = 0}; if (auth_token) { char *auth_header_val = malloc(strlen(AUTH_HEADER_STR_PREFIX) + strlen(auth_token) + 1); strlcpy(auth_header_val, AUTH_HEADER_STR_PREFIX, sizeof(auth_header_val)); strlcat(auth_header_val, auth_token, sizeof(auth_header_val)); headers = curl_slist_append(headers, auth_header_val); } if (post_data) { char *content_type_header_val = "Content-Type: application/json"; headers = curl_slist_append(headers, content_type_header_val); } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data); status = curl_easy_perform(curl); if (status != 0) { fprintf(stderr, "post_request(): unable to request data from %s: %s\n", url, curl_easy_strerror(status)); goto error; } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if (code != 200) { fprintf(stderr, "error: server responded with code %ld\n", code); goto error; } data[write_result.pos] = '\0'; curl_slist_free_all(headers); curl_easy_cleanup(curl); return data; error: if (data) free(data); if (headers) curl_slist_free_all(headers); if (curl) curl_easy_cleanup(curl); return NULL; } struct post_request_arg { char *url; char *post_data; void (*callback)(char *); }; void *post_one_url(void *arg) { struct post_request_arg *req_args = (struct post_request_arg *)arg; CURL *curl = NULL; CURLcode status; struct curl_slist *headers = NULL; char *data = NULL; long code; curl = curl_easy_init(); if (!curl) goto error; data = malloc(BUFFER_SIZE); if (!data) goto error; struct write_result write_result = {.data = data, .pos = 0}; if (auth_token) { char *auth_header_val = malloc(strlen(AUTH_HEADER_STR_PREFIX) + strlen(auth_token) + 1); strlcpy(auth_header_val, AUTH_HEADER_STR_PREFIX, sizeof(auth_header_val)); strlcat(auth_header_val, auth_token, sizeof(auth_header_val)); headers = curl_slist_append(headers, auth_header_val); } if (req_args->post_data) { char *content_type_header_val = "Content-Type: application/json"; headers = curl_slist_append(headers, content_type_header_val); } curl_easy_setopt(curl, CURLOPT_URL, req_args->url); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_result); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req_args->post_data); status = curl_easy_perform(curl); if (status != 0) { fprintf(stderr, "post_request(): unable to request data from %s: %s\n", req_args->url, curl_easy_strerror(status)); goto error; } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); if (code != 200) { fprintf(stderr, "post_request(): server responded with code %ld\n", code); goto error; } data[write_result.pos] = '\0'; curl_slist_free_all(headers); curl_easy_cleanup(curl); (*(req_args->callback))(data); return NULL; error: if (data) free(data); if (headers) curl_slist_free_all(headers); if (curl) curl_easy_cleanup(curl); (*(req_args->callback))(NULL); return NULL; } int http_post_async(char *url, char *post_data, void (*callback)(char *)) { struct post_request_arg *arg; pthread_t t; arg = calloc(1, sizeof(struct post_request_arg)); if (!arg) { err(1, "http_post_async(): calloc failed"); return -1; } arg->url = url; arg->post_data = post_data; arg->callback = callback; return pthread_create(&t, NULL, post_one_url, arg); }