[gupnp/wip/phako/libsoup3: 10/10] all: Initial port to libsoup3 API




commit 8ce9544b3c3ebc0385a7c5942c09b734dc7e4ee2
Author: Jens Georg <mail jensge org>
Date:   Sat Aug 7 09:41:50 2021 +0200

    all: Initial port to libsoup3 API

 .gitlab-ci.yml                                | 409 +++++++++++++-----------
 build-aux/org.gnome.GUPnP.json                |  59 ++++
 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                      | 314 ++++++++++--------
 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             |  65 ++--
 libgupnp/gupnp-root-device.c                  |  23 +-
 libgupnp/gupnp-service-action.c               |  51 +--
 libgupnp/gupnp-service-info.c                 |  90 +++---
 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                      | 438 +++++++++++++++-----------
 libgupnp/gupnp-service.h                      |   4 +-
 libgupnp/gupnp-unix-context-manager.c         |   1 -
 libgupnp/http-headers.c                       |  46 +--
 libgupnp/http-headers.h                       |  20 +-
 libgupnp/meson.build                          |   2 +-
 libgupnp/xml-util.c                           |  28 +-
 libgupnp/xml-util.h                           |  15 +-
 meson.build                                   |   8 +-
 tests/meson.build                             |   5 +-
 tests/test-bugs.c                             |  23 +-
 tests/test-context.c                          |  88 ++++--
 tests/test-service.c                          | 156 +++++++++
 vala/gupnp-1.6.deps                           |   2 +-
 vala/meson.build                              |   2 +-
 38 files changed, 1532 insertions(+), 1028 deletions(-)
---
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d70582e..0670d37 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,195 +1,224 @@
-include:
-    - remote: 
"https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/290b79e0e78eab67a83766f4e9691be554fc4afd/templates/ci-fairy.yml";
-    - remote: 
'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/290b79e0e78eab67a83766f4e9691be554fc4afd/templates/fedora.yml'
-
-variables:
-    MESON_TEST_TIMEOUT_MULTIPLIER: 3
-
-stages:
-    - review
-    - prepare
-    - build
-    - test
-    - analysis
-    - website
-
-.check-template: &check
-  extends:
-    - .fdo.ci-fairy
-  artifacts:
-    expire_in: 1 week
-    paths:
-      - check-junit-report.xml
-    reports:
-      junit: check-junit-report.xml
-
-check-commit-log:
-  variables:
-    GIT_DEPTH: "100"
-  stage: review
-  script:
-    - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
-      then
-        ci-fairy check-commits --junit-xml=check-junit-report.xml ;
-      else
-        echo "Not a merge request" ;
-      fi
-  <<: *check
-
-check-merge-request:
-  variables:
-    GIT_STRATEGY: none
-  stage: review
-  script:
-    - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
-      then
-        ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-junit-report.xml ;
-      else
-        echo "Not a merge request" ;
-      fi
-  <<: *check
-
-.build-template: &build
+flatpak:
+  tags:
+    - flatpak
+  image: registry.gitlab.gnome.org/gnome/gnome-runtime-images/gnome:master
   stage: build
   script:
-      - meson . build --prefix=/usr -Db_coverage=true
-      - ninja -C build
+    - flatpak-builder build-dir build-aux/org.gnome.GUPnP.json --stop-at=gupnp --user --disable-rofiles-fuse
+    - flatpak build build-dir meson _build
+    - flatpak build build-dir ninja -C _build
+    - flatpak build build-dir meson test -C _build --gdb
   artifacts:
-      expire_in: 1 day
-      paths:
-          - build
-
-.gupnp.fedora@common:
-  variables:
-    BASE_TAG: '2021-08-14.0'
-    FDO_UPSTREAM_REPO: GNOME/gupnp
-    FDO_DISTRIBUTION_PACKAGES: 'clang clang-analyzer gcovr git libasan libubsan python3-gobject python3-pip 
xmlto gobject-introspection-devel gtk-doc libsoup-devel libuuid-devel libxml2-devel vala ninja-build'
-    FDO_DISTRIBUTION_EXEC: |
-      dnf clean all &&
-      pip3 install meson markdown toml typogrify
-
-.gupnp.fedora:34@x86_64:
-  extends: .gupnp.fedora@common
-  variables:
-    FDO_DISTRIBUTION_VERSION: 34
-    FDO_DISTRIBUTION_TAG: "x86_64-${BASE_TAG}"
-
-build-fedora-container@x86_64:
-  extends:
-    - .fdo.container-build@fedora
-    - .gupnp.fedora:34@x86_64
-  stage: prepare
-  variables:
-    GIT_STRATEGY: none
-
-
-build-fedora@x86_64:
-    extends:
-        - .fdo.distribution-image@fedora
-        - .gupnp.fedora:34@x86_64
-    needs:
-        - build-fedora-container@x86_64
-    <<: *build
-
-
-.test-template: &test
-  stage: test
-  variables:
-    G_SLICE: "always-malloc"
-    MALLOC_CHECK_: "3"
-  script:
-    - cd build
-    - |
-      # Remove the many "CI_" variables from the environment. Meson dumps the
-      # whole environment for every failed test, and that gives a whole
-      # screenful of junk each time unless we strip these.
-      unset $(env|grep -o '^CI_[^=]*')
-      env LANG=C.UTF-8 LC_ALL=C.UTF-8 meson test --print-errorlogs ${MESON_TEST_EXTRA_ARGS}
-  after_script:
-    - |
-      echo "Distribution: "
-      echo
-      egrep '^NAME=|^VERSION=' /etc/os-release
-      echo
-      echo "Test suite settings:"
-      echo
-      echo "G_MESSAGES_DEBUG: ${G_MESSAGES_DEBUG}"
-      echo "MESON_TEST_EXTRA_ARGS: ${MESON_TEST_EXTRA_ARGS}"
-      echo
-      echo "These values can be set at https://gitlab.gnome.org/GNOME/gupnp/pipelines/new";
-  artifacts:
-    expire_in: 1 day
-    when: always
+    when: on_failure
+    name: "gssdp-_${CI_COMMIT_REF_NAME}"
     paths:
-    - build
-    reports:
-      junit: "build/meson-logs/testlog.junit.xml"
-
-test-fedora@x86_64:
-  extends:
-    - .fdo.distribution-image@fedora
-    - .gupnp.fedora:34@x86_64
-  needs:
-    - build-fedora@x86_64
-  <<: *test
-
-  #trigger-rygel:
-  #stage: analysis
-  #needs:
-  #  - test-fedora@x86_64
-  #trigger: GNOME/rygel
-  #only:
-  #  - master
-
-coverage-analysis:
-  extends:
-    - .fdo.distribution-image@fedora
-    - .gupnp.fedora:34@x86_64
-  stage: analysis
-  allow_failure: true
-  script:
-    - cd build
-    - mkdir -p coveragereport
-    - gcovr --html-details --print-summary --root=.. --exclude=../build --exclude=../subprojects 
--exclude=../docs/reference --exclude=../tests --exclude=../tools --exclude=../examples --output 
coveragereport/index.html
-  coverage: '/^lines: (\d+\.\d+\%)/'
-  artifacts:
-    when: always
-    paths:
-    - build/coveragereport
-  needs:
-    - test-fedora@x86_64
-
-static-scan:
-  extends:
-    - .fdo.distribution-image@fedora
-    - .gupnp.fedora:34@x86_64
-  stage: analysis
-  needs:
-    - build-fedora-container@x86_64
-  script:
-    - meson --buildtype=debug _scan_build
-    - ninja -C _scan_build scan-build
-  artifacts:
-    paths:
-      - _scan_build/meson-logs
-  allow_failure: true
-
-pages:
-  extends:
-    - .fdo.distribution-image@fedora
-    - .gupnp.fedora:34@x86_64
-  stage: website
-  script:
-      - meson doc-build -Dgtk_doc=true
-      - ninja -C doc-build gupnp-doc
-      - mkdir -p public
-      - mv doc-build/doc/html public/docs
-  artifacts:
-    paths:
-      - public
-  needs:
-    - build-fedora-container@x86_64
-  only:
-    - master
-    - /^wip\/.*\/ci.*$/
+      - "${CI_PROJECT_DIR}/_build/meson-logs"
 
+stages:
+  - build
+#
+#include:
+#    - remote: 
"https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/290b79e0e78eab67a83766f4e9691be554fc4afd/templates/ci-fairy.yml";
+#    - remote: 
'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/290b79e0e78eab67a83766f4e9691be554fc4afd/templates/fedora.yml'
+#
+#variables:
+#    MESON_TEST_TIMEOUT_MULTIPLIER: 3
+#
+#stages:
+#    - review
+#    - prepare
+#    - build
+#    - test
+#    - analysis
+#    - website
+#
+#.check-template: &check
+#  extends:
+#    - .fdo.ci-fairy
+#  artifacts:
+#    expire_in: 1 week
+#    paths:
+#      - check-junit-report.xml
+#    reports:
+#      junit: check-junit-report.xml
+#
+#check-commit-log:
+#  variables:
+#    GIT_DEPTH: "100"
+#  stage: review
+#  script:
+#    - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+#      then
+#        ci-fairy check-commits --junit-xml=check-junit-report.xml ;
+#      else
+#        echo "Not a merge request" ;
+#      fi
+#  <<: *check
+#
+#check-merge-request:
+#  variables:
+#    GIT_STRATEGY: none
+#  stage: review
+#  script:
+#    - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+#      then
+#        ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-junit-report.xml ;
+#      else
+#        echo "Not a merge request" ;
+#      fi
+#  <<: *check
+#
+#.build-template: &build
+#  stage: build
+#  script:
+#      - meson . build --prefix=/usr -Db_coverage=true 
+#      - ninja -C build
+#  artifacts:
+#      expire_in: 1 day
+#      paths:
+#          - build
+#
+#.gssdp.fedora@common:
+#  variables:
+#    BASE_TAG: '2021-06-19.0'
+#    FDO_UPSTREAM_REPO: GNOME/gssdp
+#    FDO_DISTRIBUTION_PACKAGES: 'clang clang-analyzer gcovr git libasan libubsan python3-gobject python3-pip 
xmlto gtk4-devel'
+#    FDO_DISTRIBUTION_EXEC: |
+#      dnf install -y 'dnf-command(builddep)' &&
+#      dnf builddep -y gssdp --setopt=install_weak_deps=False &&
+#      dnf clean all &&
+#      pip3 install meson markdown toml typogrify
+#
+#.gssdp.fedora:34@x86_64:
+#  extends: .gssdp.fedora@common
+#  variables:
+#    FDO_DISTRIBUTION_VERSION: 34
+#    FDO_DISTRIBUTION_TAG: "x86_64-${BASE_TAG}"
+#
+#build-fedora-container@x86_64:
+#  extends:
+#    - .fdo.container-build@fedora
+#    - .gssdp.fedora:34@x86_64
+#  stage: prepare
+#  variables:
+#    GIT_STRATEGY: none
+#
+#
+#build-fedora@x86_64:
+#    extends:
+#        - .fdo.distribution-image@fedora
+#        - .gssdp.fedora:34@x86_64
+#    needs:
+#        - build-fedora-container@x86_64
+#    <<: *build
+#
+#
+#.test-template: &test
+#  stage: test
+#  variables:
+#    G_SLICE: "always-malloc"
+#    MALLOC_CHECK_: "3"
+#  script:
+#    - cd build
+#    - |
+#      # Remove the many "CI_" variables from the environment. Meson dumps the
+#      # whole environment for every failed test, and that gives a whole
+#      # screenful of junk each time unless we strip these.
+#      unset $(env|grep -o '^CI_[^=]*')
+#      env LANG=C.UTF-8 LC_ALL=C.UTF-8 meson test --print-errorlogs ${MESON_TEST_EXTRA_ARGS}
+#  after_script:
+#    - |
+#      echo "Distribution: "
+#      echo
+#      egrep '^NAME=|^VERSION=' /etc/os-release
+#      echo
+#      echo "Test suite settings:"
+#      echo
+#      echo "G_MESSAGES_DEBUG: ${G_MESSAGES_DEBUG}"
+#      echo "MESON_TEST_EXTRA_ARGS: ${MESON_TEST_EXTRA_ARGS}"
+#      echo
+#      echo "These values can be set at https://gitlab.gnome.org/GNOME/gssdp/pipelines/new";
+#  artifacts:
+#    expire_in: 1 day
+#    when: always
+#    paths:
+#    - build
+#    reports:
+#      junit: "build/meson-logs/testlog.junit.xml"
+#
+#test-fedora@x86_64:
+#  extends:
+#    - .fdo.distribution-image@fedora
+#    - .gssdp.fedora:34@x86_64
+#  needs:
+#    - build-fedora@x86_64
+#  <<: *test
+#
+#trigger-gupnp:
+#  stage: analysis
+#  needs:
+#    - test-fedora@x86_64
+#  trigger: GNOME/gupnp
+#  only:
+#    - master
+#
+#coverage-analysis:
+#  extends:
+#    - .fdo.distribution-image@fedora
+#    - .gssdp.fedora:34@x86_64
+#  stage: analysis
+#  allow_failure: true
+#  script:
+#    - cd build
+#    - mkdir -p coveragereport
+#    - gcovr --html-details --print-summary --root=.. --exclude=../docs/reference --exclude=../tests 
--exclude=../tools --exclude=../examples --output coveragereport/index.html
+#  coverage: '/^lines: (\d+\.\d+\%)/'
+#  artifacts:
+#    when: always
+#    paths:
+#    - build/coveragereport
+#  needs:
+#    - test-fedora@x86_64
+#
+#static-scan:
+#  extends:
+#    - .fdo.distribution-image@fedora
+#    - .gssdp.fedora:34@x86_64
+#  stage: analysis
+#  needs:
+#    - build-fedora-container@x86_64
+#  script:
+#    - meson --buildtype=debug _scan_build
+#    - export SCANBUILD="$PWD/.gitlab-ci/scanbuild-wrapper.sh"
+#    - ninja -C _scan_build scan-build
+#  artifacts:
+#    paths:
+#      - _scan_build/meson-logs
+#  after_script:
+#    - .gitlab-ci/scanbuild-plist-to-junit.py _scan_build/meson-logs/scanbuild/ > 
_scan_build/junit-scan-build.xml
+#  artifacts:
+#    reports:
+#      junit: "_scan_build/junit-scan-build.xml"
+#
+#pages:
+#  extends:
+#    - .fdo.distribution-image@fedora
+#    - .gssdp.fedora:34@x86_64
+#  stage: website
+#  script:
+#      - meson doc-build -Dgtk_doc=true
+#      - ninja -C doc-build doc/GSSDP
+#      - mkdir -p public
+#      - mv doc-build/doc/GSSDP public/docs
+#
+#  artifacts:
+#    paths:
+#      - public
+#  needs:
+#    - build-fedora-container@x86_64
+#  only:
+#    - master
+#    - /^wip\/.*\/ci.*$/
+#    - /^wip\/.*\/.*doc.*$/
+#
+#
diff --git a/build-aux/org.gnome.GUPnP.json b/build-aux/org.gnome.GUPnP.json
new file mode 100644
index 0000000..b7967d3
--- /dev/null
+++ b/build-aux/org.gnome.GUPnP.json
@@ -0,0 +1,59 @@
+{
+    "app-id" : "org.gnome.GUPnP",
+    "runtime" : "org.gnome.Platform",
+    "runtime-version" : "master",
+    "sdk" : "org.gnome.Sdk",
+    "command" : "light-server",
+    "finish-args" : [
+        "--share=network",
+        "--share=ipc",
+        "--talk-name=org.gtk.vfs",
+        "--talk-name=org.gtk.vfs.*",
+        "--filesystem=xdg-pictures",
+        "--filesystem=xdg-videos",
+        "--filesystem=xdg-music",
+        "--own-name=org.gnome.Rygel1"
+    ],
+    "build-options" : {
+        "cflags" : "-O2 -g",
+        "cxxflags" : "-O2 -g",
+        "env" : {
+            "V" : "1"
+        }
+    },
+    "cleanup" : [
+        "/include",
+        "/lib/pkgconfig",
+        "/man",
+        "/share/doc",
+        "/share/gtk-doc",
+        "/share/man",
+        "/share/pkgconfig",
+        "*.la",
+        "*.a"
+    ],
+    "modules" : [
+        {
+            "name" : "gssdp",
+            "buildsystem" : "meson",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/GNOME/gssdp.git/";,
+                    "branch" : "wip/libsoup3"
+                }
+            ]
+        },
+        {
+            "name" : "gupnp",
+            "buildsystem" : "meson",
+            "sources" : [
+                {
+                    "type" : "git",
+                    "url" : "https://gitlab.gnome.org/GNOME/gupnp.git/";,
+                    "branch" : "wip/phako/libsoup3"
+                }
+            ]
+        }
+    ]
+}
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 fa29d65..35c2e50 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>
 
@@ -489,18 +488,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 7dda551..0d37269 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
@@ -68,7 +66,7 @@ struct _GUPnPContextPrivate {
         SoupSession *session;
 
         SoupServer  *server; /* Started on demand */
-        SoupURI     *server_uri;
+        GUri *server_uri;
         char        *default_language;
 
         GList       *host_path_datas;
@@ -186,15 +184,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));
         }
@@ -341,7 +337,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);
@@ -484,14 +480,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");
 }
 
 /**
@@ -556,21 +553,22 @@ 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;
@@ -580,7 +578,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;
 }
@@ -629,7 +627,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);
 }
 
 /**
@@ -826,17 +824,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);
 }
@@ -865,12 +866,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;
@@ -881,6 +881,7 @@ host_path_handler (G_GNUC_UNUSED SoupServer        *server,
         HostPathData *host_path_data;
         const char *user_agent;
         const char *host;
+        GBytes *buffer = NULL;
 
         orig_locales = NULL;
         locales      = NULL;
@@ -888,9 +889,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;
         }
@@ -899,16 +902,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
@@ -920,9 +925,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);
@@ -931,13 +937,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 */
@@ -946,7 +955,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);
@@ -955,11 +966,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;
         }
@@ -994,25 +1009,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
+        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))
@@ -1024,90 +1054,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);
 
@@ -1363,13 +1392,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);
@@ -1377,10 +1407,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;
@@ -1390,7 +1419,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,
@@ -1402,8 +1431,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),
@@ -1418,32 +1448,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);
 }
 
 /**
@@ -1498,6 +1536,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,
@@ -1542,7 +1582,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);
@@ -1551,8 +1591,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;
 }
@@ -1571,23 +1611,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));
 
@@ -1598,8 +1643,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 d93dfe0..2ef5ff1 100644
--- a/libgupnp/gupnp-error.c
+++ b/libgupnp/gupnp-error.c
@@ -147,17 +147,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 9ab34db..6d2b798 100644
--- a/libgupnp/gupnp-resource-factory.c
+++ b/libgupnp/gupnp-resource-factory.c
@@ -36,8 +36,7 @@ typedef struct _GUPnPResourceFactoryPrivate GUPnPResourceFactoryPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (GUPnPResourceFactory,
                             gupnp_resource_factory,
-                            G_TYPE_OBJECT);
-
+                            G_TYPE_OBJECT)
 
 static void
 gupnp_resource_factory_init (GUPnPResourceFactory *factory)
@@ -194,14 +193,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;
@@ -253,15 +251,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;
@@ -311,14 +308,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;
@@ -367,14 +363,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..3c337fc 100644
--- a/libgupnp/gupnp-root-device.c
+++ b/libgupnp/gupnp-root-device.c
@@ -92,10 +92,7 @@ gupnp_root_device_dispose (GObject *object)
         device = GUPNP_ROOT_DEVICE (object);
         priv = gupnp_root_device_get_instance_private (device);
 
-        if (priv->group) {
-                g_object_unref (priv->group);
-                priv->group = NULL;
-        }
+        g_clear_object (&priv->group);
 
         /* Call super */
         object_class = G_OBJECT_CLASS (gupnp_root_device_parent_class);
@@ -288,10 +285,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 +403,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 +414,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 +441,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-action.c b/libgupnp/gupnp-service-action.c
index 2c50e94..6e7c178 100644
--- a/libgupnp/gupnp-service-action.c
+++ b/libgupnp/gupnp-service-action.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
 #include <config.h>
 
 #include "gupnp-error.h"
@@ -8,6 +10,8 @@
 #include "http-headers.h"
 #include "xml-util.h"
 
+#include <gobject/gvaluecollector.h>
+
 GUPnPServiceAction *
 gupnp_service_action_new ()
 {
@@ -74,7 +78,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>");
@@ -84,28 +89,34 @@ 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,
+                headers,
                 "Server",
                 gssdp_client_get_server_id (GSSDP_CLIENT (action->context)));
 
@@ -148,7 +159,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));
 }
 
 /**
@@ -435,7 +447,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.");
@@ -477,7 +490,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.");
@@ -502,7 +516,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);
 }
@@ -584,8 +598,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);
 }
@@ -597,12 +612,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);
diff --git a/libgupnp/gupnp-service-info.c b/libgupnp/gupnp-service-info.c
index c4d76cf..97ad4cc 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;
 
@@ -81,8 +81,8 @@ typedef struct {
 static void
 get_scpd_url_data_free (GetSCPDURLData *data)
 {
-        if (data->cancellable)
-                g_object_unref (data->cancellable);
+        g_clear_object (&data->cancellable);
+        g_clear_object (&data->message);
 
         g_slice_free (GetSCPDURLData, data);
 }
@@ -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);
 
@@ -207,10 +209,7 @@ gupnp_service_info_dispose (GObject *object)
                 priv->context = NULL;
         }
 
-        if (priv->doc) {
-                g_object_unref (priv->doc);
-                priv->doc = NULL;
-        }
+        g_clear_object (&priv->doc);
 
         G_OBJECT_CLASS (gupnp_service_info_parent_class)->dispose (object);
 }
@@ -228,7 +227,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 +322,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 +424,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 +573,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 +582,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;
 
-                scpd = xmlRecoverMemory (msg->response_body->data,
-                                         msg->response_body->length);
+                gconstpointer data = g_bytes_get_data (body, &length);
+
+                scpd = xmlRecoverMemory (data, length);
                 if (scpd) {
                         introspection = gupnp_service_introspection_new (scpd, NULL);
 
@@ -607,7 +611,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) */
@@ -618,13 +622,10 @@ got_scpd_url (G_GNUC_UNUSED SoupSession *session,
         priv = gupnp_service_info_get_instance_private (data->info);
         priv->pending_gets = g_list_remove (priv->pending_gets, data);
 
-        data->callback (data->info,
-                        introspection,
-                        error,
-                        data->user_data);
+        data->callback (data->info, introspection, error, data->user_data);
 
-        if (error)
-                g_error_free (error);
+        g_clear_error (&error);
+        g_clear_pointer (&body, g_bytes_unref);
 
         get_scpd_url_data_free (data);
 }
@@ -635,7 +636,7 @@ cancellable_cancelled_cb (GCancellable *cancellable,
 {
         GUPnPServiceInfo *info;
         GetSCPDURLData *data;
-        SoupSession *session;
+        // SoupSession *session;
         GError *error;
         GUPnPServiceInfoPrivate *priv;
 
@@ -644,10 +645,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);
 
@@ -784,10 +788,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 0d6772b..5f020d7 100644
--- a/libgupnp/gupnp-service-private.h
+++ b/libgupnp/gupnp-service-private.h
@@ -19,7 +19,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 8ce7222..0f2f876 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
@@ -109,8 +108,15 @@ typedef struct {
                                            subscription */
         gboolean      initial_state_sent;
         gboolean      to_delete;
+        GCancellable *cancellable;
 } SubscriptionData;
 
+typedef struct {
+        SubscriptionData *data;
+        SoupMessage *msg;
+        GBytes *property_set;
+} NotifySubscriberData;
+
 static gboolean
 subscription_data_can_delete (SubscriptionData *data) {
     return data->initial_state_sent && data->to_delete;
@@ -143,12 +149,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 +168,17 @@ gupnp_service_get_session (GUPnPService *service)
 static void
 subscription_data_free (SubscriptionData *data)
 {
-        SoupSession *session;
+        g_cancellable_cancel (data->cancellable);
+        g_clear_object (&data->cancellable);
 
-        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;
-
-                soup_session_cancel_message (session,
-                                             msg,
-                                             SOUP_STATUS_CANCELLED);
+                NotifySubscriberData *d = data->pending_messages->data;
+                g_object_unref (d->msg);
+                g_bytes_unref (d->property_set);
+                g_free (d);
 
                 data->pending_messages =
                         g_list_delete_link (data->pending_messages,
@@ -180,7 +186,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);
 
@@ -305,12 +311,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;
@@ -324,22 +329,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");
         }
@@ -347,7 +363,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",
@@ -355,21 +371,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;
         }
@@ -387,10 +411,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;
         }
@@ -401,7 +426,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;
         }
@@ -422,7 +449,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) {
@@ -471,9 +498,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;
@@ -482,15 +509,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)
@@ -498,13 +526,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);
 }
 
 /**
@@ -552,7 +578,6 @@ static void
 send_initial_state (SubscriptionData *data)
 {
         GQueue *queue;
-        char *mem;
         GList *l;
         GUPnPServicePrivate *priv;
 
@@ -583,13 +608,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 *
@@ -597,14 +622,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
@@ -621,9 +646,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;
@@ -636,6 +659,7 @@ subscribe (GUPnPService *service,
                                         (GUPNP_SERVICE_INFO (service));
 
         data = g_slice_new0 (SubscriptionData);
+        data->cancellable = g_cancellable_new ();
 
         /* Parse callback list */
         start = (char *) callback;
@@ -672,7 +696,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);
 
@@ -703,14 +729,15 @@ subscribe (GUPnPService *service,
         /* Respond */
         subscription_response (service, msg, data->sid, SUBSCRIPTION_TIMEOUT);
 
+        // FIXME: Should we only send this if we priv->inspection is not NULL?
+        // There might not be any useful data in the notification if there is no
+        // introspection yet
         send_initial_state (data);
 }
 
 /* Resubscription request */
 static void
-resubscribe (GUPnPService *service,
-             SoupMessage  *msg,
-             const char   *sid)
+resubscribe (GUPnPService *service, SoupServerMessage *msg, const char *sid)
 {
         SubscriptionData *data;
         GUPnPServicePrivate *priv;
@@ -719,7 +746,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;
         }
@@ -747,9 +777,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;
@@ -763,27 +791,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",
@@ -791,26 +826,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);
@@ -819,8 +860,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);
@@ -828,16 +871,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);
@@ -845,14 +891,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);
         }
 }
 
@@ -926,12 +974,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;
 }
@@ -1229,7 +1283,7 @@ gupnp_service_class_init (GUPnPServiceClass *klass)
         /**
          * GUPnPService::notify-failed:
          * @service: The #GUPnPService that received the signal
-         * @callback_url: (type GList)(element-type SoupURI):A #GList of callback URLs
+         * @callback_url: (type GList)(element-type GUri):A #GList of callback URLs
          * @reason: (type GError): A pointer to a #GError describing why the notify failed
          *
          * Emitted whenever notification of a client fails.
@@ -1316,71 +1370,83 @@ gupnp_service_notify_valist (GUPnPService *service,
         }
 }
 
+
 /* 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;
+        NotifySubscriberData *data = user_data;
+
+        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);
+                // Do nothing else. The data was freed after the message was
+                // cancelled
                 return;
+        }
 
-        data = user_data;
+        // We don't need the body
+        g_clear_pointer (&body, g_bytes_unref);
+
+        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);
 
-        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));
                 } 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 */
@@ -1389,69 +1455,62 @@ 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;
 
-        /* Create message */
-        msg = soup_message_new_from_uri (GENA_METHOD_NOTIFY,
-                                         data->callbacks->data);
+        NotifySubscriberData *data = g_new0 (NotifySubscriberData, 1);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "NT",
-                                     "upnp:event");
+        data->data = value;
+        data->property_set = g_bytes_ref ((GBytes *) user_data);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "NTS",
-                                     "upnp:propchange");
+        /* Create message */
+        data->msg = soup_message_new_from_uri (GENA_METHOD_NOTIFY,
+                                               data->data->callbacks->data);
 
-        soup_message_headers_append (msg->request_headers,
-                                     "SID",
-                                     data->sid);
+        SoupMessageHeaders *request_headers =
+                soup_message_get_request_headers (data->msg);
 
-        tmp = g_strdup_printf ("%d", data->seq);
-        soup_message_headers_append (msg->request_headers,
-                                     "SEQ",
-                                     tmp);
+        soup_message_headers_append (request_headers, "NT", "upnp:event");
+        soup_message_headers_append (request_headers, "NTS", "upnp:propchange");
+        soup_message_headers_append (request_headers, "SID", data->data->sid);
+
+        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);
+        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,
+                data->data->cancellable,
+                (GAsyncReadyCallback) notify_got_response,
+                data);
 }
 
 /* Create a property set from @queue */
-static char *
+static GBytes *
 create_property_set (GQueue *queue)
 {
         NotifyData *data;
@@ -1480,28 +1539,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/meson.build b/libgupnp/meson.build
index 89688e6..bb186be 100644
--- a/libgupnp/meson.build
+++ b/libgupnp/meson.build
@@ -137,7 +137,7 @@ pkg.generate(
 )
 
 if get_option('introspection')
-    gir_includes = ['GObject-2.0', 'Gio-2.0', 'Soup-2.4', 'libxml2-2.0']
+    gir_includes = ['GObject-2.0', 'Gio-2.0', 'Soup-3.0', 'libxml2-2.0']
     if gssdp_dep.type_name() == 'internal'
         gir_includes += subproject('gssdp-1.6').get_variable('gir').get(0)
     else
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 fbd244e..8dcda0c 100644
--- a/meson.build
+++ b/meson.build
@@ -19,10 +19,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'])
@@ -36,7 +36,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/meson.build b/tests/meson.build
index 33637d7..a6f0b4b 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,11 +1,12 @@
-foreach program : ['context', 'bugs']
+foreach program : ['context', 'bugs', 'service']
     test(
         program,
         executable(
             'test-' + program,
             'test-@0@.c'.format (program),
             dependencies : gupnp,
-            c_args : '-DDATA_PATH="@0@/data"'.format(meson.current_source_dir())
+            c_args : '-DDATA_PATH="@0@/data"'.format(meson.current_source_dir()),
+            include_directories : config_h_inc,
         ),
         is_parallel : false
     )
diff --git a/tests/test-bugs.c b/tests/test-bugs.c
index 9d118c7..0b41011 100644
--- a/tests/test-bugs.c
+++ b/tests/test-bugs.c
@@ -6,9 +6,7 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-#ifdef HAVE_CONFIG_H
 #include <config.h>
-#endif
 
 #include <libgupnp/gupnp.h>
 #include <libgupnp/gupnp-service-private.h>
@@ -163,19 +161,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 +218,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 +235,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 +286,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 +340,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 +356,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);
 
diff --git a/tests/test-service.c b/tests/test-service.c
new file mode 100644
index 0000000..c69910c
--- /dev/null
+++ b/tests/test-service.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include <config.h>
+
+#include <libgupnp/gupnp-xml-doc.h>
+
+#include <libgupnp/gupnp-context-private.h>
+#include <libgupnp/gupnp-service-private.h>
+#include <libgupnp/gupnp.h>
+
+static GUPnPContext *
+create_context (guint16 port, GError **error)
+{
+        return GUPNP_CONTEXT (g_initable_new (GUPNP_TYPE_CONTEXT,
+                                              NULL,
+                                              error,
+                                              "host-ip",
+                                              "127.0.0.1",
+                                              "msearch-port",
+                                              port,
+                                              NULL));
+}
+
+typedef struct {
+        GMainLoop *loop;
+        SoupServerMessage *message;
+} TestServiceNotificationCancelledData;
+
+void
+on_notify (SoupServer *server,
+           SoupServerMessage *msg,
+           const char *path,
+           GHashTable *query,
+           gpointer user_data)
+{
+        TestServiceNotificationCancelledData *data = user_data;
+
+        // Pause message, quit mainlopp
+        soup_server_pause_message (server, msg);
+        data->message = msg;
+        g_main_loop_quit (data->loop);
+}
+
+void
+on_subscribe (GObject *source, GAsyncResult *res, gpointer user_data)
+{
+        GError *error = NULL;
+
+        GBytes *data = soup_session_send_and_read_finish (SOUP_SESSION (source),
+                                                          res,
+                                                          &error);
+
+        g_assert_no_error (error);
+        g_clear_pointer (&data, g_bytes_unref);
+}
+
+static void
+on_finished (SoupServerMessage *msg, TestServiceNotificationCancelledData *data)
+{
+        g_assert_cmpint (soup_server_message_get_status (msg),
+                         ==,
+                         SOUP_STATUS_INTERNAL_SERVER_ERROR);
+        g_main_loop_quit (data->loop);
+}
+
+static void
+test_service_notification_cancelled ()
+{
+        GUPnPContext *context = NULL;
+        GError *error = NULL;
+        GUPnPRootDevice *rd;
+        GUPnPServiceInfo *info = NULL;
+
+        TestServiceNotificationCancelledData data = { NULL, NULL };
+
+        data.loop = g_main_loop_new (NULL, FALSE);
+
+        context = create_context (0, &error);
+        g_assert_no_error (error);
+        g_assert (context != NULL);
+
+        rd = gupnp_root_device_new (context,
+                                    "TestDevice.xml",
+                                    DATA_PATH,
+                                    &error);
+        g_assert_no_error (error);
+        g_assert (rd != NULL);
+        gupnp_root_device_set_available (rd, TRUE);
+
+        SoupServer *server = soup_server_new (NULL, NULL);
+        soup_server_add_handler (server, "/Notify", on_notify, &data, NULL);
+        soup_server_listen_local (server,
+                                  0,
+                                  SOUP_SERVER_LISTEN_IPV4_ONLY,
+                                  &error);
+        g_assert_no_error (error);
+
+        // Generate SUBSCRIBE message
+        info = gupnp_device_info_get_service (
+                GUPNP_DEVICE_INFO (rd),
+                "urn:test-gupnp-org:service:TestService:1");
+        char *url = gupnp_service_info_get_event_subscription_url (info);
+        SoupMessage *msg = soup_message_new ("SUBSCRIBE", url);
+        g_free (url);
+
+        GSList *uris = soup_server_get_uris (server);
+        GUri *subscription =
+                soup_uri_copy (uris->data, SOUP_URI_PATH, "/Notify", NULL);
+        char *uri_string = g_uri_to_string (subscription);
+        char *callback = g_strdup_printf ("<%s>", uri_string);
+        g_free (uri_string);
+        g_slist_free_full (uris, (GDestroyNotify) g_uri_unref);
+        g_uri_unref (subscription);
+
+        SoupMessageHeaders *h = soup_message_get_request_headers (msg);
+        soup_message_headers_append (h, "Callback", callback);
+        g_free (callback);
+
+        soup_message_headers_append (h, "NT", "upnp:event");
+        SoupSession *session = soup_session_new ();
+        // FIXME: Add timeout header
+        soup_session_send_and_read_async (session,
+                                          msg,
+                                          G_PRIORITY_DEFAULT,
+                                          NULL,
+                                          on_subscribe,
+                                          &data);
+
+        g_main_loop_run (data.loop);
+        g_signal_connect (data.message,
+                          "finished",
+                          G_CALLBACK (on_finished),
+                          &data);
+
+        g_clear_object (&info);
+
+        soup_server_unpause_message (server, data.message);
+
+        g_main_loop_run (data.loop);
+        g_clear_object (&rd);
+        g_clear_object (&msg);
+        g_clear_object (&session);
+        g_clear_object (&server);
+        g_clear_object (&context);
+        g_main_loop_unref (data.loop);
+}
+int
+main (int argc, char *argv[])
+{
+        g_test_init (&argc, &argv, NULL);
+
+        g_test_add_func ("/service/notify/cancel",
+                         test_service_notification_cancelled);
+
+        return g_test_run ();
+}
diff --git a/vala/gupnp-1.6.deps b/vala/gupnp-1.6.deps
index c353320..4248635 100644
--- a/vala/gupnp-1.6.deps
+++ b/vala/gupnp-1.6.deps
@@ -1,3 +1,3 @@
 gssdp-1.6
-libsoup-2.4
+libsoup-3.0
 libxml-2.0
diff --git a/vala/meson.build b/vala/meson.build
index 107c908..a5e4375 100644
--- a/vala/meson.build
+++ b/vala/meson.build
@@ -8,7 +8,7 @@ endif
 
 gnome.generate_vapi(GUPNP_API_NAME,
                     sources : [gir.get(0), 'gupnp-custom.vala'],
-                    packages : [gssdp_vala_package, 'gio-2.0', 'libsoup-2.4', 'libxml-2.0'],
+                    packages : [gssdp_vala_package, 'gio-2.0', 'libsoup-3.0', 'libxml-2.0'],
                     gir_dirs : gssdp_gir_dirs,
                     install : true
 )


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