Blame src/goabackend/goahttpclient.c

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