|
Packit |
79f644 |
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
Packit |
79f644 |
/*
|
|
Packit |
79f644 |
* Copyright © 2012 – 2017 Red Hat, Inc.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* This library is free software; you can redistribute it and/or
|
|
Packit |
79f644 |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
79f644 |
* License as published by the Free Software Foundation; either
|
|
Packit |
79f644 |
* version 2 of the License, or (at your option) any later version.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
79f644 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
79f644 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
79f644 |
* Lesser General Public License for more details.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* You should have received a copy of the GNU Lesser General
|
|
Packit |
79f644 |
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include "config.h"
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include <libsoup/soup.h>
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
#include "goahttpclient.h"
|
|
Packit |
79f644 |
#include "goasouplogger.h"
|
|
Packit |
79f644 |
#include "goautils.h"
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
struct _GoaHttpClient
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GObject parent_instance;
|
|
Packit |
79f644 |
};
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
G_DEFINE_TYPE (GoaHttpClient, goa_http_client, G_TYPE_OBJECT);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
goa_http_client_init (GoaHttpClient *self)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
goa_http_client_class_init (GoaHttpClientClass *klass)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
GoaHttpClient *
|
|
Packit |
79f644 |
goa_http_client_new (void)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
return GOA_HTTP_CLIENT (g_object_new (GOA_TYPE_HTTP_CLIENT, NULL));
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GCancellable *cancellable;
|
|
Packit |
79f644 |
SoupMessage *msg;
|
|
Packit |
79f644 |
SoupSession *session;
|
|
Packit |
79f644 |
gboolean accept_ssl_errors;
|
|
Packit |
79f644 |
gulong cancellable_id;
|
|
Packit |
79f644 |
} CheckData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
gchar *password;
|
|
Packit |
79f644 |
gchar *username;
|
|
Packit |
79f644 |
} CheckAuthData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_check_data_free (gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (data->cancellable_id > 0)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_cancellable_disconnect (data->cancellable, data->cancellable_id);
|
|
Packit |
79f644 |
g_object_unref (data->cancellable);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* soup_session_queue_message stole the references to data->msg */
|
|
Packit |
79f644 |
g_object_unref (data->session);
|
|
Packit |
79f644 |
g_slice_free (CheckData, data);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_check_auth_data_free (gpointer data, GClosure *closure)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckAuthData *auth = data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_free (auth->password);
|
|
Packit |
79f644 |
g_free (auth->username);
|
|
Packit |
79f644 |
g_slice_free (CheckAuthData, auth);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_authenticate (SoupSession *session,
|
|
Packit |
79f644 |
SoupMessage *msg,
|
|
Packit |
79f644 |
SoupAuth *auth,
|
|
Packit |
79f644 |
gboolean retrying,
|
|
Packit |
79f644 |
gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckAuthData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (retrying)
|
|
Packit |
79f644 |
return;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
soup_auth_authenticate (auth, data->username, data->password);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_request_started (SoupSession *session, SoupMessage *msg, SoupSocket *socket, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckData *data;
|
|
Packit |
79f644 |
GTask *task = G_TASK (user_data);
|
|
Packit |
79f644 |
GError *error;
|
|
Packit |
79f644 |
GTlsCertificateFlags cert_flags;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data = g_task_get_task_data (task);
|
|
Packit |
79f644 |
error = NULL;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (!data->accept_ssl_errors
|
|
Packit |
79f644 |
&& soup_message_get_https_status (msg, NULL, &cert_flags)
|
|
Packit |
79f644 |
&& cert_flags != 0)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
goa_utils_set_error_ssl (&error, cert_flags);
|
|
Packit |
79f644 |
g_task_return_error (task, error);
|
|
Packit |
79f644 |
soup_session_abort (data->session);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_check_cancelled_cb (GCancellable *cancellable, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckData *data;
|
|
Packit |
79f644 |
GTask *task = G_TASK (user_data);
|
|
Packit |
79f644 |
gboolean cancelled;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data = g_task_get_task_data (task);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
cancelled = g_task_return_error_if_cancelled (task);
|
|
Packit |
79f644 |
soup_session_abort (data->session);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_if_fail (cancelled);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static gboolean
|
|
Packit |
79f644 |
http_client_check_free_in_idle (gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GTask *task = G_TASK (user_data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_object_unref (task);
|
|
Packit |
79f644 |
return G_SOURCE_REMOVE;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_check_response_cb (SoupSession *session, SoupMessage *msg, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GError *error;
|
|
Packit |
79f644 |
GMainContext *context;
|
|
Packit |
79f644 |
GSource *source;
|
|
Packit |
79f644 |
GTask *task = G_TASK (user_data);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
error = NULL;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* status == SOUP_STATUS_CANCELLED, if we are being aborted by the
|
|
Packit |
79f644 |
* GCancellable or due to an SSL error. The GTask was already
|
|
Packit |
79f644 |
* 'returned' by the respective callbacks.
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
if (msg->status_code == SOUP_STATUS_CANCELLED)
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
else if (msg->status_code != SOUP_STATUS_OK)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
g_warning ("goa_http_client_check() failed: %u — %s", msg->status_code, msg->reason_phrase);
|
|
Packit |
79f644 |
goa_utils_set_error_soup (&error, msg);
|
|
Packit |
79f644 |
g_task_return_error (task, error);
|
|
Packit |
79f644 |
goto out;
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_task_return_boolean (task, TRUE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
out:
|
|
Packit |
79f644 |
/* We might be invoked from a GCancellable::cancelled
|
|
Packit |
79f644 |
* handler, and unreffing the GTask will disconnect the
|
|
Packit |
79f644 |
* handler. Since disconnecting from inside the handler will cause a
|
|
Packit |
79f644 |
* deadlock [1], we use an idle handler to break them up.
|
|
Packit |
79f644 |
*
|
|
Packit |
79f644 |
* [1] https://bugzilla.gnome.org/show_bug.cgi?id=705395
|
|
Packit |
79f644 |
*/
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
source = g_idle_source_new ();
|
|
Packit |
79f644 |
g_source_set_priority (source, G_PRIORITY_DEFAULT_IDLE);
|
|
Packit |
79f644 |
g_source_set_callback (source, http_client_check_free_in_idle, task, NULL);
|
|
Packit |
79f644 |
g_source_set_name (source, "[goa] http_client_check_free_in_idle");
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
context = g_task_get_context (task);
|
|
Packit |
79f644 |
g_source_attach (source, context);
|
|
Packit |
79f644 |
g_source_unref (source);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
void
|
|
Packit |
79f644 |
goa_http_client_check (GoaHttpClient *self,
|
|
Packit |
79f644 |
const gchar *uri,
|
|
Packit |
79f644 |
const gchar *username,
|
|
Packit |
79f644 |
const gchar *password,
|
|
Packit |
79f644 |
gboolean accept_ssl_errors,
|
|
Packit |
79f644 |
GCancellable *cancellable,
|
|
Packit |
79f644 |
GAsyncReadyCallback callback,
|
|
Packit |
79f644 |
gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckData *data;
|
|
Packit |
79f644 |
CheckAuthData *auth;
|
|
Packit |
79f644 |
GTask *task;
|
|
Packit |
79f644 |
SoupLogger *logger;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_if_fail (GOA_IS_HTTP_CLIENT (self));
|
|
Packit |
79f644 |
g_return_if_fail (uri != NULL && uri[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (username != NULL && username[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (password != NULL && password[0] != '\0');
|
|
Packit |
79f644 |
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
task = g_task_new (self, cancellable, callback, user_data);
|
|
Packit |
79f644 |
g_task_set_source_tag (task, goa_http_client_check);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data = g_slice_new0 (CheckData);
|
|
Packit |
79f644 |
g_task_set_task_data (task, data, http_client_check_data_free);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->session = soup_session_new_with_options (SOUP_SESSION_SSL_STRICT, FALSE,
|
|
Packit |
79f644 |
NULL);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
logger = goa_soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
|
|
Packit |
79f644 |
soup_session_add_feature (data->session, SOUP_SESSION_FEATURE (logger));
|
|
Packit |
79f644 |
g_object_unref (logger);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->accept_ssl_errors = accept_ssl_errors;
|
|
Packit |
79f644 |
data->msg = soup_message_new (SOUP_METHOD_GET, uri);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
if (cancellable != NULL)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
data->cancellable = g_object_ref (cancellable);
|
|
Packit |
79f644 |
data->cancellable_id = g_cancellable_connect (cancellable,
|
|
Packit |
79f644 |
G_CALLBACK (http_client_check_cancelled_cb),
|
|
Packit |
79f644 |
task,
|
|
Packit |
79f644 |
NULL);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
auth = g_slice_new0 (CheckAuthData);
|
|
Packit |
79f644 |
auth->username = g_strdup (username);
|
|
Packit |
79f644 |
auth->password = g_strdup (password);
|
|
Packit |
79f644 |
g_signal_connect_data (data->session,
|
|
Packit |
79f644 |
"authenticate",
|
|
Packit |
79f644 |
G_CALLBACK (http_client_authenticate),
|
|
Packit |
79f644 |
auth,
|
|
Packit |
79f644 |
http_client_check_auth_data_free,
|
|
Packit |
79f644 |
0);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_signal_connect (data->session, "request-started", G_CALLBACK (http_client_request_started), task);
|
|
Packit |
79f644 |
soup_session_queue_message (data->session, data->msg, http_client_check_response_cb, g_object_ref (task));
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_object_unref (task);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
gboolean
|
|
Packit |
79f644 |
goa_http_client_check_finish (GoaHttpClient *self, GAsyncResult *res, GError **error)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GTask *task;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_val_if_fail (GOA_IS_HTTP_CLIENT (self), FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
|
|
Packit |
79f644 |
task = G_TASK (res);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_val_if_fail (g_task_get_source_tag (task) == goa_http_client_check, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return g_task_propagate_boolean (task, error);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
/* ---------------------------------------------------------------------------------------------------- */
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
typedef struct
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
GError **error;
|
|
Packit |
79f644 |
GMainLoop *loop;
|
|
Packit |
79f644 |
gboolean op_res;
|
|
Packit |
79f644 |
} CheckSyncData;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
static void
|
|
Packit |
79f644 |
http_client_check_sync_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckSyncData *data = user_data;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data->op_res = goa_http_client_check_finish (GOA_HTTP_CLIENT (source_object), res, data->error);
|
|
Packit |
79f644 |
g_main_loop_quit (data->loop);
|
|
Packit |
79f644 |
}
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
gboolean
|
|
Packit |
79f644 |
goa_http_client_check_sync (GoaHttpClient *self,
|
|
Packit |
79f644 |
const gchar *uri,
|
|
Packit |
79f644 |
const gchar *username,
|
|
Packit |
79f644 |
const gchar *password,
|
|
Packit |
79f644 |
gboolean accept_ssl_errors,
|
|
Packit |
79f644 |
GCancellable *cancellable,
|
|
Packit |
79f644 |
GError **error)
|
|
Packit |
79f644 |
{
|
|
Packit |
79f644 |
CheckSyncData data;
|
|
Packit |
79f644 |
GMainContext *context = NULL;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_return_val_if_fail (GOA_IS_HTTP_CLIENT (self), FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (uri != NULL && uri[0] != '\0', FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (username != NULL && username[0] != '\0', FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (password != NULL && password[0] != '\0', FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
|
|
Packit |
79f644 |
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
data.error = error;
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
context = g_main_context_new ();
|
|
Packit |
79f644 |
g_main_context_push_thread_default (context);
|
|
Packit |
79f644 |
data.loop = g_main_loop_new (context, FALSE);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
goa_http_client_check (self,
|
|
Packit |
79f644 |
uri,
|
|
Packit |
79f644 |
username,
|
|
Packit |
79f644 |
password,
|
|
Packit |
79f644 |
accept_ssl_errors,
|
|
Packit |
79f644 |
cancellable,
|
|
Packit |
79f644 |
http_client_check_sync_cb,
|
|
Packit |
79f644 |
&data);
|
|
Packit |
79f644 |
g_main_loop_run (data.loop);
|
|
Packit |
79f644 |
g_main_loop_unref (data.loop);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
g_main_context_pop_thread_default (context);
|
|
Packit |
79f644 |
g_main_context_unref (context);
|
|
Packit |
79f644 |
|
|
Packit |
79f644 |
return data.op_res;
|
|
Packit |
79f644 |
}
|