[grilo/wip/hadess/soup3: 7/8] net: Add support for libsoup3 through a compile-time option




commit db7e78fe36fdc9e04afbf2a267ddd3a5bbb989ea
Author: Bastien Nocera <hadess hadess net>
Date:   Mon Jul 4 16:45:54 2022 +0200

    net: Add support for libsoup3 through a compile-time option
    
    Add 'soup3' build option to make it easier to build the grl-net library
    against either libsoup 2.x or libsoup 3.x. The API version used is
    exported in the soupapiversion variable of the pkg-config file.
    
    Based on work by Daniel Kolesa <dkolesa igalia com>

 libs/net/grl-net-wc.c | 162 ++++++++++++++++++++++++++++++++++++++++----------
 libs/net/meson.build  |   3 +-
 meson.build           |   8 ++-
 meson_options.txt     |   1 +
 tests/lib-net.c       |  37 +++++++++++-
 5 files changed, 178 insertions(+), 33 deletions(-)
---
diff --git a/libs/net/grl-net-wc.c b/libs/net/grl-net-wc.c
index 664236d..20de7a9 100644
--- a/libs/net/grl-net-wc.c
+++ b/libs/net/grl-net-wc.c
@@ -39,13 +39,14 @@
 #include "config.h"
 #endif
 
-#define LIBSOUP_USE_UNSTABLE_REQUEST_API
-
 #include <errno.h>
 #include <glib/gi18n-lib.h>
 #include <glib/gstdio.h>
 #include <libsoup/soup-cache.h>
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
+#define LIBSOUP_USE_UNSTABLE_REQUEST_API
 #include <libsoup/soup-request-http.h>
+#endif
 #include <libsoup/soup.h>
 #include <string.h>
 
@@ -69,7 +70,11 @@ enum {
 };
 
 struct request_res {
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  SoupMessage *message;
+#else
   SoupRequest *request;
+#endif
   gchar *buffer;
   gsize length;
   gsize offset;
@@ -182,10 +187,15 @@ free_op_res (void *op)
 {
   struct request_res *rr = op;
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  g_object_unref (rr->message);
+#else
   g_object_unref (rr->request);
+#endif
   g_slice_free (struct request_res, rr);
 }
 
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
 /*
  * use-thread-context is available for libsoup-2.4 >= 2.39.0
  * We check in run-time if it's available
@@ -199,6 +209,25 @@ set_thread_context (GrlNetWc *self)
     if (spec)
       g_object_set (self->session, "use-thread-context", TRUE, NULL);
 }
+#endif
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+static void
+ensure_session (GrlNetWc *self)
+{
+  guint max_conns_per_host = self->throttling > 0 ? 1 : 2;
+
+  if (self->session)
+    return;
+
+  self->session = soup_session_new_with_options ("max-conns-per-host", max_conns_per_host,
+                                                 "user-agent", self->user_agent,
+                                                 NULL);
+  grl_net_wc_set_log_level (self, self->log_level);
+  grl_net_wc_set_cache (self, self->use_cache);
+  grl_net_wc_set_cache_size (self, self->cache_size);
+}
+#endif
 
 static void
 init_dump_directory (void)
@@ -223,11 +252,15 @@ static void
 cache_down (GrlNetWc *self)
 {
   GFile *cache_dir_file;
-  SoupSessionFeature *cache = soup_session_get_feature (self->session, SOUP_TYPE_CACHE);
+  SoupSessionFeature *cache;
   gchar *cache_dir;
 
   GRL_DEBUG ("cache down");
 
+  if (!self->session)
+    return;
+
+  cache = soup_session_get_feature (self->session, SOUP_TYPE_CACHE);
   if (!cache) {
     return;
   }
@@ -293,11 +326,14 @@ grl_net_wc_init (GrlNetWc *wc)
 {
   GRL_LOG_DOMAIN_INIT (wc_log_domain, "wc");
 
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
   wc->session = soup_session_async_new ();
   g_object_set (G_OBJECT (wc->session), "ssl-use-system-ca-file", TRUE, NULL);
+  set_thread_context (wc);
+#endif
+
   wc->pending = g_queue_new ();
 
-  set_thread_context (wc);
   init_mock_requester (wc);
   init_requester (wc);
 }
@@ -316,7 +352,7 @@ grl_net_wc_finalize (GObject *object)
 
   g_clear_pointer (&wc->user_agent, g_free);
   g_queue_free (wc->pending);
-  g_object_unref (wc->session);
+  g_clear_object (&wc->session);
 
   G_OBJECT_CLASS (grl_net_wc_parent_class)->finalize (object);
 }
@@ -347,9 +383,11 @@ grl_net_wc_set_property (GObject *object,
   case PROP_USER_AGENT:
     g_clear_pointer (&wc->user_agent, g_free);
     wc->user_agent = g_value_dup_string (value);
-    g_object_set (G_OBJECT (wc->session),
-                  "user-agent", wc->user_agent,
-                  NULL);
+    if (wc->session) {
+      g_object_set (G_OBJECT (wc->session),
+                    "user-agent", wc->user_agent,
+                    NULL);
+    }
     break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (wc, propid, pspec);
@@ -413,6 +451,7 @@ parse_error (guint status,
              GSimpleAsyncResult *result)
 {
   switch (status) {
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
   case SOUP_STATUS_CANT_RESOLVE:
   case SOUP_STATUS_CANT_CONNECT:
   case SOUP_STATUS_SSL_FAILED:
@@ -427,8 +466,11 @@ parse_error (guint status,
                                      G_IO_ERROR_PROXY_FAILED,
                                      _("Cannot connect to the proxy server"));
     return;
+#endif
   case SOUP_STATUS_INTERNAL_SERVER_ERROR: /* 500 */
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
   case SOUP_STATUS_MALFORMED:
+#endif
   case SOUP_STATUS_BAD_REQUEST: /* 400 */
     g_simple_async_result_set_error (result, GRL_NET_WC_ERROR,
                                      GRL_NET_WC_ERROR_PROTOCOL_ERROR,
@@ -454,11 +496,13 @@ parse_error (guint status,
                                      _("The entry has been modified since it was downloaded: %s"),
                                      reason);
     return;
+#if ! SOUP_CHECK_VERSION (2, 99, 2)
   case SOUP_STATUS_CANCELLED:
     g_simple_async_result_set_error (result, G_IO_ERROR,
                                      G_IO_ERROR_CANCELLED,
                                      _("Operation was cancelled"));
     return;
+#endif
   default:
     GRL_DEBUG ("Unhandled status: %s", soup_status_get_phrase (status));
     g_simple_async_result_set_error (result, G_IO_ERROR,
@@ -480,15 +524,13 @@ build_request_filename (const char *uri)
 }
 
 static void
-dump_data (SoupURI *uri,
+dump_data (const char *uri_string,
            const char *buffer,
            const gsize length)
 {
   if (!capture_dir)
     return;
 
-  char *uri_string = soup_uri_to_string (uri, FALSE);
-
   /* Write request content to file in capture directory. */
   char *request_filename = build_request_filename (uri_string);
   char *path = g_build_filename (capture_dir, request_filename, NULL);
@@ -521,7 +563,6 @@ dump_data (SoupURI *uri,
   }
 
   g_free (request_filename);
-  g_free (uri_string);
 }
 
 static void
@@ -584,14 +625,24 @@ read_async_cb (GObject *source,
   }
 
   {
-    SoupMessage *msg =
-      soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+    g_autoptr(SoupMessage) msg = NULL;
+    guint status_code;
+    const char *reason_phrase;
+
+#if SOUP_CHECK_VERSION (2, 99, 2)
+    msg = g_object_ref (rr->message);
+    status_code = soup_message_get_status (msg);
+    reason_phrase = soup_message_get_reason_phrase (msg);
+#else
+    msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+    status_code = msg->status_code;
+    reason_phrase = msg->reason_phrase;
+#endif
 
-    if (msg && msg->status_code != SOUP_STATUS_OK) {
-        parse_error (msg->status_code,
-                     msg->reason_phrase,
+    if (status_code != SOUP_STATUS_OK) {
+        parse_error (status_code,
+                     reason_phrase,
                      G_SIMPLE_ASYNC_RESULT (user_data));
-        g_object_unref (msg);
     }
   }
 
@@ -604,11 +655,20 @@ reply_cb (GObject *source,
           GAsyncResult *res,
           gpointer user_data)
 {
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  SoupSession *session = SOUP_SESSION (source);
+  SoupMessage *message = soup_session_get_async_result_message (session, res);
+  SoupMessageHeaders *response_hdrs = soup_message_get_response_headers (message);
+#endif
   GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT (user_data);
   struct request_res *rr = g_simple_async_result_get_op_res_gpointer (result);
 
   GError *error = NULL;
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  GInputStream *in = soup_session_send_finish (session, res, &error);
+#else
   GInputStream *in = soup_request_send_finish (rr->request, res, &error);
+#endif
 
   if (error) {
     if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
@@ -625,7 +685,11 @@ reply_cb (GObject *source,
     return;
   }
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  rr->length = soup_message_headers_get_content_length (response_hdrs) + 1;
+#else
   rr->length = soup_request_get_content_length (rr->request) + 1;
+#endif
   if (rr->length == 1)
     rr->length = 50 * 1024;
 
@@ -647,22 +711,28 @@ get_url_now (GrlNetWc *self,
              GAsyncResult *result,
              GCancellable *cancellable)
 {
-  SoupURI *uri;
   struct request_res *rr = g_slice_new0 (struct request_res);
 
   g_simple_async_result_set_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result),
                                              rr,
                                              NULL);
 
-  uri = soup_uri_new (url);
-  if (uri) {
-    rr->request = soup_session_request_uri (self->session, uri, NULL);
-    soup_uri_free (uri);
-  } else {
-    rr->request = NULL;
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  {
+    g_autoptr(GUri) uri = NULL;
+
+    uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL);
+    rr->message = soup_message_new_from_uri (SOUP_METHOD_GET, uri);
   }
+#else
+  rr->request = soup_session_request (self->session, url, NULL);
+#endif
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  if (!rr->message) {
+#else
   if (!rr->request) {
+#endif
     g_simple_async_result_set_error (G_SIMPLE_ASYNC_RESULT (result),
                                      G_IO_ERROR,
                                      G_IO_ERROR_INVALID_ARGUMENT,
@@ -674,22 +744,34 @@ get_url_now (GrlNetWc *self,
   }
 
   if (headers != NULL) {
-    SoupMessage *message;
+    g_autoptr(SoupMessage) message = NULL;
     GHashTableIter iter;
     const char *key, *value;
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+    message = g_object_ref (rr->message);
+#else
     message = soup_request_http_get_message (SOUP_REQUEST_HTTP (rr->request));
+#endif
 
     if (message) {
       g_hash_table_iter_init (&iter, headers);
       while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *)&value)) {
+#if SOUP_CHECK_VERSION (2, 99, 2)
+        soup_message_headers_append (soup_message_get_request_headers (message), key, value);
+#else
         soup_message_headers_append (message->request_headers, key, value);
+#endif
       }
-      g_object_unref (message);
     }
   }
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  soup_session_send_async (self->session, rr->message, G_PRIORITY_DEFAULT,
+                         cancellable, reply_cb, result);
+#else
   soup_request_send_async (rr->request, cancellable, reply_cb, result);
+#endif
 }
 
 static gboolean
@@ -768,7 +850,13 @@ get_content (GrlNetWc *self,
   if (is_mocked ()) {
     get_content_mocked (self, op, &(self->previous_data), length);
   } else {
-    dump_data (soup_request_get_uri (rr->request),
+    g_autofree char *uri = NULL;
+#if SOUP_CHECK_VERSION (2, 99, 2)
+    uri = g_uri_to_string (soup_message_get_uri (rr->message));
+#else
+    uri = soup_uri_to_string (soup_request_get_uri (rr->request), FALSE);
+#endif
+    dump_data (uri,
                rr->buffer,
                rr->offset);
     self->previous_data = rr->buffer;
@@ -908,6 +996,9 @@ grl_net_wc_request_with_headers_hash_async (GrlNetWc *self,
 {
   GSimpleAsyncResult *result;
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  ensure_session (self);
+#endif
   result = g_simple_async_result_new (G_OBJECT (self),
                                       callback,
                                       user_data,
@@ -990,7 +1081,11 @@ grl_net_wc_set_log_level (GrlNetWc *self,
 
   soup_session_remove_feature_by_type (self->session, SOUP_TYPE_LOGGER);
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  logger = soup_logger_new ((SoupLoggerLogLevel) log_level);
+#else
   logger = soup_logger_new ((SoupLoggerLogLevel) log_level, -1);
+#endif
   soup_session_add_feature (self->session, SOUP_SESSION_FEATURE (logger));
   g_object_unref (logger);
 
@@ -1015,14 +1110,21 @@ grl_net_wc_set_throttling (GrlNetWc *self,
   if (!self->session)
     return;
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  if (self->session) {
+    g_warning ("\"throttling\" can only be set before the first request is made.");
+    return;
+  }
+#endif
+
   if (throttling > 0) {
     /* max conns per host = 1 */
     g_object_set (self->session,
-                  SOUP_SESSION_MAX_CONNS_PER_HOST, 1, NULL);
+                  "max-conns-per-host", 1, NULL);
   } else {
     /* default value */
     g_object_set (self->session,
-                  SOUP_SESSION_MAX_CONNS_PER_HOST, 2, NULL);
+                  "max-conns-per-host", 2, NULL);
   }
 }
 
diff --git a/libs/net/meson.build b/libs/net/meson.build
index c366adf..c25f31e 100644
--- a/libs/net/meson.build
+++ b/libs/net/meson.build
@@ -56,6 +56,7 @@ grlnet_pc = pkgconfig.generate(libgrlnet,
         'datadir=${datarootdir}',
         'girdir=' + girdir_for_pc_file,
         'typelibdir=' + typelibdir_for_pc_file,
+        'soupapiversion=' + soup_api_version,
     ],
 )
 
@@ -67,7 +68,7 @@ if enable_gir
         identifier_prefix: 'GrlNet',
         symbol_prefix: 'grl_net',
         dependencies: [ gobject_dep, gio_dep, libsoup_dep ],
-        includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-2.4' ],
+        includes: [ 'GObject-2.0', 'Gio-2.0', 'Soup-' + soup_api_version ],
         include_directories: libs_inc,
         install: true,
         extra_args: [ '--c-include=net/grl-net.h' ])
diff --git a/meson.build b/meson.build
index 2bfc686..cafe44e 100644
--- a/meson.build
+++ b/meson.build
@@ -48,7 +48,13 @@ libxml_dep = dependency('libxml-2.0', required: true)
 
 enable_grlnet = get_option('enable-grl-net')
 if enable_grlnet
-    libsoup_dep = dependency('libsoup-2.4', version: '>= 2.41.3', required: true)
+    if get_option('soup3')
+        libsoup_dep = dependency('libsoup-3.0', version: '>= 2.99.2', required: true)
+        soup_api_version = '3.0'
+    else
+        libsoup_dep = dependency('libsoup-2.4', version: '>= 2.41.3', required: true)
+        soup_api_version = '2.4'
+    endif
 endif
 
 enable_grlpls = get_option('enable-grl-pls')
diff --git a/meson_options.txt b/meson_options.txt
index a2a6fc4..91418cc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -4,3 +4,4 @@ option('enable-gtk-doc', type: 'boolean', value: true, description: 'Enable gene
 option('enable-introspection', type: 'boolean', value: true, description: 'Enable GObject Introspection')
 option('enable-test-ui', type: 'boolean', value: true, description: 'Build Test UI')
 option('enable-vala', type: 'boolean', value: true, description: 'Enable Vala (enables GObject 
Introspection)')
+option('soup3', type: 'boolean', value: true, description: 'Whether to use libsoup3')
diff --git a/tests/lib-net.c b/tests/lib-net.c
index 0fc2edd..3b5d9c5 100644
--- a/tests/lib-net.c
+++ b/tests/lib-net.c
@@ -82,6 +82,20 @@ timeout (gpointer user_data)
   return G_SOURCE_REMOVE;
 }
 
+#if SOUP_CHECK_VERSION (2, 99, 2)
+static void
+soup_server_throttling_cb (SoupServer *server,
+                           SoupServerMessage *message,
+                           const char *path,
+                           GHashTable *query,
+                           gpointer user_data)
+{
+  gchar *response = g_strdup_printf ("%" G_GINT64_FORMAT, g_get_monotonic_time());
+
+  soup_server_message_set_response (message, "text/plain", SOUP_MEMORY_TAKE, response, strlen(response));
+  soup_server_message_set_status (message, SOUP_STATUS_OK, NULL);
+}
+#else
 static void
 soup_server_throttling_cb (SoupServer *server,
                            SoupMessage *message,
@@ -95,6 +109,7 @@ soup_server_throttling_cb (SoupServer *server,
   soup_message_set_response (message, "text/plain", SOUP_MEMORY_TAKE, response, strlen(response));
   soup_message_set_status (message, SOUP_STATUS_OK);
 }
+#endif
 
 static void
 test_net_wc_throttling_cb (GObject *source_object,
@@ -165,8 +180,13 @@ test_net_wc_small_throttling (Fixture *f,
 
   uris = soup_server_get_uris (f->server);
   g_assert_nonnull (uris);
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  request = g_uri_to_string_partial (uris->data, G_URI_HIDE_PASSWORD);
+  g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
+#else
   request = soup_uri_to_string (uris->data, FALSE);
   g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
+#endif
   g_assert_nonnull (request);
 
   wc = grl_net_wc_new ();
@@ -205,8 +225,13 @@ test_net_wc_big_throttling (Fixture *f,
 
   uris = soup_server_get_uris (f->server);
   g_assert_nonnull (uris);
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  request = g_uri_to_string_partial (uris->data, G_URI_HIDE_PASSWORD);
+  g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
+#else
   request = soup_uri_to_string (uris->data, FALSE);
   g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
+#endif
   g_assert_nonnull (request);
 
   wc = grl_net_wc_new ();
@@ -247,8 +272,13 @@ test_net_wc_no_throttling_stress (Fixture *f,
 
   uris = soup_server_get_uris (f->server);
   g_assert_nonnull (uris);
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  request = g_uri_to_string_partial (uris->data, G_URI_HIDE_PASSWORD);
+  g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
+#else
   request = soup_uri_to_string (uris->data, FALSE);
   g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
+#endif
   g_assert_nonnull (request);
 
   /* Under the same grl-net-wc, create NUM_STRESS_TEST async operations to our
@@ -286,8 +316,13 @@ test_net_properties (Fixture *f,
 
   uris = soup_server_get_uris (f->server);
   g_assert_nonnull (uris);
+#if SOUP_CHECK_VERSION (2, 99, 2)
+  request = g_uri_to_string_partial (uris->data, G_URI_HIDE_PASSWORD);
+  g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
+#else
   request = soup_uri_to_string (uris->data, FALSE);
   g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
+#endif
   g_assert_nonnull (request);
 
   wc = grl_net_wc_new ();
@@ -309,7 +344,7 @@ test_net_properties (Fixture *f,
 
   g_assert_nonnull (wc->session);
   g_object_get (G_OBJECT (wc->session),
-                SOUP_SESSION_MAX_CONNS_PER_HOST, &max_conns_per_host,
+                "max-conns-per-host", &max_conns_per_host,
                 "user-agent", &user_agent,
                 NULL);
   g_assert_cmpuint (max_conns_per_host, ==, 2);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]