[gupnp/wip/phako/libsoup3: 5/6] Initial port to libsoup3 API




commit 4f468436051f49213975ad3096831ee17b92fdca
Author: Jens Georg <mail jensge org>
Date:   Mon Jul 26 23:36:39 2021 +0200

    Initial port to libsoup3 API

 libgupnp/gupnp-acl-private.h                  |   6 +-
 libgupnp/gupnp-acl.c                          |   6 +-
 libgupnp/gupnp-context-manager.c              |  26 +-
 libgupnp/gupnp-context-private.h              |   7 +-
 libgupnp/gupnp-context.c                      | 337 ++++++++++--------
 libgupnp/gupnp-control-point.c                | 116 ++++---
 libgupnp/gupnp-device-info.c                  |  44 +--
 libgupnp/gupnp-device-info.h                  |   5 +-
 libgupnp/gupnp-device-proxy.c                 |   4 +-
 libgupnp/gupnp-device.c                       |   4 +-
 libgupnp/gupnp-error.c                        |  16 +-
 libgupnp/gupnp-resource-factory-private.h     |  58 ++--
 libgupnp/gupnp-resource-factory.c             |  62 ++--
 libgupnp/gupnp-root-device.c                  |  18 +-
 libgupnp/gupnp-service-info.c                 |  72 ++--
 libgupnp/gupnp-service-info.h                 |   5 +-
 libgupnp/gupnp-service-private.h              |   2 +-
 libgupnp/gupnp-service-proxy-action-private.h |   1 +
 libgupnp/gupnp-service-proxy-action.c         |  30 +-
 libgupnp/gupnp-service-proxy.c                | 381 ++++++++++++--------
 libgupnp/gupnp-service.c                      | 477 +++++++++++++++-----------
 libgupnp/gupnp-service.h                      |   4 +-
 libgupnp/gupnp-unix-context-manager.c         |   1 -
 libgupnp/http-headers.c                       |  46 ++-
 libgupnp/http-headers.h                       |  20 +-
 libgupnp/xml-util.c                           |  28 +-
 libgupnp/xml-util.h                           |  15 +-
 meson.build                                   |   8 +-
 tests/test-bugs.c                             |  21 +-
 tests/test-context.c                          |  88 +++--
 30 files changed, 1088 insertions(+), 820 deletions(-)
---
diff --git a/libgupnp/gupnp-acl-private.h b/libgupnp/gupnp-acl-private.h
index f243569..0a10f47 100644
--- a/libgupnp/gupnp-acl-private.h
+++ b/libgupnp/gupnp-acl-private.h
@@ -43,10 +43,9 @@ typedef struct _AclServerHandler
 typedef struct _AclAsyncHandler
 {
         SoupServer *server;
-        SoupMessage *message;
+        SoupServerMessage *message;
         char *path;
         GHashTable *query;
-        SoupClientContext *client;
         AclServerHandler *handler;
 } AclAsyncHandler;
 
@@ -62,10 +61,9 @@ acl_server_handler_free (AclServerHandler *handler);
 
 G_GNUC_INTERNAL AclAsyncHandler *
 acl_async_handler_new (SoupServer *server,
-                       SoupMessage *message,
+                       SoupServerMessage *message,
                        const char *path,
                        GHashTable *query,
-                       SoupClientContext *client,
                        AclServerHandler *handler);
 
 G_GNUC_INTERNAL void
diff --git a/libgupnp/gupnp-acl.c b/libgupnp/gupnp-acl.c
index ff8c4f9..4a337c6 100644
--- a/libgupnp/gupnp-acl.c
+++ b/libgupnp/gupnp-acl.c
@@ -219,10 +219,9 @@ acl_server_handler_free (AclServerHandler *handler)
  */
 AclAsyncHandler *
 acl_async_handler_new (SoupServer *server,
-                       SoupMessage *message,
+                       SoupServerMessage *message,
                        const char *path,
                        GHashTable *query,
-                       SoupClientContext *client,
                        AclServerHandler *handler)
 {
         AclAsyncHandler *data = g_slice_new0 (AclAsyncHandler);
@@ -232,7 +231,6 @@ acl_async_handler_new (SoupServer *server,
         data->path = g_strdup (path);
         if (query != NULL)
                 data->query = g_hash_table_ref (query);
-        data->client = g_boxed_copy (SOUP_TYPE_CLIENT_CONTEXT, client);
         data->handler = handler;
 
         return data;
@@ -253,7 +251,7 @@ acl_async_handler_free (AclAsyncHandler *handler)
         g_free (handler->path);
         if (handler->query != NULL)
                 g_hash_table_unref (handler->query);
-        g_boxed_free (SOUP_TYPE_CLIENT_CONTEXT, handler->client);
+        // g_boxed_free (SOUP_TYPE_CLIENT_CONTEXT, handler->client);
 
         g_slice_free (AclAsyncHandler, handler);
 }
diff --git a/libgupnp/gupnp-context-manager.c b/libgupnp/gupnp-context-manager.c
index 534899d..1f2ec73 100644
--- a/libgupnp/gupnp-context-manager.c
+++ b/libgupnp/gupnp-context-manager.c
@@ -29,7 +29,6 @@
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
-#include <libsoup/soup-address.h>
 #include <glib.h>
 #include <glib/gstdio.h>
 
@@ -487,18 +486,19 @@ gupnp_context_manager_class_init (GUPnPContextManagerClass *klass)
          * Port the contexts listen on, or 0 if you don't care what
          * port is used by #GUPnPContext objects created by this object.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_PORT,
-                 g_param_spec_uint ("port",
-                                    "Port",
-                                    "Port to create contexts for",
-                                    0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
-                                    G_PARAM_READWRITE |
-                                    G_PARAM_CONSTRUCT_ONLY |
-                                    G_PARAM_STATIC_NAME |
-                                    G_PARAM_STATIC_NICK |
-                                    G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_PORT,
+                g_param_spec_uint ("port",
+                                   "Port",
+                                   "Port to create contexts for",
+                                   0,
+                                   G_MAXUINT,
+                                   0,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                           G_PARAM_STATIC_NAME |
+                                           G_PARAM_STATIC_NICK |
+                                           G_PARAM_STATIC_BLURB));
         /**
          * GUPnPContextManager:family:
          *
diff --git a/libgupnp/gupnp-context-private.h b/libgupnp/gupnp-context-private.h
index e5cd507..b99230f 100644
--- a/libgupnp/gupnp-context-private.h
+++ b/libgupnp/gupnp-context-private.h
@@ -15,7 +15,7 @@
 
 G_BEGIN_DECLS
 
-G_GNUC_INTERNAL SoupURI *
+G_GNUC_INTERNAL GUri *
 _gupnp_context_get_server_uri (GUPnPContext *context);
 
 G_GNUC_INTERNAL void
@@ -23,9 +23,8 @@ _gupnp_context_add_server_handler_with_data (GUPnPContext *context,
                                              const char *path,
                                              AclServerHandler *data);
 
-G_GNUC_INTERNAL SoupURI *
-gupnp_context_rewrite_uri_to_uri (GUPnPContext *context,
-                                  const char   *uri);
+G_GNUC_INTERNAL GUri *
+gupnp_context_rewrite_uri_to_uri (GUPnPContext *context, const char *uri);
 
 G_GNUC_INTERNAL gboolean
 gupnp_context_validate_host_header (GUPnPContext *context, const char *host);
diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index efec13f..6a0caa4 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -36,7 +36,6 @@
 #endif
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <libsoup/soup-address.h>
 #include <glib/gstdio.h>
 
 #include "gupnp-acl.h"
@@ -52,10 +51,9 @@
 
 static void
 gupnp_acl_server_handler (SoupServer *server,
-                          SoupMessage *msg,
+                          SoupServerMessage *msg,
                           const char *path,
                           GHashTable *query,
-                          SoupClientContext *client,
                           gpointer user_data);
 
 static void
@@ -70,7 +68,7 @@ struct _GUPnPContextPrivate {
         SoupSession *session;
 
         SoupServer  *server; /* Started on demand */
-        SoupURI     *server_uri;
+        GUri *server_uri;
         char        *default_language;
 
         GList       *host_path_datas;
@@ -189,15 +187,13 @@ gupnp_context_initable_init (GInitable     *initable,
 
         user_agent = g_strdup_printf ("%s GUPnP/" VERSION " DLNADOC/1.50",
                                       g_get_prgname ()? : "");
-        g_object_set (priv->session,
-                      SOUP_SESSION_USER_AGENT,
-                      user_agent,
-                      NULL);
+
+        soup_session_set_user_agent (priv->session, user_agent);
         g_free (user_agent);
 
         if (g_getenv ("GUPNP_DEBUG")) {
                 SoupLogger *logger;
-                logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+                logger = soup_logger_new (SOUP_LOGGER_LOG_BODY);
                 soup_session_add_feature (priv->session,
                                           SOUP_SESSION_FEATURE (logger));
         }
@@ -349,7 +345,7 @@ gupnp_context_finalize (GObject *object)
         g_free (priv->default_language);
 
         if (priv->server_uri)
-                soup_uri_free (priv->server_uri);
+                g_uri_unref (priv->server_uri);
 
         /* Call super */
         object_class = G_OBJECT_CLASS (gupnp_context_parent_class);
@@ -412,18 +408,19 @@ gupnp_context_class_init (GUPnPContextClass *klass)
          *
          * The port to run on. Set to 0 if you don't care what port to run on.
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_PORT,
-                 g_param_spec_uint ("port",
-                                    "Port",
-                                    "Port to run on",
-                                    0, G_MAXUINT, SOUP_ADDRESS_ANY_PORT,
-                                    G_PARAM_READWRITE |
-                                    G_PARAM_CONSTRUCT_ONLY |
-                                    G_PARAM_STATIC_NAME |
-                                    G_PARAM_STATIC_NICK |
-                                    G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_PORT,
+                g_param_spec_uint ("port",
+                                   "Port",
+                                   "Port to run on",
+                                   0,
+                                   G_MAXUINT,
+                                   0,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                           G_PARAM_STATIC_NAME |
+                                           G_PARAM_STATIC_NICK |
+                                           G_PARAM_STATIC_BLURB));
 
         /**
          * GUPnPContext:server:
@@ -548,14 +545,15 @@ gupnp_context_get_session (GUPnPContext *context)
  * Default server handler: Return 404 not found.
  **/
 static void
-default_server_handler (G_GNUC_UNUSED SoupServer        *server,
-                        SoupMessage                     *msg,
-                        G_GNUC_UNUSED const char        *path,
-                        G_GNUC_UNUSED GHashTable        *query,
-                        G_GNUC_UNUSED SoupClientContext *client,
-                        G_GNUC_UNUSED gpointer           user_data)
+default_server_handler (G_GNUC_UNUSED SoupServer *server,
+                        SoupServerMessage *msg,
+                        G_GNUC_UNUSED const char *path,
+                        G_GNUC_UNUSED GHashTable *query,
+                        G_GNUC_UNUSED gpointer user_data)
 {
-        soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+        soup_server_message_set_status (msg,
+                                        SOUP_STATUS_NOT_FOUND,
+                                        "Not found");
 }
 
 /**
@@ -621,21 +619,21 @@ gupnp_context_get_server (GUPnPContext *context)
 /*
  * Makes a SoupURI that refers to our server.
  **/
-static SoupURI *
+static GUri *
 make_server_uri (GUPnPContext *context)
 {
         SoupServer *server = gupnp_context_get_server (context);
         GSList *uris = soup_server_get_uris (server);
         if (uris)
         {
-                SoupURI *uri = soup_uri_copy (uris->data);
-                g_slist_free_full (uris, (GDestroyNotify) soup_uri_free);
+                GUri *uri = g_uri_ref (uris->data);
+                g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
                 return uri;
         }
         return NULL;
 }
 
-SoupURI *
+GUri *
 _gupnp_context_get_server_uri (GUPnPContext *context)
 {
         GUPnPContextPrivate *priv;
@@ -645,7 +643,7 @@ _gupnp_context_get_server_uri (GUPnPContext *context)
                 priv->server_uri = make_server_uri (context);
 
         if (priv->server_uri)
-                return soup_uri_copy (priv->server_uri);
+                return g_uri_ref (priv->server_uri);
 
         return NULL;
 }
@@ -694,7 +692,7 @@ gupnp_context_get_port (GUPnPContext *context)
         if (priv->server_uri == NULL)
                 priv->server_uri = make_server_uri (context);
 
-        return soup_uri_get_port (priv->server_uri);
+        return g_uri_get_port (priv->server_uri);
 }
 
 /**
@@ -891,17 +889,20 @@ append_locale (const char *local_path, GList *locales)
 
 /* Redirect @msg to the same URI, but with a slash appended. */
 static void
-redirect_to_folder (SoupMessage *msg)
+redirect_to_folder (SoupServerMessage *msg)
 {
         char *uri, *redir_uri;
 
-        uri = soup_uri_to_string (soup_message_get_uri (msg),
-                                  FALSE);
+        uri = g_uri_to_string_partial (soup_server_message_get_uri (msg),
+                                       G_URI_HIDE_PASSWORD);
         redir_uri = g_strdup_printf ("%s/", uri);
-        soup_message_headers_append (msg->response_headers,
-                                     "Location", redir_uri);
-        soup_message_set_status (msg,
-                                 SOUP_STATUS_MOVED_PERMANENTLY);
+        soup_message_headers_append (
+                soup_server_message_get_response_headers (msg),
+                "Location",
+                redir_uri);
+        soup_server_message_set_status (msg,
+                                        SOUP_STATUS_MOVED_PERMANENTLY,
+                                        "Moved permanently");
         g_free (redir_uri);
         g_free (uri);
 }
@@ -930,12 +931,11 @@ update_client_cache (GUPnPContext *context,
 /* Serve @path. Note that we do not need to check for path including bogus
  * '..' as libsoup does this for us. */
 static void
-host_path_handler (G_GNUC_UNUSED SoupServer        *server,
-                   SoupMessage                     *msg,
-                   const char                      *path,
-                   G_GNUC_UNUSED GHashTable        *query,
-                   SoupClientContext               *client_ctx,
-                   gpointer                         user_data)
+host_path_handler (G_GNUC_UNUSED SoupServer *server,
+                   SoupServerMessage *msg,
+                   const char *path,
+                   G_GNUC_UNUSED GHashTable *query,
+                   gpointer user_data)
 {
         char *local_path, *path_to_open;
         GStatBuf st;
@@ -953,9 +953,11 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
         path_to_open = NULL;
         host_path_data = (HostPathData *) user_data;
 
-        if (msg->method != SOUP_METHOD_GET &&
-            msg->method != SOUP_METHOD_HEAD) {
-                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+        if (soup_server_message_get_method (msg) != SOUP_METHOD_GET &&
+            soup_server_message_get_method (msg) != SOUP_METHOD_HEAD) {
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_NOT_IMPLEMENTED,
+                                                "Not implemented");
 
                 goto DONE;
         }
@@ -964,16 +966,18 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
          * Also set Connection: close header, since the request originated
          * from a HTTP 1.0 client
          */
-        if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
-                soup_message_set_http_version (msg, SOUP_HTTP_1_1);
-                soup_message_headers_append (msg->response_headers,
-                                             "Connection",
-                                             "close");
+        if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) {
+                soup_server_message_set_http_version (msg, SOUP_HTTP_1_1);
+                soup_message_headers_append (
+                        soup_server_message_get_response_headers (msg),
+                        "Connection",
+                        "close");
         }
 
-        user_agent = soup_message_headers_get_one (msg->request_headers,
-                                                   "User-Agent");
-        host = soup_client_context_get_host (client_ctx);
+        user_agent = soup_message_headers_get_one (
+                soup_server_message_get_request_headers (msg),
+                "User-Agent");
+        host = soup_server_message_get_remote_host (msg);
 
         /* If there was no User-Agent in the request, try to guess from the
          * discovery message and put it into the response headers for further
@@ -985,9 +989,10 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
                 user_agent = gssdp_client_guess_user_agent (client, host);
 
                 if (user_agent != NULL) {
-                        soup_message_headers_append (msg->response_headers,
-                                                     "User-Agent",
-                                                     user_agent);
+                        soup_message_headers_append (
+                                soup_server_message_get_response_headers (msg),
+                                "User-Agent",
+                                user_agent);
                 }
         } else {
                 update_client_cache (host_path_data->context, host, user_agent);
@@ -996,13 +1001,16 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
         /* Construct base local path */
         local_path = construct_local_path (path, user_agent, host_path_data);
         if (!local_path) {
-                soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_BAD_REQUEST,
+                                                "Bad request");
 
                 goto DONE;
         }
 
         /* Get preferred locales */
-        orig_locales = locales = http_request_get_accept_locales (msg);
+        orig_locales = locales = http_request_get_accept_locales (
+                soup_server_message_get_request_headers (msg));
 
  AGAIN:
         /* Add locale suffix if available */
@@ -1011,7 +1019,9 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
         /* See what we've got */
         if (g_stat (path_to_open, &st) == -1) {
                 if (errno == EPERM)
-                        soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+                        soup_server_message_set_status (msg,
+                                                        SOUP_STATUS_FORBIDDEN,
+                                                        "Forbidden");
                 else if (errno == ENOENT) {
                         if (locales) {
                                 g_free (path_to_open);
@@ -1020,11 +1030,15 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
 
                                 goto AGAIN;
                         } else
-                                soup_message_set_status (msg,
-                                                         SOUP_STATUS_NOT_FOUND);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_NOT_FOUND,
+                                        "Not found");
                 } else
-                        soup_message_set_status
-                                (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+                        soup_server_message_set_status (
+                                msg,
+                                SOUP_STATUS_INTERNAL_SERVER_ERROR,
+                                "Internal server error");
 
                 goto DONE;
         }
@@ -1059,25 +1073,40 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
 
                 g_error_free (error);
 
-                soup_message_set_status (msg,
-                                         SOUP_STATUS_INTERNAL_SERVER_ERROR);
+                soup_server_message_set_status (
+                        msg,
+                        SOUP_STATUS_INTERNAL_SERVER_ERROR,
+                        "Internal server error");
 
                 goto DONE;
         }
 
         /* Handle method (GET or HEAD) */
         status = SOUP_STATUS_OK;
-
-        if (msg->method == SOUP_METHOD_GET) {
+        SoupMessageHeaders *response_headers =
+                soup_server_message_get_response_headers (msg);
+        SoupMessageHeaders *request_headers =
+                soup_server_message_get_request_headers (msg);
+
+        /* Add requested content */
+        // Creating the buffer here regardless of whether we use it.
+        // It will take ownership of the mapped file and we can unref it on exit
+        // This will prevent leaking the mapped file in other cases
+        GBytes *buffer = g_bytes_new_with_free_func (
+                g_mapped_file_get_contents (mapped_file),
+                g_mapped_file_get_length (mapped_file),
+                (GDestroyNotify) g_mapped_file_unref,
+                mapped_file);
+
+        if (soup_server_message_get_method (msg) == SOUP_METHOD_GET) {
                 gboolean have_range;
-                SoupBuffer *buffer;
                 SoupRange *ranges;
                 int nranges;
 
                 /* Find out range */
                 have_range = FALSE;
 
-                if (soup_message_headers_get_ranges (msg->request_headers,
+                if (soup_message_headers_get_ranges (request_headers,
                                                      st.st_size,
                                                      &ranges,
                                                      &nranges))
@@ -1089,90 +1118,89 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
                                    st.st_size < 0 ||
                                    ranges[0].start >= st.st_size ||
                                    ranges[0].start > ranges[0].end)) {
-                        soup_message_set_status
-                                (msg,
-                                 SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
-                        soup_message_headers_free_ranges (msg->request_headers,
+                        soup_server_message_set_status (
+                                msg,
+                                SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE,
+                                "Range not satisfyable");
+                        soup_message_headers_free_ranges (request_headers,
                                                           ranges);
 
                         goto DONE;
                 }
 
-                /* Add requested content */
-                buffer = soup_buffer_new_with_owner
-                             (g_mapped_file_get_contents (mapped_file),
-                              g_mapped_file_get_length (mapped_file),
-                              mapped_file,
-                              (GDestroyNotify) g_mapped_file_unref);
+                SoupMessageBody *message_body =
+                        soup_server_message_get_response_body (msg);
 
                 /* Set range and status */
                 if (have_range) {
-                        SoupBuffer *range_buffer;
+                        GBytes *range_buffer;
 
-                        soup_message_body_truncate (msg->response_body);
+                        soup_message_body_truncate (message_body);
                         soup_message_headers_set_content_range (
-                                                          msg->response_headers,
-                                                          ranges[0].start,
-                                                          ranges[0].end,
-                                                          buffer->length);
-                        range_buffer = soup_buffer_new_subbuffer (
-                                           buffer,
-                                           ranges[0].start,
-                                           ranges[0].end - ranges[0].start + 1);
-                        soup_message_body_append_buffer (msg->response_body,
-                                                         range_buffer);
+                                response_headers,
+                                ranges[0].start,
+                                ranges[0].end,
+                                g_bytes_get_size (buffer));
+                        range_buffer = g_bytes_new_from_bytes (
+                                buffer,
+                                ranges[0].start,
+                                ranges[0].end - ranges[0].start + 1);
+                        soup_message_body_append_bytes (message_body,
+                                                        range_buffer);
                         status = SOUP_STATUS_PARTIAL_CONTENT;
 
-                        soup_message_headers_free_ranges (msg->request_headers,
+                        soup_message_headers_free_ranges (request_headers,
                                                           ranges);
-                        soup_buffer_free (range_buffer);
+                        g_bytes_unref (range_buffer);
                 } else
-                        soup_message_body_append_buffer (msg->response_body, buffer);
+                        soup_message_body_append_bytes (message_body, buffer);
 
-                soup_buffer_free (buffer);
-        } else if (msg->method == SOUP_METHOD_HEAD) {
+        } else if (soup_server_message_get_method (msg) == SOUP_METHOD_HEAD) {
                 char *length;
 
-                length = g_strdup_printf ("%lu", (gulong) st.st_size);
-                soup_message_headers_append (msg->response_headers,
+                length = g_strdup_printf ("%zu", st.st_size);
+                soup_message_headers_append (response_headers,
                                              "Content-Length",
                                              length);
                 g_free (length);
 
         } else {
-                soup_message_set_status (msg,
-                                         SOUP_STATUS_METHOD_NOT_ALLOWED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_METHOD_NOT_ALLOWED,
+                                                "Method not allowed");
 
                 goto DONE;
         }
 
         /* Set Content-Type */
-        http_response_set_content_type (msg,
-                                        path_to_open, 
-                                        (guchar *) g_mapped_file_get_contents
-                                                                (mapped_file),
-                                        st.st_size);
+        http_response_set_content_type (
+                response_headers,
+                path_to_open,
+                (guchar *) g_mapped_file_get_contents (mapped_file),
+                st.st_size);
 
         /* Set Content-Language */
         if (locales)
-                http_response_set_content_locale (msg, locales->data);
-        else if (soup_message_headers_get_one (msg->request_headers,
+                http_response_set_content_locale (response_headers,
+                                                  locales->data);
+        else if (soup_message_headers_get_one (request_headers,
                                                "Accept-Language")) {
-                soup_message_headers_append (msg->response_headers,
+                soup_message_headers_append (response_headers,
                                              "Content-Language",
                                              host_path_data->default_language);
         }
 
         /* Set Accept-Ranges */
-        soup_message_headers_append (msg->response_headers,
+        soup_message_headers_append (response_headers,
                                      "Accept-Ranges",
                                      "bytes");
 
         /* Set status */
-        soup_message_set_status (msg, status);
+        soup_server_message_set_status (msg, status, NULL);
 
  DONE:
         /* Cleanup */
+        g_bytes_unref (buffer);
         g_free (path_to_open);
         g_free (local_path);
 
@@ -1428,13 +1456,14 @@ gupnp_acl_async_callback (GUPnPAcl *acl,
         allowed = gupnp_acl_is_allowed_finish (acl, res, &error);
         soup_server_unpause_message (data->server, data->message);
         if (!allowed)
-                soup_message_set_status (data->message, SOUP_STATUS_FORBIDDEN);
+                soup_server_message_set_status (data->message,
+                                                SOUP_STATUS_FORBIDDEN,
+                                                "Forbidden");
         else
                 data->handler->callback (data->server,
                                          data->message,
                                          data->path,
                                          data->query,
-                                         data->client,
                                          data->handler->user_data);
 
         acl_async_handler_free (data);
@@ -1442,10 +1471,9 @@ gupnp_acl_async_callback (GUPnPAcl *acl,
 
 static void
 gupnp_acl_server_handler (SoupServer *server,
-                          SoupMessage *msg,
+                          SoupServerMessage *msg,
                           const char *path,
                           GHashTable *query,
-                          SoupClientContext *client,
                           gpointer user_data)
 {
         AclServerHandler *handler = (AclServerHandler *) user_data;
@@ -1455,7 +1483,7 @@ gupnp_acl_server_handler (SoupServer *server,
         GUPnPContextPrivate *priv;
 
         priv = gupnp_context_get_instance_private (handler->context);
-        host = soup_client_context_get_host (client);
+        host = soup_server_message_get_remote_host (msg);
 
         if (handler->service) {
                 g_object_get (handler->service,
@@ -1467,8 +1495,9 @@ gupnp_acl_server_handler (SoupServer *server,
                 }
         }
 
-        agent = soup_message_headers_get_one (msg->request_headers,
-                                              "User-Agent");
+        agent = soup_message_headers_get_one (
+                soup_server_message_get_request_headers (msg),
+                "User-Agent");
         if (agent == NULL) {
                 agent = gssdp_client_guess_user_agent
                                 (GSSDP_CLIENT (handler->context),
@@ -1483,32 +1512,40 @@ gupnp_acl_server_handler (SoupServer *server,
                                                    path,
                                                    host,
                                                    agent)) {
-                                soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_FORBIDDEN,
+                                        "Forbidden");
 
                                 return;
                         }
                 } else {
                         AclAsyncHandler *data;
 
-                        data = acl_async_handler_new (server, msg, path, query, client, handler);
+                        data = acl_async_handler_new (server,
+                                                      msg,
+                                                      path,
+                                                      query,
+                                                      handler);
 
                         soup_server_pause_message (server, msg);
-                        gupnp_acl_is_allowed_async (priv->acl,
-                                                    device,
-                                                    handler->service,
-                                                    path,
-                                                    soup_client_context_get_host (client),
-                                                    agent,
-                                                    NULL,
-                                                    (GAsyncReadyCallback) gupnp_acl_async_callback,
-                                                    data);
+                        gupnp_acl_is_allowed_async (
+                                priv->acl,
+                                device,
+                                handler->service,
+                                path,
+                                host,
+                                agent,
+                                NULL,
+                                (GAsyncReadyCallback) gupnp_acl_async_callback,
+                                data);
 
                         return;
                 }
         }
 
         /* Delegate to orignal callback */
-        handler->callback (server, msg, path, query, client, handler->user_data);
+        handler->callback (server, msg, path, query, handler->user_data);
 }
 
 /**
@@ -1563,6 +1600,8 @@ _gupnp_context_add_server_handler_with_data (GUPnPContext *context,
 
         g_return_if_fail (GUPNP_IS_CONTEXT (context));
 
+        g_print ("Adding ACL-Protected handler for %s\n", path);
+
         priv = gupnp_context_get_instance_private (context);
         soup_server_add_handler (priv->server,
                                  path,
@@ -1607,7 +1646,7 @@ gupnp_context_remove_server_handler (GUPnPContext *context, const char *path)
 char *
 gupnp_context_rewrite_uri (GUPnPContext *context, const char *uri)
 {
-        SoupURI *soup_uri = NULL;
+        GUri *soup_uri = NULL;
         char *retval = NULL;
 
         soup_uri = gupnp_context_rewrite_uri_to_uri (context, uri);
@@ -1616,8 +1655,8 @@ gupnp_context_rewrite_uri (GUPnPContext *context, const char *uri)
                 return NULL;
         }
 
-        retval = soup_uri_to_string (soup_uri, FALSE);
-        soup_uri_free (soup_uri);
+        retval = g_uri_to_string_partial (soup_uri, G_URI_HIDE_PASSWORD);
+        g_uri_unref (soup_uri);
 
         return retval;
 }
@@ -1636,23 +1675,28 @@ gupnp_context_rewrite_uri (GUPnPContext *context, const char *uri)
  * Since: 1.2.3
  * Stability: Private
  */
-SoupURI *
+GUri *
 gupnp_context_rewrite_uri_to_uri (GUPnPContext *context, const char *uri)
 {
         const char *host = NULL;
-        SoupURI *soup_uri = NULL;
+        GUri *soup_uri = NULL;
         GInetAddress *addr = NULL;
         int index = -1;
+        GError *error = NULL;
 
-        soup_uri = soup_uri_new (uri);
+        soup_uri = g_uri_parse (uri, G_URI_FLAGS_NONE, &error);
 
-        if (soup_uri == NULL) {
-                g_warning ("Invalid call-back url: %s", uri);
+        if (error != NULL) {
+                g_warning ("Invalid call-back url: %s (%s)",
+                           uri,
+                           error->message);
+
+                g_clear_error (&error);
 
                 return NULL;
         }
 
-        host = soup_uri_get_host (soup_uri);
+        host = g_uri_get_host (soup_uri);
         addr = g_inet_address_new_from_string (host);
         index = gssdp_client_get_index (GSSDP_CLIENT (context));
 
@@ -1663,8 +1707,11 @@ gupnp_context_rewrite_uri_to_uri (GUPnPContext *context, const char *uri)
                 new_host = g_strdup_printf ("%s%%%d",
                                             host,
                                             index);
-                soup_uri_set_host (soup_uri, new_host);
+                GUri *new_uri =
+                        soup_uri_copy (soup_uri, SOUP_URI_HOST, new_host, NULL);
                 g_free (new_host);
+                g_uri_unref (soup_uri);
+                soup_uri = new_uri;
         }
 
         g_object_unref (addr);
diff --git a/libgupnp/gupnp-control-point.c b/libgupnp/gupnp-control-point.c
index dc04732..97bdfa5 100644
--- a/libgupnp/gupnp-control-point.c
+++ b/libgupnp/gupnp-control-point.c
@@ -84,6 +84,7 @@ get_description_url_data_free (GetDescriptionURLData *data)
 {
         gupnp_control_point_remove_pending_get (data->control_point, data);
         if (data->message) {
+#if 0
                 GUPnPContext *context;
                 SoupSession *session;
 
@@ -94,6 +95,7 @@ get_description_url_data_free (GetDescriptionURLData *data)
                 soup_session_cancel_message (session,
                                              data->message,
                                              SOUP_STATUS_CANCELLED);
+#endif
         }
 
         if (data->timeout_source) {
@@ -284,13 +286,13 @@ find_device_node (GUPnPControlPoint *control_point,
 }
 
 static void
-create_and_report_service_proxy (GUPnPControlPoint  *control_point,
-                                 GUPnPXMLDoc        *doc,
-                                 xmlNode            *element,
-                                 const char         *udn,
-                                 const char         *service_type,
-                                 const char         *description_url,
-                                 SoupURI            *url_base)
+create_and_report_service_proxy (GUPnPControlPoint *control_point,
+                                 GUPnPXMLDoc *doc,
+                                 xmlNode *element,
+                                 const char *udn,
+                                 const char *service_type,
+                                 const char *description_url,
+                                 GUri *url_base)
 {
         GUPnPServiceProxy *proxy;
         GUPnPResourceFactory *factory;
@@ -324,12 +326,12 @@ create_and_report_service_proxy (GUPnPControlPoint  *control_point,
 }
 
 static void
-create_and_report_device_proxy (GUPnPControlPoint  *control_point,
-                                GUPnPXMLDoc        *doc,
-                                xmlNode            *element,
-                                const char         *udn,
-                                const char         *description_url,
-                                SoupURI            *url_base)
+create_and_report_device_proxy (GUPnPControlPoint *control_point,
+                                GUPnPXMLDoc *doc,
+                                xmlNode *element,
+                                const char *udn,
+                                const char *description_url,
+                                GUri *url_base)
 {
         GUPnPDeviceProxy *proxy;
         GUPnPResourceFactory *factory;
@@ -400,13 +402,13 @@ compare_service_types_versioned (const char *searched_service,
 
 /* Search @element for matching services */
 static void
-process_service_list (xmlNode           *element,
+process_service_list (xmlNode *element,
                       GUPnPControlPoint *control_point,
-                      GUPnPXMLDoc       *doc,
-                      const char        *udn,
-                      const char        *service_type,
-                      const char        *description_url,
-                      SoupURI           *url_base)
+                      GUPnPXMLDoc *doc,
+                      const char *udn,
+                      const char *service_type,
+                      const char *description_url,
+                      GUri *url_base)
 {
         g_object_ref (control_point);
 
@@ -446,13 +448,13 @@ process_service_list (xmlNode           *element,
 
 /* Recursively search @element for matching devices */
 static void
-process_device_list (xmlNode           *element,
+process_device_list (xmlNode *element,
                      GUPnPControlPoint *control_point,
-                     GUPnPXMLDoc       *doc,
-                     const char        *udn,
-                     const char        *service_type,
-                     const char        *description_url,
-                     SoupURI           *url_base)
+                     GUPnPXMLDoc *doc,
+                     const char *udn,
+                     const char *service_type,
+                     const char *description_url,
+                     GUri *url_base)
 {
         g_object_ref (control_point);
 
@@ -531,7 +533,7 @@ description_loaded (GUPnPControlPoint *control_point,
                     const char        *description_url)
 {
         xmlNode *element;
-        SoupURI *url_base;
+        GUri *url_base;
 
         /* Save the URL base, if any */
         element = xml_util_get_element ((xmlNode *)
@@ -551,7 +553,8 @@ description_loaded (GUPnPControlPoint *control_point,
                                                            "URLBase",
                                                            NULL);
         if (!url_base)
-                url_base = soup_uri_new (description_url);
+                url_base =
+                        g_uri_parse (description_url, G_URI_FLAGS_NONE, NULL);
 
         /* Iterate matching devices */
         process_device_list (element,
@@ -563,7 +566,7 @@ description_loaded (GUPnPControlPoint *control_point,
                              url_base);
 
         /* Cleanup */
-        soup_uri_free (url_base);
+        g_uri_unref (url_base);
 }
 
 
@@ -574,15 +577,23 @@ description_url_retry_timeout (gpointer user_data);
  * Description URL downloaded.
  */
 static void
-got_description_url (SoupSession           *session,
-                     SoupMessage           *msg,
+got_description_url (GObject *source,
+                     GAsyncResult *res,
                      GetDescriptionURLData *data)
 {
         GUPnPXMLDoc *doc;
         GUPnPControlPointPrivate *priv;
+        GError *error = NULL;
+        SoupMessage *message = data->message;
 
-        if (msg->status_code == SOUP_STATUS_CANCELLED)
-                return;
+        GBytes *body = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                          res,
+                                                          &error);
+
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+                goto out;
+
+        // FIXME: What to do on other errors...
 
         data->message = NULL;
         priv = gupnp_control_point_get_instance_private (data->control_point);
@@ -600,16 +611,19 @@ got_description_url (SoupSession           *session,
 
                 get_description_url_data_free (data);
 
-                return;
+                goto out;
         }
 
         /* Not cached */
-        if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+        if (SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (message))) {
                 xmlDoc *xml_doc;
+                gsize length;
+                gconstpointer body_data;
+
+                body_data = g_bytes_get_data (body, &length);
 
                 /* Parse response */
-                xml_doc = xmlRecoverMemory (msg->response_body->data,
-                                            msg->response_body->length);
+                xml_doc = xmlRecoverMemory (body_data, length);
                 if (xml_doc) {
                         doc = gupnp_xml_doc_new (xml_doc);
 
@@ -637,18 +651,18 @@ got_description_url (SoupSession           *session,
 
                 get_description_url_data_free (data);
         } else {
-                GMainContext *async_context;
+                GMainContext *async_context =
+                        g_main_context_get_thread_default ();
 
                 /* Retry GET after a timeout */
-                async_context = soup_session_get_async_context (session);
-
                 data->tries--;
 
                 if (data->tries > 0) {
-                        g_warning ("Failed to GET %s: %s, retrying in %d seconds",
-                                   data->description_url,
-                                   msg->reason_phrase,
-                                   data->timeout);
+                        g_warning (
+                                "Failed to GET %s: %s, retrying in %d seconds",
+                                data->description_url,
+                                soup_message_get_reason_phrase (message),
+                                data->timeout);
 
                         data->timeout_source = g_timeout_source_new_seconds
                                         (data->timeout);
@@ -662,6 +676,10 @@ got_description_url (SoupSession           *session,
                         g_warning ("Maximum number of retries failed, not trying again");
                 }
         }
+
+out:
+        g_bytes_unref (body);
+        g_object_unref (message);
 }
 
 /*
@@ -735,11 +753,13 @@ load_description (GUPnPControlPoint *control_point,
                 priv->pending_gets = g_list_prepend (priv->pending_gets,
                                                      data);
 
-                soup_session_queue_message (session,
-                                            data->message,
-                                            (SoupSessionCallback)
-                                                   got_description_url,
-                                            data);
+                soup_session_send_and_read_async (
+                        session,
+                        data->message,
+                        G_PRIORITY_DEFAULT,
+                        NULL,
+                        (GAsyncReadyCallback) got_description_url,
+                        data);
         }
 }
 
diff --git a/libgupnp/gupnp-device-info.c b/libgupnp/gupnp-device-info.c
index fd3d8ce..c7d9374 100644
--- a/libgupnp/gupnp-device-info.c
+++ b/libgupnp/gupnp-device-info.c
@@ -31,7 +31,7 @@ struct _GUPnPDeviceInfoPrivate {
         char *udn;
         char *device_type;
 
-        SoupURI *url_base;
+        GUri *url_base;
 
         GUPnPXMLDoc *doc;
 
@@ -177,7 +177,7 @@ gupnp_device_info_finalize (GObject *object)
         g_free (priv->udn);
         g_free (priv->device_type);
 
-        g_clear_pointer (&priv->url_base, soup_uri_free);
+        g_clear_pointer (&priv->url_base, g_uri_unref);
 
         G_OBJECT_CLASS (gupnp_device_info_parent_class)->finalize (object);
 }
@@ -290,18 +290,17 @@ gupnp_device_info_class_init (GUPnPDeviceInfoClass *klass)
          *
          * The URL base (#SoupURI).
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_URL_BASE,
-                 g_param_spec_boxed ("url-base",
-                                     "URL base",
-                                     "The URL base",
-                                     SOUP_TYPE_URI,
-                                     G_PARAM_READWRITE |
-                                     G_PARAM_CONSTRUCT |
-                                     G_PARAM_STATIC_NAME |
-                                     G_PARAM_STATIC_NICK |
-                                     G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_URL_BASE,
+                g_param_spec_boxed ("url-base",
+                                    "URL base",
+                                    "The URL base",
+                                    G_TYPE_URI,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                                            G_PARAM_STATIC_NAME |
+                                            G_PARAM_STATIC_NICK |
+                                            G_PARAM_STATIC_BLURB));
 
         /**
          * GUPnPDeviceInfo:document:
@@ -413,7 +412,7 @@ gupnp_device_info_get_location (GUPnPDeviceInfo *info)
  *
  * Returns: A #SoupURI.
  **/
-const SoupURI *
+const GUri *
 gupnp_device_info_get_url_base (GUPnPDeviceInfo *info)
 {
         GUPnPDeviceInfoPrivate *priv;
@@ -910,12 +909,15 @@ gupnp_device_info_get_icon_url (GUPnPDeviceInfo *info,
                         *height = icon->height;
 
                 if (icon->url) {
-                        SoupURI *uri;
-
-                        uri = soup_uri_new_with_base (priv->url_base,
-                                                      (const char *) icon->url);
-                        ret = soup_uri_to_string (uri, FALSE);
-                        soup_uri_free (uri);
+                        GUri *uri;
+
+                        uri = g_uri_parse_relative (priv->url_base,
+                                                    (const char *) icon->url,
+                                                    G_URI_FLAGS_NONE,
+                                                    NULL);
+                        ret = g_uri_to_string_partial (uri,
+                                                       G_URI_HIDE_PASSWORD);
+                        g_uri_unref (uri);
                 } else
                         ret = NULL;
         } else {
diff --git a/libgupnp/gupnp-device-info.h b/libgupnp/gupnp-device-info.h
index 8ee35d7..2fb2a85 100644
--- a/libgupnp/gupnp-device-info.h
+++ b/libgupnp/gupnp-device-info.h
@@ -11,7 +11,6 @@
 
 #include <glib-object.h>
 #include <libxml/tree.h>
-#include <libsoup/soup-uri.h>
 
 #include "gupnp-context.h"
 #include "gupnp-service-info.h"
@@ -56,8 +55,8 @@ gupnp_device_info_get_context            (GUPnPDeviceInfo *info);
 const char *
 gupnp_device_info_get_location           (GUPnPDeviceInfo *info);
 
-const SoupURI *
-gupnp_device_info_get_url_base           (GUPnPDeviceInfo *info);
+const GUri *
+gupnp_device_info_get_url_base (GUPnPDeviceInfo *info);
 
 const char *
 gupnp_device_info_get_udn                (GUPnPDeviceInfo *info);
diff --git a/libgupnp/gupnp-device-proxy.c b/libgupnp/gupnp-device-proxy.c
index ea0d6ca..762dae0 100644
--- a/libgupnp/gupnp-device-proxy.c
+++ b/libgupnp/gupnp-device-proxy.c
@@ -37,7 +37,7 @@ gupnp_device_proxy_get_device (GUPnPDeviceInfo *info,
         GUPnPContext         *context;
         GUPnPXMLDoc          *doc;
         const char           *location;
-        const SoupURI        *url_base;
+        const GUri *url_base;
 
         factory = gupnp_device_info_get_resource_factory (info);
         context = gupnp_device_info_get_context (info);
@@ -65,7 +65,7 @@ gupnp_device_proxy_get_service (GUPnPDeviceInfo *info,
         GUPnPContext         *context;
         GUPnPXMLDoc          *doc;
         const char           *location, *udn;
-        const SoupURI        *url_base;
+        const GUri *url_base;
 
         factory = gupnp_device_info_get_resource_factory (info);
         context = gupnp_device_info_get_context (info);
diff --git a/libgupnp/gupnp-device.c b/libgupnp/gupnp-device.c
index 71b11cf..ea49f57 100644
--- a/libgupnp/gupnp-device.c
+++ b/libgupnp/gupnp-device.c
@@ -50,7 +50,7 @@ gupnp_device_get_device (GUPnPDeviceInfo *info,
         GUPnPContext         *context;
         GUPnPDevice          *root_device;
         const char           *location;
-        const SoupURI        *url_base;
+        const GUri *url_base;
 
         device = GUPNP_DEVICE (info);
         priv = gupnp_device_get_instance_private (device);
@@ -90,7 +90,7 @@ gupnp_device_get_service (GUPnPDeviceInfo *info,
         GUPnPContext         *context;
         GUPnPDevice          *root_device;
         const char           *location, *udn;
-        const SoupURI        *url_base;
+        const GUri *url_base;
 
         device = GUPNP_DEVICE (info);
         priv = gupnp_device_get_instance_private (device);
diff --git a/libgupnp/gupnp-error.c b/libgupnp/gupnp-error.c
index 7dcf5db..778854e 100644
--- a/libgupnp/gupnp-error.c
+++ b/libgupnp/gupnp-error.c
@@ -128,17 +128,19 @@ void
 _gupnp_error_set_server_error (GError     **error,
                                SoupMessage *msg)
 {
-        g_set_error_literal (error,
-                             GUPNP_SERVER_ERROR,
-                             code_from_status_code (msg->status_code),
-                             msg->reason_phrase);
+        g_set_error_literal (
+                error,
+                GUPNP_SERVER_ERROR,
+                code_from_status_code (soup_message_get_status (msg)),
+                soup_message_get_reason_phrase (msg));
 }
 
 /* Create a #GError with status of @msg */
 GError *
 _gupnp_error_new_server_error (SoupMessage *msg)
 {
-        return g_error_new_literal (GUPNP_SERVER_ERROR,
-                                    code_from_status_code (msg->status_code),
-                                    msg->reason_phrase);
+        return g_error_new_literal (
+                GUPNP_SERVER_ERROR,
+                code_from_status_code (soup_message_get_status (msg)),
+                soup_message_get_reason_phrase (msg));
 }
diff --git a/libgupnp/gupnp-resource-factory-private.h b/libgupnp/gupnp-resource-factory-private.h
index 9a5f10d..fe9e4a7 100644
--- a/libgupnp/gupnp-resource-factory-private.h
+++ b/libgupnp/gupnp-resource-factory-private.h
@@ -22,43 +22,41 @@
 G_BEGIN_DECLS
 
 G_GNUC_INTERNAL GUPnPDeviceProxy *
-gupnp_resource_factory_create_device_proxy
-                                      (GUPnPResourceFactory *factory,
-                                       GUPnPContext         *context,
-                                       GUPnPXMLDoc          *doc,
-                                       xmlNode              *element,
-                                       const char           *udn,
-                                       const char           *location,
-                                       const SoupURI        *url_base);
+gupnp_resource_factory_create_device_proxy (GUPnPResourceFactory *factory,
+                                            GUPnPContext *context,
+                                            GUPnPXMLDoc *doc,
+                                            xmlNode *element,
+                                            const char *udn,
+                                            const char *location,
+                                            const GUri *url_base);
 
 G_GNUC_INTERNAL GUPnPServiceProxy *
-gupnp_resource_factory_create_service_proxy
-                                      (GUPnPResourceFactory *factory,
-                                       GUPnPContext         *context,
-                                       GUPnPXMLDoc          *doc,
-                                       xmlNode              *element,
-                                       const char           *udn,
-                                       const char           *service_type,
-                                       const char           *location,
-                                       const SoupURI        *url_base);
+gupnp_resource_factory_create_service_proxy (GUPnPResourceFactory *factory,
+                                             GUPnPContext *context,
+                                             GUPnPXMLDoc *doc,
+                                             xmlNode *element,
+                                             const char *udn,
+                                             const char *service_type,
+                                             const char *location,
+                                             const GUri *url_base);
 
 G_GNUC_INTERNAL GUPnPDevice *
-gupnp_resource_factory_create_device  (GUPnPResourceFactory *factory,
-                                       GUPnPContext         *context,
-                                       GUPnPDevice          *root_device,
-                                       xmlNode              *element,
-                                       const char           *udn,
-                                       const char           *location,
-                                       const SoupURI        *url_base);
+gupnp_resource_factory_create_device (GUPnPResourceFactory *factory,
+                                      GUPnPContext *context,
+                                      GUPnPDevice *root_device,
+                                      xmlNode *element,
+                                      const char *udn,
+                                      const char *location,
+                                      const GUri *url_base);
 
 G_GNUC_INTERNAL GUPnPService *
 gupnp_resource_factory_create_service (GUPnPResourceFactory *factory,
-                                       GUPnPContext         *context,
-                                       GUPnPDevice          *root_device,
-                                       xmlNode              *element,
-                                       const char           *udn,
-                                       const char           *location,
-                                       const SoupURI        *url_base);
+                                       GUPnPContext *context,
+                                       GUPnPDevice *root_device,
+                                       xmlNode *element,
+                                       const char *udn,
+                                       const char *location,
+                                       const GUri *url_base);
 
 G_END_DECLS
 
diff --git a/libgupnp/gupnp-resource-factory.c b/libgupnp/gupnp-resource-factory.c
index 8d7e362..5ad58d2 100644
--- a/libgupnp/gupnp-resource-factory.c
+++ b/libgupnp/gupnp-resource-factory.c
@@ -192,14 +192,13 @@ lookup_type_with_fallback (GHashTable *resource_types,
  * Return value:(nullable)(transfer full): A new #GUPnPDeviceProxy.
  **/
 GUPnPDeviceProxy *
-gupnp_resource_factory_create_device_proxy
-                                (GUPnPResourceFactory *factory,
-                                 GUPnPContext         *context,
-                                 GUPnPXMLDoc          *doc,
-                                 xmlNode              *element,
-                                 const char           *udn,
-                                 const char           *location,
-                                 const SoupURI        *url_base)
+gupnp_resource_factory_create_device_proxy (GUPnPResourceFactory *factory,
+                                            GUPnPContext *context,
+                                            GUPnPXMLDoc *doc,
+                                            xmlNode *element,
+                                            const char *udn,
+                                            const char *location,
+                                            const GUri *url_base)
 {
         GUPnPDeviceProxy *proxy;
         GType proxy_type;
@@ -251,15 +250,14 @@ gupnp_resource_factory_create_device_proxy
  * Return value:(nullable)(transfer full): A new #GUPnPServiceProxy.
  **/
 GUPnPServiceProxy *
-gupnp_resource_factory_create_service_proxy
-                                (GUPnPResourceFactory *factory,
-                                 GUPnPContext         *context,
-                                 GUPnPXMLDoc          *doc,
-                                 xmlNode              *element,
-                                 const char           *udn,
-                                 const char           *service_type,
-                                 const char           *location,
-                                 const SoupURI        *url_base)
+gupnp_resource_factory_create_service_proxy (GUPnPResourceFactory *factory,
+                                             GUPnPContext *context,
+                                             GUPnPXMLDoc *doc,
+                                             xmlNode *element,
+                                             const char *udn,
+                                             const char *service_type,
+                                             const char *location,
+                                             const GUri *url_base)
 {
         GUPnPServiceProxy *proxy;
         GType              proxy_type = GUPNP_TYPE_SERVICE_PROXY;
@@ -309,14 +307,13 @@ gupnp_resource_factory_create_service_proxy
  * Return value: (nullable)(transfer full): A new #GUPnPDevice.
  **/
 GUPnPDevice *
-gupnp_resource_factory_create_device
-                                (GUPnPResourceFactory *factory,
-                                 GUPnPContext         *context,
-                                 GUPnPDevice          *root_device,
-                                 xmlNode              *element,
-                                 const char           *udn,
-                                 const char           *location,
-                                 const SoupURI        *url_base)
+gupnp_resource_factory_create_device (GUPnPResourceFactory *factory,
+                                      GUPnPContext *context,
+                                      GUPnPDevice *root_device,
+                                      xmlNode *element,
+                                      const char *udn,
+                                      const char *location,
+                                      const GUri *url_base)
 {
         GUPnPDevice *device;
         GType        device_type = GUPNP_TYPE_DEVICE;
@@ -365,14 +362,13 @@ gupnp_resource_factory_create_device
  * Return value: (nullable)(transfer full): A new #GUPnPService.
  **/
 GUPnPService *
-gupnp_resource_factory_create_service
-                                (GUPnPResourceFactory *factory,
-                                 GUPnPContext         *context,
-                                 GUPnPDevice          *root_device,
-                                 xmlNode              *element,
-                                 const char           *udn,
-                                 const char           *location,
-                                 const SoupURI        *url_base)
+gupnp_resource_factory_create_service (GUPnPResourceFactory *factory,
+                                       GUPnPContext *context,
+                                       GUPnPDevice *root_device,
+                                       xmlNode *element,
+                                       const char *udn,
+                                       const char *location,
+                                       const GUri *url_base)
 {
         GUPnPService *service;
         GType         service_type = GUPNP_TYPE_SERVICE;
diff --git a/libgupnp/gupnp-root-device.c b/libgupnp/gupnp-root-device.c
index 0ba44f9..6643dc9 100644
--- a/libgupnp/gupnp-root-device.c
+++ b/libgupnp/gupnp-root-device.c
@@ -288,10 +288,10 @@ gupnp_root_device_initable_init (GInitable     *initable,
         GUPnPRootDevice *device;
         GUPnPContext *context;
         const char *udn;
-        SoupURI *uri;
+        GUri *uri;
         char *desc_path, *location, *usn, *relative_location;
         xmlNode *root_element, *element;
-        SoupURI *url_base;
+        GUri *url_base;
         gboolean result = FALSE;
         GUPnPRootDevicePrivate *priv;
 
@@ -406,9 +406,10 @@ gupnp_root_device_initable_init (GInitable     *initable,
         gupnp_context_host_path (context, priv->description_dir, "");
 
         /* Generate full location */
-        soup_uri_set_path (uri, relative_location);
-        location = soup_uri_to_string (uri, FALSE);
-
+        GUri *new_uri =
+                soup_uri_copy (uri, SOUP_URI_PATH, relative_location, NULL);
+        location = g_uri_to_string_partial (new_uri, G_URI_HIDE_PASSWORD);
+        g_uri_unref (new_uri);
         g_free (relative_location);
 
         /* Save the URL base, if any */
@@ -416,15 +417,14 @@ gupnp_root_device_initable_init (GInitable     *initable,
                                                            "URLBase",
                                                            NULL);
         if (!url_base)
-                url_base = soup_uri_new (location);
+                url_base = g_uri_parse (location, G_URI_FLAGS_NONE, NULL);
 
         /* Set additional properties */
         g_object_set (G_OBJECT (device),
                       "location", location,
                       "url-base", url_base,
                       NULL);
-
-        soup_uri_free (url_base);
+        g_uri_unref (url_base);
 
         /* Create resource group */
         priv->group = gssdp_resource_group_new (GSSDP_CLIENT (context));
@@ -444,7 +444,7 @@ gupnp_root_device_initable_init (GInitable     *initable,
  DONE:
         /* Cleanup */
         if (uri)
-                soup_uri_free (uri);
+                g_uri_unref (uri);
 
         g_free (desc_path);
         g_free (location);
diff --git a/libgupnp/gupnp-service-info.c b/libgupnp/gupnp-service-info.c
index 9a9f4de..2a66141 100644
--- a/libgupnp/gupnp-service-info.c
+++ b/libgupnp/gupnp-service-info.c
@@ -38,7 +38,7 @@ struct _GUPnPServiceInfoPrivate {
         char *udn;
         char *service_type;
 
-        SoupURI *url_base;
+        GUri *url_base;
 
         GUPnPXMLDoc *doc;
 
@@ -178,9 +178,9 @@ gupnp_service_info_dispose (GObject *object)
 
         /* Cancel any pending SCPD GETs */
         if (priv->context) {
-                SoupSession *session;
+                // SoupSession *session;
 
-                session = gupnp_context_get_session (priv->context);
+                // session = gupnp_context_get_session (priv->context);
 
                 while (priv->pending_gets) {
                         GetSCPDURLData *data;
@@ -191,9 +191,11 @@ gupnp_service_info_dispose (GObject *object)
                                 g_cancellable_disconnect (data->cancellable,
                                                           data->cancelled_id);
 
+                        /*
                         soup_session_cancel_message (session,
                                                      data->message,
                                                      SOUP_STATUS_CANCELLED);
+                       */
 
                         get_scpd_url_data_free (data);
 
@@ -228,7 +230,7 @@ gupnp_service_info_finalize (GObject *object)
         g_free (priv->udn);
         g_free (priv->service_type);
 
-        soup_uri_free (priv->url_base);
+        g_uri_unref (priv->url_base);
 
         G_OBJECT_CLASS (gupnp_service_info_parent_class)->finalize (object);
 }
@@ -323,18 +325,17 @@ gupnp_service_info_class_init (GUPnPServiceInfoClass *klass)
          *
          * The URL base (#SoupURI).
          **/
-        g_object_class_install_property
-                (object_class,
-                 PROP_URL_BASE,
-                 g_param_spec_boxed ("url-base",
-                                     "URL base",
-                                     "The URL base",
-                                     SOUP_TYPE_URI,
-                                     G_PARAM_READWRITE |
-                                     G_PARAM_CONSTRUCT_ONLY |
-                                     G_PARAM_STATIC_NAME |
-                                     G_PARAM_STATIC_NICK |
-                                     G_PARAM_STATIC_BLURB));
+        g_object_class_install_property (
+                object_class,
+                PROP_URL_BASE,
+                g_param_spec_boxed ("url-base",
+                                    "URL base",
+                                    "The URL base",
+                                    G_TYPE_URI,
+                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                            G_PARAM_STATIC_NAME |
+                                            G_PARAM_STATIC_NICK |
+                                            G_PARAM_STATIC_BLURB));
 
         /**
          * GUPnPServiceInfo:document:
@@ -426,7 +427,7 @@ gupnp_service_info_get_location (GUPnPServiceInfo *info)
  *
  * Returns: A constant #SoupURI.
  **/
-const SoupURI *
+const GUri *
 gupnp_service_info_get_url_base (GUPnPServiceInfo *info)
 {
         GUPnPServiceInfoPrivate *priv;
@@ -575,9 +576,7 @@ gupnp_service_info_get_event_subscription_url (GUPnPServiceInfo *info)
  * SCPD URL downloaded.
  */
 static void
-got_scpd_url (G_GNUC_UNUSED SoupSession *session,
-              SoupMessage               *msg,
-              GetSCPDURLData            *data)
+got_scpd_url (GObject *source, GAsyncResult *res, GetSCPDURLData *data)
 {
         GUPnPServiceIntrospection *introspection;
         GError *error;
@@ -586,14 +585,22 @@ got_scpd_url (G_GNUC_UNUSED SoupSession *session,
         introspection = NULL;
         error = NULL;
 
-        if (msg->status_code == SOUP_STATUS_CANCELLED)
+        GBytes *body = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                          res,
+                                                          &error);
+
+        SoupStatus status = soup_message_get_status (data->message);
+
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
                 return;
 
-        if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+        if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                 xmlDoc *scpd;
+                gsize length;
+
+                gconstpointer data = g_bytes_get_data (body, &length);
 
-                scpd = xmlRecoverMemory (msg->response_body->data,
-                                         msg->response_body->length);
+                scpd = xmlRecoverMemory (data, length);
                 if (scpd) {
                         introspection = gupnp_service_introspection_new (scpd);
 
@@ -607,7 +614,7 @@ got_scpd_url (G_GNUC_UNUSED SoupSession *session,
                                          "Could not parse SCPD");
                 }
         } else
-                error = _gupnp_error_new_server_error (msg);
+                error = _gupnp_error_new_server_error (data->message);
 
         /* prevent the callback from canceling the cancellable
          * (and so freeing data just before we do) */
@@ -635,7 +642,7 @@ cancellable_cancelled_cb (GCancellable *cancellable,
 {
         GUPnPServiceInfo *info;
         GetSCPDURLData *data;
-        SoupSession *session;
+        // SoupSession *session;
         GError *error;
         GUPnPServiceInfoPrivate *priv;
 
@@ -644,10 +651,13 @@ cancellable_cancelled_cb (GCancellable *cancellable,
 
         priv = gupnp_service_info_get_instance_private (info);
 
+        /*
+        FIXME: Should have been part of the cancellable
         session = gupnp_context_get_session (priv->context);
         soup_session_cancel_message (session,
                                      data->message,
                                      SOUP_STATUS_CANCELLED);
+                                     */
 
         priv->pending_gets = g_list_remove (priv->pending_gets, data);
 
@@ -780,10 +790,12 @@ gupnp_service_info_get_introspection_async_full
 
         session = gupnp_context_get_session (priv->context);
 
-        soup_session_queue_message (session,
-                                    data->message,
-                                    (SoupSessionCallback) got_scpd_url,
-                                    data);
+        soup_session_send_and_read_async (session,
+                                          data->message,
+                                          G_PRIORITY_DEFAULT,
+                                          cancellable,
+                                          (GAsyncReadyCallback) got_scpd_url,
+                                          data);
 
         data->cancellable = cancellable;
         if (data->cancellable) {
diff --git a/libgupnp/gupnp-service-info.h b/libgupnp/gupnp-service-info.h
index a853c2f..2c561c0 100644
--- a/libgupnp/gupnp-service-info.h
+++ b/libgupnp/gupnp-service-info.h
@@ -10,7 +10,6 @@
 #define GUPNP_SERVICE_INFO_H
 
 #include <glib-object.h>
-#include <libsoup/soup-uri.h>
 
 #include "gupnp-context.h"
 #include "gupnp-service-introspection.h"
@@ -57,8 +56,8 @@ gupnp_service_info_get_context                (GUPnPServiceInfo *info);
 const char *
 gupnp_service_info_get_location               (GUPnPServiceInfo *info);
 
-const SoupURI *
-gupnp_service_info_get_url_base               (GUPnPServiceInfo *info);
+const GUri *
+gupnp_service_info_get_url_base (GUPnPServiceInfo *info);
 
 const char *
 gupnp_service_info_get_udn                    (GUPnPServiceInfo *info);
diff --git a/libgupnp/gupnp-service-private.h b/libgupnp/gupnp-service-private.h
index 1915505..c8a6096 100644
--- a/libgupnp/gupnp-service-private.h
+++ b/libgupnp/gupnp-service-private.h
@@ -14,7 +14,7 @@ struct _GUPnPServiceAction {
 
         char         *name;
 
-        SoupMessage  *msg;
+        SoupServerMessage *msg;
         gboolean      accept_gzip;
 
         GUPnPXMLDoc  *doc;
diff --git a/libgupnp/gupnp-service-proxy-action-private.h b/libgupnp/gupnp-service-proxy-action-private.h
index cb6492b..8fc8981 100644
--- a/libgupnp/gupnp-service-proxy-action-private.h
+++ b/libgupnp/gupnp-service-proxy-action-private.h
@@ -137,6 +137,7 @@ struct _GUPnPServiceProxyAction {
         gint header_pos;
 
         SoupMessage *msg;
+        GBytes *response;
         GString *msg_str;
 
         GCancellable *cancellable;
diff --git a/libgupnp/gupnp-service-proxy-action.c b/libgupnp/gupnp-service-proxy-action.c
index 4d9336c..733e1ab 100644
--- a/libgupnp/gupnp-service-proxy-action.c
+++ b/libgupnp/gupnp-service-proxy-action.c
@@ -65,7 +65,7 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
         xmlDoc *response;
         int code;
 
-        if (action->msg == NULL) {
+        if (action->msg == NULL || action->response == NULL) {
                 g_set_error (error,
                              GUPNP_SERVER_ERROR,
                              GUPNP_SERVER_ERROR_INVALID_RESPONSE,
@@ -74,8 +74,10 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
                 return NULL;
         }
 
+        SoupStatus status = soup_message_get_status (action->msg);
+
         /* Check for errors */
-        switch (action->msg->status_code) {
+        switch (status) {
         case SOUP_STATUS_OK:
         case SOUP_STATUS_INTERNAL_SERVER_ERROR:
                 break;
@@ -86,21 +88,24 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
         }
 
         /* Parse response */
-        response = xmlRecoverMemory (action->msg->response_body->data,
-                                     action->msg->response_body->length);
+        gconstpointer data;
+        gsize length;
+        data = g_bytes_get_data (action->response, &length);
+        response = xmlRecoverMemory (data, length);
+        g_clear_pointer (&action->response, g_bytes_unref);
 
         if (!response) {
-                if (action->msg->status_code == SOUP_STATUS_OK) {
+                if (status == SOUP_STATUS_OK) {
                         g_set_error (error,
                                      GUPNP_SERVER_ERROR,
                                      GUPNP_SERVER_ERROR_INVALID_RESPONSE,
                                      "Could not parse SOAP response");
                 } else {
-                        g_set_error_literal
-                                    (error,
-                                     GUPNP_SERVER_ERROR,
-                                     GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
-                                     action->msg->reason_phrase);
+                        g_set_error_literal (
+                                error,
+                                GUPNP_SERVER_ERROR,
+                                GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
+                                soup_message_get_reason_phrase (action->msg));
                 }
 
                 return NULL;
@@ -137,7 +142,7 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
         }
 
         /* Check whether we have a Fault */
-        if (action->msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+        if (status == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
                 xmlNode *param;
                 char *desc;
 
@@ -175,7 +180,8 @@ check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
                 desc = xml_util_get_child_element_content_glib
                                         (param, "errorDescription");
                 if (desc == NULL)
-                        desc = g_strdup (action->msg->reason_phrase);
+                        desc = g_strdup (
+                                soup_message_get_reason_phrase (action->msg));
 
                 g_set_error_literal (error,
                                      GUPNP_CONTROL_ERROR,
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index 9f4b61e..349d90c 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -90,9 +90,8 @@ typedef struct {
 } EmitNotifyData;
 
 static void
-subscribe_got_response (SoupSession       *session,
-                        SoupMessage       *msg,
-                        GUPnPServiceProxy *proxy);
+subscribe_got_response (GObject *source, GAsyncResult *res, gpointer user_data);
+
 static void
 subscribe (GUPnPServiceProxy *proxy);
 static void
@@ -223,7 +222,7 @@ gupnp_service_proxy_dispose (GObject *object)
         GUPnPServiceProxyPrivate *priv;
         GObjectClass *object_class;
         GUPnPContext *context;
-        SoupSession *session;
+        // SoupSession *session;
 
         proxy = GUPNP_SERVICE_PROXY (object);
         priv = gupnp_service_proxy_get_instance_private (proxy);
@@ -258,19 +257,24 @@ gupnp_service_proxy_dispose (GObject *object)
         }
 
         /* Cancel pending messages */
+#if 0
         if (context)
                 session = gupnp_context_get_session (context);
         else
                 session = NULL; /* Not the first time dispose is called. */
+#endif
 
         while (priv->pending_messages) {
+                /*
                 SoupMessage *msg;
 
                 msg = priv->pending_messages->data;
 
+                 * FIXME: Porbably cancelled above...
                 soup_session_cancel_message (session,
                                              msg,
                                              SOUP_STATUS_CANCELLED);
+                                             */
 
                 priv->pending_messages =
                         g_list_delete_link (priv->pending_messages,
@@ -604,17 +608,20 @@ on_action_cancelled (GCancellable *cancellable, gpointer user_data)
 {
         GUPnPServiceProxyAction *action = (GUPnPServiceProxyAction *) user_data;
 
-        GUPnPContext *context;
-        SoupSession *session;
+        //        GUPnPContext *context;
+        //        SoupSession *session;
 
         if (action->msg != NULL && action->proxy != NULL) {
+#if 0
                 context = gupnp_service_info_get_context
                                         (GUPNP_SERVICE_INFO (action->proxy));
                 session = gupnp_context_get_session (context);
 
+                FIXME: Cancellable?
                 soup_session_cancel_message (session,
                                              action->msg,
                                              SOUP_STATUS_CANCELLED);
+#endif
                 action->cancellable_connection_id = 0;
         }
 }
@@ -668,29 +675,26 @@ prepare_action_msg (GUPnPServiceProxy *proxy,
         action->msg = soup_message_new (SOUP_METHOD_POST, local_control_url);
         g_free (local_control_url);
 
+        SoupMessageHeaders *headers =
+                soup_message_get_request_headers (action->msg);
+
         /* Specify action */
         full_action = g_strdup_printf ("\"%s#%s\"", service_type, action->name);
-        soup_message_headers_append (action->msg->request_headers,
-                                     "SOAPAction",
-                                     full_action);
+        soup_message_headers_append (headers, "SOAPAction", full_action);
         g_free (full_action);
 
         /* Specify language */
         http_request_set_accept_language (action->msg);
 
         /* Accept gzip encoding */
-        soup_message_headers_append (action->msg->request_headers,
-                                     "Accept-Encoding", "gzip");
+        soup_message_headers_append (headers, "Accept-Encoding", "gzip");
 
         gupnp_service_proxy_action_serialize (action, service_type);
 
-        soup_message_set_request (action->msg,
-                                  "text/xml; charset=\"utf-8\"",
-                                  SOUP_MEMORY_TAKE,
-                                  action->msg_str->str,
-                                  action->msg_str->len);
-
-        g_string_free (action->msg_str, FALSE);
+        soup_message_set_request_body_from_bytes (
+                action->msg,
+                "text/xml; charset=\"utf-8\"",
+                g_string_free_to_bytes (action->msg_str));
         action->msg_str = NULL;
 
         return TRUE;
@@ -702,51 +706,55 @@ update_message_after_not_allowed (SoupMessage *msg)
         const char *full_action;
 
         /* Retry with M-POST */
-        msg->method = "M-POST";
+        soup_message_set_method (msg, "M-POST");
+
+        SoupMessageHeaders *headers = soup_message_get_request_headers (msg);
 
-        soup_message_headers_append
-                        (msg->request_headers,
-                         "Man",
-                         "\"http://schemas.xmlsoap.org/soap/envelope/\";; ns=s");
+        soup_message_headers_append (
+                headers,
+                "Man",
+                "\"http://schemas.xmlsoap.org/soap/envelope/\";; ns=s");
 
         /* Rename "SOAPAction" to "s-SOAPAction" */
-        full_action = soup_message_headers_get_one
-                        (msg->request_headers,
-                         "SOAPAction");
-        soup_message_headers_append (msg->request_headers,
-                                     "s-SOAPAction",
-                                     full_action);
-        soup_message_headers_remove (msg->request_headers,
-                                     "SOAPAction");
+        full_action = soup_message_headers_get_one (headers, "SOAPAction");
+        soup_message_headers_append (headers, "s-SOAPAction", full_action);
+        soup_message_headers_remove (headers, "SOAPAction");
 }
 
 static void
-action_task_got_response (SoupSession *session,
-                          SoupMessage *msg,
-                          gpointer    user_data)
+action_task_got_response (GObject *source,
+                          GAsyncResult *res,
+                          gpointer user_data)
 {
         GTask *task = G_TASK (user_data);
+        GError *error = NULL;
         GUPnPServiceProxyAction *action = (GUPnPServiceProxyAction *) g_task_get_task_data (task);
 
-        switch (msg->status_code) {
-        case SOUP_STATUS_CANCELLED:
+        action->response =
+                soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                   res,
+                                                   &error);
+
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
                 if (action->cancellable != NULL && action->cancellable_connection_id != 0) {
                         g_cancellable_disconnect (action->cancellable,
                                         action->cancellable_connection_id);
                         action->cancellable_connection_id = 0;
                 }
+        }
 
-                g_task_return_new_error (task,
-                                         G_IO_ERROR,
-                                         G_IO_ERROR_CANCELLED,
-                                         "Action message was cancelled");
+        if (error != NULL) {
+                g_task_return_error (task, error);
                 g_object_unref (task);
-                break;
 
+                return;
+        }
+
+        switch (soup_message_get_status (action->msg)) {
         case SOUP_STATUS_METHOD_NOT_ALLOWED:
                 /* And re-queue */
-                update_message_after_not_allowed (msg);
-                soup_session_requeue_message (session, msg);
+                update_message_after_not_allowed (action->msg);
+                // FIXME: soup_session_requeue_message (session, msg);
 
                 break;
 
@@ -783,10 +791,13 @@ gupnp_service_proxy_action_queue_task (GTask *task)
                                 (GUPNP_SERVICE_INFO (action->proxy));
         session = gupnp_context_get_session (context);
 
-        soup_session_queue_message (session,
-                                    action->msg,
-                                    (SoupSessionCallback) action_task_got_response,
-                                    task);
+        soup_session_send_and_read_async (
+                session,
+                action->msg,
+                G_PRIORITY_DEFAULT,
+                action->cancellable,
+                (GAsyncReadyCallback) action_task_got_response,
+                task);
         action->pending = TRUE;
 }
 
@@ -1493,12 +1504,11 @@ emit_notifications (gpointer user_data)
  * message with our SID.
  */
 static void
-server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
-                SoupMessage                     *msg,
-                G_GNUC_UNUSED const char        *server_path,
-                G_GNUC_UNUSED GHashTable        *query,
-                G_GNUC_UNUSED SoupClientContext *soup_client,
-                gpointer                         user_data)
+server_handler (G_GNUC_UNUSED SoupServer *soup_server,
+                SoupServerMessage *msg,
+                G_GNUC_UNUSED const char *server_path,
+                G_GNUC_UNUSED GHashTable *query,
+                gpointer user_data)
 {
         GUPnPServiceProxy *proxy;
         GUPnPServiceProxyPrivate *priv;
@@ -1510,19 +1520,27 @@ server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
         EmitNotifyData *emit_notify_data;
 
         proxy = GUPNP_SERVICE_PROXY (user_data);
+        const char *method = soup_server_message_get_method (msg);
 
-        if (strcmp (msg->method, GENA_METHOD_NOTIFY) != 0) {
+        SoupMessageHeaders *request_headers =
+                soup_server_message_get_request_headers (msg);
+
+        if (strcmp (method, GENA_METHOD_NOTIFY) != 0) {
                 /* We don't implement this method */
-                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_NOT_IMPLEMENTED,
+                                                "Method not supported");
 
                 return;
         }
 
-        nt = soup_message_headers_get_one (msg->request_headers, "NT");
-        nts = soup_message_headers_get_one (msg->request_headers, "NTS");
+        nt = soup_message_headers_get_one (request_headers, "NT");
+        nts = soup_message_headers_get_one (request_headers, "NTS");
         if (nt == NULL || nts == NULL) {
                 /* Required header is missing */
-                soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_BAD_REQUEST,
+                                                "NT or NTS is missing");
 
                 return;
         }
@@ -1530,15 +1548,19 @@ server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
         if (strcmp (nt, "upnp:event") != 0 ||
             strcmp (nts, "upnp:propchange") != 0) {
                 /* Unexpected header content */
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "Unexpected NT or NTS");
 
                 return;
         }
 
-        hdr = soup_message_headers_get_one (msg->request_headers, "SEQ");
+        hdr = soup_message_headers_get_one (request_headers, "SEQ");
         if (hdr == NULL) {
                 /* No SEQ header */
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "SEQ missing");
 
                 return;
         }
@@ -1547,32 +1569,41 @@ server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
         seq_parsed = strtoul (hdr, NULL, 10);
         if (errno != 0 || seq_parsed > G_MAXUINT32) {
                 /* Invalid SEQ header value */
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "SEQ invalid");
 
                 return;
         }
 
         seq = (guint32) seq_parsed;
 
-        hdr = soup_message_headers_get_one (msg->request_headers, "SID");
+        hdr = soup_message_headers_get_one (request_headers, "SID");
         if (hdr == NULL ||
             strlen (hdr) <= strlen ("uuid:") ||
             strncmp (hdr, "uuid:", strlen ("uuid:")) != 0) {
                 /* No SID */
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (
+                        msg,
+                        SOUP_STATUS_PRECONDITION_FAILED,
+                        "SID header missing or malformed");
 
                 return;
         }
 
+        SoupMessageBody *request_body =
+                soup_server_message_get_request_body (msg);
+
         /* Parse the actual XML message content */
-        doc = xmlRecoverMemory (msg->request_body->data,
-                                msg->request_body->length);
+        doc = xmlRecoverMemory (request_body->data, request_body->length);
         if (doc == NULL) {
                 /* Failed */
                 g_warning ("Failed to parse NOTIFY message body");
 
-                soup_message_set_status (msg,
-                                         SOUP_STATUS_INTERNAL_SERVER_ERROR);
+                soup_server_message_set_status (
+                        msg,
+                        SOUP_STATUS_INTERNAL_SERVER_ERROR,
+                        "Unable to parse NOTIFY message");
 
                 return;
         }
@@ -1584,7 +1615,7 @@ server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
                 /* Empty or unsupported */
                 xmlFreeDoc (doc);
 
-                soup_message_set_status (msg, SOUP_STATUS_OK);
+                soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 
                 return;
         }
@@ -1610,7 +1641,7 @@ server_handler (G_GNUC_UNUSED SoupServer        *soup_server,
         }
 
         /* Everything went OK */
-        soup_message_set_status (msg, SOUP_STATUS_OK);
+        soup_server_message_set_status (msg, SOUP_STATUS_OK, "Ok");
 }
 
 /*
@@ -1629,6 +1660,11 @@ make_timeout_header (GUPnPContext *context)
                 return g_strdup ("infinite");
 }
 
+typedef struct {
+        GUPnPServiceProxy *proxy;
+        SoupMessage *msg;
+} SubscriptionCallData;
+
 /*
  * Subscription expired.
  */
@@ -1658,6 +1694,7 @@ subscription_expire (gpointer user_data)
                                                 (GUPNP_SERVICE_INFO (proxy));
 
         local_sub_url = gupnp_context_rewrite_uri (context, sub_url);
+
         g_free (sub_url);
 
         msg = soup_message_new (GENA_METHOD_SUBSCRIBE, local_sub_url);
@@ -1665,15 +1702,14 @@ subscription_expire (gpointer user_data)
 
         g_return_val_if_fail (msg != NULL, FALSE);
 
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (msg);
+
         /* Add headers */
-        soup_message_headers_append (msg->request_headers,
-                                    "SID",
-                                    priv->sid);
+        soup_message_headers_append (request_headers, "SID", priv->sid);
 
         timeout = make_timeout_header (context);
-        soup_message_headers_append (msg->request_headers,
-                                     "Timeout",
-                                     timeout);
+        soup_message_headers_append (request_headers, "Timeout", timeout);
         g_free (timeout);
 
         /* And send it off */
@@ -1682,11 +1718,17 @@ subscription_expire (gpointer user_data)
 
         session = gupnp_context_get_session (context);
 
-        soup_session_queue_message (session,
-                                    msg,
-                                    (SoupSessionCallback)
-                                        subscribe_got_response,
-                                    proxy);
+        SubscriptionCallData *data = g_new0 (SubscriptionCallData, 1);
+        data->msg = msg;
+        data->proxy = proxy;
+
+        soup_session_send_and_read_async (
+                session,
+                msg,
+                G_PRIORITY_DEFAULT,
+                NULL,
+                (GAsyncReadyCallback) subscribe_got_response,
+                data);
 
         return FALSE;
 }
@@ -1695,44 +1737,56 @@ subscription_expire (gpointer user_data)
  * Received subscription response.
  */
 static void
-subscribe_got_response (G_GNUC_UNUSED SoupSession *session,
-                        SoupMessage               *msg,
-                        GUPnPServiceProxy         *proxy)
+subscribe_got_response (GObject *source, GAsyncResult *res, gpointer user_data)
 {
-        GError *error;
+        GError *error = NULL;
         GUPnPServiceProxyPrivate *priv;
+        SubscriptionCallData *data = user_data;
+
+        GBytes *body = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                          res,
+                                                          &error);
+
+        // We don't need the body, it should be empty anyway
+        g_clear_pointer (&body, g_bytes_unref);
 
         /* Cancelled? */
-        if (msg->status_code == SOUP_STATUS_CANCELLED)
-                return;
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                goto out;
+        } else if (error != NULL) {
+                // FIXME: Is just propagating the error the right way?
+                goto hdr_err;
+        }
 
         /* Remove from pending messages list */
-        priv = gupnp_service_proxy_get_instance_private (proxy);
-        priv->pending_messages = g_list_remove (priv->pending_messages, msg);
+        priv = gupnp_service_proxy_get_instance_private (data->proxy);
+        priv->pending_messages =
+                g_list_remove (priv->pending_messages, data->msg);
 
         /* Remove subscription timeout */
-        if (priv->subscription_timeout_src) {
-                g_source_destroy (priv->subscription_timeout_src);
-                priv->subscription_timeout_src = NULL;
-        }
+        g_clear_pointer (&priv->subscription_timeout_src, g_source_destroy);
 
         /* Check whether the subscription is still wanted */
-        if (!priv->subscribed)
-                return;
+        if (!priv->subscribed) {
+                goto out;
+        }
 
         /* Reset SID */
         g_free (priv->sid);
         priv->sid = NULL;
 
+        SoupStatus status = soup_message_get_status (data->msg);
+        SoupMessageHeaders *response_headers =
+                soup_message_get_response_headers (data->msg);
+
         /* Check message status */
-        if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+        if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
                 /* Success. */
                 const char *hdr;
                 int timeout;
 
                 /* Save SID. */
-                hdr = soup_message_headers_get_one (msg->response_headers,
-                                                    "SID");
+                hdr = soup_message_headers_get_one (response_headers, "SID");
                 if (hdr == NULL) {
                         error = g_error_new
                                         (GUPNP_EVENTING_ERROR,
@@ -1745,12 +1799,12 @@ subscribe_got_response (G_GNUC_UNUSED SoupSession *session,
                 priv->sid = g_strdup (hdr);
 
                 /* Figure out when the subscription times out */
-                hdr = soup_message_headers_get_one (msg->response_headers,
+                hdr = soup_message_headers_get_one (response_headers,
                                                     "Timeout");
                 if (hdr == NULL) {
                         g_warning ("No Timeout in SUBSCRIBE response.");
 
-                        return;
+                        goto out;
                 }
 
                 if (strncmp (hdr, "Second-", strlen ("Second-")) == 0) {
@@ -1772,10 +1826,10 @@ subscribe_got_response (G_GNUC_UNUSED SoupSession *session,
                         /* Add actual timeout */
                         priv->subscription_timeout_src =
                                 g_timeout_source_new_seconds (timeout);
-                        g_source_set_callback
-                                (priv->subscription_timeout_src,
-                                 subscription_expire,
-                                 proxy, NULL);
+                        g_source_set_callback (priv->subscription_timeout_src,
+                                               subscription_expire,
+                                               data->proxy,
+                                               NULL);
                         g_source_attach (priv->subscription_timeout_src,
                                          g_main_context_get_thread_default ());
 
@@ -1786,31 +1840,35 @@ subscribe_got_response (G_GNUC_UNUSED SoupSession *session,
                 SoupServer *server;
 
                 /* Subscription failed. */
-                error = g_error_new_literal
-                                (GUPNP_EVENTING_ERROR,
-                                 GUPNP_EVENTING_ERROR_SUBSCRIPTION_FAILED,
-                                 msg->reason_phrase);
+                error = g_error_new_literal (
+                        GUPNP_EVENTING_ERROR,
+                        GUPNP_EVENTING_ERROR_SUBSCRIPTION_FAILED,
+                        soup_message_get_reason_phrase (data->msg));
 
 hdr_err:
                 /* Remove listener */
-                context = gupnp_service_info_get_context
-                                        (GUPNP_SERVICE_INFO (proxy));
+                context = gupnp_service_info_get_context (
+                        GUPNP_SERVICE_INFO (data->proxy));
 
                 server = gupnp_context_get_server (context);
                 soup_server_remove_handler (server, priv->path);
 
                 priv->subscribed = FALSE;
 
-                g_object_notify (G_OBJECT (proxy), "subscribed");
+                g_object_notify (G_OBJECT (data->proxy), "subscribed");
 
                 /* Emit subscription-lost */
-                g_signal_emit (proxy,
+                g_signal_emit (data->proxy,
                                signals[SUBSCRIPTION_LOST],
                                0,
                                error);
 
                 g_error_free (error);
         }
+
+out:
+        g_object_unref (data->msg);
+        g_free (user_data);
 }
 
 /*
@@ -1824,7 +1882,7 @@ subscribe (GUPnPServiceProxy *proxy)
         SoupMessage *msg;
         SoupSession *session;
         SoupServer *server;
-        SoupURI *uri;
+        GUri *uri;
         char *uri_string;
         char *sub_url, *delivery_url, *timeout;
 
@@ -1846,6 +1904,7 @@ subscribe (GUPnPServiceProxy *proxy)
                 char *local_sub_url = NULL;
 
                 local_sub_url = gupnp_context_rewrite_uri (context, sub_url);
+                g_print ("local_sub_uri: %s\n", local_sub_url);
                 g_free (sub_url);
 
                 msg = soup_message_new (GENA_METHOD_SUBSCRIBE, local_sub_url);
@@ -1877,25 +1936,25 @@ subscribe (GUPnPServiceProxy *proxy)
 
         /* Add headers */
         uri = _gupnp_context_get_server_uri (context);
-        soup_uri_set_path (uri, priv->path);
-        uri_string = soup_uri_to_string (uri, FALSE);
-        soup_uri_free (uri);
+        GUri *new_uri = soup_uri_copy (uri, SOUP_URI_PATH, priv->path, NULL);
+
+        uri_string = g_uri_to_string_partial (new_uri, G_URI_HIDE_PASSWORD);
+        g_uri_unref (new_uri);
+        g_uri_unref (uri);
+
         delivery_url = g_strdup_printf ("<%s>", uri_string);
         g_free (uri_string);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "Callback",
-                                     delivery_url);
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (msg);
+
+        soup_message_headers_append (request_headers, "Callback", delivery_url);
         g_free (delivery_url);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "NT",
-                                     "upnp:event");
+        soup_message_headers_append (request_headers, "NT", "upnp:event");
 
         timeout = make_timeout_header (context);
-        soup_message_headers_append (msg->request_headers,
-                                     "Timeout",
-                                     timeout);
+        soup_message_headers_append (request_headers, "Timeout", timeout);
         g_free (timeout);
 
         /* Listen for events */
@@ -1913,13 +1972,28 @@ subscribe (GUPnPServiceProxy *proxy)
 
         session = gupnp_context_get_session (context);
 
-        soup_session_queue_message (session,
-                                    msg,
-                                    (SoupSessionCallback)
-                                        subscribe_got_response,
-                                    proxy);
+        SubscriptionCallData *data = g_new0 (SubscriptionCallData, 1);
+
+        data->msg = msg;
+        data->proxy = proxy;
+
+        soup_session_send_and_read_async (session,
+                                          msg,
+                                          G_PRIORITY_DEFAULT,
+                                          NULL,
+                                          subscribe_got_response,
+                                          data);
 }
 
+static void
+soup_message_dont_care_for_result (GObject *source,
+                                   GAsyncResult *res,
+                                   gpointer user_data)
+{
+        GInputStream *s =
+                soup_session_send_finish (SOUP_SESSION (source), res, NULL);
+        g_clear_object (&s);
+}
 /*
  * Unsubscribe from this service.
  */
@@ -1954,14 +2028,21 @@ unsubscribe (GUPnPServiceProxy *proxy)
 
                 if (msg != NULL) {
                         /* Add headers */
-                        soup_message_headers_append (msg->request_headers,
-                                                     "SID",
-                                                     priv->sid);
+                        soup_message_headers_append (
+                                soup_message_get_request_headers (msg),
+                                "SID",
+                                priv->sid);
 
                         /* And queue it */
                         session = gupnp_context_get_session (context);
 
-                        soup_session_queue_message (session, msg, NULL, NULL);
+                        soup_session_send_async (
+                                session,
+                                msg,
+                                G_PRIORITY_DEFAULT,
+                                NULL,
+                                soup_message_dont_care_for_result,
+                                NULL);
                 }
 
                 /* Reset SID */
@@ -2166,28 +2247,32 @@ gupnp_service_proxy_call_action (GUPnPServiceProxy       *proxy,
 
         context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (proxy));
         session = gupnp_context_get_session (context);
-        soup_session_send_message (session, action->msg);
+        action->response = soup_session_send_and_read (session,
+                                                       action->msg,
+                                                       action->cancellable,
+                                                       error);
 
-        g_cancellable_disconnect (action->cancellable,
-                                  action->cancellable_connection_id);
-        action->cancellable_connection_id = 0;
-        g_clear_object (&action->cancellable);
+        if (error != NULL && *error != NULL)
+                return NULL;
 
         /* If not allowed, try again */
-        if (action->msg->status_code == SOUP_STATUS_METHOD_NOT_ALLOWED) {
+        if (soup_message_get_status (action->msg) ==
+            SOUP_STATUS_METHOD_NOT_ALLOWED) {
                 update_message_after_not_allowed (action->msg);
-                soup_session_send_message (session, action->msg);
+                action->response =
+                        soup_session_send_and_read (session,
+                                                    action->msg,
+                                                    action->cancellable,
+                                                    error);
         }
 
-        if (action->msg->status_code == SOUP_STATUS_CANCELLED) {
-                g_propagate_error (
-                        error,
-                        g_error_new (G_IO_ERROR,
-                                     G_IO_ERROR_CANCELLED,
-                                     "Action message was cancelled"));
+        g_cancellable_disconnect (action->cancellable,
+                                  action->cancellable_connection_id);
+        action->cancellable_connection_id = 0;
+        g_clear_object (&action->cancellable);
 
+        if (error != NULL && *error != NULL)
                 return NULL;
-        }
 
         return action;
 }
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 67b3d43..14f34ce 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -19,7 +19,6 @@
 
 #include <gobject/gvaluecollector.h>
 #include <gmodule.h>
-#include <libsoup/soup-date.h>
 #include <string.h>
 
 #include "gupnp-service.h"
@@ -78,7 +77,7 @@ enum {
 
 static guint signals[LAST_SIGNAL];
 
-static char *
+static GBytes *
 create_property_set (GQueue *queue);
 
 static void
@@ -143,12 +142,14 @@ gupnp_service_get_session (GUPnPService *service)
                  * order. The session from GUPnPContext may use
                  * multiple connections.
                  */
-                priv->session = soup_session_new_with_options (SOUP_SESSION_MAX_CONNS_PER_HOST, 1,
-                                                                        NULL);
+                priv->session =
+                        soup_session_new_with_options ("max-conns-per-host",
+                                                       1,
+                                                       NULL);
 
                 if (g_getenv ("GUPNP_DEBUG")) {
                         SoupLogger *logger;
-                        logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1);
+                        logger = soup_logger_new (SOUP_LOGGER_LOG_BODY);
                         soup_session_add_feature (priv->session,
                                                   SOUP_SESSION_FEATURE (logger));
                 }
@@ -160,19 +161,22 @@ gupnp_service_get_session (GUPnPService *service)
 static void
 subscription_data_free (SubscriptionData *data)
 {
-        SoupSession *session;
+        // SoupSession *session;
 
-        session = gupnp_service_get_session (data->service);
+        // session = gupnp_service_get_session (data->service);
 
         /* Cancel pending messages */
         while (data->pending_messages) {
+                /*
                 SoupMessage *msg;
 
                 msg = data->pending_messages->data;
 
+                 * FIXME: cancel elsewhere
                 soup_session_cancel_message (session,
                                              msg,
                                              SOUP_STATUS_CANCELLED);
+                 */
 
                 data->pending_messages =
                         g_list_delete_link (data->pending_messages,
@@ -180,7 +184,7 @@ subscription_data_free (SubscriptionData *data)
         }
        
         /* Further cleanup */
-        g_list_free_full (data->callbacks, (GDestroyNotify) soup_uri_free);
+        g_list_free_full (data->callbacks, (GDestroyNotify) g_uri_unref);
 
         g_free (data->sid);
 
@@ -270,7 +274,8 @@ finalize_action (GUPnPServiceAction *action)
                                 "\"http://schemas.xmlsoap.org/soap/encoding/\";>"
                           "<s:Body>");
 
-        if (action->msg->status_code != SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+        if (soup_server_message_get_status (action->msg) !=
+            SOUP_STATUS_INTERNAL_SERVER_ERROR) {
                 g_string_append (action->response_str, "</u:");
                 g_string_append (action->response_str, action->name);
                 g_string_append (action->response_str, "Response>");
@@ -280,31 +285,36 @@ finalize_action (GUPnPServiceAction *action)
                          "</s:Body>"
                          "</s:Envelope>");
 
-        soup_message_headers_replace (action->msg->response_headers,
+        SoupMessageHeaders *headers =
+                soup_server_message_get_response_headers (action->msg);
+
+        soup_message_headers_replace (headers,
                                       "Content-Type",
                                       "text/xml; charset=\"utf-8\"");
 
         if (action->accept_gzip && action->response_str->len > 1024) {
+                // Fixme: Probably easier to use an output stream converter
+                // instead
                 http_response_set_body_gzip (action->msg,
                                              action->response_str->str,
                                              action->response_str->len);
                 g_string_free (action->response_str, TRUE);
         } else {
-                soup_message_body_append (action->msg->response_body,
-                                          SOUP_MEMORY_TAKE,
-                                          action->response_str->str,
-                                          action->response_str->len);
-                g_string_free (action->response_str, FALSE);
+                SoupMessageBody *msg_body =
+                        soup_server_message_get_response_body (action->msg);
+                soup_message_body_append_bytes (
+                        msg_body,
+                        g_string_free_to_bytes (action->response_str));
         }
+        action->response_str = NULL;
 
-        soup_message_headers_append (action->msg->response_headers, "Ext", "");
+        soup_message_headers_append (headers, "Ext", "");
 
         /* Server header on response */
-        soup_message_headers_append
-                        (action->msg->response_headers,
-                         "Server",
-                         gssdp_client_get_server_id
-                                (GSSDP_CLIENT (action->context)));
+        soup_message_headers_append (
+                headers,
+                "Server",
+                gssdp_client_get_server_id (GSSDP_CLIENT (action->context)));
 
         /* Tell soup server that response is now ready */
         server = gupnp_context_get_server (action->context);
@@ -345,7 +355,8 @@ gupnp_service_action_get_locales (GUPnPServiceAction *action)
 {
         g_return_val_if_fail (action != NULL, NULL);
 
-        return http_request_get_accept_locales (action->msg);
+        return http_request_get_accept_locales (
+                soup_server_message_get_request_headers (action->msg));
 }
 
 /**
@@ -629,7 +640,8 @@ gupnp_service_action_set_values (GUPnPServiceAction *action,
         g_return_if_fail (g_list_length (arg_names) ==
                           g_list_length (arg_values));
 
-        if (action->msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+        if (soup_server_message_get_status (action->msg) ==
+            SOUP_STATUS_INTERNAL_SERVER_ERROR) {
                 g_warning ("Calling gupnp_service_action_set_value() after "
                            "having called gupnp_service_action_return_error() "
                            "is not allowed.");
@@ -671,7 +683,8 @@ gupnp_service_action_set_value (GUPnPServiceAction *action,
         g_return_if_fail (argument != NULL);
         g_return_if_fail (value != NULL);
 
-        if (action->msg->status_code == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+        if (soup_server_message_get_status (action->msg) ==
+            SOUP_STATUS_INTERNAL_SERVER_ERROR) {
                 g_warning ("Calling gupnp_service_action_set_value() after "
                            "having called gupnp_service_action_return_error() "
                            "is not allowed.");
@@ -696,7 +709,7 @@ gupnp_service_action_return (GUPnPServiceAction *action)
 {
         g_return_if_fail (action != NULL);
 
-        soup_message_set_status (action->msg, SOUP_STATUS_OK);
+        soup_server_message_set_status (action->msg, SOUP_STATUS_OK, "Ok");
 
         finalize_action (action);
 }
@@ -778,8 +791,9 @@ gupnp_service_action_return_error (GUPnPServiceAction *action,
 
         xml_util_end_element (action->response_str, "s:Fault");
 
-        soup_message_set_status (action->msg,
-                                 SOUP_STATUS_INTERNAL_SERVER_ERROR);
+        soup_server_message_set_status (action->msg,
+                                        SOUP_STATUS_INTERNAL_SERVER_ERROR,
+                                        "Internal server error");
 
         finalize_action (action);
 }
@@ -791,12 +805,12 @@ gupnp_service_action_return_error (GUPnPServiceAction *action,
  * Get the #SoupMessage associated with @action. Mainly intended for
  * applications to be able to read HTTP headers received from clients.
  *
- * Return value: (transfer full): #SoupMessage associated with @action. Unref
- * after using it.
+ * Return value: (transfer full): #SoupServerMessage associated with @action.
+ *Unref after using it.
  *
  * Since: 0.14.0
  **/
-SoupMessage *
+SoupServerMessage *
 gupnp_service_action_get_message (GUPnPServiceAction *action)
 {
         return g_object_ref (action->msg);
@@ -903,12 +917,11 @@ query_state_variable (GUPnPService       *service,
 
 /* controlURL handler */
 static void
-control_server_handler (SoupServer                      *server,
-                        SoupMessage                     *msg,
-                        G_GNUC_UNUSED const char        *server_path,
-                        G_GNUC_UNUSED GHashTable        *query,
-                        G_GNUC_UNUSED SoupClientContext *soup_client,
-                        gpointer                         user_data)
+control_server_handler (SoupServer *server,
+                        SoupServerMessage *msg,
+                        G_GNUC_UNUSED const char *server_path,
+                        G_GNUC_UNUSED GHashTable *query,
+                        gpointer user_data)
 {
         GUPnPService *service;
         GUPnPContext *context;
@@ -922,22 +935,33 @@ control_server_handler (SoupServer                      *server,
 
         service = GUPNP_SERVICE (user_data);
 
-        if (msg->method != SOUP_METHOD_POST) {
-                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+        if (soup_server_message_get_method (msg) != SOUP_METHOD_POST) {
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_NOT_IMPLEMENTED,
+                                                "Not implemented");
 
                 return;
         }
 
-        if (msg->request_body->length == 0) {
-                soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+        SoupMessageBody *request_body =
+                soup_server_message_get_request_body (msg);
+        SoupMessageHeaders *request_headers =
+                soup_server_message_get_request_headers (msg);
+        SoupMessageHeaders *response_headers =
+                soup_server_message_get_response_headers (msg);
+
+        if (request_body->length == 0) {
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_BAD_REQUEST,
+                                                "Bad request");
 
                 return;
         }
 
         /* DLNA 7.2.5.6: Always use HTTP 1.1 */
-        if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
-                soup_message_set_http_version (msg, SOUP_HTTP_1_1);
-                soup_message_headers_append (msg->response_headers,
+        if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) {
+                soup_server_message_set_http_version (msg, SOUP_HTTP_1_1);
+                soup_message_headers_append (response_headers,
                                              "Connection",
                                              "close");
         }
@@ -945,7 +969,7 @@ control_server_handler (SoupServer                      *server,
         context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
 
         const char *host_header =
-                soup_message_headers_get_one (msg->request_headers, "Host");
+                soup_message_headers_get_one (request_headers, "Host");
 
         if (!gupnp_context_validate_host_header (context, host_header)) {
                 g_warning ("Host header mismatch, expected %s:%d, got %s",
@@ -953,21 +977,29 @@ control_server_handler (SoupServer                      *server,
                            gupnp_context_get_port (context),
                            host_header);
 
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "Host header mismatch");
+
                 return;
         }
 
         /* Get action name */
-        soap_action = soup_message_headers_get_one (msg->request_headers,
-                                                    "SOAPAction");
+        soap_action =
+                soup_message_headers_get_one (request_headers, "SOAPAction");
         if (!soap_action) {
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "No SOAPAction header");
+
                 return;
         }
 
         action_name = strchr (soap_action, '#');
         if (!action_name) {
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "No action name");
 
                 return;
         }
@@ -985,10 +1017,11 @@ control_server_handler (SoupServer                      *server,
                 *end = '\0';
 
         /* Parse action_node */
-        doc = xmlRecoverMemory (msg->request_body->data,
-                                msg->request_body->length);
+        doc = xmlRecoverMemory (request_body->data, request_body->length);
         if (doc == NULL) {
-                soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_BAD_REQUEST,
+                                                "Unable to parse action");
 
                 return;
         }
@@ -999,7 +1032,9 @@ control_server_handler (SoupServer                      *server,
                                             action_name,
                                             NULL);
         if (!action_node) {
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "Missing <action>");
 
                 return;
         }
@@ -1020,7 +1055,7 @@ control_server_handler (SoupServer                      *server,
                         action->argument_count++;
 
         /* Get accepted encodings */
-        accept_encoding = soup_message_headers_get_list (msg->request_headers,
+        accept_encoding = soup_message_headers_get_list (request_headers,
                                                          "Accept-Encoding");
 
         if (accept_encoding) {
@@ -1069,9 +1104,9 @@ control_server_handler (SoupServer                      *server,
 /* Generates a standard (re)subscription response */
 static void
 subscription_response (GUPnPService *service,
-                       SoupMessage  *msg,
-                       const char   *sid,
-                       int           timeout)
+                       SoupServerMessage *msg,
+                       const char *sid,
+                       int timeout)
 {
         GUPnPContext *context;
         GSSDPClient *client;
@@ -1080,15 +1115,16 @@ subscription_response (GUPnPService *service,
         context = gupnp_service_info_get_context (GUPNP_SERVICE_INFO (service));
         client = GSSDP_CLIENT (context);
 
+        SoupMessageHeaders *response_headers =
+                soup_server_message_get_response_headers (msg);
+
         /* Server header on response */
-        soup_message_headers_append (msg->response_headers,
+        soup_message_headers_append (response_headers,
                                      "Server",
                                      gssdp_client_get_server_id (client));
 
         /* SID header */
-        soup_message_headers_append (msg->response_headers,
-                                     "SID",
-                                     sid);
+        soup_message_headers_append (response_headers, "SID", sid);
 
         /* Timeout header */
         if (timeout > 0)
@@ -1096,13 +1132,11 @@ subscription_response (GUPnPService *service,
         else
                 tmp = g_strdup ("infinite");
 
-        soup_message_headers_append (msg->response_headers,
-                                     "Timeout",
-                                     tmp);
+        soup_message_headers_append (response_headers, "Timeout", tmp);
         g_free (tmp);
 
         /* 200 OK */
-        soup_message_set_status (msg, SOUP_STATUS_OK);
+        soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 /**
@@ -1150,7 +1184,6 @@ static void
 send_initial_state (SubscriptionData *data)
 {
         GQueue *queue;
-        char *mem;
         GList *l;
         GUPnPServicePrivate *priv;
 
@@ -1181,13 +1214,13 @@ send_initial_state (SubscriptionData *data)
                 g_queue_push_tail (queue, ndata);
         }
 
-        mem = create_property_set (queue);
-        notify_subscriber (data->sid, data, mem);
+        GBytes *property_set = create_property_set (queue);
+        notify_subscriber (data->sid, data, property_set);
 
         /* Cleanup */
         g_queue_free (queue);
 
-        g_free (mem);
+        g_bytes_unref (property_set);
 }
 
 static GList *
@@ -1195,14 +1228,14 @@ add_subscription_callback (GUPnPContext *context,
                            GList *list,
                            const char *callback)
 {
-            SoupURI *local_uri = NULL;
+        GUri *local_uri = NULL;
 
-            local_uri = gupnp_context_rewrite_uri_to_uri (context, callback);
-            if (local_uri == NULL) {
-                    return list;
+        local_uri = gupnp_context_rewrite_uri_to_uri (context, callback);
+        if (local_uri == NULL) {
+                return list;
             }
 
-            const char *host = soup_uri_get_host (local_uri);
+            const char *host = g_uri_get_host (local_uri);
             GSocketAddress *address = g_inet_socket_address_new_from_string (host, 0);
 
             // CVE-2020-12695: Ignore subscription call-backs that are not "in
@@ -1219,9 +1252,7 @@ add_subscription_callback (GUPnPContext *context,
 
 /* Subscription request */
 static void
-subscribe (GUPnPService *service,
-           SoupMessage  *msg,
-           const char   *callback)
+subscribe (GUPnPService *service, SoupServerMessage *msg, const char *callback)
 {
         SubscriptionData *data;
         char *start, *end;
@@ -1270,7 +1301,9 @@ subscribe (GUPnPService *service,
         }
 
         if (!data->callbacks) {
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_PRECONDITION_FAILED,
+                                                "No valid callbacks found");
 
                 g_slice_free (SubscriptionData, data);
 
@@ -1306,9 +1339,7 @@ subscribe (GUPnPService *service,
 
 /* Resubscription request */
 static void
-resubscribe (GUPnPService *service,
-             SoupMessage  *msg,
-             const char   *sid)
+resubscribe (GUPnPService *service, SoupServerMessage *msg, const char *sid)
 {
         SubscriptionData *data;
         GUPnPServicePrivate *priv;
@@ -1317,7 +1348,10 @@ resubscribe (GUPnPService *service,
 
         data = g_hash_table_lookup (priv->subscriptions, sid);
         if (!data) {
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (
+                        msg,
+                        SOUP_STATUS_PRECONDITION_FAILED,
+                        "No previous subscription found");
 
                 return;
         }
@@ -1345,9 +1379,7 @@ resubscribe (GUPnPService *service,
 
 /* Unsubscription request */
 static void
-unsubscribe (GUPnPService *service,
-             SoupMessage  *msg,
-             const char   *sid)
+unsubscribe (GUPnPService *service, SoupServerMessage *msg, const char *sid)
 {
         SubscriptionData *data;
         GUPnPServicePrivate *priv;
@@ -1361,27 +1393,34 @@ unsubscribe (GUPnPService *service,
                                              sid);
                 else
                         data->to_delete = TRUE;
-                soup_message_set_status (msg, SOUP_STATUS_OK);
+                soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
         } else
-                soup_message_set_status (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                soup_server_message_set_status (
+                        msg,
+                        SOUP_STATUS_PRECONDITION_FAILED,
+                        "No previous subscription found");
 }
 
 /* eventSubscriptionURL handler */
 static void
-subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
-                             SoupMessage                     *msg,
-                             G_GNUC_UNUSED const char        *server_path,
-                             G_GNUC_UNUSED GHashTable        *query,
-                             G_GNUC_UNUSED SoupClientContext *soup_client,
-                             gpointer                         user_data)
+subscription_server_handler (G_GNUC_UNUSED SoupServer *server,
+                             SoupServerMessage *msg,
+                             G_GNUC_UNUSED const char *server_path,
+                             G_GNUC_UNUSED GHashTable *query,
+                             gpointer user_data)
 {
         GUPnPService *service;
         const char *callback, *nt, *sid;
 
         service = GUPNP_SERVICE (user_data);
 
+        SoupMessageHeaders *request_headers =
+                soup_server_message_get_request_headers (msg);
+
+        g_print ("Got SUBSCRIBE handler request\n");
+
         const char *host =
-                soup_message_headers_get_one (msg->request_headers, "Host");
+                soup_message_headers_get_one (request_headers, "Host");
         GUPnPContext *context = gupnp_service_info_get_context (user_data);
         if (!gupnp_context_validate_host_header(context, host)) {
                 g_warning ("Host header mismatch, expected %s:%d, got %s",
@@ -1389,26 +1428,32 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
                            gupnp_context_get_port (context),
                            host);
 
-                soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_BAD_REQUEST,
+                                                NULL);
 
                 return;
         }
 
-        callback = soup_message_headers_get_one (msg->request_headers,
-                                                 "Callback");
-        nt       = soup_message_headers_get_one (msg->request_headers, "NT");
-        sid      = soup_message_headers_get_one (msg->request_headers, "SID");
+        callback = soup_message_headers_get_one (request_headers, "Callback");
+        nt = soup_message_headers_get_one (request_headers, "NT");
+        sid = soup_message_headers_get_one (request_headers, "SID");
+        const char *method = soup_server_message_get_method (msg);
 
         /* Choose appropriate handler */
-        if (strcmp (msg->method, GENA_METHOD_SUBSCRIBE) == 0) {
+        if (strcmp (method, GENA_METHOD_SUBSCRIBE) == 0) {
                 if (callback) {
                         if (sid) {
-                                soup_message_set_status
-                                        (msg, SOUP_STATUS_BAD_REQUEST);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_BAD_REQUEST,
+                                        "SID must not be given on SUBSCRIBE");
 
                         } else if (!nt || strcmp (nt, "upnp:event") != 0) {
-                                soup_message_set_status
-                                        (msg, SOUP_STATUS_PRECONDITION_FAILED);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_PRECONDITION_FAILED,
+                                        "NT header missing or malformed");
 
                         } else {
                                 subscribe (service, msg, callback);
@@ -1417,8 +1462,10 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
 
                 } else if (sid) {
                         if (nt) {
-                                soup_message_set_status
-                                        (msg, SOUP_STATUS_BAD_REQUEST);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_BAD_REQUEST,
+                                        "NT must not be given on RESUBSCRIBE");
 
                         } else {
                                 resubscribe (service, msg, sid);
@@ -1426,16 +1473,19 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
                         }
 
                 } else {
-                        soup_message_set_status
-                                (msg, SOUP_STATUS_PRECONDITION_FAILED);
-
+                        soup_server_message_set_status (
+                                msg,
+                                SOUP_STATUS_PRECONDITION_FAILED,
+                                NULL);
                 }
 
-        } else if (strcmp (msg->method, GENA_METHOD_UNSUBSCRIBE) == 0) {
+        } else if (strcmp (method, GENA_METHOD_UNSUBSCRIBE) == 0) {
                 if (sid) {
                         if (nt || callback) {
-                                soup_message_set_status
-                                        (msg, SOUP_STATUS_BAD_REQUEST);
+                                soup_server_message_set_status (
+                                        msg,
+                                        SOUP_STATUS_BAD_REQUEST,
+                                        NULL);
 
                         } else {
                                 unsubscribe (service, msg, sid);
@@ -1443,14 +1493,16 @@ subscription_server_handler (G_GNUC_UNUSED SoupServer        *server,
                         }
 
                 } else {
-                        soup_message_set_status
-                                (msg, SOUP_STATUS_PRECONDITION_FAILED);
-
+                        soup_server_message_set_status (
+                                msg,
+                                SOUP_STATUS_PRECONDITION_FAILED,
+                                NULL);
                 }
 
         } else {
-                soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
-
+                soup_server_message_set_status (msg,
+                                                SOUP_STATUS_NOT_IMPLEMENTED,
+                                                NULL);
         }
 }
 
@@ -1524,12 +1576,18 @@ got_introspection (GUPnPServiceInfo          *info,
 static char *
 path_from_url (const char *url)
 {
-        SoupURI *uri;
         gchar   *path;
+        const char *query = NULL;
 
-        uri = soup_uri_new (url);
-        path = soup_uri_to_string (uri, TRUE);
-        soup_uri_free (uri);
+        GUri *uri = g_uri_parse (url, G_URI_FLAGS_NONE, NULL);
+
+        query = g_uri_get_query (uri);
+        if (query == NULL) {
+                path = g_strdup (g_uri_get_path (uri));
+        } else {
+                path = g_strdup_printf ("%s?%s", g_uri_get_path (uri), query);
+        }
+        g_uri_unref (uri);
 
         return path;
 }
@@ -1921,71 +1979,90 @@ gupnp_service_notify_valist (GUPnPService *service,
         }
 }
 
+typedef struct {
+        SubscriptionData *data;
+        SoupMessage *msg;
+        GBytes *property_set;
+} NotifySubscriberData;
+
 /* Received notify response. */
 static void
-notify_got_response (G_GNUC_UNUSED SoupSession *session,
-                     SoupMessage               *msg,
-                     gpointer                   user_data)
+notify_got_response (GObject *source, GAsyncResult *res, gpointer user_data)
 {
-        SubscriptionData *data;
+
+        GBytes *body;
+        GError *error = NULL;
+
+        body = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                  res,
+                                                  &error);
 
         /* Cancelled? */
-        if (msg->status_code == SOUP_STATUS_CANCELLED)
+        if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                g_clear_error (&error);
+
                 return;
+        }
 
-        data = user_data;
+        // We don't need the body
+        g_clear_pointer (&body, g_bytes_unref);
+
+        NotifySubscriberData *data = user_data;
+
+        SoupStatus status = soup_message_get_status (data->msg);
 
         /* Remove from pending messages list */
-        data->pending_messages = g_list_remove (data->pending_messages, msg);
+        data->data->pending_messages =
+                g_list_remove (data->data->pending_messages, data->msg);
 
-        if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
-                data->initial_state_sent = TRUE;
+        if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+                data->data->initial_state_sent = TRUE;
 
                 /* Success: reset callbacks pointer */
-                data->callbacks = g_list_first (data->callbacks);
+                data->data->callbacks = g_list_first (data->data->callbacks);
 
-        } else if (msg->status_code == SOUP_STATUS_PRECONDITION_FAILED) {
+        } else if (status == SOUP_STATUS_PRECONDITION_FAILED) {
                 /* Precondition failed: Cancel subscription */
-                gupnp_service_remove_subscription (data->service, data->sid);
+                gupnp_service_remove_subscription (data->data->service,
+                                                   data->data->sid);
 
         } else {
                 /* Other failure: Try next callback or signal failure. */
-                if (data->callbacks->next) {
-                        SoupBuffer *buffer;
-                        guint8 *property_set;
-                        gsize length;
-
+                if (data->data->callbacks->next) {
                         /* Call next callback */
-                        data->callbacks = data->callbacks->next;
-
-                        /* Get property-set from old message */
-                        buffer = soup_message_body_flatten (msg->request_body);
-                        soup_buffer_get_data (buffer,
-                                              (const guint8 **) &property_set,
-                                              &length);
-                        notify_subscriber (NULL, data, property_set);
-                        soup_buffer_free (buffer);
+                        data->data->callbacks = data->data->callbacks->next;
+
+                        notify_subscriber (NULL,
+                                           data->data,
+                                           g_bytes_ref (data->property_set));
+                        g_bytes_unref (data->property_set);
                 } else {
                         /* Emit 'notify-failed' signal */
-                        GError *error;
+                        GError *inner_error;
 
-                        error = g_error_new_literal
-                                        (GUPNP_EVENTING_ERROR,
-                                         GUPNP_EVENTING_ERROR_NOTIFY_FAILED,
-                                         msg->reason_phrase);
+                        inner_error = g_error_new_literal (
+                                GUPNP_EVENTING_ERROR,
+                                GUPNP_EVENTING_ERROR_NOTIFY_FAILED,
+                                soup_message_get_reason_phrase (data->msg));
 
-                        g_signal_emit (data->service,
+                        g_signal_emit (data->data->service,
                                        signals[NOTIFY_FAILED],
                                        0,
-                                       data->callbacks,
-                                       error);
+                                       data->data->callbacks,
+                                       inner_error);
 
-                        g_error_free (error);
+                        g_error_free (inner_error);
 
                         /* Reset callbacks pointer */
-                        data->callbacks = g_list_first (data->callbacks);
+                        data->data->callbacks =
+                                g_list_first (data->data->callbacks);
                 }
         }
+
+        g_clear_error (&error);
+        g_bytes_unref (data->property_set);
+        g_object_unref (data->msg);
+        g_free (data);
 }
 
 /* Send notification @user_data to subscriber @value */
@@ -1994,69 +2071,64 @@ notify_subscriber (G_GNUC_UNUSED gpointer key,
                    gpointer value,
                    gpointer user_data)
 {
-        SubscriptionData *data;
-        const char *property_set;
         char *tmp;
-        SoupMessage *msg;
         SoupSession *session;
 
-        data = value;
-        property_set = user_data;
-
         /* Subscriber called unsubscribe */
-        if (subscription_data_can_delete (data))
+        if (subscription_data_can_delete ((SubscriptionData *) value))
                 return;
 
+        NotifySubscriberData *data = g_new0 (NotifySubscriberData, 1);
+
+        data->data = value;
+        data->property_set = g_bytes_ref ((GBytes *) user_data);
+
         /* Create message */
-        msg = soup_message_new_from_uri (GENA_METHOD_NOTIFY,
-                                         data->callbacks->data);
+        data->msg = soup_message_new_from_uri (GENA_METHOD_NOTIFY,
+                                               data->data->callbacks->data);
+
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (data->msg);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "NT",
-                                     "upnp:event");
+        soup_message_headers_append (request_headers, "NT", "upnp:event");
 
-        soup_message_headers_append (msg->request_headers,
-                                     "NTS",
-                                     "upnp:propchange");
+        soup_message_headers_append (request_headers, "NTS", "upnp:propchange");
 
-        soup_message_headers_append (msg->request_headers,
-                                     "SID",
-                                     data->sid);
+        soup_message_headers_append (request_headers, "SID", data->data->sid);
 
-        tmp = g_strdup_printf ("%d", data->seq);
-        soup_message_headers_append (msg->request_headers,
-                                     "SEQ",
-                                     tmp);
+        tmp = g_strdup_printf ("%d", data->data->seq);
+        soup_message_headers_append (request_headers, "SEQ", tmp);
         g_free (tmp);
 
         /* Handle overflow */
-        if (data->seq < G_MAXINT32)
-                data->seq++;
+        if (data->data->seq < G_MAXINT32)
+                data->data->seq++;
         else
-                data->seq = 1;
+                data->data->seq = 1;
 
         /* Add body */
-        soup_message_set_request (msg,
-                                  "text/xml; charset=\"utf-8\"",
-                                  SOUP_MEMORY_TAKE,
-                                  g_strdup (property_set),
-                                  strlen (property_set));
+        soup_message_set_request_body_from_bytes (data->msg,
+                                                  "text/xml; charset=\"utf-8\"",
+                                                  data->property_set);
 
         /* Queue */
-        data->pending_messages = g_list_prepend (data->pending_messages, msg);
-        soup_message_headers_append (msg->request_headers,
-                                     "Connection", "close");
-
-        session = gupnp_service_get_session (data->service);
-
-        soup_session_queue_message (session,
-                                    msg,
-                                    notify_got_response,
-                                    data);
+        data->data->pending_messages =
+                g_list_prepend (data->data->pending_messages, data->msg);
+        soup_message_headers_append (request_headers, "Connection", "close");
+
+        session = gupnp_service_get_session (data->data->service);
+
+        soup_session_send_and_read_async (
+                session,
+                data->msg,
+                G_PRIORITY_DEFAULT,
+                NULL,
+                (GAsyncReadyCallback) notify_got_response,
+                data);
 }
 
 /* Create a property set from @queue */
-static char *
+static GBytes *
 create_property_set (GQueue *queue)
 {
         NotifyData *data;
@@ -2085,28 +2157,27 @@ create_property_set (GQueue *queue)
         g_string_append (str, "</e:propertyset>");
 
         /* Cleanup & return */
-        return g_string_free (str, FALSE);
+        return g_string_free_to_bytes (str);
 }
 
 /* Flush all queued notifications */
 static void
 flush_notifications (GUPnPService *service)
 {
-        char *mem;
         GUPnPServicePrivate *priv;
 
         priv = gupnp_service_get_instance_private (service);
 
         /* Create property set */
-        mem = create_property_set (priv->notify_queue);
+        GBytes *property_set = create_property_set (priv->notify_queue);
 
         /* And send it off */
         g_hash_table_foreach (priv->subscriptions,
                               notify_subscriber,
-                              mem);
+                              property_set);
 
         /* Cleanup */
-        g_free (mem);
+        g_bytes_unref (property_set);
 }
 
 /**
diff --git a/libgupnp/gupnp-service.h b/libgupnp/gupnp-service.h
index dc47e7d..dc0d808 100644
--- a/libgupnp/gupnp-service.h
+++ b/libgupnp/gupnp-service.h
@@ -114,8 +114,8 @@ gupnp_service_action_return_error (GUPnPServiceAction *action,
                                    guint               error_code,
                                    const char         *error_description);
 
-SoupMessage *
-gupnp_service_action_get_message  (GUPnPServiceAction *action);
+SoupServerMessage *
+gupnp_service_action_get_message (GUPnPServiceAction *action);
 
 guint
 gupnp_service_action_get_argument_count
diff --git a/libgupnp/gupnp-unix-context-manager.c b/libgupnp/gupnp-unix-context-manager.c
index 180d362..37b7c7b 100644
--- a/libgupnp/gupnp-unix-context-manager.c
+++ b/libgupnp/gupnp-unix-context-manager.c
@@ -30,7 +30,6 @@
 #include <arpa/inet.h>
 #include <net/if.h>
 #include <ifaddrs.h>
-#include <libsoup/soup-address.h>
 #include <glib/gstdio.h>
 #include <libgssdp/gssdp-error.h>
 
diff --git a/libgupnp/http-headers.c b/libgupnp/http-headers.c
index cb4e7a6..ab0d5b1 100644
--- a/libgupnp/http-headers.c
+++ b/libgupnp/http-headers.c
@@ -141,7 +141,10 @@ http_request_set_accept_language (SoupMessage *message)
 
         g_free (lang);
 
-        soup_message_headers_append (message->request_headers,
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (message);
+
+        soup_message_headers_append (request_headers,
                                      "Accept-Language",
                                      tmp->str);
 
@@ -177,14 +180,14 @@ sort_locales_by_quality (const char *a,
 /* Parses the Accept-Language header in @message, and returns its values
  * in an ordered list in UNIX locale format */
 GList *
-http_request_get_accept_locales (SoupMessage *message)
+http_request_get_accept_locales (SoupMessageHeaders *request_headers)
 {
         const char *header;
         char **bits;
         int i;
         GList *locales;
 
-        header = soup_message_headers_get_one (message->request_headers,
+        header = soup_message_headers_get_one (request_headers,
                                                "Accept-Language");
         if (header == NULL)
                 return NULL;
@@ -224,15 +227,15 @@ http_request_get_accept_locales (SoupMessage *message)
 
 /* Set Accept-Language header according to @locale. */
 void
-http_response_set_content_locale (SoupMessage *msg,
-                                  const char  *locale)
+http_response_set_content_locale (SoupMessageHeaders *response_headers,
+                                  const char *locale)
 {
         char *lang;
 
         lang = g_strdup (locale);
         http_language_from_locale (lang);
 
-        soup_message_headers_append (msg->response_headers,
+        soup_message_headers_append (response_headers,
                                      "Content-Language",
                                      lang);
 
@@ -242,10 +245,10 @@ http_response_set_content_locale (SoupMessage *msg,
 /* Set Content-Type header guessed from @path, @data and @data_size using
  * g_content_type_guess(). */
 void
-http_response_set_content_type (SoupMessage  *msg,
-                                const char   *path,
+http_response_set_content_type (SoupMessageHeaders *response_headers,
+                                const char *path,
                                 const guchar *data,
-                                gsize         data_size)
+                                gsize data_size)
 {
         char *content_type, *mime;
 
@@ -262,9 +265,7 @@ http_response_set_content_type (SoupMessage  *msg,
                 mime = g_strdup ("text/xml; charset=\"utf-8\"");
         }
 
-        soup_message_headers_append (msg->response_headers,
-                                     "Content-Type",
-                                     mime);
+        soup_message_headers_append (response_headers, "Content-Type", mime);
 
         g_free (mime);
         g_free (content_type);
@@ -272,16 +273,22 @@ http_response_set_content_type (SoupMessage  *msg,
 
 /* Set Content-Encoding header to gzip and append compressed body */
 void
-http_response_set_body_gzip (SoupMessage *msg,
-                             const char  *body,
-                             const gsize  length)
+http_response_set_body_gzip (SoupServerMessage *msg,
+                             const char *body,
+                             const gsize length)
 {
         GZlibCompressor *compressor;
         gboolean finished = FALSE;
         gsize converted = 0;
 
-        soup_message_headers_append (msg->response_headers,
-                                     "Content-Encoding", "gzip");
+        SoupMessageBody *message_body =
+                soup_server_message_get_response_body (msg);
+        SoupMessageHeaders *response_headers =
+                soup_server_message_get_response_headers (msg);
+
+        soup_message_headers_append (response_headers,
+                                     "Content-Encoding",
+                                     "gzip");
 
         compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1);
 
@@ -317,9 +324,10 @@ http_response_set_body_gzip (SoupMessage *msg,
                 }
 
                 if (bytes_written)
-                        soup_message_body_append (msg->response_body,
+                        soup_message_body_append (message_body,
                                                   SOUP_MEMORY_COPY,
-                                                  buf, bytes_written);
+                                                  buf,
+                                                  bytes_written);
         }
 
         g_object_unref (compressor);
diff --git a/libgupnp/http-headers.h b/libgupnp/http-headers.h
index 5b07a10..ff65314 100644
--- a/libgupnp/http-headers.h
+++ b/libgupnp/http-headers.h
@@ -23,17 +23,17 @@ G_GNUC_INTERNAL void
 http_request_set_accept_language (SoupMessage  *message);
 
 G_GNUC_INTERNAL GList *
-http_request_get_accept_locales  (SoupMessage  *message);
+http_request_get_accept_locales (SoupMessageHeaders *message);
 
 G_GNUC_INTERNAL void
-http_response_set_content_locale (SoupMessage  *message,
-                                  const char   *locale);
+http_response_set_content_locale (SoupMessageHeaders *message,
+                                  const char *locale);
 
 G_GNUC_INTERNAL void
-http_response_set_content_type   (SoupMessage  *message,
-                                  const char   *path,
-                                  const guchar *data,
-                                  gsize         data_size);
+http_response_set_content_type (SoupMessageHeaders *response_headers,
+                                const char *path,
+                                const guchar *data,
+                                gsize data_size);
 
 G_GNUC_INTERNAL void
 http_response_set_content_range  (SoupMessage  *message,
@@ -42,9 +42,9 @@ http_response_set_content_range  (SoupMessage  *message,
                                   gsize         total);
 
 G_GNUC_INTERNAL void
-http_response_set_body_gzip      (SoupMessage   *msg,
-                                  const char    *body,
-                                  const gsize    length);
+http_response_set_body_gzip (SoupServerMessage *msg,
+                             const char *body,
+                             const gsize length);
 
 G_END_DECLS
 
diff --git a/libgupnp/xml-util.c b/libgupnp/xml-util.c
index 6c05668..cdef2ed 100644
--- a/libgupnp/xml-util.c
+++ b/libgupnp/xml-util.c
@@ -93,22 +93,27 @@ xml_util_get_child_element_content_glib (xmlNode    *node,
         return copy;
 }
 
-SoupURI *
-xml_util_get_child_element_content_uri (xmlNode    *node,
+GUri *
+xml_util_get_child_element_content_uri (xmlNode *node,
                                         const char *child_name,
-                                        SoupURI    *base)
+                                        GUri *base)
 {
         xmlChar *content;
-        SoupURI *uri;
+        GUri *uri;
 
         content = xml_util_get_child_element_content (node, child_name);
         if (!content)
                 return NULL;
 
         if (base != NULL)
-                uri = soup_uri_new_with_base (base, (const char *) content);
+                uri = g_uri_parse_relative (base,
+                                            (const char *) content,
+                                            G_URI_FLAGS_NONE,
+                                            NULL);
         else
-                uri = soup_uri_new ((const char *) content);
+                uri = g_uri_parse ((const char *) content,
+                                   G_URI_FLAGS_NONE,
+                                   NULL);
 
         xmlFree (content);
 
@@ -116,20 +121,19 @@ xml_util_get_child_element_content_uri (xmlNode    *node,
 }
 
 char *
-xml_util_get_child_element_content_url (xmlNode    *node,
+xml_util_get_child_element_content_url (xmlNode *node,
                                         const char *child_name,
-                                        SoupURI    *base)
+                                        GUri *base)
 {
-        SoupURI *uri;
+        GUri *uri;
         char *url;
 
         uri = xml_util_get_child_element_content_uri (node, child_name, base);
         if (!uri)
                 return NULL;
 
-        url = soup_uri_to_string (uri, FALSE);
-
-        soup_uri_free (uri);
+        url = g_uri_to_string_partial (uri, G_URI_HIDE_PASSWORD);
+        g_uri_unref (uri);
 
         return url;
 }
diff --git a/libgupnp/xml-util.h b/libgupnp/xml-util.h
index 51d62ba..a02eb3d 100644
--- a/libgupnp/xml-util.h
+++ b/libgupnp/xml-util.h
@@ -10,7 +10,6 @@
 #define GUPNP_XML_UTIL_H
 
 #include <libxml/tree.h>
-#include <libsoup/soup-uri.h>
 #include <stdarg.h>
 #include <glib-object.h>
 
@@ -31,15 +30,15 @@ G_GNUC_INTERNAL char *
 xml_util_get_child_element_content_glib (xmlNode    *node,
                                          const char *child_name);
 
-G_GNUC_INTERNAL SoupURI *
-xml_util_get_child_element_content_uri  (xmlNode    *node,
-                                         const char *child_name,
-                                         SoupURI    *base);
+G_GNUC_INTERNAL GUri *
+xml_util_get_child_element_content_uri (xmlNode *node,
+                                        const char *child_name,
+                                        GUri *base);
 
 G_GNUC_INTERNAL char *
-xml_util_get_child_element_content_url  (xmlNode    *node,
-                                         const char *child_name,
-                                         SoupURI    *base);
+xml_util_get_child_element_content_url (xmlNode *node,
+                                        const char *child_name,
+                                        GUri *base);
 G_GNUC_INTERNAL xmlChar *
 xml_util_get_attribute_contents         (xmlNode    *node,
                                          const char *attribute_name);
diff --git a/meson.build b/meson.build
index 68b43f5..0b544e2 100644
--- a/meson.build
+++ b/meson.build
@@ -13,10 +13,10 @@ conf.set('HAVE_NETLINK', netlink_available)
 conf.set('HAVE_IFADDRS_H', ifaddrs_available)
 conf.set('HAVE_LINUX_WIRELESS_H', cc.has_header('linux/wireless.h'))
 
-glib_version = '2.66'
+glib_version = '2.69'
 add_project_arguments(cc.get_supported_arguments('-Werror=deprecated-declarations'), language: 'c')
-conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_' + glib_version.underscorify())
-conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_' + glib_version.underscorify())
+conf.set('GLIB_VERSION_MIN_REQUIRED', 'GLIB_VERSION_2_70'.format(glib_version.underscorify()))
+conf.set('GLIB_VERSION_MAX_ALLOWED', 'GLIB_VERSION_2_70'.format(glib_version.underscorify()))
 
 subdir('internal')
 guul = subproject('guul', default_options : ['default_library=static'])
@@ -30,7 +30,7 @@ dependencies = [
     dependency('gio-2.0', version : '>= ' + glib_version),
     dependency('gmodule-2.0', version : '>= ' + glib_version),
     dependency('gobject-2.0', version : '>= ' + glib_version),
-    dependency('libsoup-2.4', version : '>= 2.48.0'),
+    dependency('libsoup-3.0', version : '>= 2.99.0'),
     gssdp_dep,
     dependency('libxml-2.0'),
 ]
diff --git a/tests/test-bugs.c b/tests/test-bugs.c
index 9d118c7..b99ffd2 100644
--- a/tests/test-bugs.c
+++ b/tests/test-bugs.c
@@ -163,19 +163,20 @@ test_bgo_690400_query_variable (GUPnPService *service,
 }
 
 static gboolean
-test_on_timeout (G_GNUC_UNUSED gpointer user_data)
+test_on_timeout (gpointer user_data)
 {
-    g_assert_not_reached ();
+        g_print ("Timeout in %s\n", (const char *) user_data);
+        g_assert_not_reached ();
 
-    return FALSE;
+        return FALSE;
 }
 
 static void
-test_run_loop (GMainLoop *loop)
+test_run_loop (GMainLoop *loop, const char *name)
 {
     guint timeout_id = 0;
 
-    timeout_id = g_timeout_add_seconds (2, test_on_timeout, NULL);
+    timeout_id = g_timeout_add_seconds (2, test_on_timeout, (gpointer) name);
     g_main_loop_run (loop);
     g_source_remove (timeout_id);
 }
@@ -219,7 +220,7 @@ test_bgo_696762 (void)
                       G_CALLBACK (test_bgo_696762_on_browse_call),
                       &data);
 
-    test_run_loop (data.loop);
+    test_run_loop (data.loop, g_test_get_path ());
     g_assert (data.proxy != NULL);
 
     G_GNUC_BEGIN_IGNORE_DEPRECATIONS
@@ -236,7 +237,7 @@ test_bgo_696762 (void)
                                       NULL);
     G_GNUC_END_IGNORE_DEPRECATIONS
 
-    test_run_loop (data.loop);
+    test_run_loop (data.loop, g_test_get_path ());
 
     g_main_loop_unref (data.loop);
     g_object_unref (data.proxy);
@@ -287,7 +288,7 @@ test_bgo_678701 (void)
                       G_CALLBACK (test_bgo_678701_on_dp_available),
                       &data);
 
-    test_run_loop (data.loop);
+    test_run_loop (data.loop, g_test_get_path ());
     g_assert (data.proxy != NULL);
 
     info = gupnp_device_info_get_service (GUPNP_DEVICE_INFO (data.proxy),
@@ -341,7 +342,7 @@ test_bgo_690400 (void)
                       G_CALLBACK (test_bgo_690400_query_variable), NULL);
     gupnp_root_device_set_available (rd, TRUE);
 
-    test_run_loop (data.loop);
+    test_run_loop (data.loop, "690400 - waiting for query_variable");
     g_assert (data.proxy != NULL);
 
     gupnp_service_proxy_add_notify (data.proxy,
@@ -357,7 +358,7 @@ test_bgo_690400 (void)
 
     gupnp_service_proxy_set_subscribed (data.proxy, TRUE);
 
-    test_run_loop (data.loop);
+    test_run_loop (data.loop, "690400 - waiting for event");
 
     g_main_loop_unref (data.loop);
     g_object_unref (data.proxy);
diff --git a/tests/test-context.c b/tests/test-context.c
index c60ac72..2733512 100644
--- a/tests/test-context.c
+++ b/tests/test-context.c
@@ -25,13 +25,22 @@ create_context (guint16 port, GError **error) {
                                               NULL));
 }
 
+typedef struct {
+        GMainLoop *loop;
+        GBytes *body;
+        GError *error;
+} RangeHelper;
+
 static void
-on_message_finished (G_GNUC_UNUSED SoupSession *session,
-                     G_GNUC_UNUSED SoupMessage *message,
-                     gpointer                   user_data) {
-        GMainLoop *loop = (GMainLoop*) user_data;
+on_message_finished (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+        RangeHelper *h = (RangeHelper *) user_data;
 
-        g_main_loop_quit (loop);
+        h->body = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                     res,
+                                                     &h->error);
+
+        g_main_loop_quit (h->loop);
 }
 
 static void
@@ -50,11 +59,11 @@ request_range_and_compare (GMappedFile *file,
         full_length = g_mapped_file_get_length (file);
 
         message = soup_message_new ("GET", uri);
-        g_object_ref (message);
 
-        soup_message_headers_set_range (message->request_headers,
-                                        want_start,
-                                        want_end);
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (message);
+
+        soup_message_headers_set_range (request_headers, want_start, want_end);
 
         /* interpretation according to SoupRange documentation */
         if (want_end == -1) {
@@ -70,33 +79,39 @@ request_range_and_compare (GMappedFile *file,
         } else
                 want_length = want_end - want_start + 1;
 
-
-        soup_session_queue_message (session,
-                                    message,
-                                    on_message_finished,
-                                    loop);
+        RangeHelper h = { loop, NULL, NULL };
+        soup_session_send_and_read_async (session,
+                                          message,
+                                          G_PRIORITY_DEFAULT,
+                                          NULL,
+                                          on_message_finished,
+                                          &h);
 
         g_main_loop_run (loop);
-        g_assert_cmpint (message->status_code, ==, SOUP_STATUS_PARTIAL_CONTENT);
-        g_assert_cmpint (message->response_body->length, ==, want_length);
-        got_length = soup_message_headers_get_content_length
-                                        (message->response_headers);
+        g_assert_no_error (h.error);
+        g_assert_nonnull (h.body);
+
+        g_assert_cmpint (soup_message_get_status (message),
+                         ==,
+                         SOUP_STATUS_PARTIAL_CONTENT);
+        g_assert_cmpint (g_bytes_get_size (h.body), ==, want_length);
+        SoupMessageHeaders *response_headers =
+                soup_message_get_response_headers (message);
+        got_length = soup_message_headers_get_content_length (response_headers);
         g_assert_cmpint (got_length, ==, want_length);
-        soup_message_headers_get_content_range (message->response_headers,
+        soup_message_headers_get_content_range (response_headers,
                                                 &got_start,
                                                 &got_end,
                                                 &got_length);
         g_assert_cmpint (got_start, ==, want_start);
         g_assert_cmpint (got_end, ==, want_end);
         result = memcmp (g_mapped_file_get_contents (file) + want_start,
-                         message->response_body->data,
+                         g_bytes_get_data (h.body, NULL),
                          want_length);
         g_assert_cmpint (result, ==, 0);
 
         g_object_unref (message);
-
-        message = soup_message_new ("GET", uri);
-        g_object_ref (message);
+        g_bytes_unref (h.body);
 }
 
 static void
@@ -170,18 +185,27 @@ test_gupnp_context_http_ranged_requests (void)
 
         /* Try to get 1 byte after the end of the file */
         message = soup_message_new ("GET", uri);
-        g_object_ref (message);
 
-        soup_message_headers_set_range (message->request_headers,
-                                        file_length,
-                                        file_length);
-        soup_session_queue_message (session,
-                                    message,
-                                    on_message_finished,
-                                    loop);
+        RangeHelper h = { loop, NULL, NULL };
+        soup_message_headers_set_range (
+                soup_message_get_request_headers (message),
+                file_length,
+                file_length);
+        soup_session_send_and_read_async (session,
+                                          message,
+                                          G_PRIORITY_DEFAULT,
+                                          NULL,
+                                          on_message_finished,
+                                          &h);
 
         g_main_loop_run (loop);
-        g_assert_cmpint (message->status_code, ==, SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
+
+        g_assert_no_error (h.error);
+        g_assert_nonnull (h.body);
+        g_bytes_unref (h.body);
+        g_assert_cmpint (soup_message_get_status (message),
+                         ==,
+                         SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE);
 
         g_object_unref (message);
 


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