summary refs log tree commit diff
path: root/clients/cloud-setup/nm-http-client.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /clients/cloud-setup/nm-http-client.c
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'clients/cloud-setup/nm-http-client.c')
-rw-r--r--clients/cloud-setup/nm-http-client.c801
1 files changed, 0 insertions, 801 deletions
diff --git a/clients/cloud-setup/nm-http-client.c b/clients/cloud-setup/nm-http-client.c
deleted file mode 100644
index 144513fc..00000000
--- a/clients/cloud-setup/nm-http-client.c
+++ /dev/null
@@ -1,801 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include "libnm/nm-default-client.h"
-
-#include "nm-http-client.h"
-
-#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;
-} NMHttpClientPrivate;
-
-struct _NMHttpClient {
-    GObject             parent;
-    NMHttpClientPrivate _priv;
-};
-
-struct _NMHttpClientClass {
-    GObjectClass parent;
-};
-
-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
-
-/*****************************************************************************/
-
-G_LOCK_DEFINE_STATIC(_my_curl_initalized_lock);
-static bool _my_curl_initialized = FALSE;
-
-__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);
-}
-
-static 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);
-}
-
-/*****************************************************************************/
-
-GMainContext *
-nm_http_client_get_main_context(NMHttpClient *self)
-{
-    g_return_val_if_fail(NM_IS_HTTP_CLIENT(self), NULL);
-
-    return NM_HTTP_CLIENT_GET_PRIVATE(self)->context;
-}
-
-/*****************************************************************************/
-
-static GSource *
-_source_attach(NMHttpClient *self, GSource *source)
-{
-    return nm_g_source_attach(source, NM_HTTP_CLIENT_GET_PRIVATE(self)->context);
-}
-
-/*****************************************************************************/
-
-typedef struct {
-    long    response_code;
-    GBytes *response_data;
-} GetResult;
-
-static void
-_get_result_free(gpointer data)
-{
-    GetResult *get_result = data;
-
-    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;
-    NMStrBuf           recv_data;
-    struct curl_slist *headers;
-    gssize             max_data;
-    gulong             cancellable_id;
-} EHandleData;
-
-static void
-_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);
-
-        curl_multi_remove_handle(priv->mhandle, edata->ehandle);
-        curl_easy_cleanup(g_steal_pointer(&edata->ehandle));
-    }
-}
-
-static void
-_ehandle_free(EHandleData *edata)
-{
-    nm_assert(!edata->ehandle);
-    nm_assert(!edata->timeout_source);
-
-    g_object_unref(edata->task);
-
-    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)
-{
-    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)
-{
-    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)
-{
-    _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)
-{
-    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);
-}
-
-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)
-{
-    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)
-{
-    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);
-
-    get_result = g_task_propagate_pointer(G_TASK(result), error);
-
-    nm_assert(!error || (!!get_result) == (!*error));
-
-    if (!get_result) {
-        NM_SET_OUT(out_response_code, -1);
-        NM_SET_OUT(out_response_data, NULL);
-        return FALSE;
-    }
-
-    NM_SET_OUT(out_response_code, get_result->response_code);
-
-    /* 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));
-
-    _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;
-} PollGetData;
-
-static void
-_poll_get_data_free(gpointer data)
-{
-    PollGetData *poll_get_data = data;
-
-    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_g_slice_free(poll_get_data);
-}
-
-static void
-_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);
-}
-
-static gboolean
-_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);
-
-    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)
-{
-    PollGetData * poll_get_data = user_data;
-    gs_free_error GError *error = NULL;
-    gboolean              success;
-
-    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, TRUE);
-
-    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_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)
-{
-    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);
-
-    task = G_TASK(result);
-
-    success = g_task_propagate_boolean(task, &local_error);
-
-    nm_assert((!!success) == (!local_error));
-
-    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;
-    }
-
-    poll_get_data = g_task_get_task_data(task);
-
-    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)
-{
-    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)
-{
-    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;
-
-    _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)
-{
-    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_action(user_data, CURL_SOCKET_TIMEOUT, 0);
-    return G_SOURCE_REMOVE;
-}
-
-static int
-_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;
-}
-
-/*****************************************************************************/
-
-static void
-nm_http_client_init(NMHttpClient *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);
-}
-
-static void
-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 *
-nm_http_client_new(void)
-{
-    return g_object_new(NM_TYPE_HTTP_CLIENT, NULL);
-}
-
-static void
-dispose(GObject *object)
-{
-    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_g_source_inst(&priv->mhandle_source_timeout);
-
-    G_OBJECT_CLASS(nm_http_client_parent_class)->dispose(object);
-}
-
-static void
-finalize(GObject *object)
-{
-    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_main_context_unref(priv->context);
-
-    curl_global_cleanup();
-}
-
-static void
-nm_http_client_class_init(NMHttpClientClass *klass)
-{
-    GObjectClass *object_class = G_OBJECT_CLASS(klass);
-
-    object_class->constructed = constructed;
-    object_class->dispose     = dispose;
-    object_class->finalize    = finalize;
-
-    nm_http_client_curl_global_init();
-}