diff options
Diffstat (limited to 'clients/cloud-setup/nm-http-client.c')
| -rw-r--r-- | clients/cloud-setup/nm-http-client.c | 1171 |
1 files changed, 593 insertions, 578 deletions
diff --git a/clients/cloud-setup/nm-http-client.c b/clients/cloud-setup/nm-http-client.c index b4260ffe..221e8c71 100644 --- a/clients/cloud-setup/nm-http-client.c +++ b/clients/cloud-setup/nm-http-client.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: LGPL-2.1+ +/* SPDX-License-Identifier: LGPL-2.1+ */ #include "nm-default.h" @@ -7,780 +7,795 @@ #include <curl/curl.h> #include "nm-cloud-setup-utils.h" +#include "nm-glib-aux/nm-str-buf.h" #define NM_CURL_DEBUG 0 /*****************************************************************************/ typedef struct { - GMainContext *context; - CURLM *mhandle; - GSource *mhandle_source_timeout; - GHashTable *source_sockets_hashtable; + GMainContext *context; + CURLM * mhandle; + GSource * mhandle_source_timeout; + GHashTable * source_sockets_hashtable; } NMHttpClientPrivate; struct _NMHttpClient { - GObject parent; - NMHttpClientPrivate _priv; + GObject parent; + NMHttpClientPrivate _priv; }; struct _NMHttpClientClass { - GObjectClass parent; + GObjectClass parent; }; -G_DEFINE_TYPE (NMHttpClient, nm_http_client, G_TYPE_OBJECT); +G_DEFINE_TYPE(NMHttpClient, nm_http_client, G_TYPE_OBJECT); #define NM_HTTP_CLIENT_GET_PRIVATE(self) _NM_GET_PRIVATE(self, NMHttpClient, NM_IS_HTTP_CLIENT) /*****************************************************************************/ -#define _NMLOG2(level, edata, ...) \ - G_STMT_START { \ - EHandleData *_edata = (edata); \ - \ - _NMLOG (level, \ - "http-request["NM_HASH_OBFUSCATE_PTR_FMT", \"%s\"]: " \ - _NM_UTILS_MACRO_FIRST (__VA_ARGS__), \ - NM_HASH_OBFUSCATE_PTR (_edata), \ - (_edata)->url \ - _NM_UTILS_MACRO_REST (__VA_ARGS__)); \ - } G_STMT_END +#define _NMLOG2(level, edata, ...) \ + G_STMT_START \ + { \ + EHandleData *_edata = (edata); \ + \ + _NMLOG(level, \ + "http-request[" NM_HASH_OBFUSCATE_PTR_FMT \ + ", \"%s\"]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + NM_HASH_OBFUSCATE_PTR(_edata), \ + (_edata)->url _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END /*****************************************************************************/ -G_LOCK_DEFINE_STATIC (_my_curl_initalized_lock); +G_LOCK_DEFINE_STATIC(_my_curl_initalized_lock); static bool _my_curl_initialized = FALSE; -__attribute__((destructor)) -static void -_my_curl_global_cleanup (void) +__attribute__((destructor)) static void +_my_curl_global_cleanup(void) { - G_LOCK (_my_curl_initalized_lock); - if (_my_curl_initialized) { - _my_curl_initialized = FALSE; - curl_global_cleanup (); - } - G_UNLOCK (_my_curl_initalized_lock); + G_LOCK(_my_curl_initalized_lock); + if (_my_curl_initialized) { + _my_curl_initialized = FALSE; + curl_global_cleanup(); + } + G_UNLOCK(_my_curl_initalized_lock); } static void -nm_http_client_curl_global_init (void) +nm_http_client_curl_global_init(void) { - G_LOCK (_my_curl_initalized_lock); - if (!_my_curl_initialized) { - _my_curl_initialized = TRUE; - if (curl_global_init (CURL_GLOBAL_ALL) != CURLE_OK) { - /* Even if this fails, we are partly initialized. WTF. */ - _LOGE ("curl: curl_global_init() failed!"); - } - } - G_UNLOCK (_my_curl_initalized_lock); + G_LOCK(_my_curl_initalized_lock); + if (!_my_curl_initialized) { + _my_curl_initialized = TRUE; + if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + /* Even if this fails, we are partly initialized. WTF. */ + _LOGE("curl: curl_global_init() failed!"); + } + } + G_UNLOCK(_my_curl_initalized_lock); } /*****************************************************************************/ GMainContext * -nm_http_client_get_main_context (NMHttpClient *self) +nm_http_client_get_main_context(NMHttpClient *self) { - g_return_val_if_fail (NM_IS_HTTP_CLIENT (self), NULL); + g_return_val_if_fail(NM_IS_HTTP_CLIENT(self), NULL); - return NM_HTTP_CLIENT_GET_PRIVATE (self)->context; + return NM_HTTP_CLIENT_GET_PRIVATE(self)->context; } /*****************************************************************************/ static GSource * -_source_attach (NMHttpClient *self, - GSource *source) +_source_attach(NMHttpClient *self, GSource *source) { - return nm_g_source_attach (source, NM_HTTP_CLIENT_GET_PRIVATE (self)->context); + return nm_g_source_attach(source, NM_HTTP_CLIENT_GET_PRIVATE(self)->context); } /*****************************************************************************/ typedef struct { - long response_code; - GBytes *response_data; + long response_code; + GBytes *response_data; } GetResult; static void -_get_result_free (gpointer data) +_get_result_free(gpointer data) { - GetResult *get_result = data; + GetResult *get_result = data; - g_bytes_unref (get_result->response_data); - nm_g_slice_free (get_result); + g_bytes_unref(get_result->response_data); + nm_g_slice_free(get_result); } typedef struct { - GTask *task; - GSource *timeout_source; - CURLcode ehandle_result; - CURL *ehandle; - char *url; - GString *recv_data; - struct curl_slist *headers; - gssize max_data; - gulong cancellable_id; + GTask * task; + GSource * timeout_source; + CURLcode ehandle_result; + CURL * ehandle; + char * url; + NMStrBuf recv_data; + struct curl_slist *headers; + gssize max_data; + gulong cancellable_id; } EHandleData; static void -_ehandle_free_ehandle (EHandleData *edata) +_ehandle_free_ehandle(EHandleData *edata) { - if (edata->ehandle) { - NMHttpClient *self = g_task_get_source_object (edata->task); - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); + if (edata->ehandle) { + NMHttpClient * self = g_task_get_source_object(edata->task); + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); - curl_multi_remove_handle (priv->mhandle, edata->ehandle); - curl_easy_cleanup (g_steal_pointer (&edata->ehandle)); - } + curl_multi_remove_handle(priv->mhandle, edata->ehandle); + curl_easy_cleanup(g_steal_pointer(&edata->ehandle)); + } } static void -_ehandle_free (EHandleData *edata) +_ehandle_free(EHandleData *edata) { - nm_assert (!edata->ehandle); - nm_assert (!edata->timeout_source); + nm_assert(!edata->ehandle); + nm_assert(!edata->timeout_source); - g_object_unref (edata->task); + g_object_unref(edata->task); - if (edata->recv_data) - g_string_free (edata->recv_data, TRUE); - if (edata->headers) - curl_slist_free_all (edata->headers); - g_free (edata->url); - nm_g_slice_free (edata); + nm_str_buf_destroy(&edata->recv_data); + if (edata->headers) + curl_slist_free_all(edata->headers); + g_free(edata->url); + nm_g_slice_free(edata); } static void -_ehandle_complete (EHandleData *edata, - GError *error_take) +_ehandle_complete(EHandleData *edata, GError *error_take) { - GetResult *get_result; - gs_free char *str_tmp_1 = NULL; - long response_code = -1; - - nm_clear_pointer (&edata->timeout_source, nm_g_source_destroy_and_unref); - - nm_clear_g_cancellable_disconnect (g_task_get_cancellable (edata->task), - &edata->cancellable_id); - - if (error_take) { - if (nm_utils_error_is_cancelled (error_take)) - _LOG2T (edata, "cancelled"); - else - _LOG2D (edata, "failed with %s", error_take->message); - } else if (edata->ehandle_result != CURLE_OK) { - _LOG2D (edata, "failed with curl error \"%s\"", curl_easy_strerror (edata->ehandle_result)); - nm_utils_error_set (&error_take, - NM_UTILS_ERROR_UNKNOWN, - "failed with curl error \"%s\"", - curl_easy_strerror (edata->ehandle_result)); - } - - if (error_take) { - _ehandle_free_ehandle (edata); - g_task_return_error (edata->task, error_take); - _ehandle_free (edata); - return; - } - - if (curl_easy_getinfo (edata->ehandle, - CURLINFO_RESPONSE_CODE, - &response_code) != CURLE_OK) - _LOG2E (edata, "failed to get response code from curl easy handle"); - - _LOG2D (edata, "success getting %"G_GSIZE_FORMAT" bytes (response code %ld)", - edata->recv_data->len, - response_code); - - _LOG2T (edata, "received %"G_GSIZE_FORMAT" bytes: [[%s]]", - edata->recv_data->len, - nm_utils_buf_utf8safe_escape (edata->recv_data->str, edata->recv_data->len, NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, &str_tmp_1)); - - _ehandle_free_ehandle (edata); - - get_result = g_slice_new (GetResult); - *get_result = (GetResult) { - .response_code = response_code, - /* This ensures that response_data is always NUL terminated. This is an important guarantee - * that NMHttpClient makes. */ - .response_data = g_string_free_to_bytes (g_steal_pointer (&edata->recv_data)), - }; - - g_task_return_pointer (edata->task, get_result, _get_result_free); - - _ehandle_free (edata); + GetResult * get_result; + gs_free char *str_tmp_1 = NULL; + long response_code = -1; + + nm_clear_pointer(&edata->timeout_source, nm_g_source_destroy_and_unref); + + nm_clear_g_cancellable_disconnect(g_task_get_cancellable(edata->task), &edata->cancellable_id); + + if (error_take) { + if (nm_utils_error_is_cancelled(error_take)) + _LOG2T(edata, "cancelled"); + else + _LOG2D(edata, "failed with %s", error_take->message); + } else if (edata->ehandle_result != CURLE_OK) { + _LOG2D(edata, "failed with curl error \"%s\"", curl_easy_strerror(edata->ehandle_result)); + nm_utils_error_set(&error_take, + NM_UTILS_ERROR_UNKNOWN, + "failed with curl error \"%s\"", + curl_easy_strerror(edata->ehandle_result)); + } + + if (error_take) { + _ehandle_free_ehandle(edata); + g_task_return_error(edata->task, error_take); + _ehandle_free(edata); + return; + } + + if (curl_easy_getinfo(edata->ehandle, CURLINFO_RESPONSE_CODE, &response_code) != CURLE_OK) + _LOG2E(edata, "failed to get response code from curl easy handle"); + + _LOG2D(edata, + "success getting %" G_GSIZE_FORMAT " bytes (response code %ld)", + edata->recv_data.len, + response_code); + + _LOG2T(edata, + "received %" G_GSIZE_FORMAT " bytes: [[%s]]", + edata->recv_data.len, + nm_utils_buf_utf8safe_escape(nm_str_buf_get_str(&edata->recv_data), + edata->recv_data.len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &str_tmp_1)); + + _ehandle_free_ehandle(edata); + + get_result = g_slice_new(GetResult); + *get_result = (GetResult){ + .response_code = response_code, + /* This ensures that response_data is always NUL terminated. This is an important guarantee + * that NMHttpClient makes. */ + .response_data = nm_str_buf_finalize_to_gbytes(&edata->recv_data), + }; + + g_task_return_pointer(edata->task, get_result, _get_result_free); + + _ehandle_free(edata); } /*****************************************************************************/ static size_t -_get_writefunction_cb (char *ptr, size_t size, size_t nmemb, void *user_data) +_get_writefunction_cb(char *ptr, size_t size, size_t nmemb, void *user_data) { - EHandleData *edata = user_data; - gsize nconsume; - - /* size should always be 1, but still. Multiply them to be sure. */ - nmemb *= size; - - if (edata->max_data >= 0) { - nm_assert (edata->recv_data->len <= edata->max_data); - nconsume = (((gsize) edata->max_data) - edata->recv_data->len); - if (nconsume > nmemb) - nconsume = nmemb; - } else - nconsume = nmemb; - - g_string_append_len (edata->recv_data, ptr, nconsume); - return nconsume; + EHandleData *edata = user_data; + gsize nconsume; + + /* size should always be 1, but still. Multiply them to be sure. */ + nmemb *= size; + + if (edata->max_data >= 0) { + nm_assert(edata->recv_data.len <= edata->max_data); + nconsume = (((gsize) edata->max_data) - edata->recv_data.len); + if (nconsume > nmemb) + nconsume = nmemb; + } else + nconsume = nmemb; + + nm_str_buf_append_len(&edata->recv_data, ptr, nconsume); + return nconsume; } static gboolean -_get_timeout_cb (gpointer user_data) +_get_timeout_cb(gpointer user_data) { - _ehandle_complete (user_data, - g_error_new_literal (NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "HTTP request timed out")); - return G_SOURCE_REMOVE; + _ehandle_complete( + user_data, + g_error_new_literal(NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, "HTTP request timed out")); + return G_SOURCE_REMOVE; } static void -_get_cancelled_cb (GObject *object, gpointer user_data) +_get_cancelled_cb(GObject *object, gpointer user_data) { - EHandleData *edata = user_data; - GError *error = NULL; + EHandleData *edata = user_data; + GError * error = NULL; - nm_clear_g_signal_handler (g_task_get_cancellable (edata->task), - &edata->cancellable_id); - nm_utils_error_set_cancelled (&error, FALSE, NULL); - _ehandle_complete (edata, error); + nm_clear_g_signal_handler(g_task_get_cancellable(edata->task), &edata->cancellable_id); + nm_utils_error_set_cancelled(&error, FALSE, NULL); + _ehandle_complete(edata, error); } void -nm_http_client_get (NMHttpClient *self, - const char *url, - int timeout_msec, - gssize max_data, - const char *const *http_headers, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +nm_http_client_get(NMHttpClient * self, + const char * url, + int timeout_msec, + gssize max_data, + const char *const * http_headers, + GCancellable * cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - NMHttpClientPrivate *priv; - EHandleData *edata; - guint i; - - g_return_if_fail (NM_IS_HTTP_CLIENT (self)); - g_return_if_fail (url); - g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); - g_return_if_fail (timeout_msec >= 0); - g_return_if_fail (max_data >= -1); - - priv = NM_HTTP_CLIENT_GET_PRIVATE (self); - - edata = g_slice_new (EHandleData); - *edata = (EHandleData) { - .task = nm_g_task_new (self, cancellable, nm_http_client_get, callback, user_data), - .recv_data = g_string_sized_new (NM_MIN (max_data, 245)), - .max_data = max_data, - .url = g_strdup (url), - .headers = NULL, - }; - - nmcs_wait_for_objects_register (edata->task); - - _LOG2D (edata, "start get ..."); - - edata->ehandle = curl_easy_init (); - if (!edata->ehandle) { - _ehandle_complete (edata, - g_error_new_literal (NM_UTILS_ERROR, - NM_UTILS_ERROR_UNKNOWN, - "HTTP request failed to create curl handle")); - return; - } - - curl_easy_setopt (edata->ehandle, CURLOPT_URL, url); - - curl_easy_setopt (edata->ehandle, CURLOPT_WRITEFUNCTION, _get_writefunction_cb); - curl_easy_setopt (edata->ehandle, CURLOPT_WRITEDATA, edata); - curl_easy_setopt (edata->ehandle, CURLOPT_PRIVATE, edata); - - if (http_headers) { - for (i = 0; http_headers[i]; ++i) { - struct curl_slist *tmp; - - tmp = curl_slist_append (edata->headers, - http_headers[i]); - if (!tmp) { - curl_slist_free_all (tmp); - _LOGE ("curl: curl_slist_append() failed adding %s", http_headers[i]); - continue; - } - edata->headers = tmp; - } - - curl_easy_setopt (edata->ehandle, CURLOPT_HTTPHEADER, edata->headers); - } - - if (timeout_msec > 0) { - edata->timeout_source = _source_attach (self, - nm_g_timeout_source_new (timeout_msec, - G_PRIORITY_DEFAULT, - _get_timeout_cb, - edata, - NULL)); - } - - curl_multi_add_handle (priv->mhandle, edata->ehandle); - - if (cancellable) { - gulong signal_id; - - signal_id = g_cancellable_connect (cancellable, - G_CALLBACK (_get_cancelled_cb), - edata, - NULL); - if (signal_id == 0) { - /* the request is already cancelled. Return. */ - return; - } - edata->cancellable_id = signal_id; - } + NMHttpClientPrivate *priv; + EHandleData * edata; + guint i; + + g_return_if_fail(NM_IS_HTTP_CLIENT(self)); + g_return_if_fail(url); + g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(timeout_msec >= 0); + g_return_if_fail(max_data >= -1); + + priv = NM_HTTP_CLIENT_GET_PRIVATE(self); + + edata = g_slice_new(EHandleData); + *edata = (EHandleData){ + .task = nm_g_task_new(self, cancellable, nm_http_client_get, callback, user_data), + .recv_data = NM_STR_BUF_INIT(0, FALSE), + .max_data = max_data, + .url = g_strdup(url), + .headers = NULL, + }; + + nmcs_wait_for_objects_register(edata->task); + + _LOG2D(edata, "start get ..."); + + edata->ehandle = curl_easy_init(); + if (!edata->ehandle) { + _ehandle_complete(edata, + g_error_new_literal(NM_UTILS_ERROR, + NM_UTILS_ERROR_UNKNOWN, + "HTTP request failed to create curl handle")); + return; + } + + curl_easy_setopt(edata->ehandle, CURLOPT_URL, url); + + curl_easy_setopt(edata->ehandle, CURLOPT_WRITEFUNCTION, _get_writefunction_cb); + curl_easy_setopt(edata->ehandle, CURLOPT_WRITEDATA, edata); + curl_easy_setopt(edata->ehandle, CURLOPT_PRIVATE, edata); + + if (http_headers) { + for (i = 0; http_headers[i]; ++i) { + struct curl_slist *tmp; + + tmp = curl_slist_append(edata->headers, http_headers[i]); + if (!tmp) { + curl_slist_free_all(tmp); + _LOGE("curl: curl_slist_append() failed adding %s", http_headers[i]); + continue; + } + edata->headers = tmp; + } + + curl_easy_setopt(edata->ehandle, CURLOPT_HTTPHEADER, edata->headers); + } + + if (timeout_msec > 0) { + edata->timeout_source = _source_attach(self, + nm_g_timeout_source_new(timeout_msec, + G_PRIORITY_DEFAULT, + _get_timeout_cb, + edata, + NULL)); + } + + curl_multi_add_handle(priv->mhandle, edata->ehandle); + + if (cancellable) { + gulong signal_id; + + signal_id = g_cancellable_connect(cancellable, G_CALLBACK(_get_cancelled_cb), edata, NULL); + if (signal_id == 0) { + /* the request is already cancelled. Return. */ + return; + } + edata->cancellable_id = signal_id; + } } +/** + * nm_http_client_get_finish: + * @self: the #NMHttpClient instance + * @result: the #GAsyncResult which to complete. + * @out_response_code: (allow-none) (out): the HTTP response code or -1 on other error. + * @out_response_data: (allow-none) (transfer full): the HTTP response data, if any. + * The GBytes buffer is guaranteed to have a trailing NUL character *after* the + * returned buffer size. That means, you can always trust that the buffer is NUL terminated + * and that there is one additional hidden byte after the data. + * Also, the returned buffer is allocated just for you. While GBytes is immutable, you are + * allowed to modify the buffer as it's not used by anybody else. + * @error: the error + * + * Returns: %TRUE on success or %FALSE with an error code. + */ gboolean -nm_http_client_get_finish (NMHttpClient *self, - GAsyncResult *result, - long *out_response_code, - GBytes **out_response_data, - GError **error) +nm_http_client_get_finish(NMHttpClient *self, + GAsyncResult *result, + long * out_response_code, + GBytes ** out_response_data, + GError ** error) { - GetResult *get_result; + GetResult *get_result; + + g_return_val_if_fail(NM_IS_HTTP_CLIENT(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_http_client_get), FALSE); - g_return_val_if_fail (NM_IS_HTTP_CLIENT (self), FALSE); - g_return_val_if_fail (nm_g_task_is_valid (result, self, nm_http_client_get), FALSE); + get_result = g_task_propagate_pointer(G_TASK(result), error); - get_result = g_task_propagate_pointer (G_TASK (result), error); - if (!get_result) { - NM_SET_OUT (out_response_code, -1); - NM_SET_OUT (out_response_data, NULL); - return FALSE; - } + nm_assert(!error || (!!get_result) == (!*error)); - NM_SET_OUT (out_response_code, get_result->response_code); + if (!get_result) { + NM_SET_OUT(out_response_code, -1); + NM_SET_OUT(out_response_data, NULL); + return FALSE; + } - /* response_data is binary, but is also guaranteed to be NUL terminated! */ - NM_SET_OUT (out_response_data, g_steal_pointer (&get_result->response_data)); + NM_SET_OUT(out_response_code, get_result->response_code); - _get_result_free (get_result); + /* response_data is binary, but is also guaranteed to be NUL terminated! */ + NM_SET_OUT(out_response_data, g_steal_pointer(&get_result->response_data)); - return TRUE; + _get_result_free(get_result); + return TRUE; } /*****************************************************************************/ typedef struct { - GTask *task; - char *uri; - const char *const *http_headers; - NMHttpClientPollGetCheckFcn check_fcn; - gpointer check_user_data; - GBytes *response_data; - gsize request_max_data; - long response_code; - int request_timeout_ms; + GTask * task; + char * uri; + const char *const * http_headers; + NMHttpClientPollGetCheckFcn check_fcn; + gpointer check_user_data; + GBytes * response_data; + gsize request_max_data; + long response_code; + int request_timeout_ms; } PollGetData; static void -_poll_get_data_free (gpointer data) +_poll_get_data_free(gpointer data) { - PollGetData *poll_get_data = data; + PollGetData *poll_get_data = data; - g_free (poll_get_data->uri); + g_free(poll_get_data->uri); - nm_clear_pointer (&poll_get_data->response_data, g_bytes_unref); - g_strfreev ((char **) poll_get_data->http_headers); + nm_clear_pointer(&poll_get_data->response_data, g_bytes_unref); + g_strfreev((char **) poll_get_data->http_headers); - nm_g_slice_free (poll_get_data); + nm_g_slice_free(poll_get_data); } static void -_poll_get_probe_start_fcn (GCancellable *cancellable, - gpointer probe_user_data, - GAsyncReadyCallback callback, - gpointer user_data) +_poll_get_probe_start_fcn(GCancellable * cancellable, + gpointer probe_user_data, + GAsyncReadyCallback callback, + gpointer user_data) { - PollGetData *poll_get_data = probe_user_data; - - /* balanced by _poll_get_probe_finish_fcn() */ - g_object_ref (poll_get_data->task); - - nm_http_client_get (g_task_get_source_object (poll_get_data->task), - poll_get_data->uri, - poll_get_data->request_timeout_ms, - poll_get_data->request_max_data, - poll_get_data->http_headers, - cancellable, - callback, - user_data); + PollGetData *poll_get_data = probe_user_data; + + /* balanced by _poll_get_probe_finish_fcn() */ + g_object_ref(poll_get_data->task); + + nm_http_client_get(g_task_get_source_object(poll_get_data->task), + poll_get_data->uri, + poll_get_data->request_timeout_ms, + poll_get_data->request_max_data, + poll_get_data->http_headers, + cancellable, + callback, + user_data); } static gboolean -_poll_get_probe_finish_fcn (GObject *source, - GAsyncResult *result, - gpointer probe_user_data, - GError **error) +_poll_get_probe_finish_fcn(GObject * source, + GAsyncResult *result, + gpointer probe_user_data, + GError ** error) { - PollGetData *poll_get_data = probe_user_data; - _nm_unused gs_unref_object GTask *task = poll_get_data->task; /* balance ref from _poll_get_probe_start_fcn() */ - gboolean success; - gs_free_error GError *local_error = NULL; - gs_unref_bytes GBytes *response_data = NULL; - long response_code = -1; - - success = nm_http_client_get_finish (g_task_get_source_object (poll_get_data->task), - result, - &response_code, - &response_data, - &local_error); - - if (!success) { - if (nm_utils_error_is_cancelled (local_error)) { - g_propagate_error (error, g_steal_pointer (&local_error)); - return TRUE; - } - return FALSE; - } - - if (poll_get_data->check_fcn) { - success = poll_get_data->check_fcn (response_code, - response_data, - poll_get_data->check_user_data, - &local_error); - } else - success = (response_code == 200); - - if (local_error) { - g_propagate_error (error, g_steal_pointer (&local_error)); - return TRUE; - } - - if (!success) - return FALSE; - - poll_get_data->response_code = response_code; - poll_get_data->response_data = g_steal_pointer (&response_data); - return TRUE; + PollGetData * poll_get_data = probe_user_data; + _nm_unused gs_unref_object GTask *task = + poll_get_data->task; /* balance ref from _poll_get_probe_start_fcn() */ + gboolean success; + gs_free_error GError *local_error = NULL; + gs_unref_bytes GBytes *response_data = NULL; + long response_code = -1; + + success = nm_http_client_get_finish(g_task_get_source_object(poll_get_data->task), + result, + &response_code, + &response_data, + &local_error); + + nm_assert((!!success) == (!local_error)); + + if (local_error) { + if (nm_utils_error_is_cancelled(local_error)) { + g_propagate_error(error, g_steal_pointer(&local_error)); + return TRUE; + } + /* any other error. Continue polling. */ + return FALSE; + } + + if (poll_get_data->check_fcn) { + success = poll_get_data->check_fcn(response_code, + response_data, + poll_get_data->check_user_data, + &local_error); + } else + success = (response_code == 200); + + if (local_error) { + g_propagate_error(error, g_steal_pointer(&local_error)); + return TRUE; + } + + if (!success) { + /* Not yet ready. Continue polling. */ + return FALSE; + } + + poll_get_data->response_code = response_code; + poll_get_data->response_data = g_steal_pointer(&response_data); + return TRUE; } static void -_poll_get_done_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) +_poll_get_done_cb(GObject *source, GAsyncResult *result, gpointer user_data) { - PollGetData *poll_get_data = user_data; - gs_free_error GError *error = NULL; - gboolean success; + PollGetData * poll_get_data = user_data; + gs_free_error GError *error = NULL; + gboolean success; + + success = nmcs_utils_poll_finish(result, NULL, &error); - success = nmcs_utils_poll_finish (result, NULL, &error); + nm_assert((!!success) == (!error)); - if (error) - g_task_return_error (poll_get_data->task, g_steal_pointer (&error)); - else - g_task_return_boolean (poll_get_data->task, success); + if (error) + g_task_return_error(poll_get_data->task, g_steal_pointer(&error)); + else + g_task_return_boolean(poll_get_data->task, TRUE); - g_object_unref (poll_get_data->task); + g_object_unref(poll_get_data->task); } void -nm_http_client_poll_get (NMHttpClient *self, - const char *uri, - int request_timeout_ms, - gssize request_max_data, - int poll_timeout_ms, - int ratelimit_timeout_ms, - const char *const *http_headers, - GCancellable *cancellable, - NMHttpClientPollGetCheckFcn check_fcn, - gpointer check_user_data, - GAsyncReadyCallback callback, - gpointer user_data) +nm_http_client_poll_get(NMHttpClient * self, + const char * uri, + int request_timeout_ms, + gssize request_max_data, + int poll_timeout_ms, + int ratelimit_timeout_ms, + const char *const * http_headers, + GCancellable * cancellable, + NMHttpClientPollGetCheckFcn check_fcn, + gpointer check_user_data, + GAsyncReadyCallback callback, + gpointer user_data) { - nm_auto_pop_gmaincontext GMainContext *context = NULL; - PollGetData *poll_get_data; - - g_return_if_fail (NM_IS_HTTP_CLIENT (self)); - g_return_if_fail (uri && uri[0]); - g_return_if_fail (request_timeout_ms >= -1); - g_return_if_fail (request_max_data >= -1); - g_return_if_fail (poll_timeout_ms >= -1); - g_return_if_fail (ratelimit_timeout_ms >= -1); - g_return_if_fail (!cancellable || G_CANCELLABLE (cancellable)); - - poll_get_data = g_slice_new (PollGetData); - *poll_get_data = (PollGetData) { - .task = nm_g_task_new (self, cancellable, nm_http_client_poll_get, callback, user_data), - .uri = g_strdup (uri), - .request_timeout_ms = request_timeout_ms, - .request_max_data = request_max_data, - .check_fcn = check_fcn, - .check_user_data = check_user_data, - .response_code = -1, - .http_headers = NM_CAST_STRV_CC (g_strdupv ((char **) http_headers)), - }; - - nmcs_wait_for_objects_register (poll_get_data->task); - - g_task_set_task_data (poll_get_data->task, - poll_get_data, - _poll_get_data_free); - - context = nm_g_main_context_push_thread_default_if_necessary (nm_http_client_get_main_context (self)); - - nmcs_utils_poll (poll_timeout_ms, - ratelimit_timeout_ms, - 0, - _poll_get_probe_start_fcn, - _poll_get_probe_finish_fcn, - poll_get_data, - cancellable, - _poll_get_done_cb, - poll_get_data); + nm_auto_pop_gmaincontext GMainContext *context = NULL; + PollGetData * poll_get_data; + + g_return_if_fail(NM_IS_HTTP_CLIENT(self)); + g_return_if_fail(uri && uri[0]); + g_return_if_fail(request_timeout_ms >= -1); + g_return_if_fail(request_max_data >= -1); + g_return_if_fail(poll_timeout_ms >= -1); + g_return_if_fail(ratelimit_timeout_ms >= -1); + g_return_if_fail(!cancellable || G_CANCELLABLE(cancellable)); + + poll_get_data = g_slice_new(PollGetData); + *poll_get_data = (PollGetData){ + .task = nm_g_task_new(self, cancellable, nm_http_client_poll_get, callback, user_data), + .uri = g_strdup(uri), + .request_timeout_ms = request_timeout_ms, + .request_max_data = request_max_data, + .check_fcn = check_fcn, + .check_user_data = check_user_data, + .response_code = -1, + .http_headers = NM_CAST_STRV_CC(g_strdupv((char **) http_headers)), + }; + + nmcs_wait_for_objects_register(poll_get_data->task); + + g_task_set_task_data(poll_get_data->task, poll_get_data, _poll_get_data_free); + + context = + nm_g_main_context_push_thread_default_if_necessary(nm_http_client_get_main_context(self)); + + nmcs_utils_poll(poll_timeout_ms, + ratelimit_timeout_ms, + 0, + _poll_get_probe_start_fcn, + _poll_get_probe_finish_fcn, + poll_get_data, + cancellable, + _poll_get_done_cb, + poll_get_data); } gboolean -nm_http_client_poll_get_finish (NMHttpClient *self, - GAsyncResult *result, - long *out_response_code, - GBytes **out_response_data, - GError **error) +nm_http_client_poll_get_finish(NMHttpClient *self, + GAsyncResult *result, + long * out_response_code, + GBytes ** out_response_data, + GError ** error) { - PollGetData *poll_get_data; - GTask *task; - gboolean success; - gs_free_error GError *local_error = NULL; + PollGetData * poll_get_data; + GTask * task; + gboolean success; + gs_free_error GError *local_error = NULL; + + g_return_val_if_fail(NM_HTTP_CLIENT(self), FALSE); + g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_http_client_poll_get), FALSE); - g_return_val_if_fail (NM_HTTP_CLIENT (self), FALSE); - g_return_val_if_fail (nm_g_task_is_valid (result, self, nm_http_client_poll_get), FALSE); + task = G_TASK(result); - task = G_TASK (result); + success = g_task_propagate_boolean(task, &local_error); - success = g_task_propagate_boolean (task, &local_error); - if ( local_error - || !success) { - if (local_error) - g_propagate_error (error, g_steal_pointer (&local_error)); - NM_SET_OUT (out_response_code, -1); - NM_SET_OUT (out_response_data, NULL); - return FALSE; - } + nm_assert((!!success) == (!local_error)); - poll_get_data = g_task_get_task_data (task); + if (local_error) { + g_propagate_error(error, g_steal_pointer(&local_error)); + NM_SET_OUT(out_response_code, -1); + NM_SET_OUT(out_response_data, NULL); + return FALSE; + } - NM_SET_OUT (out_response_code, poll_get_data->response_code); - NM_SET_OUT (out_response_data, g_steal_pointer (&poll_get_data->response_data)); + poll_get_data = g_task_get_task_data(task); - return TRUE; + NM_SET_OUT(out_response_code, poll_get_data->response_code); + NM_SET_OUT(out_response_data, g_steal_pointer(&poll_get_data->response_data)); + return TRUE; } /*****************************************************************************/ static void -_mhandle_action (NMHttpClient *self, - int sockfd, - int ev_bitmask) +_mhandle_action(NMHttpClient *self, int sockfd, int ev_bitmask) { - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); - EHandleData *edata; - CURLMsg *msg; - CURLcode eret; - int m_left; - CURLMcode ret; - int running_handles; - - ret = curl_multi_socket_action (priv->mhandle, sockfd, ev_bitmask, &running_handles); - if (ret != CURLM_OK) { - _LOGE ("curl: curl_multi_socket_action() failed: (%d) %s", ret, curl_multi_strerror (ret)); - /* really unexpected. Not clear how to handle this. */ - } - - while ((msg = curl_multi_info_read (priv->mhandle, &m_left))) { - - if (msg->msg != CURLMSG_DONE) - continue; - - eret = curl_easy_getinfo (msg->easy_handle, CURLINFO_PRIVATE, (char **) &edata); - - nm_assert (eret == CURLE_OK); - nm_assert (edata); - - edata->ehandle_result = msg->data.result; - _ehandle_complete (edata, NULL); - } + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); + EHandleData * edata; + CURLMsg * msg; + CURLcode eret; + int m_left; + CURLMcode ret; + int running_handles; + + ret = curl_multi_socket_action(priv->mhandle, sockfd, ev_bitmask, &running_handles); + if (ret != CURLM_OK) { + _LOGE("curl: curl_multi_socket_action() failed: (%d) %s", ret, curl_multi_strerror(ret)); + /* really unexpected. Not clear how to handle this. */ + } + + while ((msg = curl_multi_info_read(priv->mhandle, &m_left))) { + if (msg->msg != CURLMSG_DONE) + continue; + + eret = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char **) &edata); + + nm_assert(eret == CURLE_OK); + nm_assert(edata); + + edata->ehandle_result = msg->data.result; + _ehandle_complete(edata, NULL); + } } static gboolean -_mhandle_socket_cb (int fd, - GIOCondition condition, - gpointer user_data) +_mhandle_socket_cb(int fd, GIOCondition condition, gpointer user_data) { - int ev_bitmask = 0; + int ev_bitmask = 0; - if (condition & G_IO_IN) - ev_bitmask |= CURL_CSELECT_IN; - if (condition & G_IO_OUT) - ev_bitmask |= CURL_CSELECT_OUT; - if (condition & G_IO_ERR) - ev_bitmask |= CURL_CSELECT_ERR; + if (condition & G_IO_IN) + ev_bitmask |= CURL_CSELECT_IN; + if (condition & G_IO_OUT) + ev_bitmask |= CURL_CSELECT_OUT; + if (condition & G_IO_ERR) + ev_bitmask |= CURL_CSELECT_ERR; - _mhandle_action (user_data, fd, ev_bitmask); - return G_SOURCE_CONTINUE; + _mhandle_action(user_data, fd, ev_bitmask); + return G_SOURCE_CONTINUE; } static int -_mhandle_socketfunction_cb (CURL *e_handle, curl_socket_t fd, int what, void *user_data, void *socketp) +_mhandle_socketfunction_cb(CURL * e_handle, + curl_socket_t fd, + int what, + void * user_data, + void * socketp) { - GSource *source_socket; - NMHttpClient *self = user_data; - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); - - (void) _NM_ENSURE_TYPE (int, fd); - - g_hash_table_remove (priv->source_sockets_hashtable, GINT_TO_POINTER (fd)); - - if (what != CURL_POLL_REMOVE) { - GIOCondition condition = 0; - - if (what == CURL_POLL_IN) - condition = G_IO_IN; - else if (what == CURL_POLL_OUT) - condition = G_IO_OUT; - else if (what == CURL_POLL_INOUT) - condition = G_IO_IN | G_IO_OUT; - else - condition = 0; - - if (condition) { - source_socket = nm_g_unix_fd_source_new (fd, - condition, - G_PRIORITY_DEFAULT, - _mhandle_socket_cb, - self, - NULL); - g_source_attach (source_socket, priv->context); - - g_hash_table_insert (priv->source_sockets_hashtable, - GINT_TO_POINTER (fd), - source_socket); - } - } - - return CURLM_OK; + GSource * source_socket; + NMHttpClient * self = user_data; + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); + + (void) _NM_ENSURE_TYPE(int, fd); + + g_hash_table_remove(priv->source_sockets_hashtable, GINT_TO_POINTER(fd)); + + if (what != CURL_POLL_REMOVE) { + GIOCondition condition = 0; + + if (what == CURL_POLL_IN) + condition = G_IO_IN; + else if (what == CURL_POLL_OUT) + condition = G_IO_OUT; + else if (what == CURL_POLL_INOUT) + condition = G_IO_IN | G_IO_OUT; + else + condition = 0; + + if (condition) { + source_socket = nm_g_unix_fd_source_new(fd, + condition, + G_PRIORITY_DEFAULT, + _mhandle_socket_cb, + self, + NULL); + g_source_attach(source_socket, priv->context); + + g_hash_table_insert(priv->source_sockets_hashtable, GINT_TO_POINTER(fd), source_socket); + } + } + + return CURLM_OK; } static gboolean -_mhandle_timeout_cb (gpointer user_data) +_mhandle_timeout_cb(gpointer user_data) { - _mhandle_action (user_data, CURL_SOCKET_TIMEOUT, 0); - return G_SOURCE_REMOVE; + _mhandle_action(user_data, CURL_SOCKET_TIMEOUT, 0); + return G_SOURCE_REMOVE; } static int -_mhandle_timerfunction_cb (CURLM *multi, long timeout_msec, void *user_data) +_mhandle_timerfunction_cb(CURLM *multi, long timeout_msec, void *user_data) { - NMHttpClient *self = user_data; - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); - - nm_clear_pointer (&priv->mhandle_source_timeout, nm_g_source_destroy_and_unref); - if (timeout_msec >= 0) { - priv->mhandle_source_timeout = _source_attach (self, - nm_g_timeout_source_new (NM_MIN (timeout_msec, G_MAXINT), - G_PRIORITY_DEFAULT, - _mhandle_timeout_cb, - self, - NULL)); - } - return 0; + NMHttpClient * self = user_data; + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); + + nm_clear_pointer(&priv->mhandle_source_timeout, nm_g_source_destroy_and_unref); + if (timeout_msec >= 0) { + priv->mhandle_source_timeout = + _source_attach(self, + nm_g_timeout_source_new(NM_MIN(timeout_msec, G_MAXINT), + G_PRIORITY_DEFAULT, + _mhandle_timeout_cb, + self, + NULL)); + } + return 0; } /*****************************************************************************/ static void -nm_http_client_init (NMHttpClient *self) +nm_http_client_init(NMHttpClient *self) { - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); - priv->source_sockets_hashtable = g_hash_table_new_full (nm_direct_hash, - NULL, - NULL, - (GDestroyNotify) nm_g_source_destroy_and_unref); + priv->source_sockets_hashtable = + g_hash_table_new_full(nm_direct_hash, + NULL, + NULL, + (GDestroyNotify) nm_g_source_destroy_and_unref); } static void -constructed (GObject *object) +constructed(GObject *object) { - NMHttpClient *self = NM_HTTP_CLIENT (object); - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); - - priv->context = g_main_context_ref_thread_default (); - - priv->mhandle = curl_multi_init (); - if (!priv->mhandle) - _LOGE ("curl: failed to create multi-handle"); - else { - curl_multi_setopt (priv->mhandle, CURLMOPT_SOCKETFUNCTION, _mhandle_socketfunction_cb); - curl_multi_setopt (priv->mhandle, CURLMOPT_SOCKETDATA, self); - curl_multi_setopt (priv->mhandle, CURLMOPT_TIMERFUNCTION, _mhandle_timerfunction_cb); - curl_multi_setopt (priv->mhandle, CURLMOPT_TIMERDATA, self); - } - - G_OBJECT_CLASS (nm_http_client_parent_class)->constructed (object); + NMHttpClient * self = NM_HTTP_CLIENT(object); + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); + + priv->context = g_main_context_ref_thread_default(); + + priv->mhandle = curl_multi_init(); + if (!priv->mhandle) + _LOGE("curl: failed to create multi-handle"); + else { + curl_multi_setopt(priv->mhandle, CURLMOPT_SOCKETFUNCTION, _mhandle_socketfunction_cb); + curl_multi_setopt(priv->mhandle, CURLMOPT_SOCKETDATA, self); + curl_multi_setopt(priv->mhandle, CURLMOPT_TIMERFUNCTION, _mhandle_timerfunction_cb); + curl_multi_setopt(priv->mhandle, CURLMOPT_TIMERDATA, self); + } + + G_OBJECT_CLASS(nm_http_client_parent_class)->constructed(object); } NMHttpClient * -nm_http_client_new (void) +nm_http_client_new(void) { - return g_object_new (NM_TYPE_HTTP_CLIENT, NULL); + return g_object_new(NM_TYPE_HTTP_CLIENT, NULL); } static void -dispose (GObject *object) +dispose(GObject *object) { - NMHttpClient *self = NM_HTTP_CLIENT (object); - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); + NMHttpClient * self = NM_HTTP_CLIENT(object); + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); - nm_clear_pointer (&priv->mhandle, curl_multi_cleanup); - nm_clear_pointer (&priv->source_sockets_hashtable, g_hash_table_unref); + nm_clear_pointer(&priv->mhandle, curl_multi_cleanup); + nm_clear_pointer(&priv->source_sockets_hashtable, g_hash_table_unref); - nm_clear_g_source_inst (&priv->mhandle_source_timeout); + nm_clear_g_source_inst(&priv->mhandle_source_timeout); - G_OBJECT_CLASS (nm_http_client_parent_class)->dispose (object); + G_OBJECT_CLASS(nm_http_client_parent_class)->dispose(object); } static void -finalize (GObject *object) +finalize(GObject *object) { - NMHttpClient *self = NM_HTTP_CLIENT (object); - NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE (self); + NMHttpClient * self = NM_HTTP_CLIENT(object); + NMHttpClientPrivate *priv = NM_HTTP_CLIENT_GET_PRIVATE(self); - G_OBJECT_CLASS (nm_http_client_parent_class)->finalize (object); + G_OBJECT_CLASS(nm_http_client_parent_class)->finalize(object); - g_main_context_unref (priv->context); + g_main_context_unref(priv->context); - curl_global_cleanup (); + curl_global_cleanup(); } static void -nm_http_client_class_init (NMHttpClientClass *klass) +nm_http_client_class_init(NMHttpClientClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->constructed = constructed; - object_class->dispose = dispose; - object_class->finalize = finalize; + object_class->constructed = constructed; + object_class->dispose = dispose; + object_class->finalize = finalize; - nm_http_client_curl_global_init (); + nm_http_client_curl_global_init(); } |