[libsoup/carlosgc/split-io] Split SoupMessage into client and server parts




commit a75699c0a91d887107f77222e662cbc172a8d556
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Fri Oct 9 11:29:11 2020 +0200

    Split SoupMessage into client and server parts
    
    Add SoupServerMessage and move there all the server only functionality.

 docs/reference/libsoup-3.0-docs.xml          |   6 +
 docs/reference/libsoup-3.0-sections.txt      |  45 +-
 libsoup/meson.build                          |   3 +
 libsoup/server/soup-auth-domain-basic.c      |  27 +-
 libsoup/server/soup-auth-domain-basic.h      |  10 +-
 libsoup/server/soup-auth-domain-digest.c     |  31 +-
 libsoup/server/soup-auth-domain-digest.h     |   8 +-
 libsoup/server/soup-auth-domain.c            |  48 +-
 libsoup/server/soup-auth-domain.h            |  46 +-
 libsoup/server/soup-server-io.c              | 465 +++++++++-----
 libsoup/server/soup-server-message-private.h |  50 ++
 libsoup/server/soup-server-message.c         | 881 +++++++++++++++++++++++++++
 libsoup/server/soup-server-message.h         |  79 +++
 libsoup/server/soup-server-private.h         |  23 -
 libsoup/server/soup-server.c                 | 625 +++++--------------
 libsoup/server/soup-server.h                 |  54 +-
 libsoup/soup-client-input-stream.c           |   5 +-
 libsoup/soup-connection.c                    |   8 +-
 libsoup/soup-connection.h                    |   8 +-
 libsoup/soup-form.c                          |  26 +-
 libsoup/soup-form.h                          |  10 +-
 libsoup/soup-logger.c                        |  39 +-
 libsoup/soup-message-io-data.c               | 266 ++++++++
 libsoup/soup-message-io-data.h               |  99 +++
 libsoup/soup-message-io.c                    | 468 ++++----------
 libsoup/soup-message-private.h               |  97 +--
 libsoup/soup-message.c                       | 427 +------------
 libsoup/soup-message.h                       |  41 +-
 libsoup/soup-session.c                       |   9 +-
 libsoup/soup-status.c                        |   8 +
 libsoup/soup-status.h                        |   5 +
 libsoup/soup-types.h                         |   1 +
 libsoup/soup.h                               |   1 +
 libsoup/websocket/soup-websocket.c           | 128 ++--
 libsoup/websocket/soup-websocket.h           |  34 +-
 tests/auth-test.c                            |  56 +-
 tests/cache-test.c                           |  51 +-
 tests/coding-test.c                          |  39 +-
 tests/connection-test.c                      |  60 +-
 tests/context-test.c                         |  33 +-
 tests/continue-test.c                        | 167 +++--
 tests/cookies-test.c                         |  25 +-
 tests/forms-test.c                           |  88 +--
 tests/hsts-db-test.c                         |  25 +-
 tests/hsts-test.c                            |  49 +-
 tests/misc-test.c                            |  42 +-
 tests/multipart-test.c                       |  25 +-
 tests/no-ssl-test.c                          |  11 +-
 tests/ntlm-test.c                            |  42 +-
 tests/proxy-test.c                           |  12 +-
 tests/pull-api-test.c                        |  42 --
 tests/range-test.c                           |   9 +-
 tests/redirect-test.c                        |  78 ++-
 tests/request-body-test.c                    |  20 +-
 tests/server-auth-test.c                     |  55 +-
 tests/server-test.c                          | 188 +++---
 tests/session-test.c                         |  11 +-
 tests/sniffing-test.c                        |  46 +-
 tests/ssl-test.c                             |  11 +-
 tests/streaming-test.c                       |  39 +-
 tests/timeout-test.c                         |  11 +-
 tests/websocket-test.c                       | 167 ++++-
 62 files changed, 3119 insertions(+), 2364 deletions(-)
---
diff --git a/docs/reference/libsoup-3.0-docs.xml b/docs/reference/libsoup-3.0-docs.xml
index 479edf8c..adb0d12c 100644
--- a/docs/reference/libsoup-3.0-docs.xml
+++ b/docs/reference/libsoup-3.0-docs.xml
@@ -42,6 +42,12 @@
     <xi:include href="xml/soup-uri.xml"/>
   </chapter>
 
+  <chapter>
+    <title>HTTP Server</title>
+    <xi:include href="xml/soup-server.xml"/>
+    <xi:include href="xml/soup-server-message.xml"/>
+  </chapter>
+
   <chapter>
     <title>Additional Features</title>
     <xi:include href="xml/soup-session-feature.xml"/>
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index 4b1d56ac..64989b04 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -8,7 +8,6 @@ soup_message_new
 soup_message_new_from_uri
 soup_message_set_request_body
 soup_message_set_request_body_from_bytes
-soup_message_set_response
 <SUBSECTION>
 SoupHTTPVersion
 soup_message_set_http_version
@@ -18,7 +17,6 @@ soup_message_set_uri
 <SUBSECTION>
 soup_message_set_status
 soup_message_set_status_full
-soup_message_set_redirect
 soup_message_is_keepalive
 soup_message_get_https_status
 <SUBSECTION>
@@ -74,15 +72,12 @@ SOUP_IS_MESSAGE_CLASS
 SOUP_MESSAGE_GET_CLASS
 SoupMessageClass
 <SUBSECTION Private>
-soup_message_wrote_informational
 soup_message_wrote_headers
-soup_message_wrote_chunk
 soup_message_wrote_body_data
 soup_message_wrote_body
 soup_message_got_informational
 soup_message_got_headers
 soup_message_content_sniffed
-soup_message_got_chunk
 soup_message_got_body
 soup_message_finished
 soup_message_restarted
@@ -260,17 +255,6 @@ soup_server_add_websocket_handler
 soup_server_add_websocket_extension
 soup_server_remove_websocket_extension
 <SUBSECTION>
-SoupClientContext
-soup_client_context_get_local_address
-soup_client_context_get_remote_address
-soup_client_context_get_host
-soup_client_context_get_auth_domain
-soup_client_context_get_auth_user
-soup_client_context_get_socket
-soup_client_context_steal_connection
-<SUBSECTION Private>
-soup_client_context_get_soup_socket
-<SUBSECTION>
 soup_server_add_auth_domain
 soup_server_remove_auth_domain
 <SUBSECTION>
@@ -295,6 +279,35 @@ SOUP_TYPE_CLIENT_CONTEXT
 soup_client_context_get_type
 </SECTION>
 
+<SECTION>
+<FILE>soup-server-message</FILE>
+<TITLE>SoupServerMessage</TITLE>
+SoupServerMessage
+soup_server_message_get_request_headers
+soup_server_message_get_response_headers
+soup_server_message_get_request_body
+soup_server_message_get_response_body
+soup_server_message_get_method
+soup_server_message_get_http_version
+soup_server_message_set_http_version
+soup_server_message_get_status
+soup_server_message_set_status
+soup_server_message_get_uri
+soup_server_message_set_response
+soup_server_message_set_redirect
+soup_server_message_get_socket
+soup_server_message_get_local_address
+soup_server_message_get_remote_address
+soup_server_message_get_remote_host
+soup_server_message_steal_connection
+<SUBSECTION Standard>
+SOUP_SERVER_MESSAGE
+SOUP_IS_SERVER_MESSAGE
+SOUP_TYPE_SERVER_MESSAGE
+<SUBSECTION Private>
+soup_server_message_get_type
+</SECTION>
+
 <SECTION>
 <FILE>soup-auth-domain</FILE>
 <TITLE>SoupAuthDomain</TITLE>
diff --git a/libsoup/meson.build b/libsoup/meson.build
index c3a691d4..3a700870 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -44,6 +44,7 @@ soup_sources = [
   'server/soup-path-map.c',
   'server/soup-server.c',
   'server/soup-server-io.c',
+  'server/soup-server-message.c',
 
   'websocket/soup-websocket.c',
   'websocket/soup-websocket-connection.c',
@@ -67,6 +68,7 @@ soup_sources = [
   'soup-message-body.c',
   'soup-message-headers.c',
   'soup-message-io.c',
+  'soup-message-io-data.c',
   'soup-message-queue.c',
   'soup-method.c',
   'soup-misc.c',
@@ -117,6 +119,7 @@ soup_introspection_headers = [
   'server/soup-auth-domain-basic.h',
   'server/soup-auth-domain-digest.h',
   'server/soup-server.h',
+  'server/soup-server-message.h',
 
   'websocket/soup-websocket.h',
   'websocket/soup-websocket-connection.h',
diff --git a/libsoup/server/soup-auth-domain-basic.c b/libsoup/server/soup-auth-domain-basic.c
index 9a850050..0cba37d2 100644
--- a/libsoup/server/soup-auth-domain-basic.c
+++ b/libsoup/server/soup-auth-domain-basic.c
@@ -204,8 +204,9 @@ pw_free (char *pw)
 }
 
 static gboolean
-parse_basic (SoupMessage *msg, const char *header,
-            char **username, char **password)
+parse_basic (const char *header,
+            char      **username,
+            char      **password)
 {
        char *decoded, *colon;
        gsize len, plen;
@@ -232,15 +233,16 @@ parse_basic (SoupMessage *msg, const char *header,
 }
 
 static char *
-soup_auth_domain_basic_accepts (SoupAuthDomain *domain, SoupMessage *msg,
-                               const char *header)
+soup_auth_domain_basic_accepts (SoupAuthDomain    *domain,
+                               SoupServerMessage *msg,
+                               const char        *header)
 {
        SoupAuthDomainBasicPrivate *priv =
                soup_auth_domain_basic_get_instance_private (SOUP_AUTH_DOMAIN_BASIC (domain));
        char *username, *password;
        gboolean ok = FALSE;
 
-       if (!parse_basic (msg, header, &username, &password))
+       if (!parse_basic (header, &username, &password))
                return NULL;
 
        if (priv->auth_callback) {
@@ -262,7 +264,8 @@ soup_auth_domain_basic_accepts (SoupAuthDomain *domain, SoupMessage *msg,
 }
 
 static char *
-soup_auth_domain_basic_challenge (SoupAuthDomain *domain, SoupMessage *msg)
+soup_auth_domain_basic_challenge (SoupAuthDomain    *domain,
+                                 SoupServerMessage *msg)
 {
        GString *challenge;
 
@@ -272,18 +275,18 @@ soup_auth_domain_basic_challenge (SoupAuthDomain *domain, SoupMessage *msg)
 }
 
 static gboolean
-soup_auth_domain_basic_check_password (SoupAuthDomain *domain,
-                                      SoupMessage    *msg,
-                                      const char     *username,
-                                      const char     *password)
+soup_auth_domain_basic_check_password (SoupAuthDomain    *domain,
+                                      SoupServerMessage *msg,
+                                      const char        *username,
+                                      const char        *password)
 {
        const char *header;
        char *msg_username, *msg_password;
        gboolean ok;
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
                                               "Authorization");
-       if (!parse_basic (msg, header, &msg_username, &msg_password))
+       if (!parse_basic (header, &msg_username, &msg_password))
                return FALSE;
 
        ok = (!strcmp (username, msg_username) &&
diff --git a/libsoup/server/soup-auth-domain-basic.h b/libsoup/server/soup-auth-domain-basic.h
index a260e891..d0397051 100644
--- a/libsoup/server/soup-auth-domain-basic.h
+++ b/libsoup/server/soup-auth-domain-basic.h
@@ -20,11 +20,11 @@ SOUP_AVAILABLE_IN_2_4
 SoupAuthDomain *soup_auth_domain_basic_new (const char *optname1,
                                            ...) G_GNUC_NULL_TERMINATED;
 
-typedef        gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain *domain,
-                                                    SoupMessage    *msg,
-                                                    const char     *username,
-                                                    const char     *password,
-                                                    gpointer        user_data);
+typedef        gboolean (*SoupAuthDomainBasicAuthCallback) (SoupAuthDomain    *domain,
+                                                    SoupServerMessage *msg,
+                                                    const char        *username,
+                                                    const char        *password,
+                                                    gpointer           user_data);
 
 SOUP_AVAILABLE_IN_2_4
 void      soup_auth_domain_basic_set_auth_callback  (SoupAuthDomain *domain,
diff --git a/libsoup/server/soup-auth-domain-digest.c b/libsoup/server/soup-auth-domain-digest.c
index ae9fa266..1ad34a91 100644
--- a/libsoup/server/soup-auth-domain-digest.c
+++ b/libsoup/server/soup-auth-domain-digest.c
@@ -191,9 +191,11 @@ soup_auth_domain_digest_set_auth_callback (SoupAuthDomain *domain,
 }
 
 static gboolean
-check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
-              GHashTable *params, const char *username,
-              const char *hex_urp)
+check_hex_urp (SoupAuthDomain    *domain,
+              SoupServerMessage *msg,
+              GHashTable        *params,
+              const char        *username,
+              const char        *hex_urp)
 {
        const char *uri, *qop, *realm, *msg_username;
        const char *nonce, *nc, *cnonce, *response;
@@ -210,7 +212,7 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
        if (!uri)
                return FALSE;
 
-       req_uri = soup_message_get_uri (msg);
+       req_uri = soup_server_message_get_uri (msg);
        dig_uri = soup_uri_new (uri);
        if (dig_uri) {
                if (!soup_uri_equal (dig_uri, req_uri)) {
@@ -263,7 +265,8 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
        soup_auth_digest_compute_hex_a1 (hex_urp,
                                         SOUP_AUTH_DIGEST_ALGORITHM_MD5,
                                         nonce, cnonce, hex_a1);
-       soup_auth_digest_compute_response (msg->method, uri,
+       soup_auth_digest_compute_response (soup_server_message_get_method (msg),
+                                          uri,
                                           hex_a1,
                                           SOUP_AUTH_DIGEST_QOP_AUTH,
                                           nonce, cnonce, nonce_count,
@@ -272,8 +275,9 @@ check_hex_urp (SoupAuthDomain *domain, SoupMessage *msg,
 }
 
 static char *
-soup_auth_domain_digest_accepts (SoupAuthDomain *domain, SoupMessage *msg,
-                                const char *header)
+soup_auth_domain_digest_accepts (SoupAuthDomain    *domain,
+                                SoupServerMessage *msg,
+                                const char        *header)
 {
        SoupAuthDomainDigestPrivate *priv =
                soup_auth_domain_digest_get_instance_private (SOUP_AUTH_DOMAIN_DIGEST (domain));
@@ -317,7 +321,8 @@ soup_auth_domain_digest_accepts (SoupAuthDomain *domain, SoupMessage *msg,
 }
 
 static char *
-soup_auth_domain_digest_challenge (SoupAuthDomain *domain, SoupMessage *msg)
+soup_auth_domain_digest_challenge (SoupAuthDomain    *domain,
+                                  SoupServerMessage *msg)
 {
        GString *str;
 
@@ -366,10 +371,10 @@ soup_auth_domain_digest_encode_password (const char *username,
 }
 
 static gboolean
-soup_auth_domain_digest_check_password (SoupAuthDomain *domain,
-                                       SoupMessage    *msg,
-                                       const char     *username,
-                                       const char     *password)
+soup_auth_domain_digest_check_password (SoupAuthDomain    *domain,
+                                       SoupServerMessage *msg,
+                                       const char        *username,
+                                       const char        *password)
 {
        const char *header;
        GHashTable *params;
@@ -377,7 +382,7 @@ soup_auth_domain_digest_check_password (SoupAuthDomain *domain,
        char hex_urp[33];
        gboolean accept;
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
                                               "Authorization");
        if (!header || (strncmp (header, "Digest ", 7) != 0))
                return FALSE;
diff --git a/libsoup/server/soup-auth-domain-digest.h b/libsoup/server/soup-auth-domain-digest.h
index 523497ce..c5630470 100644
--- a/libsoup/server/soup-auth-domain-digest.h
+++ b/libsoup/server/soup-auth-domain-digest.h
@@ -20,10 +20,10 @@ SOUP_AVAILABLE_IN_2_4
 SoupAuthDomain *soup_auth_domain_digest_new (const char *optname1,
                                            ...) G_GNUC_NULL_TERMINATED;
 
-typedef        char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain *domain,
-                                                   SoupMessage    *msg,
-                                                   const char     *username,
-                                                   gpointer        user_data);
+typedef        char * (*SoupAuthDomainDigestAuthCallback) (SoupAuthDomain    *domain,
+                                                   SoupServerMessage *msg,
+                                                   const char        *username,
+                                                   gpointer           user_data);
 
 SOUP_AVAILABLE_IN_2_4
 void    soup_auth_domain_digest_set_auth_callback  (SoupAuthDomain *domain,
diff --git a/libsoup/server/soup-auth-domain.c b/libsoup/server/soup-auth-domain.c
index 8785c474..f22cd9ea 100644
--- a/libsoup/server/soup-auth-domain.c
+++ b/libsoup/server/soup-auth-domain.c
@@ -364,7 +364,7 @@ soup_auth_domain_remove_path (SoupAuthDomain *domain, const char *path)
 /**
  * SoupAuthDomainFilter:
  * @domain: a #SoupAuthDomain
- * @msg: a #SoupMessage
+ * @msg: a #SoupServerMessage
  * @user_data: the data passed to soup_auth_domain_set_filter()
  *
  * The prototype for a #SoupAuthDomain filter; see
@@ -444,7 +444,7 @@ soup_auth_domain_get_realm (SoupAuthDomain *domain)
 /**
  * SoupAuthDomainGenericAuthCallback:
  * @domain: a #SoupAuthDomain
- * @msg: the #SoupMessage being authenticated
+ * @msg: the #SoupServerMessage being authenticated
  * @username: the username from @msg
  * @user_data: the data passed to
  * soup_auth_domain_set_generic_auth_callback()
@@ -504,9 +504,9 @@ soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
 }
 
 gboolean
-soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
-                                           SoupMessage    *msg,
-                                           const char     *username)
+soup_auth_domain_try_generic_auth_callback (SoupAuthDomain    *domain,
+                                           SoupServerMessage *msg,
+                                           const char        *username)
 {
        SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain);
 
@@ -519,7 +519,7 @@ soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
 /**
  * soup_auth_domain_check_password:
  * @domain: a #SoupAuthDomain
- * @msg: a #SoupMessage
+ * @msg: a #SoupServerMessage
  * @username: a username
  * @password: a password
  *
@@ -530,10 +530,10 @@ soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
  * Return value: whether or not the message is authenticated
  **/
 gboolean
-soup_auth_domain_check_password (SoupAuthDomain *domain,
-                                SoupMessage    *msg,
-                                const char     *username,
-                                const char     *password)
+soup_auth_domain_check_password (SoupAuthDomain    *domain,
+                                SoupServerMessage *msg,
+                                const char        *username,
+                                const char        *password)
 {
        return SOUP_AUTH_DOMAIN_GET_CLASS (domain)->check_password (domain, msg,
                                                                    username,
@@ -543,7 +543,7 @@ soup_auth_domain_check_password (SoupAuthDomain *domain,
 /**
  * soup_auth_domain_covers:
  * @domain: a #SoupAuthDomain
- * @msg: a #SoupMessage
+ * @msg: a #SoupServerMessage
  *
  * Checks if @domain requires @msg to be authenticated (according to
  * its paths and filter function). This does not actually look at
@@ -556,13 +556,14 @@ soup_auth_domain_check_password (SoupAuthDomain *domain,
  * Return value: %TRUE if @domain requires @msg to be authenticated
  **/
 gboolean
-soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
+soup_auth_domain_covers (SoupAuthDomain    *domain,
+                        SoupServerMessage *msg)
 {
        SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain);
        const char *path;
 
        if (!priv->proxy) {
-               path = soup_message_get_uri (msg)->path;
+               path = soup_server_message_get_uri (msg)->path;
                if (!soup_path_map_lookup (priv->paths, path))
                        return FALSE;
        }
@@ -576,7 +577,7 @@ soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
 /**
  * soup_auth_domain_accepts:
  * @domain: a #SoupAuthDomain
- * @msg: a #SoupMessage
+ * @msg: a #SoupServerMessage
  *
  * Checks if @msg contains appropriate authorization for @domain to
  * accept it. Mirroring soup_auth_domain_covers(), this does not check
@@ -590,12 +591,13 @@ soup_auth_domain_covers (SoupAuthDomain *domain, SoupMessage *msg)
  * as, if in fact it has authenticated. %NULL otherwise.
  **/
 char *
-soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg)
+soup_auth_domain_accepts (SoupAuthDomain    *domain,
+                         SoupServerMessage *msg)
 {
        SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain);
        const char *header;
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
                                               priv->proxy ?
                                               "Proxy-Authorization" :
                                               "Authorization");
@@ -607,7 +609,7 @@ soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg)
 /**
  * soup_auth_domain_challenge: (virtual challenge)
  * @domain: a #SoupAuthDomain
- * @msg: a #SoupMessage
+ * @msg: a #SoupServerMessage
  *
  * Adds a "WWW-Authenticate" or "Proxy-Authenticate" header to @msg,
  * requesting that the client authenticate, and sets @msg's status
@@ -617,16 +619,18 @@ soup_auth_domain_accepts (SoupAuthDomain *domain, SoupMessage *msg)
  * anyone else.
  **/
 void
-soup_auth_domain_challenge (SoupAuthDomain *domain, SoupMessage *msg)
+soup_auth_domain_challenge (SoupAuthDomain    *domain,
+                           SoupServerMessage *msg)
 {
        SoupAuthDomainPrivate *priv = soup_auth_domain_get_instance_private (domain);
        char *challenge;
 
        challenge = SOUP_AUTH_DOMAIN_GET_CLASS (domain)->challenge (domain, msg);
-       soup_message_set_status (msg, priv->proxy ?
-                                SOUP_STATUS_PROXY_UNAUTHORIZED :
-                                SOUP_STATUS_UNAUTHORIZED);
-       soup_message_headers_append (msg->response_headers,
+       soup_server_message_set_status (msg, priv->proxy ?
+                                       SOUP_STATUS_PROXY_UNAUTHORIZED :
+                                       SOUP_STATUS_UNAUTHORIZED,
+                                       NULL);
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
                                     priv->proxy ?
                                     "Proxy-Authenticate" :
                                     "WWW-Authenticate",
diff --git a/libsoup/server/soup-auth-domain.h b/libsoup/server/soup-auth-domain.h
index 397dc53b..c904d01b 100644
--- a/libsoup/server/soup-auth-domain.h
+++ b/libsoup/server/soup-auth-domain.h
@@ -16,15 +16,15 @@ G_DECLARE_DERIVABLE_TYPE (SoupAuthDomain, soup_auth_domain, SOUP, AUTH_DOMAIN, G
 struct _SoupAuthDomainClass {
        GObjectClass parent_class;
 
-       char *   (*accepts)        (SoupAuthDomain *domain,
-                                   SoupMessage    *msg,
-                                   const char     *header);
-       char *   (*challenge)      (SoupAuthDomain *domain,
-                                   SoupMessage    *msg);
-       gboolean (*check_password) (SoupAuthDomain *domain,
-                                   SoupMessage    *msg,
-                                   const char     *username,
-                                   const char     *password);
+       char *   (*accepts)        (SoupAuthDomain    *domain,
+                                   SoupServerMessage *msg,
+                                   const char        *header);
+       char *   (*challenge)      (SoupAuthDomain    *domain,
+                                   SoupServerMessage *msg);
+       gboolean (*check_password) (SoupAuthDomain    *domain,
+                                   SoupServerMessage *msg,
+                                   const char        *username,
+                                   const char        *password);
        gpointer padding[6];
 };
 
@@ -37,14 +37,14 @@ struct _SoupAuthDomainClass {
 #define SOUP_AUTH_DOMAIN_GENERIC_AUTH_CALLBACK "generic-auth-callback"
 #define SOUP_AUTH_DOMAIN_GENERIC_AUTH_DATA     "generic-auth-data"
 
-typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain *domain,
-                                         SoupMessage    *msg,
-                                         gpointer        user_data);
+typedef gboolean (*SoupAuthDomainFilter) (SoupAuthDomain    *domain,
+                                         SoupServerMessage *msg,
+                                         gpointer           user_data);
 
-typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain *domain,
-                                                      SoupMessage    *msg,
-                                                      const char     *username,
-                                                      gpointer        user_data);
+typedef gboolean (*SoupAuthDomainGenericAuthCallback) (SoupAuthDomain    *domain,
+                                                      SoupServerMessage *msg,
+                                                      const char        *username,
+                                                      gpointer           user_data);
 
 SOUP_AVAILABLE_IN_2_4
 void        soup_auth_domain_add_path    (SoupAuthDomain       *domain,
@@ -69,24 +69,24 @@ void        soup_auth_domain_set_generic_auth_callback (SoupAuthDomain *domain,
                                                        GDestroyNotify  dnotify);
 SOUP_AVAILABLE_IN_2_4
 gboolean    soup_auth_domain_check_password (SoupAuthDomain    *domain,
-                                            SoupMessage       *msg,
+                                            SoupServerMessage *msg,
                                             const char        *username,
                                             const char        *password);
 
 SOUP_AVAILABLE_IN_2_4
 gboolean    soup_auth_domain_covers      (SoupAuthDomain       *domain,
-                                         SoupMessage          *msg);
+                                         SoupServerMessage    *msg);
 SOUP_AVAILABLE_IN_2_4
 char       *soup_auth_domain_accepts     (SoupAuthDomain       *domain,
-                                         SoupMessage          *msg);
+                                         SoupServerMessage    *msg);
 SOUP_AVAILABLE_IN_2_4
 void        soup_auth_domain_challenge   (SoupAuthDomain       *domain,
-                                         SoupMessage          *msg);
+                                         SoupServerMessage    *msg);
 
 /* protected */
 SOUP_AVAILABLE_IN_2_4
-gboolean    soup_auth_domain_try_generic_auth_callback (SoupAuthDomain *domain,
-                                                       SoupMessage    *msg,
-                                                       const char     *username);
+gboolean    soup_auth_domain_try_generic_auth_callback (SoupAuthDomain    *domain,
+                                                       SoupServerMessage *msg,
+                                                       const char        *username);
 
 G_END_DECLS
diff --git a/libsoup/server/soup-server-io.c b/libsoup/server/soup-server-io.c
index 4556635f..6e89a70a 100644
--- a/libsoup/server/soup-server-io.c
+++ b/libsoup/server/soup-server-io.c
@@ -15,34 +15,115 @@
 #include "soup-body-input-stream.h"
 #include "soup-body-output-stream.h"
 #include "soup-filter-input-stream.h"
-#include "soup-message-private.h"
+#include "soup-server-message-private.h"
 #include "soup-misc.h"
 #include "soup-socket-private.h"
 
+struct _SoupServerMessageIOData {
+        SoupMessageIOData base;
+
+       GBytes  *write_chunk;
+       goffset  write_body_offset;
+
+       GSource *unpause_source;
+};
+
 #define RESPONSE_BLOCK_SIZE 8192
 #define HEADER_SIZE_LIMIT (64 * 1024)
 
+void
+soup_server_message_io_data_free (SoupServerMessageIOData *io)
+{
+        if (!io)
+                return;
+
+        soup_message_io_data_cleanup (&io->base);
+
+       if (io->unpause_source) {
+               g_source_destroy (io->unpause_source);
+                g_source_unref (io->unpause_source);
+               io->unpause_source = NULL;
+       }
+
+       g_clear_pointer (&io->write_chunk, g_bytes_unref);
+
+        g_slice_free (SoupServerMessageIOData, io);
+}
+
+void
+soup_server_message_io_finished (SoupServerMessage *msg)
+{
+       SoupServerMessageIOData *io;
+        SoupMessageIOCompletionFn completion_cb;
+        gpointer completion_data;
+        SoupMessageIOCompletion completion;
+
+       io = soup_server_message_get_io_data (msg);
+        if (!io)
+                return;
+
+       completion_cb = io->base.completion_cb;
+        completion_data = io->base.completion_data;
+
+        if ((io->base.read_state >= SOUP_MESSAGE_IO_STATE_FINISHING &&
+             io->base.write_state >= SOUP_MESSAGE_IO_STATE_FINISHING))
+                completion = SOUP_MESSAGE_IO_COMPLETE;
+        else
+               completion = SOUP_MESSAGE_IO_INTERRUPTED;
+
+        g_object_ref (msg);
+        soup_server_message_set_io_data (msg, NULL);
+       if (completion_cb)
+                completion_cb (G_OBJECT (msg), completion, completion_data);
+        g_object_unref (msg);
+}
+
+GIOStream *
+soup_server_message_io_steal (SoupServerMessage *msg)
+{
+        SoupServerMessageIOData *io;
+        SoupMessageIOCompletionFn completion_cb;
+       gpointer completion_data;
+       GIOStream *iostream;
+
+        io = soup_server_message_get_io_data (msg);
+        if (!io || !io->base.iostream)
+                return NULL;
+
+        iostream = g_object_ref (io->base.iostream);
+        completion_cb = io->base.completion_cb;
+       completion_data = io->base.completion_data;
+
+        g_object_ref (msg);
+       soup_server_message_set_io_data (msg, NULL);
+        if (completion_cb)
+                completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data);
+        g_object_unref (msg);
+
+       return iostream;
+}
+
 static void
 closed_async (GObject      *source,
               GAsyncResult *result,
               gpointer      user_data)
 {
         GOutputStream *body_ostream = G_OUTPUT_STREAM (source);
-        SoupMessage *msg = user_data;
-        SoupMessageIOData *io;
+        SoupServerMessage *msg = user_data;
+        SoupServerMessageIOData *io;
         GCancellable *async_wait;
 
-        io = soup_message_get_io_data (msg);
-        if (!io || !io->async_wait || io->body_ostream != body_ostream) {
+        io = soup_server_message_get_io_data (msg);
+        if (!io || !io->base.async_wait || io->base.body_ostream != body_ostream) {
                 g_object_unref (msg);
                 return;
         }
 
-        g_output_stream_close_finish (body_ostream, result, &io->async_error);
-        g_clear_object (&io->body_ostream);
+        g_output_stream_close_finish (body_ostream, result, &io->base.async_error);
+        g_clear_object (&io->base.body_ostream);
 
-        async_wait = io->async_wait;
-        io->async_wait = NULL;
+        async_wait = io->base.async_wait;
+        io->base.async_wait = NULL;
         g_cancellable_cancel (async_wait);
         g_object_unref (async_wait);
 
@@ -76,12 +157,19 @@ closed_async (GObject      *source,
  */
 
 static void
-handle_partial_get (SoupMessage *msg)
+handle_partial_get (SoupServerMessage *msg)
 {
         SoupRange *ranges;
         int nranges;
         GBytes *full_response;
         guint status;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *response_body;
+
+       request_headers = soup_server_message_get_request_headers (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+       response_body = soup_server_message_get_response_body (msg);
 
         /* Make sure the message is set up right for us to return a
          * partial response; it has to be a GET, the status must be
@@ -89,50 +177,50 @@ handle_partial_get (SoupMessage *msg)
          * Content), and the SoupServer must have already filled in
          * the response body
          */
-        if (msg->method != SOUP_METHOD_GET ||
-            msg->status_code != SOUP_STATUS_OK ||
-            soup_message_headers_get_encoding (msg->response_headers) !=
+        if (soup_server_message_get_method (msg) != SOUP_METHOD_GET ||
+            soup_server_message_get_status (msg, NULL) != SOUP_STATUS_OK ||
+            soup_message_headers_get_encoding (response_headers) !=
             SOUP_ENCODING_CONTENT_LENGTH ||
-            msg->response_body->length == 0 ||
-            !soup_message_body_get_accumulate (msg->response_body))
+            response_body->length == 0 ||
+            !soup_message_body_get_accumulate (response_body))
                 return;
 
         /* Oh, and there has to have been a valid Range header on the
          * request, of course.
          */
-        status = soup_message_headers_get_ranges_internal (msg->request_headers,
-                                                           msg->response_body->length,
+        status = soup_message_headers_get_ranges_internal (request_headers,
+                                                           response_body->length,
                                                            TRUE,
                                                            &ranges, &nranges);
         if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
-                soup_message_set_status (msg, status);
-                soup_message_body_truncate (msg->response_body);
+                soup_server_message_set_status (msg, status, NULL);
+                soup_message_body_truncate (response_body);
                 return;
         } else if (status != SOUP_STATUS_PARTIAL_CONTENT)
                 return;
 
-        full_response = soup_message_body_flatten (msg->response_body);
+        full_response = soup_message_body_flatten (response_body);
         if (!full_response) {
-                soup_message_headers_free_ranges (msg->request_headers, ranges);
+                soup_message_headers_free_ranges (request_headers, ranges);
                 return;
         }
 
-        soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
-        soup_message_body_truncate (msg->response_body);
+        soup_server_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT, NULL);
+        soup_message_body_truncate (response_body);
 
         if (nranges == 1) {
                 GBytes *range_buf;
 
                 /* Single range, so just set Content-Range and fix the body. */
 
-                soup_message_headers_set_content_range (msg->response_headers,
+                soup_message_headers_set_content_range (response_headers,
                                                         ranges[0].start,
                                                         ranges[0].end,
                                                         g_bytes_get_size (full_response));
                 range_buf = g_bytes_new_from_bytes (full_response,
                                                     ranges[0].start,
                                                     ranges[0].end - ranges[0].start + 1);
-                soup_message_body_append_bytes (msg->response_body, range_buf);
+                soup_message_body_append_bytes (response_body, range_buf);
                 g_bytes_unref (range_buf);
         } else {
                 SoupMultipart *multipart;
@@ -147,7 +235,7 @@ handle_partial_get (SoupMessage *msg)
                  */
 
                 multipart = soup_multipart_new ("multipart/byteranges");
-                content_type = soup_message_headers_get_one (msg->response_headers,
+                content_type = soup_message_headers_get_one (response_headers,
                                                              "Content-Type");
                 for (i = 0; i < nranges; i++) {
                         part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
@@ -169,52 +257,62 @@ handle_partial_get (SoupMessage *msg)
                         g_bytes_unref (part_body);
                 }
 
-                soup_multipart_to_message (multipart, msg->response_headers, &body);
-                soup_message_body_append_bytes (msg->response_body, body);
+                soup_multipart_to_message (multipart, response_headers, &body);
+                soup_message_body_append_bytes (response_body, body);
                 g_bytes_unref (body);
                 soup_multipart_free (multipart);
         }
 
         g_bytes_unref (full_response);
-        soup_message_headers_free_ranges (msg->request_headers, ranges);
+        soup_message_headers_free_ranges (request_headers, ranges);
 }
 
 static void
-write_headers (SoupMessage  *msg,
-               GString      *headers,
-               SoupEncoding *encoding)
+write_headers (SoupServerMessage  *msg,
+               GString            *headers,
+               SoupEncoding       *encoding)
 {
         SoupEncoding claimed_encoding;
         SoupMessageHeadersIter iter;
         const char *name, *value;
+       guint status_code;
+       const char *reason_phrase;
+       const char *method;
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *response_body;
 
-        if (msg->status_code == 0)
-                soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+        if (soup_server_message_get_status (msg, NULL) == 0)
+                soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL);
 
         handle_partial_get (msg);
 
+       status_code = soup_server_message_get_status (msg, &reason_phrase);
+
         g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n",
-                                soup_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1',
-                                msg->status_code, msg->reason_phrase);
-
-        claimed_encoding = soup_message_headers_get_encoding (msg->response_headers);
-        if ((msg->method == SOUP_METHOD_HEAD ||
-             msg->status_code  == SOUP_STATUS_NO_CONTENT ||
-             msg->status_code  == SOUP_STATUS_NOT_MODIFIED ||
-             SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
-            (msg->method == SOUP_METHOD_CONNECT &&
-             SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
+                               soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1',
+                               status_code, reason_phrase);
+
+       method = soup_server_message_get_method (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+        claimed_encoding = soup_message_headers_get_encoding (response_headers);
+        if ((method == SOUP_METHOD_HEAD ||
+             status_code  == SOUP_STATUS_NO_CONTENT ||
+             status_code  == SOUP_STATUS_NOT_MODIFIED ||
+             SOUP_STATUS_IS_INFORMATIONAL (status_code)) ||
+            (method == SOUP_METHOD_CONNECT &&
+             SOUP_STATUS_IS_SUCCESSFUL (status_code)))
                 *encoding = SOUP_ENCODING_NONE;
         else
                 *encoding = claimed_encoding;
 
+       response_body = soup_server_message_get_response_body (msg);
         if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH &&
-            !soup_message_headers_get_content_length (msg->response_headers)) {
-                soup_message_headers_set_content_length (msg->response_headers,
-                                                         msg->response_body->length);
+            !soup_message_headers_get_content_length (response_headers)) {
+                soup_message_headers_set_content_length (response_headers,
+                                                         response_body->length);
         }
 
-        soup_message_headers_iter_init (&iter, msg->response_headers);
+        soup_message_headers_iter_init (&iter, response_headers);
         while (soup_message_headers_iter_next (&iter, &name, &value))
                 g_string_append_printf (headers, "%s: %s\r\n", name, value);
         g_string_append (headers, "\r\n");
@@ -227,13 +325,15 @@ write_headers (SoupMessage  *msg,
  * socket not writable, write is complete, etc).
  */
 static gboolean
-io_write (SoupMessage  *msg,
-          GCancellable *cancellable,
-          GError      **error)
+io_write (SoupServerMessage *msg,
+          GCancellable     *cancellable,
+          GError          **error)
 {
-        SoupMessageIOData *io = soup_message_get_io_data (msg);
+        SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg);
+       SoupMessageIOData *io = &server_io->base;
         GBytes *chunk;
         gssize nwrote;
+       guint status_code;
 
         if (io->async_error) {
                 g_propagate_error (error, io->async_error);
@@ -248,11 +348,12 @@ io_write (SoupMessage  *msg,
 
         switch (io->write_state) {
         case SOUP_MESSAGE_IO_STATE_HEADERS:
-                if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && msg->status_code == 0) {
+               status_code = soup_server_message_get_status (msg, NULL);
+                if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING && status_code == 0) {
                         /* Client requested "Expect: 100-continue", and
                          * server did not set an error.
                          */
-                        soup_message_set_status (msg, SOUP_STATUS_CONTINUE);
+                        soup_server_message_set_status (msg, SOUP_STATUS_CONTINUE, NULL);
                 }
 
                 if (!io->write_buf->len)
@@ -272,8 +373,9 @@ io_write (SoupMessage  *msg,
                 io->written = 0;
                 g_string_truncate (io->write_buf, 0);
 
-                if (SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
-                        if (msg->status_code == SOUP_STATUS_CONTINUE) {
+               status_code = soup_server_message_get_status (msg, NULL);
+                if (SOUP_STATUS_IS_INFORMATIONAL (status_code)) {
+                        if (status_code == SOUP_STATUS_CONTINUE) {
                                 /* Stop and wait for the body now */
                                 io->write_state =
                                         SOUP_MESSAGE_IO_STATE_BLOCKING;
@@ -288,20 +390,20 @@ io_write (SoupMessage  *msg,
                                  */
                         }
 
-                        soup_message_wrote_informational (msg);
+                        soup_server_message_wrote_informational (msg);
 
                         /* If this was "101 Switching Protocols", then
                          * the server probably stole the connection...
                          */
-                        if (io != soup_message_get_io_data (msg))
+                        if (server_io != soup_server_message_get_io_data (msg))
                                 return FALSE;
 
-                        soup_message_cleanup_response (msg);
+                        soup_server_message_cleanup_response (msg);
                         break;
                 }
 
                 if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH)
-                        io->write_length = soup_message_headers_get_content_length (msg->response_headers);
+                        io->write_length = soup_message_headers_get_content_length 
(soup_server_message_get_response_headers (msg));
 
                 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_START;
                 /* If the client was waiting for a Continue
@@ -311,7 +413,7 @@ io_write (SoupMessage  *msg,
                 if (io->read_state == SOUP_MESSAGE_IO_STATE_BLOCKING)
                         io->read_state = SOUP_MESSAGE_IO_STATE_DONE;
 
-                soup_message_wrote_headers (msg);
+                soup_server_message_wrote_headers (msg);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_BODY_START:
@@ -329,51 +431,53 @@ io_write (SoupMessage  *msg,
                         break;
                 }
 
-                if (!io->write_chunk) {
-                        io->write_chunk = soup_message_body_get_chunk (msg->response_body, 
io->write_body_offset);
-                        if (!io->write_chunk) {
-                                soup_message_io_pause (msg);
+                if (!server_io->write_chunk) {
+                        server_io->write_chunk = soup_message_body_get_chunk 
(soup_server_message_get_response_body (msg),
+                                                                             server_io->write_body_offset);
+                        if (!server_io->write_chunk) {
+                                soup_server_message_io_pause (msg);
                                 return FALSE;
                         }
-                        if (!g_bytes_get_size (io->write_chunk)) {
+                        if (!g_bytes_get_size (server_io->write_chunk)) {
                                 io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH;
                                 break;
                         }
                 }
 
                 nwrote = g_pollable_stream_write (io->body_ostream,
-                                                  (guchar*)g_bytes_get_data (io->write_chunk, NULL) + 
io->written,
-                                                  g_bytes_get_size (io->write_chunk) - io->written,
+                                                  (guchar*)g_bytes_get_data (server_io->write_chunk, NULL) + 
io->written,
+                                                  g_bytes_get_size (server_io->write_chunk) - io->written,
                                                   FALSE,
                                                   cancellable, error);
                 if (nwrote == -1)
                         return FALSE;
 
-                chunk = g_bytes_new_from_bytes (io->write_chunk, io->written, nwrote);
+                chunk = g_bytes_new_from_bytes (server_io->write_chunk, io->written, nwrote);
                 io->written += nwrote;
                 if (io->write_length)
                         io->write_length -= nwrote;
 
-                if (io->written == g_bytes_get_size (io->write_chunk))
+                if (io->written == g_bytes_get_size (server_io->write_chunk))
                         io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA;
 
-                soup_message_wrote_body_data (msg, chunk);
+                soup_server_message_wrote_body_data (msg, g_bytes_get_size (chunk));
                 g_bytes_unref (chunk);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_BODY_DATA:
                 io->written = 0;
-                if (g_bytes_get_size (io->write_chunk) == 0) {
+                if (g_bytes_get_size (server_io->write_chunk) == 0) {
                         io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH;
                         break;
                 }
 
-                soup_message_body_wrote_chunk (msg->response_body, io->write_chunk);
-                io->write_body_offset += g_bytes_get_size (io->write_chunk);
-                g_clear_pointer (&io->write_chunk, g_bytes_unref);
+                soup_message_body_wrote_chunk (soup_server_message_get_response_body (msg),
+                                              server_io->write_chunk);
+                server_io->write_body_offset += g_bytes_get_size (server_io->write_chunk);
+                g_clear_pointer (&server_io->write_chunk, g_bytes_unref);
 
                 io->write_state = SOUP_MESSAGE_IO_STATE_BODY;
-                soup_message_wrote_chunk (msg);
+                soup_server_message_wrote_chunk (msg);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_BODY_FLUSH:
@@ -397,7 +501,7 @@ io_write (SoupMessage  *msg,
 
         case SOUP_MESSAGE_IO_STATE_BODY_DONE:
                 io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
-                soup_message_wrote_body (msg);
+                soup_server_message_wrote_body (msg);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_FINISHING:
@@ -435,21 +539,24 @@ parse_connect_authority (const char *req_path)
 }
 
 static guint
-parse_headers (SoupMessage  *msg,
-               char         *headers,
-               guint         headers_len,
-               SoupEncoding *encoding,
-               SoupSocket   *sock,
-               GError      **error)
+parse_headers (SoupServerMessage *msg,
+               char              *headers,
+               guint              headers_len,
+               SoupEncoding      *encoding,
+               GError           **error)
 {
         char *req_method, *req_path, *url;
         SoupHTTPVersion version;
+       SoupSocket *sock;
         const char *req_host;
         guint status;
         SoupURI *uri;
+       SoupMessageHeaders *request_headers;
+
+       request_headers = soup_server_message_get_request_headers (msg);
 
         status = soup_headers_parse_request (headers, headers_len,
-                                             msg->request_headers,
+                                             request_headers,
                                              &req_method,
                                              &req_path,
                                              &version);
@@ -462,28 +569,28 @@ parse_headers (SoupMessage  *msg,
                 return status;
         }
 
-        g_object_set (G_OBJECT (msg),
-                      SOUP_MESSAGE_METHOD, req_method,
-                      SOUP_MESSAGE_HTTP_VERSION, version,
-                      NULL);
+       soup_server_message_set_method (msg, req_method);
+       soup_server_message_set_http_version (msg, version);
         g_free (req_method);
 
         /* Handle request body encoding */
-        *encoding = soup_message_headers_get_encoding (msg->request_headers);
+        *encoding = soup_message_headers_get_encoding (request_headers);
         if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
-                if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding"))
+                if (soup_message_headers_get_list (request_headers, "Transfer-Encoding"))
                         return SOUP_STATUS_NOT_IMPLEMENTED;
                 else
                         return SOUP_STATUS_BAD_REQUEST;
         }
 
         /* Generate correct context for request */
-        req_host = soup_message_headers_get_one (msg->request_headers, "Host");
+        req_host = soup_message_headers_get_one (request_headers, "Host");
         if (req_host && strchr (req_host, '/')) {
                 g_free (req_path);
                 return SOUP_STATUS_BAD_REQUEST;
         }
 
+       sock = soup_server_message_get_soup_socket (msg);
+
         if (!strcmp (req_path, "*") && req_host) {
                 /* Eg, "OPTIONS * HTTP/1.1" */
                 url = g_strdup_printf ("%s://%s",
@@ -493,7 +600,7 @@ parse_headers (SoupMessage  *msg,
                 if (uri)
                         soup_uri_set_path (uri, "*");
                 g_free (url);
-        } else if (msg->method == SOUP_METHOD_CONNECT) {
+        } else if (soup_server_message_get_method (msg) == SOUP_METHOD_CONNECT) {
                 /* Authority */
                 uri = parse_connect_authority (req_path);
         } else if (*req_path != '/') {
@@ -505,7 +612,7 @@ parse_headers (SoupMessage  *msg,
                                        req_host, req_path);
                 uri = soup_uri_new (url);
                 g_free (url);
-        } else if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
+        } else if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) {
                 /* No Host header, no AbsoluteUri */
                 GInetSocketAddress *addr = soup_socket_get_local_address (sock);
                 GInetAddress *inet_addr = g_inet_socket_address_get_address (addr);
@@ -530,7 +637,7 @@ parse_headers (SoupMessage  *msg,
                 return SOUP_STATUS_BAD_REQUEST;
         }
 
-        soup_message_set_uri (msg, uri);
+        soup_server_message_set_uri (msg, uri);
         soup_uri_free (uri);
 
         return SOUP_STATUS_OK;
@@ -543,27 +650,33 @@ parse_headers (SoupMessage  *msg,
  * socket not readable, read is complete, etc).
  */
 static gboolean
-io_read (SoupMessage  *msg,
-         GCancellable *cancellable,
-         GError      **error)
+io_read (SoupServerMessage *msg,
+         GCancellable      *cancellable,
+         GError           **error)
 {
-        SoupMessageIOData *io = soup_message_get_io_data (msg);
+        SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg);
+       SoupMessageIOData *io = &server_io->base;
         gssize nread;
         guint status;
+       SoupMessageHeaders *request_headers;
 
         switch (io->read_state) {
         case SOUP_MESSAGE_IO_STATE_HEADERS:
-                if (!soup_message_io_read_headers (msg, io->istream, io->read_header_buf, FALSE, 
cancellable, error))
+                if (!soup_message_io_data_read_headers (io, FALSE, cancellable, error)) {
+                       if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT))
+                               soup_server_message_set_status (msg, SOUP_STATUS_MALFORMED, NULL);
                         return FALSE;
+               }
 
                 status = parse_headers (msg,
                                         (char *)io->read_header_buf->data,
                                         io->read_header_buf->len,
                                         &io->read_encoding,
-                                        io->sock,
                                         error);
                 g_byte_array_set_size (io->read_header_buf, 0);
 
+               request_headers = soup_server_message_get_request_headers (msg);
+
                 if (status != SOUP_STATUS_OK) {
                         /* Either we couldn't parse the headers, or they
                          * indicated something that would mean we wouldn't
@@ -572,14 +685,13 @@ io_read (SoupMessage  *msg,
                          * reading, and make sure the connection gets
                          * closed when we're done.
                          */
-                        soup_message_set_status (msg, status);
-                        soup_message_headers_append (msg->request_headers,
-                                                     "Connection", "close");
+                        soup_server_message_set_status (msg, status, NULL);
+                        soup_message_headers_append (request_headers, "Connection", "close");
                         io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
                         break;
                 }
 
-                if (soup_message_headers_get_expectations (msg->request_headers) & 
SOUP_EXPECTATION_CONTINUE) {
+                if (soup_message_headers_get_expectations (request_headers) & SOUP_EXPECTATION_CONTINUE) {
                         /* We must return a status code and response
                          * headers to the client; either an error to
                          * be set by a got-headers handler below, or
@@ -591,11 +703,11 @@ io_read (SoupMessage  *msg,
                         io->read_state = SOUP_MESSAGE_IO_STATE_BODY_START;
 
                 if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH)
-                        io->read_length = soup_message_headers_get_content_length (msg->request_headers);
+                        io->read_length = soup_message_headers_get_content_length (request_headers);
                 else
                         io->read_length = -1;
 
-                soup_message_got_headers (msg);
+                soup_server_message_got_headers (msg);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_BODY_START:
@@ -618,10 +730,13 @@ io_read (SoupMessage  *msg,
                                                 FALSE,
                                                 cancellable, error);
                 if (nread > 0) {
-                        if (msg->request_body) {
+                       SoupMessageBody *request_body;
+
+                       request_body = soup_server_message_get_request_body (msg);
+                        if (request_body) {
                                 GBytes *bytes = g_bytes_new (buf, nread);
-                                soup_message_body_got_chunk (msg->request_body, bytes);
-                                soup_message_got_chunk (msg, bytes);
+                                soup_message_body_got_chunk (request_body, bytes);
+                                soup_server_message_got_chunk (msg, bytes);
                                 g_bytes_unref (bytes);
                         }
                         break;
@@ -637,7 +752,7 @@ io_read (SoupMessage  *msg,
 
         case SOUP_MESSAGE_IO_STATE_BODY_DONE:
                 io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
-                soup_message_got_body (msg);
+                soup_server_message_got_body (msg);
                 break;
 
         case SOUP_MESSAGE_IO_STATE_FINISHING:
@@ -653,13 +768,14 @@ io_read (SoupMessage  *msg,
 }
 
 static gboolean
-io_run_until (SoupMessage       *msg,
+io_run_until (SoupServerMessage *msg,
               SoupMessageIOState read_state,
               SoupMessageIOState write_state,
               GCancellable      *cancellable,
               GError           **error)
 {
-        SoupMessageIOData *io = soup_message_get_io_data (msg);
+        SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg);
+       SoupMessageIOData *io = &server_io->base;
         gboolean progress = TRUE, done;
         GError *my_error = NULL;
 
@@ -674,7 +790,7 @@ io_run_until (SoupMessage       *msg,
 
         g_object_ref (msg);
 
-        while (progress && soup_message_get_io_data (msg) == io && !io->paused && !io->async_wait &&
+        while (progress && soup_server_message_get_io_data (msg) == server_io && !io->paused && 
!io->async_wait &&
                (io->read_state < read_state || io->write_state < write_state)) {
 
                 if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
@@ -689,7 +805,7 @@ io_run_until (SoupMessage       *msg,
                 g_propagate_error (error, my_error);
                 g_object_unref (msg);
                 return FALSE;
-        } else if (soup_message_get_io_data (msg) != io) {
+        } else if (soup_server_message_get_io_data (msg) != server_io) {
                 g_set_error_literal (error, G_IO_ERROR,
                                      G_IO_ERROR_CANCELLED,
                                      _("Operation was cancelled"));
@@ -716,22 +832,22 @@ io_run_until (SoupMessage       *msg,
         return done;
 }
 
-static void io_run (SoupMessage *msg);
+static void io_run (SoupServerMessage *msg);
 
 static gboolean
-io_run_ready (SoupMessage *msg,
-              gpointer     user_data)
+io_run_ready (SoupServerMessage *msg,
+              gpointer           user_data)
 {
         io_run (msg);
         return FALSE;
 }
 
 static void
-io_run (SoupMessage *msg)
+io_run (SoupServerMessage *msg)
 {
-        SoupMessageIOData *io = soup_message_get_io_data (msg);
+        SoupServerMessageIOData *server_io = soup_server_message_get_io_data (msg);
+       SoupMessageIOData *io = &server_io->base;
         GError *error = NULL;
-        GCancellable *cancellable;
 
         if (io->io_source) {
                 g_source_destroy (io->io_source);
@@ -740,58 +856,109 @@ io_run (SoupMessage *msg)
         }
 
         g_object_ref (msg);
-        cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL;
-
         if (io_run_until (msg,
                           SOUP_MESSAGE_IO_STATE_DONE,
                           SOUP_MESSAGE_IO_STATE_DONE,
-                          cancellable, &error)) {
-                soup_message_io_finished (msg);
+                          NULL, &error)) {
+                soup_server_message_io_finished (msg);
         } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                 g_clear_error (&error);
-                io->io_source = soup_message_io_get_source (msg, NULL, io_run_ready, msg);
+                io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), NULL,
+                                                                (SoupMessageIOSourceFunc)io_run_ready,
+                                                                NULL);
                 g_source_attach (io->io_source, io->async_context);
         } else {
-                if (soup_message_get_io_data (msg) == io) {
-                       if (!SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code) &&
+                if (soup_server_message_get_io_data (msg) == server_io) {
+                       if (!SOUP_STATUS_IS_TRANSPORT_ERROR (soup_server_message_get_status (msg, NULL)) &&
                            !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
-                                soup_message_set_status (msg, SOUP_STATUS_IO_ERROR);
+                                soup_server_message_set_status (msg, SOUP_STATUS_IO_ERROR, NULL);
                         }
-                        soup_message_io_finished (msg);
+                        soup_server_message_io_finished (msg);
                 }
                 g_error_free (error);
 
         }
-
         g_object_unref (msg);
-        g_clear_object (&cancellable);
 }
 
 void
-soup_message_read_request (SoupMessage               *msg,
-                           SoupSocket                *sock,
-                           SoupMessageCompletionFn    completion_cb,
-                           gpointer                   user_data)
+soup_server_message_read_request (SoupServerMessage        *msg,
+                                 SoupMessageIOCompletionFn completion_cb,
+                                 gpointer                  user_data)
+{
+        SoupServerMessageIOData *io;
+       SoupSocket *sock;
+
+        io = g_slice_new0 (SoupServerMessageIOData);
+        io->base.completion_cb = completion_cb;
+        io->base.completion_data = user_data;
+
+       sock = soup_server_message_get_soup_socket (msg);
+        io->base.iostream = g_object_ref (soup_socket_get_iostream (sock));
+        io->base.istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->base.iostream));
+        io->base.ostream = g_io_stream_get_output_stream (io->base.iostream);
+        io->base.async_context = g_main_context_ref_thread_default ();
+
+        io->base.read_header_buf = g_byte_array_new ();
+        io->base.write_buf = g_string_new (NULL);
+
+        io->base.read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+        io->base.write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
+
+        soup_server_message_set_io_data (msg, io);
+
+        io_run (msg);
+}
+
+void
+soup_server_message_io_pause (SoupServerMessage *msg)
 {
-        SoupMessageIOData *io;
+       SoupServerMessageIOData *io = soup_server_message_get_io_data (msg);
 
-        io = g_slice_new0 (SoupMessageIOData);
-        io->completion_cb = completion_cb;
-        io->completion_data = user_data;
+        g_return_if_fail (io != NULL);
 
-        io->sock = sock;
-        io->iostream = g_object_ref (soup_socket_get_iostream (io->sock));
-        io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->iostream));
-        io->ostream = g_io_stream_get_output_stream (io->iostream);
-        io->async_context = g_main_context_ref_thread_default ();
+       if (io->unpause_source) {
+                g_source_destroy (io->unpause_source);
+               g_source_unref (io->unpause_source);
+                io->unpause_source = NULL;
+       }
 
-        io->read_header_buf = g_byte_array_new ();
-        io->write_buf = g_string_new (NULL);
+       soup_message_io_data_pause (&io->base);
+}
 
-        io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
-        io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
+static gboolean
+io_unpause_internal (gpointer msg)
+{
+        SoupServerMessageIOData *io = soup_server_message_get_io_data (msg);
 
-        soup_message_set_io_data (msg, io);
+       g_return_val_if_fail (io != NULL, FALSE);
+
+       g_clear_pointer (&io->unpause_source, g_source_unref);
+       soup_message_io_data_unpause (&io->base);
+        if (io->base.io_source)
+               return FALSE;
 
         io_run (msg);
+       return FALSE;
+}
+
+void
+soup_server_message_io_unpause (SoupServerMessage *msg)
+{
+       SoupServerMessageIOData *io = soup_server_message_get_io_data (msg);
+
+        g_return_if_fail (io != NULL);
+
+        if (!io->unpause_source) {
+               io->unpause_source = soup_add_completion_reffed (io->base.async_context,
+                                                                io_unpause_internal, msg, NULL);
+        }
+}
+
+gboolean
+soup_server_message_is_io_paused (SoupServerMessage *msg)
+{
+       SoupServerMessageIOData *io = soup_server_message_get_io_data (msg);
+
+       return io && io->base.paused;
 }
diff --git a/libsoup/server/soup-server-message-private.h b/libsoup/server/soup-server-message-private.h
new file mode 100644
index 00000000..057b058d
--- /dev/null
+++ b/libsoup/server/soup-server-message-private.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 Igalia S.L.
+ */
+
+#ifndef __SOUP_SERVER_MESSAGE_PRIVATE_H__
+#define __SOUP_SERVER_MESSAGE_PRIVATE_H__ 1
+
+#include "soup-server-message.h"
+#include "soup-auth-domain.h"
+#include "soup-message-io-data.h"
+#include "soup-socket.h"
+
+SoupServerMessage *soup_server_message_new                 (SoupSocket               *sock);
+void               soup_server_message_set_uri             (SoupServerMessage        *msg,
+                                                            SoupURI                  *uri);
+void               soup_server_message_set_method          (SoupServerMessage        *msg,
+                                                            const char               *method);
+SoupSocket        *soup_server_message_get_soup_socket     (SoupServerMessage        *msg);
+void               soup_server_message_set_auth            (SoupServerMessage        *msg,
+                                                            SoupAuthDomain           *domain,
+                                                            char                     *user);
+gboolean           soup_server_message_is_keepalive        (SoupServerMessage        *msg);
+GIOStream         *soup_server_message_io_steal            (SoupServerMessage        *msg);
+void               soup_server_message_io_pause            (SoupServerMessage        *msg);
+void               soup_server_message_io_unpause          (SoupServerMessage        *msg);
+gboolean           soup_server_message_is_io_paused        (SoupServerMessage        *msg);
+void               soup_server_message_io_finished         (SoupServerMessage        *msg);
+void               soup_server_message_cleanup_response    (SoupServerMessage        *msg);
+void               soup_server_message_wrote_informational (SoupServerMessage        *msg);
+void               soup_server_message_wrote_headers       (SoupServerMessage        *msg);
+void               soup_server_message_wrote_chunk         (SoupServerMessage        *msg);
+void               soup_server_message_wrote_body_data     (SoupServerMessage        *msg,
+                                                            gsize                     chunk_size);
+void               soup_server_message_wrote_body          (SoupServerMessage        *msg);
+void               soup_server_message_got_headers         (SoupServerMessage        *msg);
+void               soup_server_message_got_chunk           (SoupServerMessage        *msg,
+                                                            GBytes                   *chunk);
+void               soup_server_message_got_body            (SoupServerMessage        *msg);
+void               soup_server_message_finished            (SoupServerMessage        *msg);
+void               soup_server_message_read_request        (SoupServerMessage        *msg,
+                                                            SoupMessageIOCompletionFn completion_cb,
+                                                            gpointer                  user_data);
+
+typedef struct _SoupServerMessageIOData SoupServerMessageIOData;
+void                     soup_server_message_io_data_free  (SoupServerMessageIOData *io);
+void                     soup_server_message_set_io_data   (SoupServerMessage        *msg,
+                                                            SoupServerMessageIOData  *io);
+SoupServerMessageIOData *soup_server_message_get_io_data   (SoupServerMessage        *msg);
+
+#endif /* __SOUP_SERVER_MESSAGE_PRIVATE_H__ */
diff --git a/libsoup/server/soup-server-message.c b/libsoup/server/soup-server-message.c
new file mode 100644
index 00000000..1f2e8515
--- /dev/null
+++ b/libsoup/server/soup-server-message.c
@@ -0,0 +1,881 @@
+/*
+ * soup-server-message.c: HTTP server request/response
+ *
+ * Copyright (C) 2020 Igalia S.L.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-server-message.h"
+#include "soup.h"
+#include "soup-connection.h"
+#include "soup-server-message-private.h"
+#include "soup-socket-private.h"
+
+/**
+ * SECTION:soup-server-message
+ * @short_description: An HTTP server request and response.
+ * @see_also: #SoupMessageHeaders, #SoupMessageBody
+ *
+ * A SoupServerMessage represents an HTTP message that is being sent or
+ * received on a #SoupServer
+ *
+ * #SoupServer will create #SoupServerMessage<!-- -->s automatically for
+ * incoming requests, which your application will receive via handlers.
+ *
+ * Note that libsoup's terminology here does not quite match the HTTP
+ * specification: in RFC 2616, an "HTTP-message" is
+ * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a
+ * Response. In libsoup, a #SoupServerMessage combines both the request and
+ * the response.
+ **/
+
+struct _SoupServerMessage {
+        GObject             parent;
+
+        SoupSocket         *sock;
+        GSocket            *gsock;
+        SoupAuthDomain     *auth_domain;
+        char               *auth_user;
+
+        GSocketAddress     *remote_addr;
+        char               *remote_ip;
+        GSocketAddress     *local_addr;
+
+        const char         *method;
+        SoupHTTPVersion     http_version;
+        SoupHTTPVersion     orig_http_version;
+
+        guint               status_code;
+        char               *reason_phrase;
+
+        SoupURI            *uri;
+
+        SoupMessageBody    *request_body;
+        SoupMessageHeaders *request_headers;
+
+        SoupMessageBody    *response_body;
+        SoupMessageHeaders *response_headers;
+
+        SoupServerMessageIOData *io_data;
+};
+
+struct _SoupServerMessageClass {
+        GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (SoupServerMessage, soup_server_message, G_TYPE_OBJECT)
+
+enum {
+        WROTE_INFORMATIONAL,
+        WROTE_HEADERS,
+        WROTE_CHUNK,
+        WROTE_BODY_DATA,
+        WROTE_BODY,
+
+        GOT_HEADERS,
+        GOT_CHUNK,
+        GOT_BODY,
+
+        DISCONNECTED,
+        FINISHED,
+
+        LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+soup_server_message_init (SoupServerMessage *msg)
+{
+        msg->request_body = soup_message_body_new ();
+        msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
+        msg->response_body = soup_message_body_new ();
+        msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
+        soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CONTENT_LENGTH);
+}
+
+static void
+soup_server_message_finalize (GObject *object)
+{
+        SoupServerMessage *msg = SOUP_SERVER_MESSAGE (object);
+
+        soup_server_message_io_data_free (msg->io_data);
+
+        g_clear_object (&msg->auth_domain);
+        g_clear_pointer (&msg->auth_user, g_free);
+        g_clear_object (&msg->remote_addr);
+        g_clear_object (&msg->local_addr);
+
+        if (msg->sock) {
+                g_signal_handlers_disconnect_by_data (msg->sock, msg);
+                g_object_unref (msg->sock);
+        }
+        g_clear_object (&msg->gsock);
+        g_clear_pointer (&msg->remote_ip, g_free);
+
+        g_clear_pointer (&msg->uri, soup_uri_free);
+        g_free (msg->reason_phrase);
+
+        soup_message_body_free (msg->request_body);
+        soup_message_headers_free (msg->request_headers);
+        soup_message_body_free (msg->response_body);
+        soup_message_headers_free (msg->response_headers);
+
+        G_OBJECT_CLASS (soup_server_message_parent_class)->finalize (object);
+}
+
+static void
+soup_server_message_class_init (SoupServerMessageClass *klass)
+{
+        GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = soup_server_message_finalize;
+
+        /**
+         * SoupServerMessage::wrote-informational:
+         * @msg: the message
+         *
+         * Emitted immediately after writing a 1xx (Informational) response.
+         */
+        signals[WROTE_INFORMATIONAL] =
+                g_signal_new ("wrote-informational",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::wrote-headers:
+         * @msg: the message
+         *
+         * Emitted immediately after writing the response headers for a
+         * message.
+         */
+        signals[WROTE_HEADERS] =
+                g_signal_new ("wrote-headers",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::wrote-chunk:
+         * @msg: the message
+         *
+         * Emitted immediately after writing a body chunk for a message.
+         *
+         * Note that this signal is not parallel to
+         * #SoupServerMessage::got-chunk; it is emitted only when a complete
+         * chunk (added with soup_message_body_append() or
+         * soup_message_body_append_bytes()) has been written. To get
+         * more useful continuous progress information, use
+         * #SoupServerMessage::wrote-body-data.
+         */
+        signals[WROTE_CHUNK] =
+                g_signal_new ("wrote-chunk",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::wrote-body-data:
+         * @msg: the message
+         * @chunk_size: the number of bytes written
+         *
+         * Emitted immediately after writing a portion of the message
+         * body to the network.
+         */
+        signals[WROTE_BODY_DATA] =
+                g_signal_new ("wrote-body-data",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 1,
+                              G_TYPE_UINT);
+
+        /**
+         * SoupServerMessage::wrote-body:
+         * @msg: the message
+         *
+         * Emitted immediately after writing the complete response body for a
+         * message.
+         */
+        signals[WROTE_BODY] =
+                g_signal_new ("wrote-body",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::got-headers:
+         * @msg: the message
+         *
+         * Emitted after receiving the Request-Line and request headers.
+         */
+        signals[GOT_HEADERS] =
+                g_signal_new ("got-headers",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::got-chunk:
+         * @msg: the message
+         * @chunk: the just-read chunk
+         *
+         * Emitted after receiving a chunk of a message body. Note
+         * that "chunk" in this context means any subpiece of the
+         * body, not necessarily the specific HTTP 1.1 chunks sent by
+         * the other side.
+         */
+        signals[GOT_CHUNK] =
+                g_signal_new ("got-chunk",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 1,
+                              G_TYPE_BYTES);
+
+        /**
+         * SoupServerMessage::got-body:
+         * @msg: the message
+         *
+         * Emitted after receiving the complete request body.
+         */
+        signals[GOT_BODY] =
+                g_signal_new ("got-body",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::finished:
+         * @msg: the message
+         *
+         * Emitted when all HTTP processing is finished for a message.
+         * (After #SoupServerMessage::wrote-body).
+         */
+        signals[FINISHED] =
+                g_signal_new ("finished",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+
+        /**
+         * SoupServerMessage::disconnected:
+         * @msg: the message
+         *
+         * Emitted when the @msg's socket is disconnected.
+         */
+        signals[DISCONNECTED] =
+                g_signal_new ("disconnected",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              0,
+                              NULL, NULL,
+                              NULL,
+                              G_TYPE_NONE, 0);
+}
+
+static void
+socket_disconnected (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[DISCONNECTED], 0);
+}
+
+SoupServerMessage *
+soup_server_message_new (SoupSocket *sock)
+{
+        SoupServerMessage *msg;
+
+        msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+        msg->sock = g_object_ref (sock);
+        msg->gsock = soup_socket_get_gsocket (sock);
+        if (msg->gsock)
+                g_object_ref (msg->gsock);
+
+        g_signal_connect_object (sock, "disconnected",
+                                 G_CALLBACK (socket_disconnected),
+                                 msg, G_CONNECT_SWAPPED);
+
+        return msg;
+}
+
+void
+soup_server_message_set_uri (SoupServerMessage *msg,
+                             SoupURI           *uri)
+{
+        if (msg->uri)
+                soup_uri_free (msg->uri);
+        msg->uri = soup_uri_copy (uri);
+}
+
+SoupSocket *
+soup_server_message_get_soup_socket (SoupServerMessage *msg)
+{
+        return msg->sock;
+}
+
+void
+soup_server_message_set_auth (SoupServerMessage *msg,
+                              SoupAuthDomain    *domain,
+                              char              *user)
+{
+        if (msg->auth_domain)
+                g_object_unref (msg->auth_domain);
+        msg->auth_domain = domain;
+
+        if (msg->auth_user)
+                g_free (msg->auth_user);
+        msg->auth_user = user;
+}
+
+gboolean
+soup_server_message_is_keepalive (SoupServerMessage *msg)
+{
+        if (msg->status_code == SOUP_STATUS_OK && msg->method == SOUP_METHOD_CONNECT)
+                return TRUE;
+
+        /* Not persistent if the server sent a terminate-by-EOF response */
+        if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
+                return FALSE;
+
+        if (msg->http_version == SOUP_HTTP_1_0) {
+                /* In theory, HTTP/1.0 connections are only persistent
+                 * if the client requests it, and the server agrees.
+                 * But some servers do keep-alive even if the client
+                 * doesn't request it. So ignore c_conn.
+                 */
+
+                if (!soup_message_headers_header_contains (msg->response_headers,
+                                                           "Connection", "Keep-Alive"))
+                        return FALSE;
+        } else {
+                /* Normally persistent unless either side requested otherwise */
+                if (soup_message_headers_header_contains (msg->request_headers,
+                                                          "Connection", "close") ||
+                    soup_message_headers_header_contains (msg->response_headers,
+                                                          "Connection", "close"))
+                        return FALSE;
+
+                return TRUE;
+        }
+
+        return TRUE;
+}
+
+void
+soup_server_message_set_io_data (SoupServerMessage       *msg,
+                                 SoupServerMessageIOData *io)
+{
+        soup_server_message_io_data_free (msg->io_data);
+        msg->io_data = io;
+}
+
+SoupServerMessageIOData *
+soup_server_message_get_io_data (SoupServerMessage *msg)
+{
+        return msg->io_data;
+}
+
+void
+soup_server_message_cleanup_response (SoupServerMessage *msg)
+{
+        soup_message_body_truncate (msg->response_body);
+        soup_message_headers_clear (msg->response_headers);
+        soup_message_headers_set_encoding (msg->response_headers,
+                                           SOUP_ENCODING_CONTENT_LENGTH);
+        msg->status_code = SOUP_STATUS_NONE;
+        g_clear_pointer (&msg->reason_phrase, g_free);
+        msg->http_version = msg->orig_http_version;
+}
+
+void
+soup_server_message_wrote_informational (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
+}
+
+void
+soup_server_message_wrote_headers (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[WROTE_HEADERS], 0);
+}
+
+void
+soup_server_message_wrote_chunk (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[WROTE_CHUNK], 0);
+}
+
+void
+soup_server_message_wrote_body_data (SoupServerMessage *msg,
+                                     gsize              chunk_size)
+{
+        g_signal_emit (msg, signals[WROTE_BODY_DATA], 0, chunk_size);
+}
+
+void
+soup_server_message_wrote_body (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[WROTE_BODY], 0);
+}
+
+void
+soup_server_message_got_headers (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[GOT_HEADERS], 0);
+}
+
+void
+soup_server_message_got_chunk (SoupServerMessage *msg,
+                               GBytes            *chunk)
+{
+        g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
+}
+
+void
+soup_server_message_got_body (SoupServerMessage *msg)
+{
+        if (soup_message_body_get_accumulate (msg->request_body))
+                g_bytes_unref (soup_message_body_flatten (msg->request_body));
+        g_signal_emit (msg, signals[GOT_BODY], 0);
+}
+
+void
+soup_server_message_finished (SoupServerMessage *msg)
+{
+        g_signal_emit (msg, signals[FINISHED], 0);
+}
+
+/**
+ * soup_server_message_get_request_headers:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the request headers of @msg.
+ *
+ * Returns: (transfer none): a #SoupMessageHeaders with the request headers.
+ */
+SoupMessageHeaders *
+soup_server_message_get_request_headers (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->request_headers;
+}
+
+/**
+ * soup_server_message_get_response_headers:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the response headers of @msg.
+ *
+ * Returns: (transfer none): a #SoupMessageHeaders with the response headers.
+ */
+SoupMessageHeaders *
+soup_server_message_get_response_headers (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->response_headers;
+}
+
+/**
+ * soup_server_message_get_request_body:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the request body of @msg.
+ *
+ * Returns: (transfer none): a #SoupMessageBody.
+ */
+SoupMessageBody *
+soup_server_message_get_request_body (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->request_body;
+}
+
+/**
+ * soup_server_message_get_response_body:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the response body of @msg.
+ *
+ * Returns: (transfer none): a #SoupMessageBody.
+ */
+SoupMessageBody *
+soup_server_message_get_response_body (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->response_body;
+}
+
+/**
+ * soup_server_message_get_method:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the HTTP method of @msg.
+ *
+ * Returns: the HTTP method.
+ */
+const char *
+soup_server_message_get_method (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->method;
+}
+
+void
+soup_server_message_set_method (SoupServerMessage *msg,
+                                const char        *method)
+{
+        msg->method = g_intern_string (method);
+}
+
+/**
+ * soup_server_message_get_http_version:
+ * @msg: a #SoupServerMessage
+ *
+ * Get the HTTP version of @msg.
+ *
+ * Returns: a #SoupHTTPVersion.
+ */
+SoupHTTPVersion
+soup_server_message_get_http_version (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), SOUP_HTTP_1_1);
+
+        return msg->http_version;
+}
+
+/**
+ * soup_server_message_set_http_version:
+ * @msg: a #SoupServerMessage
+ * @version: a #SoupHTTPVersion
+ *
+ * Set the HTTP version of @msg.
+ */
+void
+soup_server_message_set_http_version (SoupServerMessage *msg,
+                                      SoupHTTPVersion    version)
+{
+        g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
+
+        msg->http_version = version;
+        if (msg->status_code == SOUP_STATUS_NONE)
+                msg->orig_http_version = version;
+}
+
+/**
+ * soup_server_message_get_status:
+ * @msg: a #SoupServerMessage
+ * @reason_phrase: (out) (nullable) (transfer none): a location to store the reason phrase or %NULL
+ *
+ * Get the HTTP status code of @msg and optionally the reason phrase if @reason_phrase is not %NULL.
+ *
+ * Returns: the HTTP status code.
+ */
+guint
+soup_server_message_get_status (SoupServerMessage *msg,
+                                const char       **reason_phrase)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), 0);
+
+        if (reason_phrase)
+                *reason_phrase = msg->reason_phrase;
+
+        return msg->status_code;
+}
+
+/**
+ * soup_server_message_set_status:
+ * @msg: a #SoupServerMessage
+ * @status_code: an HTTP status code
+ * @reason_phrase: (nullable): a reason phrase
+ *
+ * Sets @msg's status code to @status_code. If @status_code is a
+ * known value and @reason_phrase is %NULL, the reason_phrase will
+ * be set automatically.
+ **/
+void
+soup_server_message_set_status (SoupServerMessage *msg,
+                                guint              status_code,
+                                const char        *reason_phrase)
+{
+        g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
+        g_return_if_fail (status_code != 0);
+
+        g_free (msg->reason_phrase);
+
+        msg->status_code = status_code;
+        msg->reason_phrase = g_strdup (reason_phrase ? reason_phrase : soup_status_get_phrase (status_code));
+}
+
+/**
+ * soup_server_message_get_uri:
+ * @msg: a #SoupServerMessage
+ *
+ * Get @msg's URI.
+ *
+ * Returns: (transfer none): a #SoupURI
+ */
+SoupURI *
+soup_server_message_get_uri (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->uri;
+}
+
+/**
+ * soup_server_message_set_response:
+ * @msg: the message
+ * @content_type: (allow-none): MIME Content-Type of the body
+ * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
+ * @resp_body: (allow-none) (array length=resp_length) (element-type guint8):
+ *   a data buffer containing the body of the message response.
+ * @resp_length: the byte length of @resp_body.
+ *
+ * Convenience function to set the response body of a #SoupServerMessage. If
+ * @content_type is %NULL, the response body must be empty as well.
+ */
+void
+soup_server_message_set_response (SoupServerMessage *msg,
+                                  const char        *content_type,
+                                  SoupMemoryUse      resp_use,
+                                  const char        *resp_body,
+                                  gsize              resp_length)
+{
+        g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
+        g_return_if_fail (content_type != NULL || resp_length == 0);
+
+        if (content_type) {
+                g_warn_if_fail (strchr (content_type, '/') != NULL);
+
+                soup_message_headers_replace (msg->response_headers,
+                                              "Content-Type", content_type);
+                soup_message_body_append (msg->response_body, resp_use,
+                                          resp_body, resp_length);
+        } else {
+                soup_message_headers_remove (msg->response_headers,
+                                             "Content-Type");
+                soup_message_body_truncate (msg->response_body);
+        }
+}
+
+/**
+ * soup_server_message_set_redirect:
+ * @msg: a #SoupServerMessage
+ * @status_code: a 3xx status code
+ * @redirect_uri: the URI to redirect @msg to
+ *
+ * Sets @msg's status_code to @status_code and adds a Location header
+ * pointing to @redirect_uri. Use this from a #SoupServer when you
+ * want to redirect the client to another URI.
+ *
+ * @redirect_uri can be a relative URI, in which case it is
+ * interpreted relative to @msg's current URI. In particular, if
+ * @redirect_uri is just a path, it will replace the path
+ * <emphasis>and query</emphasis> of @msg's URI.
+ */
+void
+soup_server_message_set_redirect (SoupServerMessage *msg,
+                                  guint              status_code,
+                                  const char        *redirect_uri)
+{
+        SoupURI *location;
+        char *location_str;
+
+        g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
+
+        location = soup_uri_new_with_base (soup_server_message_get_uri (msg), redirect_uri);
+        g_return_if_fail (location != NULL);
+
+        soup_server_message_set_status (msg, status_code, NULL);
+        location_str = soup_uri_to_string (location, FALSE);
+        soup_message_headers_replace (msg->response_headers, "Location",
+                                      location_str);
+        g_free (location_str);
+        soup_uri_free (location);
+}
+
+/**
+ * soup_server_message_get_socket:
+ * @msg: a #SoupServerMessage
+ *
+ * Retrieves the #GSocket that @msg is associated with.
+ *
+ * If you are using this method to observe when multiple requests are
+ * made on the same persistent HTTP connection (eg, as the ntlm-test
+ * test program does), you will need to pay attention to socket
+ * destruction as well (eg, by using weak references), so that you do
+ * not get fooled when the allocator reuses the memory address of a
+ * previously-destroyed socket to represent a new socket.
+ *
+ * Return value: (nullable) (transfer none): the #GSocket that @msg is
+ * associated with, %NULL if you used soup_server_accept_iostream().
+ */
+GSocket *
+soup_server_message_get_socket (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        return msg->gsock;
+}
+
+/**
+ * soup_server_message_get_remote_address:
+ * @msg: a #SoupServerMessage
+ *
+ * Retrieves the #GSocketAddress associated with the remote end
+ * of a connection.
+ *
+ * Return value: (nullable) (transfer none): the #GSocketAddress
+ *     associated with the remote end of a connection, it may be
+ *     %NULL if you used soup_server_accept_iostream().
+ */
+GSocketAddress *
+soup_server_message_get_remote_address (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        if (msg->remote_addr)
+                return msg->remote_addr;
+
+        msg->remote_addr = msg->gsock ?
+                g_socket_get_remote_address (msg->gsock, NULL) :
+                G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (msg->sock)));
+
+        return msg->remote_addr;
+}
+
+/**
+ * soup_server_message_get_local_address:
+ * @msg: a #SoupServerMessage
+ *
+ * Retrieves the #GSocketAddress associated with the local end
+ * of a connection.
+ *
+ * Return value: (nullable) (transfer none): the #GSocketAddress
+ *     associated with the local end of a connection, it may be
+ *     %NULL if you used soup_server_accept_iostream().
+ */
+GSocketAddress *
+soup_server_message_get_local_address (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        if (msg->local_addr)
+                return msg->local_addr;
+
+        msg->local_addr = msg->gsock ?
+                g_socket_get_local_address (msg->gsock, NULL) :
+                G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (msg->sock)));
+
+        return msg->local_addr;
+}
+
+/**
+ * soup_server_message_get_remote_host:
+ * @msg: a #SoupServerMessage
+ *
+ * Retrieves the IP address associated with the remote end of a
+ * connection.
+ *
+ * Return value: (nullable): the IP address associated with the remote
+ *     end of a connection, it may be %NULL if you used
+ *     soup_server_accept_iostream().
+ */
+const char *
+soup_server_message_get_remote_host (SoupServerMessage *msg)
+{
+        g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), NULL);
+
+        if (msg->remote_ip)
+                return msg->remote_ip;
+
+        if (msg->gsock) {
+                GSocketAddress *addr = soup_server_message_get_remote_address (msg);
+                GInetAddress *iaddr;
+
+                if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr))
+                        return NULL;
+                iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
+                msg->remote_ip = g_inet_address_to_string (iaddr);
+        } else {
+                GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address 
(msg->sock));
+                GInetAddress *inet_addr = g_inet_socket_address_get_address (addr);
+                msg->remote_ip = g_inet_address_to_string (inet_addr);
+        }
+
+        return msg->remote_ip;
+}
+
+/**
+ * soup_server_message_steal_connection:
+ * @msg: a #SoupServerMessage
+ *
+ * "Steals" the HTTP connection associated with @msg from its
+ * #SoupServer. This happens immediately, regardless of the current
+ * state of the connection; if the response to @msg has not yet finished
+ * being sent, then it will be discarded; you can steal the connection from a
+ * #SoupServerMessage:wrote-informational or #SoupServerMessage:wrote-body signal
+ * handler if you need to wait for part or all of the response to be sent.
+ *
+ * Note that when calling this function from C, @msg will most
+ * likely be freed as a side effect.
+ *
+ * Return value: (transfer full): the #GIOStream formerly associated
+ *   with @msg (or %NULL if @msg was no longer associated with a
+ *   connection). No guarantees are made about what kind of #GIOStream
+ *   is returned.
+ */
+GIOStream *
+soup_server_message_steal_connection (SoupServerMessage *msg)
+{
+        GIOStream *stream;
+
+        g_object_ref (msg);
+        stream = soup_server_message_io_steal (msg);
+        if (stream) {
+                g_object_set_data_full (G_OBJECT (stream), "GSocket",
+                                        soup_socket_steal_gsocket (msg->sock),
+                                        g_object_unref);
+        }
+
+        socket_disconnected (msg);
+        g_object_unref (msg);
+
+        return stream;
+}
diff --git a/libsoup/server/soup-server-message.h b/libsoup/server/soup-server-message.h
new file mode 100644
index 00000000..5102648e
--- /dev/null
+++ b/libsoup/server/soup-server-message.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2020 Igalia S.L.
+ */
+
+#ifndef __SOUP_SERVER_MESSAGE_H__
+#define __SOUP_SERVER_MESSAGE_H__ 1
+
+#include "soup-types.h"
+#include "soup-message-body.h"
+#include "soup-message-headers.h"
+#include "soup-method.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SERVER_MESSAGE (soup_server_message_get_type ())
+SOUP_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (SoupServerMessage, soup_server_message, SOUP, SERVER_MESSAGE, GObject)
+
+SOUP_AVAILABLE_IN_ALL
+SoupMessageHeaders *soup_server_message_get_request_headers  (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+SoupMessageHeaders *soup_server_message_get_response_headers (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+SoupMessageBody    *soup_server_message_get_request_body     (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+SoupMessageBody    *soup_server_message_get_response_body    (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+const char         *soup_server_message_get_method           (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+SoupHTTPVersion     soup_server_message_get_http_version     (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+void                soup_server_message_set_http_version     (SoupServerMessage *msg,
+                                                              SoupHTTPVersion    version);
+
+SOUP_AVAILABLE_IN_ALL
+guint               soup_server_message_get_status           (SoupServerMessage *msg,
+                                                              const char       **reason_phrase);
+SOUP_AVAILABLE_IN_ALL
+void                soup_server_message_set_status           (SoupServerMessage *msg,
+                                                              guint              status_code,
+                                                              const char        *reason_phrase);
+SOUP_AVAILABLE_IN_ALL
+SoupURI            *soup_server_message_get_uri              (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+void                soup_server_message_set_response         (SoupServerMessage *msg,
+                                                              const char        *content_type,
+                                                              SoupMemoryUse      resp_use,
+                                                              const char        *resp_body,
+                                                              gsize              resp_length);
+SOUP_AVAILABLE_IN_ALL
+void                soup_server_message_set_redirect          (SoupServerMessage *msg,
+                                                               guint              status_code,
+                                                               const char        *redirect_uri);
+
+SOUP_AVAILABLE_IN_ALL
+GSocket            *soup_server_message_get_socket            (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+GSocketAddress     *soup_server_message_get_local_address     (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+GSocketAddress     *soup_server_message_get_remote_address    (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+const char         *soup_server_message_get_remote_host       (SoupServerMessage *msg);
+
+SOUP_AVAILABLE_IN_ALL
+GIOStream          *soup_server_message_steal_connection      (SoupServerMessage *msg);
+
+G_END_DECLS
+
+#endif /* __SOUP_SERVER_MESSAGE_H__ */
diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c
index aa5f2203..2802f66a 100644
--- a/libsoup/server/soup-server.c
+++ b/libsoup/server/soup-server.c
@@ -13,9 +13,10 @@
 
 #include <glib/gi18n-lib.h>
 
-#include "soup-server-private.h"
+#include "soup-server.h"
+#include "soup-server-message-private.h"
 #include "soup.h"
-#include "soup-message-private.h"
+#include "soup-server-message-private.h"
 #include "soup-misc.h"
 #include "soup-path-map.h" 
 #include "soup-socket-private.h"
@@ -129,21 +130,6 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
-struct _SoupClientContext {
-       SoupServer     *server;
-       SoupSocket     *sock;
-       GSocket        *gsock;
-       SoupMessage    *msg;
-       SoupAuthDomain *auth_domain;
-       char           *auth_user;
-
-       GSocketAddress *remote_addr;
-       char           *remote_ip;
-       GSocketAddress *local_addr;
-
-       int             ref_count;
-};
-
 typedef struct {
        char               *path;
 
@@ -207,9 +193,8 @@ enum {
 
 G_DEFINE_TYPE_WITH_PRIVATE (SoupServer, soup_server, G_TYPE_OBJECT)
 
-static SoupClientContext *soup_client_context_ref (SoupClientContext *client);
-static void soup_client_context_unref (SoupClientContext *client);
-
+static void start_request (SoupServer        *server,
+                          SoupServerMessage *msg);
 static void
 free_handler (SoupServerHandler *handler)
 {
@@ -398,7 +383,6 @@ soup_server_class_init (SoupServerClass *server_class)
         * SoupServer::request-started:
         * @server: the server
         * @message: the new message
-        * @client: the client context
         *
         * Emitted when the server has started reading a new request.
         * @message will be completely blank; not even the
@@ -419,15 +403,13 @@ soup_server_class_init (SoupServerClass *server_class)
                              G_STRUCT_OFFSET (SoupServerClass, request_started),
                              NULL, NULL,
                              NULL,
-                             G_TYPE_NONE, 2, 
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_CLIENT_CONTEXT);
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_SERVER_MESSAGE);
 
        /**
         * SoupServer::request-read:
         * @server: the server
         * @message: the message
-        * @client: the client context
         *
         * Emitted when the server has successfully read a request.
         * @message will have all of its request-side information
@@ -444,15 +426,13 @@ soup_server_class_init (SoupServerClass *server_class)
                              G_STRUCT_OFFSET (SoupServerClass, request_read),
                              NULL, NULL,
                              NULL,
-                             G_TYPE_NONE, 2,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_CLIENT_CONTEXT);
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_SERVER_MESSAGE);
 
        /**
         * SoupServer::request-finished:
         * @server: the server
         * @message: the message
-        * @client: the client context
         *
         * Emitted when the server has finished writing a response to
         * a request.
@@ -464,15 +444,13 @@ soup_server_class_init (SoupServerClass *server_class)
                              G_STRUCT_OFFSET (SoupServerClass, request_finished),
                              NULL, NULL,
                              NULL,
-                             G_TYPE_NONE, 2,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_CLIENT_CONTEXT);
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_SERVER_MESSAGE);
 
        /**
         * SoupServer::request-aborted:
         * @server: the server
         * @message: the message
-        * @client: the client context
         *
         * Emitted when processing has failed for a message; this
         * could mean either that it could not be read (if
@@ -493,9 +471,8 @@ soup_server_class_init (SoupServerClass *server_class)
                              G_STRUCT_OFFSET (SoupServerClass, request_aborted),
                              NULL, NULL,
                              NULL,
-                             G_TYPE_NONE, 2,
-                             SOUP_TYPE_MESSAGE,
-                             SOUP_TYPE_CLIENT_CONTEXT);
+                             G_TYPE_NONE, 1,
+                             SOUP_TYPE_SERVER_MESSAGE);
 
        /* properties */
        /**
@@ -818,97 +795,6 @@ soup_server_get_listeners (SoupServer *server)
        return listeners;
 }
 
-static void start_request (SoupServer *, SoupClientContext *);
-static void socket_disconnected (SoupSocket *sock, SoupClientContext *client);
-
-static SoupClientContext *
-soup_client_context_new (SoupServer *server, SoupSocket *sock)
-{
-       SoupClientContext *client = g_slice_new0 (SoupClientContext);
-
-       client->server = server;
-       client->sock = g_object_ref (sock);
-       client->gsock = soup_socket_get_gsocket (sock);
-       if (client->gsock)
-               g_object_ref (client->gsock);
-       g_signal_connect (sock, "disconnected",
-                         G_CALLBACK (socket_disconnected), client);
-       client->ref_count = 1;
-
-       return client;
-}
-
-static void
-soup_client_context_cleanup (SoupClientContext *client)
-{
-       g_clear_object (&client->auth_domain);
-       g_clear_pointer (&client->auth_user, g_free);
-       g_clear_object (&client->remote_addr);
-       g_clear_object (&client->local_addr);
-
-       client->msg = NULL;
-}
-
-static SoupClientContext *
-soup_client_context_ref (SoupClientContext *client)
-{
-       g_atomic_int_inc (&client->ref_count);
-       return client;
-}
-
-static void
-soup_client_context_unref (SoupClientContext *client)
-{
-       if (!g_atomic_int_dec_and_test (&client->ref_count))
-               return;
-
-       soup_client_context_cleanup (client);
-
-       g_signal_handlers_disconnect_by_func (client->sock, socket_disconnected, client);
-       g_object_unref (client->sock);
-       g_clear_object (&client->gsock);
-       g_clear_pointer (&client->remote_ip, g_free);
-       g_slice_free (SoupClientContext, client);
-}
-
-static void
-request_finished (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer user_data)
-{
-       SoupClientContext *client = user_data;
-       SoupServer *server = client->server;
-       SoupServerPrivate *priv = soup_server_get_instance_private (server);
-       SoupSocket *sock = client->sock;
-       gboolean failed;
-
-       if (completion == SOUP_MESSAGE_IO_STOLEN) {
-               soup_client_context_unref (client);
-               g_object_unref (msg);
-               return;
-       }
-
-       /* Complete the message, assuming it actually really started. */
-       if (msg->method) {
-               soup_message_finished (msg);
-
-               failed = (completion == SOUP_MESSAGE_IO_INTERRUPTED ||
-                         msg->status_code == SOUP_STATUS_IO_ERROR);
-               g_signal_emit (server,
-                              failed ? signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED],
-                              0, msg, client);
-       }
-
-       if (completion == SOUP_MESSAGE_IO_COMPLETE &&
-           soup_socket_is_connected (sock) &&
-           soup_message_is_keepalive (msg) &&
-           priv->listeners) {
-               start_request (server, client);
-       } else {
-               soup_socket_disconnect (client->sock);
-               soup_client_context_unref (client);
-       }
-       g_object_unref (msg);
-}
-
 /* "" was never documented as meaning the same thing as "/", but it
  * effectively was. We have to special case it now or otherwise it
  * would match "*" too.
@@ -916,19 +802,21 @@ request_finished (SoupMessage *msg, SoupMessageIOCompletion completion, gpointer
 #define NORMALIZED_PATH(path) ((path) && *(path) ? (path) : "/")
 
 static SoupServerHandler *
-get_handler (SoupServer *server, SoupMessage *msg)
+get_handler (SoupServer        *server,
+            SoupServerMessage *msg)
 {
        SoupServerPrivate *priv = soup_server_get_instance_private (server);
        SoupURI *uri;
 
-       uri = soup_message_get_uri (msg);
+       uri = soup_server_message_get_uri (msg);
        return soup_path_map_lookup (priv->handlers, NORMALIZED_PATH (uri->path));
 }
 
 static void
-call_handler (SoupServer *server, SoupServerHandler *handler,
-             SoupClientContext *client, SoupMessage *msg,
-             gboolean early)
+call_handler (SoupServer        *server,
+             SoupServerHandler *handler,
+             SoupServerMessage *msg,
+             gboolean           early)
 {
        GHashTable *form_data_set;
        SoupURI *uri;
@@ -938,10 +826,10 @@ call_handler (SoupServer *server, SoupServerHandler *handler,
        else if (!early && !handler->callback)
                return;
 
-       if (msg->status_code != 0)
+       if (soup_server_message_get_status (msg, NULL) != 0)
                return;
 
-       uri = soup_message_get_uri (msg);
+       uri = soup_server_message_get_uri (msg);
        if (uri->query)
                form_data_set = soup_form_decode (uri->query);
        else
@@ -950,11 +838,11 @@ call_handler (SoupServer *server, SoupServerHandler *handler,
        if (early) {
                (*handler->early_callback) (server, msg,
                                            uri->path, form_data_set,
-                                           client, handler->early_user_data);
+                                           handler->early_user_data);
        } else {
                (*handler->callback) (server, msg,
                                      uri->path, form_data_set,
-                                     client, handler->user_data);
+                                     handler->user_data);
        }
 
        if (form_data_set)
@@ -962,9 +850,9 @@ call_handler (SoupServer *server, SoupServerHandler *handler,
 }
 
 static void
-got_headers (SoupMessage *msg, SoupClientContext *client)
+got_headers (SoupServer        *server,
+            SoupServerMessage *msg)
 {
-       SoupServer *server = client->server;
        SoupServerPrivate *priv = soup_server_get_instance_private (server);
        SoupServerHandler *handler;
        SoupURI *uri;
@@ -974,22 +862,26 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
        GSList *iter;
        gboolean rejected = FALSE;
        char *auth_user;
+       SoupMessageHeaders *headers;
+       SoupSocket *sock;
 
        /* Add required response headers */
+       headers = soup_server_message_get_response_headers (msg);
+
        date = g_date_time_new_now_utc ();
        date_string = soup_date_time_to_string (date, SOUP_DATE_HTTP);
-       soup_message_headers_replace (msg->response_headers, "Date",
-                                     date_string);
+       soup_message_headers_replace (headers, "Date", date_string);
        g_free (date_string);
        g_date_time_unref (date);
 
-       if (msg->status_code != 0)
+       if (soup_server_message_get_status (msg, NULL) != 0)
                return;
 
-       uri = soup_message_get_uri (msg);
-       if ((soup_socket_is_ssl (client->sock) && !soup_uri_is_https (uri, priv->https_aliases)) ||
-           (!soup_socket_is_ssl (client->sock) && !soup_uri_is_http (uri, priv->http_aliases))) {
-               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+       sock = soup_server_message_get_soup_socket (msg);
+       uri = soup_server_message_get_uri (msg);
+       if ((soup_socket_is_ssl (sock) && !soup_uri_is_https (uri, priv->https_aliases)) ||
+           (!soup_socket_is_ssl (sock) && !soup_uri_is_http (uri, priv->http_aliases))) {
+               soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                return;
        }
 
@@ -1010,7 +902,7 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
                    ) {
                        /* Introducing new ".." segments is not allowed */
                        g_free (decoded_path);
-                       soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+                       soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                        return;
                }
 
@@ -1029,8 +921,7 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
                if (soup_auth_domain_covers (domain, msg)) {
                        auth_user = soup_auth_domain_accepts (domain, msg);
                        if (auth_user) {
-                               client->auth_domain = g_object_ref (domain);
-                               client->auth_user = auth_user;
+                               soup_server_message_set_auth (msg, g_object_ref (domain), auth_user);
                                return;
                        }
 
@@ -1052,15 +943,14 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
        /* Otherwise, call the early handlers. */
        handler = get_handler (server, msg);
        if (handler)
-               call_handler (server, handler, client, msg, TRUE);
+               call_handler (server, handler, msg, TRUE);
 }
 
 static void
-complete_websocket_upgrade (SoupMessage *msg, gpointer user_data)
+complete_websocket_upgrade (SoupServer        *server,
+                           SoupServerMessage *msg)
 {
-       SoupClientContext *client = user_data;
-       SoupServer *server = client->server;
-       SoupURI *uri = soup_message_get_uri (msg);
+       SoupURI *uri = soup_server_message_get_uri (msg);
        SoupServerHandler *handler;
        GIOStream *stream;
        SoupWebsocketConnection *conn;
@@ -1069,42 +959,41 @@ complete_websocket_upgrade (SoupMessage *msg, gpointer user_data)
        if (!handler || !handler->websocket_callback)
                return;
 
-       soup_client_context_ref (client);
-       stream = soup_client_context_steal_connection (client);
+       g_object_ref (msg);
+       stream = soup_server_message_steal_connection (msg);
        conn = soup_websocket_connection_new_with_extensions (stream, uri,
                                                              SOUP_WEBSOCKET_CONNECTION_SERVER,
-                                                             soup_message_headers_get_one 
(msg->request_headers, "Origin"),
-                                                             soup_message_headers_get_one 
(msg->response_headers, "Sec-WebSocket-Protocol"),
+                                                             soup_message_headers_get_one 
(soup_server_message_get_request_headers (msg), "Origin"),
+                                                             soup_message_headers_get_one 
(soup_server_message_get_response_headers (msg), "Sec-WebSocket-Protocol"),
                                                              handler->websocket_extensions);
        handler->websocket_extensions = NULL;
        g_object_unref (stream);
-       soup_client_context_unref (client);
 
-       (*handler->websocket_callback) (server, conn, uri->path, client,
+       (*handler->websocket_callback) (server, msg, uri->path, conn,
                                        handler->websocket_user_data);
        g_object_unref (conn);
-       soup_client_context_unref (client);
+       g_object_unref (msg);
 }
 
 static void
-got_body (SoupMessage *msg, SoupClientContext *client)
+got_body (SoupServer        *server,
+         SoupServerMessage *msg)
 {
-       SoupServer *server = client->server;
        SoupServerHandler *handler;
 
-       g_signal_emit (server, signals[REQUEST_READ], 0, msg, client);
+       g_signal_emit (server, signals[REQUEST_READ], 0, msg);
 
-       if (msg->status_code != 0)
+       if (soup_server_message_get_status (msg, NULL) != 0)
                return;
 
        handler = get_handler (server, msg);
        if (!handler) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                return;
        }
 
-       call_handler (server, handler, client, msg, FALSE);
-       if (msg->status_code != 0)
+       call_handler (server, handler, msg, FALSE);
+       if (soup_server_message_get_status (msg, NULL) != 0)
                return;
 
        if (handler->websocket_callback) {
@@ -1116,65 +1005,110 @@ got_body (SoupMessage *msg, SoupClientContext *client)
                                                                             handler->websocket_protocols,
                                                                             priv->websocket_extension_types,
                                                                             &handler->websocket_extensions)) 
{
-                       g_signal_connect (msg, "wrote-informational",
-                                         G_CALLBACK (complete_websocket_upgrade),
-                                         soup_client_context_ref (client));
+                       g_signal_connect_object (msg, "wrote-informational",
+                                                G_CALLBACK (complete_websocket_upgrade),
+                                                server, G_CONNECT_SWAPPED);
                }
        }
 }
 
 static void
-start_request (SoupServer *server, SoupClientContext *client)
+client_disconnected (SoupServer        *server,
+                    SoupServerMessage *msg)
 {
        SoupServerPrivate *priv = soup_server_get_instance_private (server);
-       SoupMessage *msg;
-
-       soup_client_context_cleanup (client);
 
-       /* Listen for another request on this connection */
-       msg = g_object_new (SOUP_TYPE_MESSAGE,
-                           SOUP_MESSAGE_SERVER_SIDE, TRUE,
-                           NULL);
-       client->msg = msg;
+       priv->clients = g_slist_remove (priv->clients, msg);
 
-       if (priv->server_header) {
-               soup_message_headers_append (msg->response_headers, "Server",
-                                            priv->server_header);
+       if (soup_server_message_get_status (msg, NULL) != 0) {
+               soup_server_message_set_status (msg, SOUP_STATUS_IO_ERROR, NULL);
+               soup_server_message_io_finished (msg);
        }
+}
 
-       g_signal_connect (msg, "got_headers", G_CALLBACK (got_headers), client);
-       g_signal_connect (msg, "got_body", G_CALLBACK (got_body), client);
-
-       g_signal_emit (server, signals[REQUEST_STARTED], 0,
-                      msg, client);
-
-       soup_message_read_request (msg, client->sock,
-                                  request_finished, client);
+static void
+soup_server_accept_socket (SoupServer *server,
+                          SoupSocket *sock)
+{
+       SoupServerPrivate *priv = soup_server_get_instance_private (server);
+       SoupServerMessage *msg;
+
+       msg = soup_server_message_new (sock);
+       g_signal_connect_object (msg, "disconnected",
+                                G_CALLBACK (client_disconnected),
+                                server, G_CONNECT_SWAPPED);
+       priv->clients = g_slist_prepend (priv->clients, msg);
+       start_request (server, msg);
 }
 
 static void
-socket_disconnected (SoupSocket *sock, SoupClientContext *client)
+request_finished (SoupServerMessage      *msg,
+                 SoupMessageIOCompletion completion,
+                 SoupServer             *server)
 {
-       SoupServerPrivate *priv = soup_server_get_instance_private (client->server);
+       SoupServerPrivate *priv = soup_server_get_instance_private (server);
+       SoupSocket *sock = soup_server_message_get_soup_socket (msg);
+       gboolean failed;
+
+       if (completion == SOUP_MESSAGE_IO_STOLEN) {
+               g_object_unref (msg);
+               return;
+       }
 
-       priv->clients = g_slist_remove (priv->clients, client);
+       /* Complete the message, assuming it actually really started. */
+       if (soup_server_message_get_method (msg)) {
+               soup_server_message_finished (msg);
+
+               failed = (completion == SOUP_MESSAGE_IO_INTERRUPTED ||
+                         soup_server_message_get_status (msg, NULL) == SOUP_STATUS_IO_ERROR);
+               g_signal_emit (server,
+                              failed ? signals[REQUEST_ABORTED] : signals[REQUEST_FINISHED],
+                              0, msg);
+       }
 
-       if (client->msg) {
-               soup_message_set_status (client->msg, SOUP_STATUS_IO_ERROR);
-               soup_message_io_finished (client->msg);
+       if (completion == SOUP_MESSAGE_IO_COMPLETE &&
+           soup_socket_is_connected (sock) &&
+           soup_server_message_is_keepalive (msg) &&
+           priv->listeners) {
+               g_object_ref (sock);
+               priv->clients = g_slist_remove (priv->clients, msg);
+               g_object_unref (msg);
+
+               soup_server_accept_socket (server, sock);
+               g_object_unref (sock);
+               return;
        }
+
+       soup_socket_disconnect (sock);
+       g_object_unref (msg);
 }
 
 static void
-soup_server_accept_socket (SoupServer *server,
-                          SoupSocket *sock)
+start_request (SoupServer        *server,
+              SoupServerMessage *msg)
 {
        SoupServerPrivate *priv = soup_server_get_instance_private (server);
-       SoupClientContext *client;
 
-       client = soup_client_context_new (server, sock);
-       priv->clients = g_slist_prepend (priv->clients, client);
-       start_request (server, client);
+       if (priv->server_header) {
+               SoupMessageHeaders *headers;
+
+               headers = soup_server_message_get_response_headers (msg);
+               soup_message_headers_append (headers, "Server",
+                                            priv->server_header);
+       }
+
+       g_signal_connect_object (msg, "got-headers",
+                                G_CALLBACK (got_headers),
+                                server, G_CONNECT_SWAPPED);
+       g_signal_connect_object (msg, "got-body",
+                                G_CALLBACK (got_body),
+                                server, G_CONNECT_SWAPPED);
+
+       g_signal_emit (server, signals[REQUEST_STARTED], 0, msg);
+
+       soup_server_message_read_request (msg,
+                                         (SoupMessageIOCompletionFn)request_finished,
+                                         server);
 }
 
 /**
@@ -1194,11 +1128,11 @@ soup_server_accept_socket (SoupServer *server,
  * Since: 2.50
  **/
 gboolean
-soup_server_accept_iostream   (SoupServer     *server,
-                              GIOStream      *stream,
-                              GSocketAddress *local_addr,
-                              GSocketAddress *remote_addr,
-                              GError        **error)
+soup_server_accept_iostream (SoupServer     *server,
+                            GIOStream      *stream,
+                            GSocketAddress *local_addr,
+                            GSocketAddress *remote_addr,
+                            GError        **error)
 {
        SoupSocket *sock;
 
@@ -1244,7 +1178,6 @@ soup_server_disconnect (SoupServer *server)
        SoupServerPrivate *priv;
        GSList *listeners, *clients, *iter;
        SoupSocket *listener;
-       SoupClientContext *client;
 
        g_return_if_fail (SOUP_IS_SERVER (server));
        priv = soup_server_get_instance_private (server);
@@ -1255,8 +1188,9 @@ soup_server_disconnect (SoupServer *server)
        priv->listeners = NULL;
 
        for (iter = clients; iter; iter = iter->next) {
-               client = iter->data;
-               soup_socket_disconnect (client->sock);
+               SoupServerMessage *msg = iter->data;
+
+               soup_socket_disconnect (soup_server_message_get_soup_socket (msg));
        }
        g_slist_free (clients);
 
@@ -1678,250 +1612,6 @@ soup_server_get_uris (SoupServer *server)
        return uris;
 }
 
-/**
- * SoupClientContext:
- *
- * A #SoupClientContext provides additional information about the
- * client making a particular request. In particular, you can use
- * soup_client_context_get_auth_domain() and
- * soup_client_context_get_auth_user() to determine if HTTP
- * authentication was used successfully.
- *
- * soup_client_context_get_remote_address() and/or
- * soup_client_context_get_host() can be used to get information for
- * logging or debugging purposes. soup_client_context_get_socket() may
- * also be of use in some situations (eg, tracking when multiple
- * requests are made on the same connection).
- **/
-G_DEFINE_BOXED_TYPE (SoupClientContext, soup_client_context, soup_client_context_ref, 
soup_client_context_unref)
-
-/**
- * soup_client_context_get_soup_socket:
- * @client: a #SoupClientContext
- *
- * Retrieves the #SoupSocket that @client is associated with.
- *
- * If you are using this method to observe when multiple requests are
- * made on the same persistent HTTP connection (eg, as the ntlm-test
- * test program does), you will need to pay attention to socket
- * destruction as well (either by using weak references, or by
- * connecting to the #SoupSocket::disconnected signal), so that you do
- * not get fooled when the allocator reuses the memory address of a
- * previously-destroyed socket to represent a new socket.
- *
- * Return value: (transfer none): the #SoupSocket that @client is
- * associated with.
- **/
-SoupSocket *
-soup_client_context_get_soup_socket (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       return client->sock;
-}
-
-/**
- * soup_client_context_get_socket:
- * @client: a #SoupClientContext
- *
- * Retrieves the #GSocket that @client is associated with.
- *
- * If you are using this method to observe when multiple requests are
- * made on the same persistent HTTP connection (eg, as the ntlm-test
- * test program does), you will need to pay attention to socket
- * destruction as well (eg, by using weak references), so that you do
- * not get fooled when the allocator reuses the memory address of a
- * previously-destroyed socket to represent a new socket.
- *
- * Return value: (nullable) (transfer none): the #GSocket that @client is
- * associated with, %NULL if you used soup_server_accept_iostream().
- *
- * Since: 2.48
- **/
-GSocket *
-soup_client_context_get_socket (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       return client->gsock;
-}
-
-/**
- * soup_client_context_get_remote_address:
- * @client: a #SoupClientContext
- *
- * Retrieves the #GSocketAddress associated with the remote end
- * of a connection.
- *
- * Return value: (nullable) (transfer none): the #GSocketAddress
- * associated with the remote end of a connection, it may be
- * %NULL if you used soup_server_accept_iostream().
- *
- * Since: 2.48
- **/
-GSocketAddress *
-soup_client_context_get_remote_address (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       if (client->remote_addr)
-               return client->remote_addr;
-
-       client->remote_addr = client->gsock ?
-               g_socket_get_remote_address (client->gsock, NULL) :
-               G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (client->sock)));
-
-       return client->remote_addr;
-}
-
-/**
- * soup_client_context_get_local_address:
- * @client: a #SoupClientContext
- *
- * Retrieves the #GSocketAddress associated with the local end
- * of a connection.
- *
- * Return value: (nullable) (transfer none): the #GSocketAddress
- * associated with the local end of a connection, it may be
- * %NULL if you used soup_server_accept_iostream().
- *
- * Since: 2.48
- **/
-GSocketAddress *
-soup_client_context_get_local_address (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       if (client->local_addr)
-               return client->local_addr;
-
-       client->local_addr = client->gsock ?
-               g_socket_get_local_address (client->gsock, NULL) :
-               G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (client->sock)));
-
-       return client->local_addr;
-}
-
-/**
- * soup_client_context_get_host:
- * @client: a #SoupClientContext
- *
- * Retrieves the IP address associated with the remote end of a
- * connection.
- *
- * Return value: (nullable): the IP address associated with the remote
- * end of a connection, it may be %NULL if you used
- * soup_server_accept_iostream().
- **/
-const char *
-soup_client_context_get_host (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       if (client->remote_ip)
-               return client->remote_ip;
-
-       if (client->gsock) {
-               GSocketAddress *addr = soup_client_context_get_remote_address (client);
-               GInetAddress *iaddr;
-
-               if (!addr || !G_IS_INET_SOCKET_ADDRESS (addr))
-                       return NULL;
-               iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
-               client->remote_ip = g_inet_address_to_string (iaddr);
-       } else {
-               GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address 
(client->sock));
-                GInetAddress *inet_addr = g_inet_socket_address_get_address (addr);
-               client->remote_ip = g_inet_address_to_string (inet_addr);
-       }
-
-       return client->remote_ip;
-}
-
-/**
- * soup_client_context_get_auth_domain:
- * @client: a #SoupClientContext
- *
- * Checks whether the request associated with @client has been
- * authenticated, and if so returns the #SoupAuthDomain that
- * authenticated it.
- *
- * Return value: (transfer none) (nullable): a #SoupAuthDomain, or
- * %NULL if the request was not authenticated.
- **/
-SoupAuthDomain *
-soup_client_context_get_auth_domain (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       return client->auth_domain;
-}
-
-/**
- * soup_client_context_get_auth_user:
- * @client: a #SoupClientContext
- *
- * Checks whether the request associated with @client has been
- * authenticated, and if so returns the username that the client
- * authenticated as.
- *
- * Return value: (nullable): the authenticated-as user, or %NULL if
- * the request was not authenticated.
- **/
-const char *
-soup_client_context_get_auth_user (SoupClientContext *client)
-{
-       g_return_val_if_fail (client != NULL, NULL);
-
-       return client->auth_user;
-}
-
-/**
- * soup_client_context_steal_connection:
- * @client: a #SoupClientContext
- *
- * "Steals" the HTTP connection associated with @client from its
- * #SoupServer. This happens immediately, regardless of the current
- * state of the connection; if the response to the current
- * #SoupMessage has not yet finished being sent, then it will be
- * discarded; you can steal the connection from a
- * #SoupMessage::wrote-informational or #SoupMessage::wrote-body signal
- * handler if you need to wait for part or all of the response to be
- * sent.
- *
- * Note that when calling this function from C, @client will most
- * likely be freed as a side effect.
- *
- * Return value: (transfer full): the #GIOStream formerly associated
- *   with @client (or %NULL if @client was no longer associated with a
- *   connection). No guarantees are made about what kind of #GIOStream
- *   is returned.
- *
- * Since: 2.50
- **/
-GIOStream *
-soup_client_context_steal_connection (SoupClientContext *client)
-{
-       GIOStream *stream;
-
-       g_return_val_if_fail (client != NULL, NULL);
-
-       soup_client_context_ref (client);
-
-       stream = soup_message_io_steal (client->msg);
-       if (stream) {
-               g_object_set_data_full (G_OBJECT (stream), "GSocket",
-                                       soup_socket_steal_gsocket (client->sock),
-                                       g_object_unref);
-       }
-
-       socket_disconnected (client->sock, client);
-       soup_client_context_unref (client);
-
-       return stream;
-}
-
-
 /**
  * SoupServerCallback:
  * @server: the #SoupServer
@@ -1929,7 +1619,6 @@ soup_client_context_steal_connection (SoupClientContext *client)
  * @path: the path component of @msg's Request-URI
  * @query: (element-type utf8 utf8) (allow-none): the parsed query
  *   component of @msg's Request-URI
- * @client: additional contextual information about the client
  * @user_data: the data passed to soup_server_add_handler() or
  *   soup_server_add_early_handler().
  *
@@ -2108,7 +1797,7 @@ soup_server_add_early_handler (SoupServer            *server,
  * @server: the #SoupServer
  * @path: the path component of @msg's Request-URI
  * @connection: the newly created WebSocket connection
- * @client: additional contextual information about the client
+ * @msg: the #SoupServerMessage
  * @user_data: the data passed to @soup_server_add_handler
  *
  * A callback used to handle WebSocket requests to a #SoupServer. The
@@ -2249,30 +1938,30 @@ soup_server_remove_auth_domain (SoupServer *server, SoupAuthDomain *auth_domain)
 /**
  * soup_server_pause_message:
  * @server: a #SoupServer
- * @msg: a #SoupMessage associated with @server.
+ * @msg: a #SoupServerMessage associated with @server.
  *
  * Pauses I/O on @msg. This can be used when you need to return from
  * the server handler without having the full response ready yet. Use
  * soup_server_unpause_message() to resume I/O.
  *
- * This must only be called on a #SoupMessage which was created by the
+ * This must only be called on a #SoupServerMessage which was created by the
  * #SoupServer and are currently doing I/O, such as those passed into a
  * #SoupServerCallback or emitted in a #SoupServer::request-read signal.
  **/
 void
-soup_server_pause_message (SoupServer *server,
-                          SoupMessage *msg)
+soup_server_pause_message (SoupServer        *server,
+                          SoupServerMessage *msg)
 {
        g_return_if_fail (SOUP_IS_SERVER (server));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
 
-       soup_message_io_pause (msg);
+       soup_server_message_io_pause (msg);
 }
 
 /**
  * soup_server_unpause_message:
  * @server: a #SoupServer
- * @msg: a #SoupMessage associated with @server.
+ * @msg: a #SoupServerMessage associated with @server.
  *
  * Resumes I/O on @msg. Use this to resume after calling
  * soup_server_pause_message(), or after adding a new chunk to a
@@ -2280,18 +1969,18 @@ soup_server_pause_message (SoupServer *server,
  *
  * I/O won't actually resume until you return to the main loop.
  *
- * This must only be called on a #SoupMessage which was created by the
+ * This must only be called on a #SoupServerMessage which was created by the
  * #SoupServer and are currently doing I/O, such as those passed into a
  * #SoupServerCallback or emitted in a #SoupServer::request-read signal.
  **/
 void
-soup_server_unpause_message (SoupServer *server,
-                            SoupMessage *msg)
+soup_server_unpause_message (SoupServer        *server,
+                            SoupServerMessage *msg)
 {
        g_return_if_fail (SOUP_IS_SERVER (server));
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
+       g_return_if_fail (SOUP_IS_SERVER_MESSAGE (msg));
 
-       soup_message_io_unpause (msg);
+       soup_server_message_io_unpause (msg);
 }
 
 /**
diff --git a/libsoup/server/soup-server.h b/libsoup/server/soup-server.h
index eb9a13b7..b5225207 100644
--- a/libsoup/server/soup-server.h
+++ b/libsoup/server/soup-server.h
@@ -15,11 +15,6 @@ G_BEGIN_DECLS
 SOUP_AVAILABLE_IN_2_4
 G_DECLARE_DERIVABLE_TYPE (SoupServer, soup_server, SOUP, SERVER, GObject)
 
-typedef struct _SoupClientContext SoupClientContext;
-SOUP_AVAILABLE_IN_2_4
-GType soup_client_context_get_type (void);
-#define SOUP_TYPE_CLIENT_CONTEXT (soup_client_context_get_type ())
-
 typedef enum {
        SOUP_SERVER_LISTEN_HTTPS     = (1 << 0),
        SOUP_SERVER_LISTEN_IPV4_ONLY = (1 << 1),
@@ -30,14 +25,14 @@ struct _SoupServerClass {
        GObjectClass parent_class;
 
        /* signals */
-       void (*request_started)  (SoupServer *server, SoupMessage *msg,
-                                 SoupClientContext *client);
-       void (*request_read)     (SoupServer *server, SoupMessage *msg,
-                                 SoupClientContext *client);
-       void (*request_finished) (SoupServer *server, SoupMessage *msg,
-                                 SoupClientContext *client);
-       void (*request_aborted)  (SoupServer *server, SoupMessage *msg,
-                                 SoupClientContext *client);
+       void (*request_started)  (SoupServer        *server,
+                                 SoupServerMessage *msg);
+       void (*request_read)     (SoupServer        *server,
+                                 SoupServerMessage *msg);
+       void (*request_finished) (SoupServer        *server,
+                                 SoupServerMessage *msg);
+       void (*request_aborted)  (SoupServer        *server,
+                                 SoupServerMessage *msg);
 
        gpointer padding[6];
 };
@@ -98,10 +93,9 @@ gboolean        soup_server_accept_iostream    (SoupServer               *server
 /* Handlers and auth */
 
 typedef void  (*SoupServerCallback)            (SoupServer         *server,
-                                               SoupMessage        *msg,
+                                               SoupServerMessage  *msg,
                                                const char         *path,
                                                GHashTable         *query,
-                                               SoupClientContext  *client,
                                                gpointer            user_data);
 
 SOUP_AVAILABLE_IN_2_4
@@ -121,9 +115,9 @@ void            soup_server_add_early_handler  (SoupServer         *server,
 #define SOUP_SERVER_REMOVE_WEBSOCKET_EXTENSION "remove-websocket-extension"
 
 typedef void (*SoupServerWebsocketCallback) (SoupServer              *server,
-                                            SoupWebsocketConnection *connection,
+                                            SoupServerMessage       *msg,
                                             const char              *path,
-                                            SoupClientContext       *client,
+                                            SoupWebsocketConnection *connection,
                                             gpointer                 user_data);
 SOUP_AVAILABLE_IN_2_50
 void            soup_server_add_websocket_handler (SoupServer                   *server,
@@ -153,28 +147,10 @@ void            soup_server_remove_auth_domain (SoupServer         *server,
 
 /* I/O */
 SOUP_AVAILABLE_IN_2_4
-void            soup_server_pause_message   (SoupServer  *server,
-                                            SoupMessage *msg);
-SOUP_AVAILABLE_IN_2_4
-void            soup_server_unpause_message (SoupServer  *server,
-                                            SoupMessage *msg);
-
-/* Client context */
-
-SOUP_AVAILABLE_IN_2_48
-GSocket        *soup_client_context_get_socket        (SoupClientContext *client);
-SOUP_AVAILABLE_IN_2_48
-GSocketAddress *soup_client_context_get_local_address  (SoupClientContext *client);
-SOUP_AVAILABLE_IN_2_48
-GSocketAddress *soup_client_context_get_remote_address (SoupClientContext *client);
-SOUP_AVAILABLE_IN_2_4
-const char     *soup_client_context_get_host           (SoupClientContext *client);
+void            soup_server_pause_message   (SoupServer        *server,
+                                            SoupServerMessage *msg);
 SOUP_AVAILABLE_IN_2_4
-SoupAuthDomain *soup_client_context_get_auth_domain    (SoupClientContext *client);
-SOUP_AVAILABLE_IN_2_4
-const char     *soup_client_context_get_auth_user      (SoupClientContext *client);
-
-SOUP_AVAILABLE_IN_2_50
-GIOStream      *soup_client_context_steal_connection   (SoupClientContext *client);
+void            soup_server_unpause_message (SoupServer        *server,
+                                            SoupServerMessage *msg);
 
 G_END_DECLS
diff --git a/libsoup/soup-client-input-stream.c b/libsoup/soup-client-input-stream.c
index 16abb411..8fa28c0b 100644
--- a/libsoup/soup-client-input-stream.c
+++ b/libsoup/soup-client-input-stream.c
@@ -193,8 +193,9 @@ soup_client_input_stream_close_async (GInputStream        *stream,
        g_task_set_priority (task, priority);
 
        if (close_async_ready (cistream->priv->msg, task) == G_SOURCE_CONTINUE) {
-               source = soup_message_io_get_source (cistream->priv->msg,
-                                                    cancellable, NULL, NULL);
+               source = soup_message_io_data_get_source ((SoupMessageIOData *)soup_message_get_io_data 
(cistream->priv->msg),
+                                                         G_OBJECT (cistream->priv->msg),
+                                                         cancellable, NULL, NULL);
 
                g_task_attach_source (task, source, (GSourceFunc) close_async_ready);
                g_source_unref (source);
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index df2cdb59..6e39bc7c 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -730,10 +730,10 @@ soup_connection_get_ever_used (SoupConnection *conn)
 }
 
 void
-soup_connection_send_request (SoupConnection          *conn,
-                             SoupMessageQueueItem    *item,
-                             SoupMessageCompletionFn  completion_cb,
-                             gpointer                 user_data)
+soup_connection_send_request (SoupConnection           *conn,
+                             SoupMessageQueueItem     *item,
+                             SoupMessageIOCompletionFn completion_cb,
+                             gpointer                  user_data)
 {
        SoupConnectionPrivate *priv;
 
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 19888f1f..00c030cf 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -71,10 +71,10 @@ void                soup_connection_set_state  (SoupConnection   *conn,
 
 gboolean        soup_connection_get_ever_used  (SoupConnection   *conn);
 
-void            soup_connection_send_request   (SoupConnection          *conn,
-                                               SoupMessageQueueItem    *item,
-                                               SoupMessageCompletionFn  completion_cb,
-                                               gpointer                 user_data);
+void            soup_connection_send_request   (SoupConnection           *conn,
+                                               SoupMessageQueueItem     *item,
+                                               SoupMessageIOCompletionFn completion_cb,
+                                               gpointer                  user_data);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-form.c b/libsoup/soup-form.c
index 32e7de3c..e73cca93 100644
--- a/libsoup/soup-form.c
+++ b/libsoup/soup-form.c
@@ -112,13 +112,13 @@ soup_form_decode (const char *encoded_form)
 
 /**
  * soup_form_decode_multipart:
- * @msg: a #SoupMessage containing a "multipart/form-data" request body
+ * @multipart: a #SoupMultipart
  * @file_control_name: (allow-none): the name of the HTML file upload control, or %NULL
  * @filename: (out) (allow-none): return location for the name of the uploaded file, or %NULL
  * @content_type: (out) (allow-none): return location for the MIME type of the uploaded file, or %NULL
  * @file: (out) (allow-none): return location for the uploaded file data, or %NULL
  *
- * Decodes the "multipart/form-data" request in @msg; this is a
+ * Decodes the "multipart/form-data" request in @multipart; this is a
  * convenience method for the case when you have a single file upload
  * control in a form. (Or when you don't have any file upload
  * controls, but are still using "multipart/form-data" anyway.) Pass
@@ -142,29 +142,21 @@ soup_form_decode (const char *encoded_form)
  * a hash table containing the name/value pairs (other than
  * @file_control_name) from @msg, which you can free with
  * g_hash_table_destroy(). On error, it will return %NULL.
- *
- * Since: 2.26
- **/
+ */
 GHashTable *
-soup_form_decode_multipart (SoupMessage *msg, const char *file_control_name,
-                           char **filename, char **content_type,
-                           GBytes **file)
+soup_form_decode_multipart (SoupMultipart *multipart,
+                           const char    *file_control_name,
+                           char         **filename,
+                           char         **content_type,
+                           GBytes       **file)
 {
-       SoupMultipart *multipart;
        GHashTable *form_data_set, *params;
        SoupMessageHeaders *part_headers;
-       GBytes *body;
        GBytes *part_body;
        char *disposition, *name;
        int i;
 
-       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
-
-       body = soup_message_body_flatten (msg->request_body);
-       multipart = soup_multipart_new_from_message (msg->request_headers, body);
-       g_bytes_unref (body);
-       if (!multipart)
-               return NULL;
+       g_return_val_if_fail (multipart != NULL, NULL);
 
        if (filename)
                *filename = NULL;
diff --git a/libsoup/soup-form.h b/libsoup/soup-form.h
index 4880e7be..27ca8aa1 100644
--- a/libsoup/soup-form.h
+++ b/libsoup/soup-form.h
@@ -17,11 +17,11 @@ G_BEGIN_DECLS
 SOUP_AVAILABLE_IN_2_4
 GHashTable  *soup_form_decode           (const char   *encoded_form);
 SOUP_AVAILABLE_IN_2_26
-GHashTable  *soup_form_decode_multipart (SoupMessage  *msg,
-                                        const char   *file_control_name,
-                                        char        **filename,
-                                        char        **content_type,
-                                        GBytes      **file);
+GHashTable  *soup_form_decode_multipart (SoupMultipart *multipart,
+                                        const char    *file_control_name,
+                                        char         **filename,
+                                        char         **content_type,
+                                        GBytes       **file);
 
 SOUP_AVAILABLE_IN_2_4
 char        *soup_form_encode           (const char   *first_field,
diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c
index d76701a2..402f4f57 100644
--- a/libsoup/soup-logger.c
+++ b/libsoup/soup-logger.c
@@ -597,22 +597,6 @@ print_request (SoupLogger *logger, SoupMessage *msg,
                                           "%s: %s", name, value);
                }
        }
-       if (log_level == SOUP_LOGGER_LOG_HEADERS)
-               return;
-
-       if (msg->request_body->length &&
-           soup_message_body_get_accumulate (msg->request_body)) {
-               GBytes *request;
-
-               request = soup_message_body_flatten (msg->request_body);
-               g_return_if_fail (request != NULL);
-               g_bytes_unref (request);
-
-               if (soup_message_headers_get_expectations (msg->request_headers) != 
SOUP_EXPECTATION_CONTINUE) {
-                       soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
-                                          "\n%s", msg->request_body->data);
-               }
-       }
 }
 
 static void
@@ -653,13 +637,6 @@ print_response (SoupLogger *logger, SoupMessage *msg)
                soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '<',
                                   "%s: %s", name, value);
        }
-       if (log_level == SOUP_LOGGER_LOG_HEADERS)
-               return;
-
-       if (msg->response_body->data) {
-               soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '<',
-                                  "\n%s", msg->response_body->data);
-       }
 }
 
 static void
@@ -688,23 +665,9 @@ got_informational (SoupMessage *msg, gpointer user_data)
        print_response (logger, msg);
        soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
 
-       if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) {
-               SoupLoggerLogLevel log_level;
-
+       if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body_stream) {
                soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
                                   "[Now sending request body...]");
-
-               if (priv->request_filter) {
-                       log_level = priv->request_filter (logger, msg,
-                                                         priv->request_filter_data);
-               } else
-                       log_level = priv->level;
-
-               if (log_level == SOUP_LOGGER_LOG_BODY) {
-                       soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
-                                          "%s", msg->request_body->data);
-               }
-
                soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
        }
 
diff --git a/libsoup/soup-message-io-data.c b/libsoup/soup-message-io-data.c
new file mode 100644
index 00000000..8af99467
--- /dev/null
+++ b/libsoup/soup-message-io-data.c
@@ -0,0 +1,266 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-io-data.c: HTTP message I/O data
+ *
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib/gi18n-lib.h>
+
+#include "soup-message-io-data.h"
+#include "soup-message-private.h"
+#include "soup-server-message-private.h"
+#include "soup.h"
+
+#define RESPONSE_BLOCK_SIZE 8192
+#define HEADER_SIZE_LIMIT (64 * 1024)
+
+void
+soup_message_io_data_cleanup (SoupMessageIOData *io)
+{
+       if (io->io_source) {
+               g_source_destroy (io->io_source);
+               g_source_unref (io->io_source);
+               io->io_source = NULL;
+       }
+
+       if (io->iostream)
+               g_object_unref (io->iostream);
+       if (io->body_istream)
+               g_object_unref (io->body_istream);
+       if (io->body_ostream)
+               g_object_unref (io->body_ostream);
+       if (io->async_context)
+               g_main_context_unref (io->async_context);
+
+       g_byte_array_free (io->read_header_buf, TRUE);
+
+       g_string_free (io->write_buf, TRUE);
+
+       if (io->async_wait) {
+               g_cancellable_cancel (io->async_wait);
+               g_clear_object (&io->async_wait);
+       }
+       g_clear_error (&io->async_error);
+}
+
+gboolean
+soup_message_io_data_read_headers (SoupMessageIOData *io,
+                                  gboolean           blocking,
+                                  GCancellable      *cancellable,
+                                  GError           **error)
+{
+       gssize nread, old_len;
+       gboolean got_lf;
+
+       while (1) {
+               old_len = io->read_header_buf->len;
+               g_byte_array_set_size (io->read_header_buf, old_len + RESPONSE_BLOCK_SIZE);
+               nread = soup_filter_input_stream_read_line (io->istream,
+                                                           io->read_header_buf->data + old_len,
+                                                           RESPONSE_BLOCK_SIZE,
+                                                           blocking,
+                                                           &got_lf,
+                                                           cancellable, error);
+               io->read_header_buf->len = old_len + MAX (nread, 0);
+               if (nread == 0) {
+                       if (io->read_header_buf->len > 0)
+                               break;
+
+                       g_set_error_literal (error, G_IO_ERROR,
+                                            G_IO_ERROR_PARTIAL_INPUT,
+                                            _("Connection terminated unexpectedly"));
+               }
+               if (nread <= 0)
+                       return FALSE;
+
+               if (got_lf) {
+                       if (nread == 1 && old_len >= 2 &&
+                           !strncmp ((char *)io->read_header_buf->data +
+                                     io->read_header_buf->len - 2,
+                                     "\n\n", 2)) {
+                               io->read_header_buf->len--;
+                               break;
+                       } else if (nread == 2 && old_len >= 3 &&
+                                !strncmp ((char *)io->read_header_buf->data +
+                                          io->read_header_buf->len - 3,
+                                          "\n\r\n", 3)) {
+                               io->read_header_buf->len -= 2;
+                               break;
+                       }
+               }
+
+               if (io->read_header_buf->len > HEADER_SIZE_LIMIT) {
+                       g_set_error_literal (error, G_IO_ERROR,
+                                            G_IO_ERROR_PARTIAL_INPUT,
+                                            _("Header too big"));
+                       return FALSE;
+               }
+       }
+
+       io->read_header_buf->data[io->read_header_buf->len] = '\0';
+       return TRUE;
+}
+
+static gboolean
+message_io_is_paused (GObject *msg)
+{
+       if (SOUP_IS_MESSAGE (msg))
+               return soup_message_is_io_paused (SOUP_MESSAGE (msg));
+
+       if (SOUP_IS_SERVER_MESSAGE (msg))
+               return soup_server_message_is_io_paused (SOUP_SERVER_MESSAGE (msg));
+
+       return FALSE;
+}
+
+typedef struct {
+       GSource source;
+       GObject *msg;
+       gboolean paused;
+} SoupMessageIOSource;
+
+static gboolean
+message_io_source_check (GSource *source)
+{
+       SoupMessageIOSource *message_source = (SoupMessageIOSource *)source;
+
+       if (message_source->paused) {
+               if (message_io_is_paused (message_source->msg))
+                       return FALSE;
+               return TRUE;
+       } else
+               return FALSE;
+}
+
+static gboolean
+message_io_source_prepare (GSource *source,
+                          gint    *timeout)
+{
+       *timeout = -1;
+       return message_io_source_check (source);
+}
+
+static gboolean
+message_io_source_dispatch (GSource     *source,
+                           GSourceFunc  callback,
+                           gpointer     user_data)
+{
+       SoupMessageIOSourceFunc func = (SoupMessageIOSourceFunc)callback;
+       SoupMessageIOSource *message_source = (SoupMessageIOSource *)source;
+
+       return (*func) (message_source->msg, user_data);
+}
+
+static void
+message_io_source_finalize (GSource *source)
+{
+       SoupMessageIOSource *message_source = (SoupMessageIOSource *)source;
+
+       g_object_unref (message_source->msg);
+}
+
+static gboolean
+message_io_source_closure_callback (GObject *msg,
+                                   gpointer data)
+{
+       GClosure *closure = data;
+       GValue param = G_VALUE_INIT;
+       GValue result_value = G_VALUE_INIT;
+       gboolean result;
+
+       g_value_init (&result_value, G_TYPE_BOOLEAN);
+
+       g_value_init (&param, G_TYPE_OBJECT);
+       g_value_set_object (&param, msg);
+
+       g_closure_invoke (closure, &result_value, 1, &param, NULL);
+
+       result = g_value_get_boolean (&result_value);
+       g_value_unset (&result_value);
+       g_value_unset (&param);
+
+       return result;
+}
+
+static GSourceFuncs message_io_source_funcs =
+{
+       message_io_source_prepare,
+       message_io_source_check,
+       message_io_source_dispatch,
+       message_io_source_finalize,
+       (GSourceFunc)message_io_source_closure_callback,
+       (GSourceDummyMarshal)g_cclosure_marshal_generic,
+};
+
+GSource *
+soup_message_io_data_get_source (SoupMessageIOData     *io,
+                                GObject                *msg,
+                                GCancellable           *cancellable,
+                                SoupMessageIOSourceFunc callback,
+                                gpointer                user_data)
+{
+       GSource *base_source, *source;
+       SoupMessageIOSource *message_source;
+
+       if (!io) {
+               base_source = g_timeout_source_new (0);
+       } else if (io->paused) {
+               base_source = NULL;
+       } else if (io->async_wait) {
+               base_source = g_cancellable_source_new (io->async_wait);
+       } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) {
+               GPollableInputStream *istream;
+
+               if (io->body_istream)
+                       istream = G_POLLABLE_INPUT_STREAM (io->body_istream);
+               else
+                       istream = G_POLLABLE_INPUT_STREAM (io->istream);
+               base_source = g_pollable_input_stream_create_source (istream, cancellable);
+       } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) {
+               GPollableOutputStream *ostream;
+
+               if (io->body_ostream)
+                       ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream);
+               else
+                       ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream);
+               base_source = g_pollable_output_stream_create_source (ostream, cancellable);
+       } else
+               base_source = g_timeout_source_new (0);
+
+       source = g_source_new (&message_io_source_funcs, sizeof (SoupMessageIOSource));
+       g_source_set_name (source, "SoupMessageIOSource");
+       message_source = (SoupMessageIOSource *)source;
+       message_source->msg = g_object_ref (msg);
+       message_source->paused = io && io->paused;
+
+       if (base_source) {
+               g_source_set_dummy_callback (base_source);
+               g_source_add_child_source (source, base_source);
+               g_source_unref (base_source);
+       }
+       g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL);
+       return source;
+}
+
+void
+soup_message_io_data_pause (SoupMessageIOData *io)
+{
+       if (io->io_source) {
+               g_source_destroy (io->io_source);
+               g_source_unref (io->io_source);
+               io->io_source = NULL;
+       }
+
+       io->paused = TRUE;
+}
+
+void
+soup_message_io_data_unpause (SoupMessageIOData *io)
+{
+        io->paused = FALSE;
+}
diff --git a/libsoup/soup-message-io-data.h b/libsoup/soup-message-io-data.h
new file mode 100644
index 00000000..0476a425
--- /dev/null
+++ b/libsoup/soup-message-io-data.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2000-2003, Ximian, Inc.
+ */
+
+#ifndef __SOUP_MESSAGE_IO_DATA_H__
+#define __SOUP_MESSAGE_IO_DATA_H__ 1
+
+#include "soup-filter-input-stream.h"
+#include "soup-message-headers.h"
+
+typedef enum {
+       SOUP_MESSAGE_IO_STATE_NOT_STARTED,
+       SOUP_MESSAGE_IO_STATE_ANY = SOUP_MESSAGE_IO_STATE_NOT_STARTED,
+       SOUP_MESSAGE_IO_STATE_HEADERS,
+       SOUP_MESSAGE_IO_STATE_BLOCKING,
+       SOUP_MESSAGE_IO_STATE_BODY_START,
+       SOUP_MESSAGE_IO_STATE_BODY,
+       SOUP_MESSAGE_IO_STATE_BODY_DATA,
+       SOUP_MESSAGE_IO_STATE_BODY_FLUSH,
+       SOUP_MESSAGE_IO_STATE_BODY_DONE,
+       SOUP_MESSAGE_IO_STATE_FINISHING,
+       SOUP_MESSAGE_IO_STATE_DONE
+} SoupMessageIOState;
+
+#define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \
+       (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \
+        state != SOUP_MESSAGE_IO_STATE_BLOCKING && \
+        state != SOUP_MESSAGE_IO_STATE_DONE)
+#define SOUP_MESSAGE_IO_STATE_POLLABLE(state) \
+       (SOUP_MESSAGE_IO_STATE_ACTIVE (state) && \
+        state != SOUP_MESSAGE_IO_STATE_BODY_DONE)
+
+typedef enum {
+       SOUP_MESSAGE_IO_COMPLETE,
+        SOUP_MESSAGE_IO_INTERRUPTED,
+        SOUP_MESSAGE_IO_STOLEN
+} SoupMessageIOCompletion;
+
+typedef void (*SoupMessageIOCompletionFn) (GObject                *msg,
+                                          SoupMessageIOCompletion completion,
+                                          gpointer                user_data);
+
+typedef struct {
+       GIOStream              *iostream;
+       SoupFilterInputStream  *istream;
+       GInputStream           *body_istream;
+       GOutputStream          *ostream;
+       GOutputStream          *body_ostream;
+       GMainContext           *async_context;
+
+       SoupMessageIOState    read_state;
+       SoupEncoding          read_encoding;
+       GByteArray           *read_header_buf;
+       goffset               read_length;
+
+       SoupMessageIOState    write_state;
+       SoupEncoding          write_encoding;
+       GString              *write_buf;
+       GBytes               *write_chunk;
+       goffset               write_body_offset;
+       goffset               write_length;
+       goffset               written;
+
+       GSource *io_source;
+       gboolean paused;
+
+       GCancellable *async_wait;
+       GError       *async_error;
+
+       SoupMessageIOCompletionFn   completion_cb;
+       gpointer                    completion_data;
+
+#ifdef HAVE_SYSPROF
+       gint64 begin_time_nsec;
+#endif
+} SoupMessageIOData;
+
+void     soup_message_io_data_cleanup      (SoupMessageIOData *io);
+
+gboolean soup_message_io_data_read_headers (SoupMessageIOData *io,
+                                           gboolean           blocking,
+                                           GCancellable      *cancellable,
+                                           GError           **error);
+
+typedef gboolean (*SoupMessageIOSourceFunc) (GObject *msg,
+                                            gpointer user_data);
+
+GSource *soup_message_io_data_get_source   (SoupMessageIOData      *io,
+                                           GObject                *msg,
+                                           GCancellable           *cancellable,
+                                           SoupMessageIOSourceFunc callback,
+                                           gpointer                user_data);
+
+void    soup_message_io_data_pause         (SoupMessageIOData      *io);
+void    soup_message_io_data_unpause       (SoupMessageIOData      *io);
+
+
+#endif /* __SOUP_MESSAGE_IO_DATA_H__ */
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index bfa14417..efa84ef6 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -28,60 +28,37 @@
 #include "soup-misc.h"
 #include "soup-socket-private.h"
 
+struct _SoupClientMessageIOData {
+       SoupMessageIOData base;
+
+        SoupMessageQueueItem *item;
+        GCancellable         *cancellable;
+
+#ifdef HAVE_SYSPROF
+        gint64 begin_time_nsec;
+#endif
+};
+
 #define RESPONSE_BLOCK_SIZE 8192
 #define HEADER_SIZE_LIMIT (64 * 1024)
 
 void
-soup_message_io_cleanup (SoupMessage *msg)
+soup_client_message_io_data_free (SoupClientMessageIOData *io)
 {
-       SoupMessageIOData *io;
-
-       io = soup_message_get_io_data (msg);
        if (!io)
                return;
 
-       if (io->io_source) {
-               g_source_destroy (io->io_source);
-               g_source_unref (io->io_source);
-               io->io_source = NULL;
-       }
-
-       if (io->unpause_source) {
-               g_source_destroy (io->unpause_source);
-               g_source_unref (io->unpause_source);
-               io->unpause_source = NULL;
-       }
+       soup_message_io_data_cleanup (&io->base);
+       soup_message_queue_item_unref (io->item);
 
-       if (io->iostream)
-               g_object_unref (io->iostream);
-       if (io->body_istream)
-               g_object_unref (io->body_istream);
-       if (io->body_ostream)
-               g_object_unref (io->body_ostream);
-       if (io->async_context)
-               g_main_context_unref (io->async_context);
-       if (io->item)
-               soup_message_queue_item_unref (io->item);
-
-       g_byte_array_free (io->read_header_buf, TRUE);
-
-       g_string_free (io->write_buf, TRUE);
-       g_clear_pointer (&io->write_chunk, g_bytes_unref);
-
-       if (io->async_wait) {
-               g_cancellable_cancel (io->async_wait);
-               g_clear_object (&io->async_wait);
-       }
-       g_clear_error (&io->async_error);
-
-       g_slice_free (SoupMessageIOData, io);
+       g_slice_free (SoupClientMessageIOData, io);
 }
 
 void
 soup_message_io_finished (SoupMessage *msg)
 {
-       SoupMessageIOData *io;
-       SoupMessageCompletionFn completion_cb;
+       SoupClientMessageIOData *io;
+       SoupMessageIOCompletionFn completion_cb;
        gpointer completion_data;
        SoupMessageIOCompletion completion;
 
@@ -89,11 +66,11 @@ soup_message_io_finished (SoupMessage *msg)
        if (!io)
                return;
 
-       completion_cb = io->completion_cb;
-       completion_data = io->completion_data;
+       completion_cb = io->base.completion_cb;
+       completion_data = io->base.completion_data;
 
-       if ((io->read_state >= SOUP_MESSAGE_IO_STATE_FINISHING &&
-            io->write_state >= SOUP_MESSAGE_IO_STATE_FINISHING))
+       if ((io->base.read_state >= SOUP_MESSAGE_IO_STATE_FINISHING &&
+            io->base.write_state >= SOUP_MESSAGE_IO_STATE_FINISHING))
                completion = SOUP_MESSAGE_IO_COMPLETE;
        else
                completion = SOUP_MESSAGE_IO_INTERRUPTED;
@@ -101,96 +78,35 @@ soup_message_io_finished (SoupMessage *msg)
        g_object_ref (msg);
        soup_message_set_io_data (msg, NULL);
        if (completion_cb)
-               completion_cb (msg, completion, completion_data);
+               completion_cb (G_OBJECT (msg), completion, completion_data);
        g_object_unref (msg);
 }
 
 GIOStream *
 soup_message_io_steal (SoupMessage *msg)
 {
-       SoupMessageIOData *io;
-       SoupMessageCompletionFn completion_cb;
+       SoupClientMessageIOData *io;
+       SoupMessageIOCompletionFn completion_cb;
        gpointer completion_data;
        GIOStream *iostream;
 
        io = soup_message_get_io_data (msg);
-       if (!io || !io->iostream)
+       if (!io || !io->base.iostream)
                return NULL;
 
-       iostream = g_object_ref (io->iostream);
-       completion_cb = io->completion_cb;
-       completion_data = io->completion_data;
+       iostream = g_object_ref (io->base.iostream);
+       completion_cb = io->base.completion_cb;
+       completion_data = io->base.completion_data;
 
        g_object_ref (msg);
        soup_message_set_io_data (msg, NULL);
        if (completion_cb)
-               completion_cb (msg, SOUP_MESSAGE_IO_STOLEN, completion_data);
+               completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data);
        g_object_unref (msg);
 
        return iostream;
 }
 
-gboolean
-soup_message_io_read_headers (SoupMessage           *msg,
-                             SoupFilterInputStream *stream,
-                             GByteArray            *buffer,
-                             gboolean               blocking,
-                             GCancellable          *cancellable,
-                             GError               **error)
-{
-       gssize nread, old_len;
-       gboolean got_lf;
-
-       while (1) {
-               old_len = buffer->len;
-               g_byte_array_set_size (buffer, old_len + RESPONSE_BLOCK_SIZE);
-               nread = soup_filter_input_stream_read_line (stream,
-                                                           buffer->data + old_len,
-                                                           RESPONSE_BLOCK_SIZE,
-                                                           blocking,
-                                                           &got_lf,
-                                                           cancellable, error);
-               buffer->len = old_len + MAX (nread, 0);
-               if (nread == 0) {
-                       if (buffer->len > 0)
-                               break;
-                       soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
-                       g_set_error_literal (error, G_IO_ERROR,
-                                            G_IO_ERROR_PARTIAL_INPUT,
-                                            _("Connection terminated unexpectedly"));
-               }
-               if (nread <= 0)
-                       return FALSE;
-
-               if (got_lf) {
-                       if (nread == 1 && old_len >= 2 &&
-                           !strncmp ((char *)buffer->data +
-                                     buffer->len - 2,
-                                     "\n\n", 2)) {
-                               buffer->len--;
-                               break;
-                       } else if (nread == 2 && old_len >= 3 &&
-                                !strncmp ((char *)buffer->data +
-                                          buffer->len - 3,
-                                          "\n\r\n", 3)) {
-                               buffer->len -= 2;
-                               break;
-                       }
-               }
-
-               if (buffer->len > HEADER_SIZE_LIMIT) {
-                       soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
-                       g_set_error_literal (error, G_IO_ERROR,
-                                            G_IO_ERROR_PARTIAL_INPUT,
-                                            _("Header too big"));
-                       return FALSE;
-               }
-       }
-
-       buffer->data[buffer->len] = '\0';
-       return TRUE;
-}
-
 static gint
 processing_stage_cmp (gconstpointer a,
                       gconstpointer b)
@@ -257,7 +173,7 @@ request_body_stream_wrote_cb (GOutputStream *ostream,
                              GAsyncResult  *result,
                              SoupMessage   *msg)
 {
-       SoupMessageIOData *io;
+       SoupClientMessageIOData *io;
        gssize nwrote;
        GCancellable *async_wait;
        GError *error = NULL;
@@ -265,19 +181,19 @@ request_body_stream_wrote_cb (GOutputStream *ostream,
        nwrote = g_output_stream_splice_finish (ostream, result, &error);
 
        io = soup_message_get_io_data (msg);
-       if (!io || !io->async_wait || io->body_ostream != ostream) {
+       if (!io || !io->base.async_wait || io->base.body_ostream != ostream) {
                g_clear_error (&error);
                g_object_unref (msg);
                return;
        }
 
        if (nwrote != -1)
-               io->write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH;
+               io->base.write_state = SOUP_MESSAGE_IO_STATE_BODY_FLUSH;
 
        if (error)
-               g_propagate_error (&io->async_error, error);
-       async_wait = io->async_wait;
-       io->async_wait = NULL;
+               g_propagate_error (&io->base.async_error, error);
+       async_wait = io->base.async_wait;
+       io->base.async_wait = NULL;
        g_cancellable_cancel (async_wait);
        g_object_unref (async_wait);
 
@@ -291,20 +207,20 @@ closed_async (GObject      *source,
 {
        GOutputStream *body_ostream = G_OUTPUT_STREAM (source);
        SoupMessage *msg = user_data;
-       SoupMessageIOData *io;
+       SoupClientMessageIOData *io;
        GCancellable *async_wait;
 
        io = soup_message_get_io_data (msg);
-       if (!io || !io->async_wait || io->body_ostream != body_ostream) {
+       if (!io || !io->base.async_wait || io->base.body_ostream != body_ostream) {
                g_object_unref (msg);
                return;
        }
 
-       g_output_stream_close_finish (body_ostream, result, &io->async_error);
-       g_clear_object (&io->body_ostream);
+       g_output_stream_close_finish (body_ostream, result, &io->base.async_error);
+       g_clear_object (&io->base.body_ostream);
 
-       async_wait = io->async_wait;
-       io->async_wait = NULL;
+       async_wait = io->base.async_wait;
+       io->base.async_wait = NULL;
        g_cancellable_cancel (async_wait);
        g_object_unref (async_wait);
 
@@ -393,15 +309,6 @@ write_headers (SoupMessage          *msg,
                g_free (uri_host);
 
        *encoding = soup_message_headers_get_encoding (msg->request_headers);
-       if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
-            *encoding == SOUP_ENCODING_NONE) &&
-           (msg->request_body->length > 0 ||
-            soup_message_headers_get_one (msg->request_headers, "Content-Type")) &&
-           !soup_message_headers_get_content_length (msg->request_headers)) {
-               *encoding = SOUP_ENCODING_CONTENT_LENGTH;
-               soup_message_headers_set_content_length (msg->request_headers,
-                                                        msg->request_body->length);
-       }
 
        soup_message_headers_iter_init (&iter, msg->request_headers);
        while (soup_message_headers_iter_next (&iter, &name, &value))
@@ -419,7 +326,8 @@ static gboolean
 io_write (SoupMessage *msg, gboolean blocking,
          GCancellable *cancellable, GError **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
        gssize nwrote;
 
        if (io->async_error) {
@@ -436,7 +344,7 @@ io_write (SoupMessage *msg, gboolean blocking,
        switch (io->write_state) {
        case SOUP_MESSAGE_IO_STATE_HEADERS:
                if (!io->write_buf->len)
-                       write_headers (msg, io->write_buf, io->item->conn, &io->write_encoding);
+                       write_headers (msg, io->write_buf, client_io->item->conn, &io->write_encoding);
 
                while (io->written < io->write_buf->len) {
                        nwrote = g_pollable_stream_write (io->ostream,
@@ -607,13 +515,17 @@ static gboolean
 io_read (SoupMessage *msg, gboolean blocking,
         GCancellable *cancellable, GError **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
        guint status;
 
        switch (io->read_state) {
        case SOUP_MESSAGE_IO_STATE_HEADERS:
-               if (!soup_message_io_read_headers (msg, io->istream, io->read_header_buf, blocking, 
cancellable, error))
+               if (!soup_message_io_data_read_headers (io, blocking, cancellable, error)) {
+                       if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT))
+                                soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
                        return FALSE;
+               }
 
                status = parse_headers (msg,
                                        (char *)io->read_header_buf->data,
@@ -658,7 +570,7 @@ io_read (SoupMessage *msg, gboolean blocking,
                        /* If this was "101 Switching Protocols", then
                         * the session may have stolen the connection...
                         */
-                       if (io != soup_message_get_io_data (msg))
+                       if (client_io != soup_message_get_io_data (msg))
                                return FALSE;
 
                        soup_message_cleanup_response (msg);
@@ -698,7 +610,7 @@ io_read (SoupMessage *msg, gboolean blocking,
                                                                                 io->read_length);
 
                        io->body_istream = soup_message_setup_body_istream (body_istream, msg,
-                                                                           io->item->session,
+                                                                           client_io->item->session,
                                                                            SOUP_STAGE_MESSAGE_BODY);
                        g_object_unref (body_istream);
                }
@@ -753,148 +665,18 @@ io_read (SoupMessage *msg, gboolean blocking,
        return TRUE;
 }
 
-typedef struct {
-       GSource source;
-       SoupMessage *msg;
-       gboolean paused;
-} SoupMessageSource;
-
-static gboolean
-message_source_check (GSource *source)
-{
-       SoupMessageSource *message_source = (SoupMessageSource *)source;
-
-       if (message_source->paused) {
-               SoupMessageIOData *io = soup_message_get_io_data (message_source->msg);
-
-               if (io && io->paused)
-                       return FALSE;
-               else
-                       return TRUE;
-       } else
-               return FALSE;
-}
-
-static gboolean
-message_source_prepare (GSource *source,
-                       gint    *timeout)
-{
-       *timeout = -1;
-       return message_source_check (source);
-}
-
-static gboolean
-message_source_dispatch (GSource     *source,
-                        GSourceFunc  callback,
-                        gpointer     user_data)
-{
-       SoupMessageSourceFunc func = (SoupMessageSourceFunc)callback;
-       SoupMessageSource *message_source = (SoupMessageSource *)source;
-
-       return (*func) (message_source->msg, user_data);
-}
-
-static void
-message_source_finalize (GSource *source)
-{
-       SoupMessageSource *message_source = (SoupMessageSource *)source;
-
-       g_object_unref (message_source->msg);
-}
-
-static gboolean
-message_source_closure_callback (SoupMessage *msg,
-                                gpointer     data)
-{
-       GClosure *closure = data;
-       GValue param = G_VALUE_INIT;
-       GValue result_value = G_VALUE_INIT;
-       gboolean result;
-
-       g_value_init (&result_value, G_TYPE_BOOLEAN);
-
-       g_value_init (&param, SOUP_TYPE_MESSAGE);
-       g_value_set_object (&param, msg);
-
-       g_closure_invoke (closure, &result_value, 1, &param, NULL);
-
-       result = g_value_get_boolean (&result_value);
-       g_value_unset (&result_value);
-       g_value_unset (&param);
-
-       return result;
-}
-
-static GSourceFuncs message_source_funcs =
-{
-       message_source_prepare,
-       message_source_check,
-       message_source_dispatch,
-       message_source_finalize,
-       (GSourceFunc)message_source_closure_callback,
-       (GSourceDummyMarshal)g_cclosure_marshal_generic,
-};
-
-GSource *
-soup_message_io_get_source (SoupMessage *msg, GCancellable *cancellable,
-                           SoupMessageSourceFunc callback, gpointer user_data)
-{
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
-       GSource *base_source, *source;
-       SoupMessageSource *message_source;
-
-       if (!io) {
-               base_source = g_timeout_source_new (0);
-       } else if (io->paused) {
-               base_source = NULL;
-       } else if (io->async_wait) {
-               base_source = g_cancellable_source_new (io->async_wait);
-       } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) {
-               GPollableInputStream *istream;
-
-               if (io->body_istream)
-                       istream = G_POLLABLE_INPUT_STREAM (io->body_istream);
-               else
-                       istream = G_POLLABLE_INPUT_STREAM (io->istream);
-               base_source = g_pollable_input_stream_create_source (istream, cancellable);
-       } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) {
-               GPollableOutputStream *ostream;
-
-               if (io->body_ostream)
-                       ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream);
-               else
-                       ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream);
-               base_source = g_pollable_output_stream_create_source (ostream, cancellable);
-       } else
-               base_source = g_timeout_source_new (0);
-
-       source = g_source_new (&message_source_funcs,
-                              sizeof (SoupMessageSource));
-       g_source_set_name (source, "SoupMessageSource");
-       message_source = (SoupMessageSource *)source;
-       message_source->msg = g_object_ref (msg);
-       message_source->paused = io && io->paused;
-
-       if (base_source) {
-               g_source_set_dummy_callback (base_source);
-               g_source_add_child_source (source, base_source);
-               g_source_unref (base_source);
-       }
-       g_source_set_callback (source, (GSourceFunc) callback, user_data, NULL);
-       return source;
-}
-
 static gboolean
 request_is_restartable (SoupMessage *msg, GError *error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
 
-       if (!io)
+       if (!client_io)
                return FALSE;
 
        return (io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS &&
                io->read_header_buf->len == 0 &&
-               soup_connection_get_ever_used (io->item->conn) &&
+               soup_connection_get_ever_used (client_io->item->conn) &&
                !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
                !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) &&
                error->domain != G_TLS_ERROR &&
@@ -906,7 +688,8 @@ io_run_until (SoupMessage *msg, gboolean blocking,
              SoupMessageIOState read_state, SoupMessageIOState write_state,
              GCancellable *cancellable, GError **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
        gboolean progress = TRUE, done;
        GError *my_error = NULL;
 
@@ -921,7 +704,7 @@ io_run_until (SoupMessage *msg, gboolean blocking,
 
        g_object_ref (msg);
 
-       while (progress && soup_message_get_io_data (msg) == io && !io->paused && !io->async_wait &&
+       while (progress && soup_message_get_io_data (msg) == client_io && !io->paused && !io->async_wait &&
               (io->read_state < read_state || io->write_state < write_state)) {
 
                if (SOUP_MESSAGE_IO_STATE_ACTIVE (io->read_state))
@@ -945,7 +728,7 @@ io_run_until (SoupMessage *msg, gboolean blocking,
                g_propagate_error (error, my_error);
                g_object_unref (msg);
                return FALSE;
-       } else if (soup_message_get_io_data (msg) != io) {
+       } else if (soup_message_get_io_data (msg) != client_io) {
                g_set_error_literal (error, G_IO_ERROR,
                                     G_IO_ERROR_CANCELLED,
                                     _("Operation was cancelled"));
@@ -979,7 +762,8 @@ io_run_until (SoupMessage *msg, gboolean blocking,
 
                /* FIXME: Expand and generalise sysprof support:
                 * https://gitlab.gnome.org/GNOME/sysprof/-/issues/43 */
-               sysprof_collector_mark_printf (io->begin_time_nsec, SYSPROF_CAPTURE_CURRENT_TIME - 
io->begin_time_nsec,
+               sysprof_collector_mark_printf (client_io->begin_time_nsec,
+                                              SYSPROF_CAPTURE_CURRENT_TIME - client_io->begin_time_nsec,
                                               "libsoup", "message",
                                               "%s request/response to %s: "
                                               "read %" G_GOFFSET_FORMAT "B, "
@@ -1003,7 +787,7 @@ soup_message_io_update_status (SoupMessage  *msg,
                               GError       *error)
 {
        if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_TRY_AGAIN)) {
-               SoupMessageIOData *io = soup_message_get_io_data (msg);
+               SoupClientMessageIOData *io = soup_message_get_io_data (msg);
 
                io->item->state = SOUP_MESSAGE_RESTARTING;
        } else if (error->domain == G_TLS_ERROR) {
@@ -1029,7 +813,8 @@ void
 soup_message_io_run (SoupMessage *msg,
                     gboolean     blocking)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
        GError *error = NULL;
        GCancellable *cancellable;
 
@@ -1040,7 +825,7 @@ soup_message_io_run (SoupMessage *msg,
        }
 
        g_object_ref (msg);
-       cancellable = io->cancellable ? g_object_ref (io->cancellable) : NULL;
+       cancellable = client_io->cancellable ? g_object_ref (client_io->cancellable) : NULL;
 
        if (io_run_until (msg, blocking,
                          SOUP_MESSAGE_IO_STATE_DONE,
@@ -1049,10 +834,12 @@ soup_message_io_run (SoupMessage *msg,
                soup_message_io_finished (msg);
        } else if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                g_clear_error (&error);
-               io->io_source = soup_message_io_get_source (msg, NULL, io_run_ready, msg);
+               io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), NULL,
+                                                                (SoupMessageIOSourceFunc)io_run_ready,
+                                                                NULL);
                g_source_attach (io->io_source, io->async_context);
        } else {
-               if (soup_message_get_io_data (msg) == io)
+               if (soup_message_get_io_data (msg) == client_io)
                        soup_message_io_update_status (msg, error);
                g_error_free (error);
 
@@ -1067,7 +854,7 @@ soup_message_io_run_until_read (SoupMessage  *msg,
                                GCancellable *cancellable,
                                GError      **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
 
        if (io_run_until (msg, TRUE,
                          SOUP_MESSAGE_IO_STATE_BODY,
@@ -1098,7 +885,8 @@ static void
 io_run_until_read_async (SoupMessage *msg,
                          GTask       *task)
 {
-        SoupMessageIOData *io = soup_message_get_io_data (msg);
+        SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+       SoupMessageIOData *io = &client_io->base;
         GError *error = NULL;
 
         if (io->io_source) {
@@ -1119,12 +907,14 @@ io_run_until_read_async (SoupMessage *msg,
 
         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
                 g_error_free (error);
-                io->io_source = soup_message_io_get_source (msg, NULL, io_run_until_read_ready, task);
+                io->io_source = soup_message_io_data_get_source (io, G_OBJECT (msg), NULL,
+                                                                
(SoupMessageIOSourceFunc)io_run_until_read_ready,
+                                                                task);
                 g_source_attach (io->io_source, io->async_context);
                 return;
         }
 
-        if (soup_message_get_io_data (msg) == io)
+        if (soup_message_get_io_data (msg) == client_io)
                 soup_message_io_update_status (msg, error);
 
         g_task_return_error (task, error);
@@ -1157,14 +947,14 @@ soup_message_io_run_until_finish (SoupMessage   *msg,
                                  GCancellable  *cancellable,
                                  GError       **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
        gboolean success;
 
        g_object_ref (msg);
 
        if (io) {
-               if (io->read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE)
-                       io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
+               if (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY_DONE)
+                       io->base.read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
        }
 
        success = io_run_until (msg, blocking,
@@ -1180,17 +970,17 @@ static void
 client_stream_eof (SoupClientInputStream *stream, gpointer user_data)
 {
        SoupMessage *msg = user_data;
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
 
-       if (io && io->read_state == SOUP_MESSAGE_IO_STATE_BODY)
-               io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
+       if (io && io->base.read_state == SOUP_MESSAGE_IO_STATE_BODY)
+               io->base.read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
 }
 
 GInputStream *
 soup_message_io_get_response_istream (SoupMessage  *msg,
                                      GError      **error)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
        GInputStream *client_stream;
 
        if (SOUP_STATUS_IS_TRANSPORT_ERROR (msg->status_code)) {
@@ -1199,7 +989,7 @@ soup_message_io_get_response_istream (SoupMessage  *msg,
                return NULL;
        }
 
-       client_stream = soup_client_input_stream_new (io->body_istream, msg);
+       client_stream = soup_client_input_stream_new (io->base.body_istream, msg);
        g_signal_connect (client_stream, "eof",
                          G_CALLBACK (client_stream_eof), msg);
 
@@ -1208,28 +998,28 @@ soup_message_io_get_response_istream (SoupMessage  *msg,
 
 void
 soup_message_send_request (SoupMessageQueueItem      *item,
-                          SoupMessageCompletionFn    completion_cb,
+                          SoupMessageIOCompletionFn  completion_cb,
                           gpointer                   user_data)
 {
-       SoupMessageIOData *io;
+       SoupClientMessageIOData *io;
 
-       io = g_slice_new0 (SoupMessageIOData);
-       io->completion_cb = completion_cb;
-       io->completion_data = user_data;
+       io = g_slice_new0 (SoupClientMessageIOData);
+       io->base.completion_cb = completion_cb;
+       io->base.completion_data = user_data;
 
        io->item = item;
        soup_message_queue_item_ref (item);
        io->cancellable = io->item->cancellable;
-       io->iostream = g_object_ref (soup_socket_get_iostream (soup_connection_get_socket (io->item->conn)));
-       io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->iostream));
-       io->ostream = g_io_stream_get_output_stream (io->iostream);
-       io->async_context = g_main_context_ref_thread_default ();
+       io->base.iostream = g_object_ref (soup_socket_get_iostream (soup_connection_get_socket 
(io->item->conn)));
+       io->base.istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->base.iostream));
+       io->base.ostream = g_io_stream_get_output_stream (io->base.iostream);
+       io->base.async_context = g_main_context_ref_thread_default ();
 
-       io->read_header_buf = g_byte_array_new ();
-       io->write_buf = g_string_new (NULL);
+       io->base.read_header_buf = g_byte_array_new ();
+       io->base.write_buf = g_string_new (NULL);
 
-       io->read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
-       io->write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+       io->base.read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
+       io->base.write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
 
 #ifdef HAVE_SYSPROF
        io->begin_time_nsec = SYSPROF_CAPTURE_CURRENT_TIME;
@@ -1238,65 +1028,25 @@ soup_message_send_request (SoupMessageQueueItem      *item,
        soup_message_set_io_data (io->item->msg, io);
 }
 
-void  
+void
 soup_message_io_pause (SoupMessage *msg)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
 
        g_return_if_fail (io != NULL);
+       g_return_if_fail (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY);
 
-       if (io->item)
-               g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY);
-
-       if (io->io_source) {
-               g_source_destroy (io->io_source);
-               g_source_unref (io->io_source);
-               io->io_source = NULL;
-       }
-
-       if (io->unpause_source) {
-               g_source_destroy (io->unpause_source);
-               g_source_unref (io->unpause_source);
-               io->unpause_source = NULL;
-       }
-
-       io->paused = TRUE;
-}
-
-static gboolean
-io_unpause_internal (gpointer msg)
-{
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
-
-       g_return_val_if_fail (io != NULL, FALSE);
-
-       g_clear_pointer (&io->unpause_source, g_source_unref);
-       io->paused = FALSE;
-
-       if (io->io_source)
-               return FALSE;
-
-       soup_message_io_run (msg, FALSE);
-       return FALSE;
+       soup_message_io_data_pause (&io->base);
 }
 
 void
 soup_message_io_unpause (SoupMessage *msg)
 {
-       SoupMessageIOData *io = soup_message_get_io_data (msg);
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
 
        g_return_if_fail (io != NULL);
-
-       if (io->item) {
-               g_return_if_fail (io->read_state < SOUP_MESSAGE_IO_STATE_BODY);
-               io->paused = FALSE;
-               return;
-       }
-
-       if (!io->unpause_source) {
-               io->unpause_source = soup_add_completion_reffed (io->async_context,
-                                                                io_unpause_internal, msg, NULL);
-       }
+       g_return_if_fail (io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY);
+       io->base.paused = FALSE;
 }
 
 /**
@@ -1312,3 +1062,11 @@ soup_message_io_in_progress (SoupMessage *msg)
 {
        return soup_message_get_io_data (msg) != NULL;
 }
+
+gboolean
+soup_message_is_io_paused (SoupMessage *msg)
+{
+       SoupClientMessageIOData *io = soup_message_get_io_data (msg);
+
+       return io && io->base.paused;
+}
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 1200adba..0ce07e8f 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -8,13 +8,17 @@
 
 #include "soup-filter-input-stream.h"
 #include "soup-message.h"
+#include "soup-message-io-data.h"
 #include "auth/soup-auth.h"
 #include "content-sniffer/soup-content-processor.h"
 #include "content-sniffer/soup-content-sniffer.h"
 #include "soup-session.h"
 
+typedef struct _SoupClientMessageIOData SoupClientMessageIOData;
+void soup_client_message_io_data_free (SoupClientMessageIOData *io);
+
 typedef struct {
-       gpointer           io_data;
+       SoupClientMessageIOData *io_data;
 
        guint              msg_flags;
        gboolean           server_side;
@@ -46,12 +50,6 @@ typedef struct {
 
 void             soup_message_cleanup_response (SoupMessage      *msg);
 
-typedef enum {
-       SOUP_MESSAGE_IO_COMPLETE,
-       SOUP_MESSAGE_IO_INTERRUPTED,
-       SOUP_MESSAGE_IO_STOLEN
-} SoupMessageIOCompletion;
-
 typedef void     (*SoupMessageGetHeadersFn)  (SoupMessage      *msg,
                                              GString          *headers,
                                              SoupEncoding     *encoding,
@@ -62,17 +60,9 @@ typedef guint    (*SoupMessageParseHeadersFn)(SoupMessage      *msg,
                                              SoupEncoding     *encoding,
                                              gpointer          user_data,
                                              GError          **error);
-typedef void     (*SoupMessageCompletionFn)  (SoupMessage      *msg,
-                                             SoupMessageIOCompletion completion,
-                                             gpointer          user_data);
-
 
 void soup_message_send_request (SoupMessageQueueItem      *item,
-                               SoupMessageCompletionFn    completion_cb,
-                               gpointer                   user_data);
-void soup_message_read_request (SoupMessage               *msg,
-                               SoupSocket                *sock,
-                               SoupMessageCompletionFn    completion_cb,
+                               SoupMessageIOCompletionFn  completion_cb,
                                gpointer                   user_data);
 
 /* Auth handling */
@@ -84,77 +74,13 @@ void           soup_message_set_proxy_auth (SoupMessage *msg,
 SoupAuth      *soup_message_get_proxy_auth (SoupMessage *msg);
 
 /* I/O */
-typedef enum {
-       SOUP_MESSAGE_IO_STATE_NOT_STARTED,
-       SOUP_MESSAGE_IO_STATE_ANY = SOUP_MESSAGE_IO_STATE_NOT_STARTED,
-       SOUP_MESSAGE_IO_STATE_HEADERS,
-       SOUP_MESSAGE_IO_STATE_BLOCKING,
-       SOUP_MESSAGE_IO_STATE_BODY_START,
-       SOUP_MESSAGE_IO_STATE_BODY,
-       SOUP_MESSAGE_IO_STATE_BODY_DATA,
-       SOUP_MESSAGE_IO_STATE_BODY_FLUSH,
-       SOUP_MESSAGE_IO_STATE_BODY_DONE,
-       SOUP_MESSAGE_IO_STATE_FINISHING,
-       SOUP_MESSAGE_IO_STATE_DONE
-} SoupMessageIOState;
-
-#define SOUP_MESSAGE_IO_STATE_ACTIVE(state) \
-       (state != SOUP_MESSAGE_IO_STATE_NOT_STARTED && \
-        state != SOUP_MESSAGE_IO_STATE_BLOCKING && \
-        state != SOUP_MESSAGE_IO_STATE_DONE)
-#define SOUP_MESSAGE_IO_STATE_POLLABLE(state) \
-       (SOUP_MESSAGE_IO_STATE_ACTIVE (state) && \
-        state != SOUP_MESSAGE_IO_STATE_BODY_DONE)
-
-typedef struct {
-       /* Client only */
-       SoupMessageQueueItem *item;
-       GCancellable         *cancellable;
-
-       /* Server only */
-       SoupSocket           *sock;
-
-       GIOStream              *iostream;
-       SoupFilterInputStream  *istream;
-       GInputStream           *body_istream;
-       GOutputStream          *ostream;
-       GOutputStream          *body_ostream;
-       GMainContext           *async_context;
-
-       SoupMessageIOState    read_state;
-       SoupEncoding          read_encoding;
-       GByteArray           *read_header_buf;
-       goffset               read_length;
-
-       SoupMessageIOState    write_state;
-       SoupEncoding          write_encoding;
-       GString              *write_buf;
-       GBytes               *write_chunk;
-       goffset               write_body_offset;
-       goffset               write_length;
-       goffset               written;
-
-       GSource *io_source;
-       GSource *unpause_source;
-       gboolean paused;
-
-       GCancellable *async_wait;
-       GError       *async_error;
-
-       SoupMessageCompletionFn   completion_cb;
-       gpointer                  completion_data;
-
-#ifdef HAVE_SYSPROF
-       gint64 begin_time_nsec;
-#endif
-} SoupMessageIOData;
-
 void       soup_message_io_run         (SoupMessage *msg,
                                        gboolean     blocking);
 void       soup_message_io_finished    (SoupMessage *msg);
 void       soup_message_io_cleanup     (SoupMessage *msg);
 void       soup_message_io_pause       (SoupMessage *msg);
 void       soup_message_io_unpause     (SoupMessage *msg);
+gboolean   soup_message_is_io_paused   (SoupMessage *msg);
 gboolean   soup_message_io_in_progress (SoupMessage *msg);
 GIOStream *soup_message_io_steal       (SoupMessage *msg);
 
@@ -214,9 +140,9 @@ SoupConnection *soup_message_get_connection (SoupMessage    *msg);
 void            soup_message_set_connection (SoupMessage    *msg,
                                             SoupConnection *conn);
 
-gpointer        soup_message_get_io_data (SoupMessage       *msg);
-void            soup_message_set_io_data (SoupMessage       *msg,
-                                         gpointer           io);
+SoupClientMessageIOData *soup_message_get_io_data (SoupMessage             *msg);
+void                     soup_message_set_io_data (SoupMessage             *msg,
+                                                  SoupClientMessageIOData *io);
 
 SoupContentSniffer *soup_message_get_content_sniffer    (SoupMessage        *msg);
 void                soup_message_set_content_sniffer    (SoupMessage        *msg,
@@ -224,7 +150,4 @@ void                soup_message_set_content_sniffer    (SoupMessage        *msg
 void                soup_message_set_bytes_for_sniffing (SoupMessage        *msg,
                                                         gsize               bytes);
 
-const char *soup_http_version_to_string      (SoupHTTPVersion version);
-
-
 #endif /* __SOUP_MESSAGE_PRIVATE_H__ */
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 5ead16d9..0a265dea 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -19,13 +19,13 @@
 /**
  * SECTION:soup-message
  * @short_description: An HTTP request and response.
- * @see_also: #SoupMessageHeaders, #SoupMessageBody
+ * @see_also: #SoupMessageHeaders
  *
  * A #SoupMessage represents an HTTP message that is being sent or
  * received.
  *
- * For client-side usage, you would create a #SoupMessage with
- * soup_message_new() or soup_message_new_from_uri(), set up its
+ * You would create a #SoupMessage with soup_message_new() or
+ * soup_message_new_from_uri(), set up its
  * fields appropriately, and send it. If you are using the newer
  * #SoupRequest API, you would create a request with
  * soup_session_request_http() or soup_session_request_http_uri(), and
@@ -33,10 +33,6 @@
  * #SoupMessage that you can retrieve via
  * soup_request_http_get_message().
  *
- * For server-side usage, #SoupServer will create #SoupMessage<!--
- * -->s automatically for incoming requests, which your application
- * will receive via handlers.
- *
  * Note that libsoup's terminology here does not quite match the HTTP
  * specification: in RFC 2616, an "HTTP-message" is
  * <emphasis>either</emphasis> a Request, <emphasis>or</emphasis> a
@@ -49,9 +45,7 @@
  * @method: the HTTP method
  * @status_code: the HTTP status code
  * @reason_phrase: the status phrase associated with @status_code
- * @request_body: the request body
  * @request_headers: the request headers
- * @response_body: the response body
  * @response_headers: the response headers
  *
  * Represents an HTTP message being sent or received.
@@ -66,48 +60,17 @@
  * messages. Rather, you should look at @status_code, and determine an
  * end-user-appropriate message based on that and on what you were
  * trying to do.
- *
- * As described in the #SoupMessageBody documentation, the
- * @request_body and @response_body <literal>data</literal> fields
- * will not necessarily be filled in at all times. When the body
- * fields are filled in, they will be terminated with a '\0' byte
- * (which is not included in the <literal>length</literal>), so you
- * can use them as ordinary C strings (assuming that you know that the
- * body doesn't have any other '\0' bytes).
- *
- * For a client-side #SoupMessage, @request_body's
- * <literal>data</literal> is usually filled in right before libsoup
- * writes the request to the network, but you should not count on
- * this; use soup_message_body_flatten() if you want to ensure that
- * <literal>data</literal> is filled in. If you are not using
- * #SoupRequest to read the response, then @response_body's
- * <literal>data</literal> will be filled in before
- * #SoupMessage::finished is emitted. (If you are using #SoupRequest,
- * then the message body is not accumulated by default, so
- * @response_body's <literal>data</literal> will always be %NULL.)
- *
- * For a server-side #SoupMessage, @request_body's %data will be
- * filled in before #SoupMessage::got_body is emitted.
- *
- * To prevent the %data field from being filled in at all (eg, if you
- * are handling the data from a #SoupMessage::got_chunk, and so don't
- * need to see it all at the end), call
- * soup_message_body_set_accumulate() on @response_body or
- * @request_body as appropriate, passing %FALSE.
- **/
+ */
 
 G_DEFINE_TYPE_WITH_PRIVATE (SoupMessage, soup_message, G_TYPE_OBJECT)
 
 enum {
-       WROTE_INFORMATIONAL,
        WROTE_HEADERS,
-       WROTE_CHUNK,
        WROTE_BODY_DATA,
        WROTE_BODY,
 
        GOT_INFORMATIONAL,
        GOT_HEADERS,
-       GOT_CHUNK,
        GOT_BODY,
        CONTENT_SNIFFED,
 
@@ -129,15 +92,10 @@ enum {
        PROP_URI,
        PROP_HTTP_VERSION,
        PROP_FLAGS,
-       PROP_SERVER_SIDE,
        PROP_STATUS_CODE,
        PROP_REASON_PHRASE,
        PROP_FIRST_PARTY,
-       PROP_REQUEST_BODY,
-       PROP_REQUEST_BODY_DATA,
        PROP_REQUEST_HEADERS,
-       PROP_RESPONSE_BODY,
-       PROP_RESPONSE_BODY_DATA,
        PROP_RESPONSE_HEADERS,
        PROP_TLS_CERTIFICATE,
        PROP_TLS_ERRORS,
@@ -156,9 +114,7 @@ soup_message_init (SoupMessage *msg)
        priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
        priv->priority = SOUP_MESSAGE_PRIORITY_NORMAL;
 
-       msg->request_body = soup_message_body_new ();
        msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
-       msg->response_body = soup_message_body_new ();
        msg->response_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE);
 }
 
@@ -168,7 +124,7 @@ soup_message_finalize (GObject *object)
        SoupMessage *msg = SOUP_MESSAGE (object);
        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
 
-       soup_message_io_cleanup (msg);
+       soup_client_message_io_data_free (priv->io_data);
 
        g_clear_pointer (&priv->uri, soup_uri_free);
        g_clear_pointer (&priv->first_party, soup_uri_free);
@@ -181,11 +137,9 @@ soup_message_finalize (GObject *object)
 
        g_clear_object (&priv->tls_certificate);
 
-       soup_message_body_free (msg->request_body);
        soup_message_headers_free (msg->request_headers);
-       g_clear_object (&msg->request_body_stream);
-       soup_message_body_free (msg->response_body);
        soup_message_headers_free (msg->response_headers);
+       g_clear_object (&msg->request_body_stream);
 
        g_free (msg->reason_phrase);
 
@@ -218,13 +172,6 @@ soup_message_set_property (GObject *object, guint prop_id,
        case PROP_FLAGS:
                soup_message_set_flags (msg, g_value_get_flags (value));
                break;
-       case PROP_SERVER_SIDE:
-               priv->server_side = g_value_get_boolean (value);
-               if (priv->server_side) {
-                       soup_message_headers_set_encoding (msg->response_headers,
-                                                          SOUP_ENCODING_CONTENT_LENGTH);
-               }
-               break;
        case PROP_STATUS_CODE:
                soup_message_set_status (msg, g_value_get_uint (value));
                break;
@@ -266,7 +213,6 @@ soup_message_get_property (GObject *object, guint prop_id,
 {
        SoupMessage *msg = SOUP_MESSAGE (object);
        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
-       GBytes *buf;
 
        switch (prop_id) {
        case PROP_METHOD:
@@ -287,9 +233,6 @@ soup_message_get_property (GObject *object, guint prop_id,
        case PROP_FLAGS:
                g_value_set_flags (value, priv->msg_flags);
                break;
-       case PROP_SERVER_SIDE:
-               g_value_set_boolean (value, priv->server_side);
-               break;
        case PROP_STATUS_CODE:
                g_value_set_uint (value, msg->status_code);
                break;
@@ -299,23 +242,9 @@ soup_message_get_property (GObject *object, guint prop_id,
        case PROP_FIRST_PARTY:
                g_value_set_boxed (value, priv->first_party);
                break;
-       case PROP_REQUEST_BODY:
-               g_value_set_boxed (value, msg->request_body);
-               break;
-       case PROP_REQUEST_BODY_DATA:
-               buf = soup_message_body_flatten (msg->request_body);
-               g_value_take_boxed (value, buf);
-               break;
        case PROP_REQUEST_HEADERS:
                g_value_set_boxed (value, msg->request_headers);
                break;
-       case PROP_RESPONSE_BODY:
-               g_value_set_boxed (value, msg->response_body);
-               break;
-       case PROP_RESPONSE_BODY_DATA:
-               buf = soup_message_body_flatten (msg->response_body);
-               g_value_take_boxed (value, buf);
-               break;
        case PROP_RESPONSE_HEADERS:
                g_value_set_boxed (value, msg->response_headers);
                break;
@@ -334,27 +263,11 @@ soup_message_get_property (GObject *object, guint prop_id,
        }
 }
 
-static void
-soup_message_real_got_body (SoupMessage *msg)
-{
-       SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
-       SoupMessageBody *body;
-
-       body = priv->server_side ? msg->request_body : msg->response_body;
-       if (soup_message_body_get_accumulate (body)) {
-               GBytes *buffer = soup_message_body_flatten (body);
-               g_bytes_unref (buffer);
-       }
-}
-
 static void
 soup_message_class_init (SoupMessageClass *message_class)
 {
        GObjectClass *object_class = G_OBJECT_CLASS (message_class);
 
-       /* virtual method definition */
-       message_class->got_body = soup_message_real_got_body;
-
        /* virtual method override */
        object_class->finalize = soup_message_finalize;
        object_class->set_property = soup_message_set_property;
@@ -362,30 +275,12 @@ soup_message_class_init (SoupMessageClass *message_class)
 
        /* signals */
 
-       /**
-        * SoupMessage::wrote-informational:
-        * @msg: the message
-        *
-        * Emitted immediately after writing a 1xx (Informational)
-        * response for a (server-side) message.
-        **/
-       signals[WROTE_INFORMATIONAL] =
-               g_signal_new ("wrote_informational",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupMessageClass, wrote_informational),
-                             NULL, NULL,
-                             NULL,
-                             G_TYPE_NONE, 0);
-
        /**
         * SoupMessage::wrote-headers:
         * @msg: the message
         *
-        * Emitted immediately after writing the headers for a
-        * message. (For a client-side message, this is after writing
-        * the request headers; for a server-side message, it is after
-        * writing the response headers.)
+        * Emitted immediately after writing the request headers for a
+        * message.
         **/
        signals[WROTE_HEADERS] =
                g_signal_new ("wrote_headers",
@@ -396,28 +291,6 @@ soup_message_class_init (SoupMessageClass *message_class)
                              NULL,
                              G_TYPE_NONE, 0);
 
-       /**
-        * SoupMessage::wrote-chunk:
-        * @msg: the message
-        *
-        * Emitted immediately after writing a body chunk for a message.
-        *
-        * Note that this signal is not parallel to
-        * #SoupMessage::got_chunk; it is emitted only when a complete
-        * chunk (added with soup_message_body_append() or
-        * soup_message_body_append_bytes()) has been written. To get
-        * more useful continuous progress information, use
-        * #SoupMessage::wrote_body_data.
-        **/
-       signals[WROTE_CHUNK] =
-               g_signal_new ("wrote_chunk",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupMessageClass, wrote_chunk),
-                             NULL, NULL,
-                             NULL,
-                             G_TYPE_NONE, 0);
-
        /**
         * SoupMessage::wrote-body-data:
         * @msg: the message
@@ -426,10 +299,6 @@ soup_message_class_init (SoupMessageClass *message_class)
         * Emitted immediately after writing a portion of the message
         * body to the network.
         *
-        * Unlike #SoupMessage::wrote_chunk, this is emitted after
-        * every successful write() call, not only after finishing a
-        * complete "chunk".
-        *
         * Since: 2.24
         **/
        signals[WROTE_BODY_DATA] =
@@ -447,11 +316,7 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted immediately after writing the complete body for a
-        * message. (For a client-side message, this means that
-        * libsoup is done writing and is now waiting for the response
-        * from the server. For a server-side message, this means that
-        * libsoup has finished writing the response and is nearly
-        * done with the message.)
+        * message.
         **/
        signals[WROTE_BODY] =
                g_signal_new ("wrote_body",
@@ -489,11 +354,7 @@ soup_message_class_init (SoupMessageClass *message_class)
         * SoupMessage::got-headers:
         * @msg: the message
         *
-        * Emitted after receiving all message headers for a message.
-        * (For a client-side message, this is after receiving the
-        * Status-Line and response headers; for a server-side
-        * message, it is after receiving the Request-Line and request
-        * headers.)
+        * Emitted after receiving the Status-Line and response headers.
         *
         * See also soup_message_add_header_handler() and
         * soup_message_add_status_code_handler(), which can be used
@@ -517,38 +378,11 @@ soup_message_class_init (SoupMessageClass *message_class)
                              NULL,
                              G_TYPE_NONE, 0);
 
-       /**
-        * SoupMessage::got-chunk:
-        * @msg: the message
-        * @chunk: the just-read chunk
-        *
-        * Emitted after receiving a chunk of a message body. Note
-        * that "chunk" in this context means any subpiece of the
-        * body, not necessarily the specific HTTP 1.1 chunks sent by
-        * the other side.
-        *
-        * If you cancel or requeue @msg while processing this signal,
-        * then the current HTTP I/O will be stopped after this signal
-        * emission finished, and @msg's connection will be closed.
-        **/
-       signals[GOT_CHUNK] =
-               g_signal_new ("got_chunk",
-                             G_OBJECT_CLASS_TYPE (object_class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
-                             NULL, NULL,
-                             NULL,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_BYTES);
-
        /**
         * SoupMessage::got-body:
         * @msg: the message
         *
-        * Emitted after receiving the complete message body. (For a
-        * server-side message, this means it has received the request
-        * body. For a client-side message, this means it has received
-        * the response body and is nearly done with the message.)
+        * Emitted after receiving the complete message request body.
         *
         * See also soup_message_add_header_handler() and
         * soup_message_add_status_code_handler(), which can be used
@@ -642,8 +476,7 @@ soup_message_class_init (SoupMessageClass *message_class)
         * @msg: the message
         *
         * Emitted when all HTTP processing is finished for a message.
-        * (After #SoupMessage::got_body for client-side messages, or
-        * after #SoupMessage::wrote_body for server-side messages.)
+        * (After #SoupMessage::got_body).
         **/
        signals[FINISHED] =
                g_signal_new ("finished",
@@ -745,20 +578,6 @@ soup_message_class_init (SoupMessageClass *message_class)
                                    0,
                                    G_PARAM_READWRITE |
                                    G_PARAM_STATIC_STRINGS));
-       /**
-        * SOUP_MESSAGE_SERVER_SIDE:
-        *
-        * Alias for the #SoupMessage:server-side property. (%TRUE if
-        * the message was created by #SoupServer.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_SERVER_SIDE,
-               g_param_spec_boolean (SOUP_MESSAGE_SERVER_SIDE,
-                                     "Server-side",
-                                     "Whether or not the message is server-side rather than client-side",
-                                     FALSE,
-                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
-                                     G_PARAM_STATIC_STRINGS));
        /**
         * SOUP_MESSAGE_STATUS_CODE:
         *
@@ -840,43 +659,6 @@ soup_message_class_init (SoupMessageClass *message_class)
                                     "If the current messsage is navigating between top-levels",
                                     FALSE,
                                     G_PARAM_READWRITE));
-       /**
-        * SOUP_MESSAGE_REQUEST_BODY:
-        *
-        * Alias for the #SoupMessage:request-body property. (The
-        * message's HTTP request body.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_REQUEST_BODY,
-               g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY,
-                                   "Request Body",
-                                   "The HTTP request content",
-                                   SOUP_TYPE_MESSAGE_BODY,
-                                   G_PARAM_READABLE |
-                                   G_PARAM_STATIC_STRINGS));
-       /**
-        * SOUP_MESSAGE_REQUEST_BODY_DATA:
-        *
-        * Alias for the #SoupMessage:request-body-data property. (The
-        * message's HTTP request body, as a #GBytes.)
-        *
-        * Since: 2.46
-        **/
-       /**
-        * SoupMessage:request-body-data:
-        *
-        * The message's HTTP request body, as a #GBytes.
-        *
-        * Since: 2.46
-        **/
-       g_object_class_install_property (
-               object_class, PROP_REQUEST_BODY_DATA,
-               g_param_spec_boxed (SOUP_MESSAGE_REQUEST_BODY_DATA,
-                                   "Request Body Data",
-                                   "The HTTP request body",
-                                   G_TYPE_BYTES,
-                                   G_PARAM_READABLE |
-                                   G_PARAM_STATIC_STRINGS));
        /**
         * SOUP_MESSAGE_REQUEST_HEADERS:
         *
@@ -891,43 +673,6 @@ soup_message_class_init (SoupMessageClass *message_class)
                                    SOUP_TYPE_MESSAGE_HEADERS,
                                    G_PARAM_READABLE |
                                    G_PARAM_STATIC_STRINGS));
-       /**
-        * SOUP_MESSAGE_RESPONSE_BODY:
-        *
-        * Alias for the #SoupMessage:response-body property. (The
-        * message's HTTP response body.)
-        **/
-       g_object_class_install_property (
-               object_class, PROP_RESPONSE_BODY,
-               g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY,
-                                   "Response Body",
-                                   "The HTTP response content",
-                                   SOUP_TYPE_MESSAGE_BODY,
-                                   G_PARAM_READABLE |
-                                   G_PARAM_STATIC_STRINGS));
-       /**
-        * SOUP_MESSAGE_RESPONSE_BODY_DATA:
-        *
-        * Alias for the #SoupMessage:response-body-data property. (The
-        * message's HTTP response body, as a #GBytes.)
-        *
-        * Since: 2.46
-        **/
-       /**
-        * SoupMessage:response-body-data:
-        *
-        * The message's HTTP response body, as a #GBytes.
-        *
-        * Since: 2.46
-        **/
-       g_object_class_install_property (
-               object_class, PROP_RESPONSE_BODY_DATA,
-               g_param_spec_boxed (SOUP_MESSAGE_RESPONSE_BODY_DATA,
-                                   "Response Body Data",
-                                   "The HTTP response body",
-                                   G_TYPE_BYTES,
-                                   G_PARAM_READABLE |
-                                   G_PARAM_STATIC_STRINGS));
        /**
         * SOUP_MESSAGE_RESPONSE_HEADERS:
         *
@@ -1058,42 +803,6 @@ soup_message_new_from_uri (const char *method, SoupURI *uri)
                             NULL);
 }
 
-/**
- * soup_message_set_response:
- * @msg: the message
- * @content_type: (allow-none): MIME Content-Type of the body
- * @resp_use: a #SoupMemoryUse describing how to handle @resp_body
- * @resp_body: (allow-none) (array length=resp_length) (element-type guint8):
- *   a data buffer containing the body of the message response.
- * @resp_length: the byte length of @resp_body.
- * 
- * Convenience function to set the response body of a #SoupMessage. If
- * @content_type is %NULL, the response body must be empty as well.
- */
-void
-soup_message_set_response (SoupMessage    *msg,
-                          const char     *content_type,
-                          SoupMemoryUse   resp_use,
-                          const char     *resp_body,
-                          gsize           resp_length)
-{
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-       g_return_if_fail (content_type != NULL || resp_length == 0);
-
-       if (content_type) {
-               g_warn_if_fail (strchr (content_type, '/') != NULL);
-
-               soup_message_headers_replace (msg->response_headers,
-                                             "Content-Type", content_type);
-               soup_message_body_append (msg->response_body, resp_use,
-                                         resp_body, resp_length);
-       } else {
-               soup_message_headers_remove (msg->response_headers,
-                                            "Content-Type");
-               soup_message_body_truncate (msg->response_body);
-       }
-}
-
 /**
  * soup_message_set_request_body:
  * @msg: the message
@@ -1164,24 +873,12 @@ soup_message_set_request_body_from_bytes (SoupMessage  *msg,
                soup_message_set_request_body (msg, NULL, NULL, 0);
 }
 
-void
-soup_message_wrote_informational (SoupMessage *msg)
-{
-       g_signal_emit (msg, signals[WROTE_INFORMATIONAL], 0);
-}
-
 void
 soup_message_wrote_headers (SoupMessage *msg)
 {
        g_signal_emit (msg, signals[WROTE_HEADERS], 0);
 }
 
-void
-soup_message_wrote_chunk (SoupMessage *msg)
-{
-       g_signal_emit (msg, signals[WROTE_CHUNK], 0);
-}
-
 void
 soup_message_wrote_body_data (SoupMessage *msg, GBytes *chunk)
 {
@@ -1206,12 +903,6 @@ soup_message_got_headers (SoupMessage *msg)
        g_signal_emit (msg, signals[GOT_HEADERS], 0);
 }
 
-void
-soup_message_got_chunk (SoupMessage *msg, GBytes *chunk)
-{
-       g_signal_emit (msg, signals[GOT_CHUNK], 0, chunk);
-}
-
 void
 soup_message_got_body (SoupMessage *msg)
 {
@@ -1233,11 +924,6 @@ soup_message_starting (SoupMessage *msg)
 void
 soup_message_restarted (SoupMessage *msg)
 {
-       SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
-
-       if (priv->msg_flags & SOUP_MESSAGE_CAN_REBUILD)
-               soup_message_body_truncate (msg->request_body);
-
        g_clear_object (&msg->request_body_stream);
 
        g_signal_emit (msg, signals[RESTARTED], 0);
@@ -1270,12 +956,9 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value,
                            gpointer invocation_hint, gpointer marshal_data)
 {
        SoupMessage *msg = g_value_get_object (&param_values[0]);
-       SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
        const char *header_name = marshal_data;
-       SoupMessageHeaders *hdrs;
 
-       hdrs = priv->server_side ? msg->request_headers : msg->response_headers;
-       if (soup_message_headers_get_one (hdrs, header_name)) {
+       if (soup_message_headers_get_one (msg->response_headers, header_name)) {
                closure->marshal (closure, return_value, n_param_values,
                                  param_values, invocation_hint,
                                  ((GCClosure *)closure)->callback);
@@ -1292,9 +975,7 @@ header_handler_metamarshal (GClosure *closure, GValue *return_value,
  *
  * Adds a signal handler to @msg for @signal, as with
  * g_signal_connect(), but the @callback will only be run if @msg's
- * incoming messages headers (that is, the
- * <literal>request_headers</literal> for a client #SoupMessage, or
- * the <literal>response_headers</literal> for a server #SoupMessage)
+ * incoming messages headers (that is, the <literal>request_headers</literal>)
  * contain a header named @header.
  *
  * Return value: the handler ID from g_signal_connect()
@@ -1353,9 +1034,7 @@ status_handler_metamarshal (GClosure *closure, GValue *return_value,
  * the status @status_code.
  *
  * @signal must be a signal that will be emitted after @msg's status
- * is set. For a client #SoupMessage, this means it can't be a "wrote"
- * signal. For a server #SoupMessage, this means it can't be a "got"
- * signal.
+ * is set (this means it can't be a "wrote" signal).
  *
  * Return value: the handler ID from g_signal_connect()
  **/
@@ -1379,7 +1058,6 @@ soup_message_add_status_code_handler (SoupMessage *msg,
        return g_signal_connect_closure (msg, signal, closure, FALSE);
 }
 
-
 void
 soup_message_set_auth (SoupMessage *msg, SoupAuth *auth)
 {
@@ -1470,12 +1148,7 @@ soup_message_cleanup_response (SoupMessage *msg)
 {
        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
 
-       soup_message_body_truncate (msg->response_body);
        soup_message_headers_clear (msg->response_headers);
-       if (priv->server_side) {
-               soup_message_headers_set_encoding (msg->response_headers,
-                                                  SOUP_ENCODING_CONTENT_LENGTH);
-       }
 
        priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED;
 
@@ -1498,9 +1171,6 @@ soup_message_cleanup_response (SoupMessage *msg)
  * SoupMessageFlags:
  * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
  *   (3xx) responses received by this message.
- * @SOUP_MESSAGE_CAN_REBUILD: The caller will rebuild the request
- *   body if the message is restarted; see
- *   soup_message_body_set_accumulate() for more details.
  * @SOUP_MESSAGE_CONTENT_DECODED: Set by #SoupContentDecoder to
  *   indicate that it has removed the Content-Encoding on a message (and
  *   so headers such as Content-Length may no longer accurately describe
@@ -1574,14 +1244,6 @@ soup_message_get_flags (SoupMessage *msg)
        return priv->msg_flags;
 }
 
-/**
- * SoupHTTPVersion:
- * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
- * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
- *
- * Indicates the HTTP protocol version being used.
- **/
-
 /**
  * soup_message_set_http_version:
  * @msg: a #SoupMessage
@@ -2061,9 +1723,6 @@ soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn)
  * showing what problems, if any, have been found with that
  * certificate.
  *
- * <note><para>This is only meaningful with messages processed by a #SoupSession and is
- * not useful for messages received by a #SoupServer</para></note>
- *
  * Return value: %TRUE if @msg used/attempted https, %FALSE if not
  *
  * Since: 2.34
@@ -2086,41 +1745,6 @@ soup_message_get_https_status (SoupMessage           *msg,
        return priv->tls_certificate != NULL;
 }
 
-/**
- * soup_message_set_redirect:
- * @msg: a #SoupMessage
- * @status_code: a 3xx status code
- * @redirect_uri: the URI to redirect @msg to
- *
- * Sets @msg's status_code to @status_code and adds a Location header
- * pointing to @redirect_uri. Use this from a #SoupServer when you
- * want to redirect the client to another URI.
- *
- * @redirect_uri can be a relative URI, in which case it is
- * interpreted relative to @msg's current URI. In particular, if
- * @redirect_uri is just a path, it will replace the path
- * <emphasis>and query</emphasis> of @msg's URI.
- *
- * Since: 2.38
- */
-void
-soup_message_set_redirect (SoupMessage *msg, guint status_code,
-                          const char *redirect_uri)
-{
-       SoupURI *location;
-       char *location_str;
-
-       location = soup_uri_new_with_base (soup_message_get_uri (msg), redirect_uri);
-       g_return_if_fail (location != NULL);
-
-       soup_message_set_status (msg, status_code);
-       location_str = soup_uri_to_string (location, FALSE);
-       soup_message_headers_replace (msg->response_headers, "Location",
-                                     location_str);
-       g_free (location_str);
-       soup_uri_free (location);
-}
-
 void
 soup_message_set_soup_request (SoupMessage *msg,
                               SoupRequest *req)
@@ -2223,7 +1847,7 @@ soup_message_get_priority (SoupMessage *msg)
        return priv->priority;
 }
 
-gpointer
+SoupClientMessageIOData *
 soup_message_get_io_data (SoupMessage *msg)
 {
        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
@@ -2232,11 +1856,12 @@ soup_message_get_io_data (SoupMessage *msg)
 }
 
 void
-soup_message_set_io_data (SoupMessage *msg, gpointer io)
+soup_message_set_io_data (SoupMessage             *msg,
+                         SoupClientMessageIOData *io)
 {
        SoupMessagePrivate *priv = soup_message_get_instance_private (msg);
 
-       soup_message_io_cleanup (msg);
+       soup_client_message_io_data_free (priv->io_data);
        priv->io_data = io;
 }
 
@@ -2266,17 +1891,3 @@ soup_message_set_bytes_for_sniffing (SoupMessage *msg, gsize bytes)
 
        priv->bytes_for_sniffing = bytes;
 }
-
-const char *
-soup_http_version_to_string (SoupHTTPVersion version)
-{
-       switch (version) {
-       case SOUP_HTTP_1_0:
-               return "HTTP/1.0";
-       case SOUP_HTTP_1_1:
-               return "HTTP/1.1";
-       }
-
-       g_assert_not_reached ();
-       return "";
-}
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 35560bf9..01664ab6 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -29,11 +29,8 @@ struct _SoupMessage {
        guint               status_code;
        char               *reason_phrase;
 
-       SoupMessageBody    *request_body;
        GInputStream       *request_body_stream;
        SoupMessageHeaders *request_headers;
-
-       SoupMessageBody    *response_body;
        SoupMessageHeaders *response_headers;
 };
 
@@ -41,13 +38,10 @@ typedef struct {
        GObjectClass parent_class;
 
        /* signals */
-       void     (*wrote_informational) (SoupMessage *msg);
        void     (*wrote_headers)       (SoupMessage *msg);
-       void     (*wrote_chunk)         (SoupMessage *msg);
        void     (*wrote_body)          (SoupMessage *msg);
        void     (*got_informational)   (SoupMessage *msg);
        void     (*got_headers)         (SoupMessage *msg);
-       void     (*got_chunk)           (SoupMessage *msg, GBytes *chunk);
        void     (*got_body)            (SoupMessage *msg);
        void     (*restarted)           (SoupMessage *msg);
        void     (*finished)            (SoupMessage *msg);
@@ -89,12 +83,6 @@ SOUP_AVAILABLE_IN_2_4
 SoupMessage   *soup_message_new_from_uri        (const char        *method,
                                                 SoupURI           *uri);
 
-SOUP_AVAILABLE_IN_2_4
-void           soup_message_set_response        (SoupMessage       *msg,
-                                                const char        *content_type,
-                                                SoupMemoryUse      resp_use,
-                                                const char        *resp_body,
-                                                gsize              resp_length);
 SOUP_AVAILABLE_IN_ALL
 void           soup_message_set_request_body    (SoupMessage       *msg,
                                                 const char        *content_type,
@@ -105,11 +93,6 @@ void           soup_message_set_request_body_from_bytes (SoupMessage  *msg,
                                                         const char   *content_type,
                                                         GBytes       *bytes);
 
-typedef enum {
-       SOUP_HTTP_1_0 = 0, /*< nick=http-1-0 >*/
-       SOUP_HTTP_1_1 = 1  /*< nick=http-1-1 >*/
-} SoupHTTPVersion;
-
 SOUP_AVAILABLE_IN_2_4
 void             soup_message_set_http_version    (SoupMessage       *msg,
                                                   SoupHTTPVersion    version);
@@ -142,13 +125,12 @@ gboolean         soup_message_get_is_top_level_navigation (SoupMessage      *msg
 
 typedef enum {
        SOUP_MESSAGE_NO_REDIRECT              = (1 << 1),
-       SOUP_MESSAGE_CAN_REBUILD              = (1 << 2),
-       SOUP_MESSAGE_CONTENT_DECODED          = (1 << 3),
-       SOUP_MESSAGE_CERTIFICATE_TRUSTED      = (1 << 4),
-       SOUP_MESSAGE_NEW_CONNECTION           = (1 << 5),
-       SOUP_MESSAGE_IDEMPOTENT               = (1 << 6),
-       SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 7),
-       SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE    = (1 << 8)
+       SOUP_MESSAGE_CONTENT_DECODED          = (1 << 2),
+       SOUP_MESSAGE_CERTIFICATE_TRUSTED      = (1 << 3),
+       SOUP_MESSAGE_NEW_CONNECTION           = (1 << 4),
+       SOUP_MESSAGE_IDEMPOTENT               = (1 << 5),
+       SOUP_MESSAGE_IGNORE_CONNECTION_LIMITS = (1 << 6),
+       SOUP_MESSAGE_DO_NOT_USE_AUTH_CACHE    = (1 << 7)
 } SoupMessageFlags;
 
 SOUP_AVAILABLE_IN_2_4
@@ -192,11 +174,6 @@ void           soup_message_set_status_full     (SoupMessage       *msg,
                                                 guint              status_code, 
                                                 const char        *reason_phrase);
 
-SOUP_AVAILABLE_IN_2_38
-void           soup_message_set_redirect        (SoupMessage       *msg,
-                                                guint              status_code,
-                                                const char        *redirect_uri);
-
 SOUP_AVAILABLE_IN_2_28
 void           soup_message_disable_feature     (SoupMessage       *msg,
                                                 GType              feature_type);
@@ -225,13 +202,9 @@ void                soup_message_set_priority   (SoupMessage        *msg,
 SOUP_AVAILABLE_IN_2_44
 SoupMessagePriority soup_message_get_priority   (SoupMessage        *msg);
 
-SOUP_AVAILABLE_IN_2_4
-void soup_message_wrote_informational (SoupMessage *msg);
 SOUP_AVAILABLE_IN_2_4
 void soup_message_wrote_headers       (SoupMessage *msg);
 SOUP_AVAILABLE_IN_2_4
-void soup_message_wrote_chunk         (SoupMessage *msg);
-SOUP_AVAILABLE_IN_2_4
 void soup_message_wrote_body_data     (SoupMessage *msg, GBytes *chunk);
 SOUP_AVAILABLE_IN_2_4
 void soup_message_wrote_body          (SoupMessage *msg);
@@ -240,8 +213,6 @@ void soup_message_got_informational   (SoupMessage *msg);
 SOUP_AVAILABLE_IN_2_4
 void soup_message_got_headers         (SoupMessage *msg);
 SOUP_AVAILABLE_IN_2_4
-void soup_message_got_chunk           (SoupMessage *msg, GBytes *chunk);
-SOUP_AVAILABLE_IN_2_4
 void soup_message_got_body            (SoupMessage *msg);
 SOUP_AVAILABLE_IN_2_4
 void soup_message_content_sniffed     (SoupMessage *msg, const char *content_type, GHashTable *params);
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index ce7dc713..d336a62d 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1013,7 +1013,7 @@ soup_session_append_queue_item (SoupSession        *session,
 static void
 soup_session_send_queue_item (SoupSession *session,
                              SoupMessageQueueItem *item,
-                             SoupMessageCompletionFn completion_cb)
+                             SoupMessageIOCompletionFn completion_cb)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
 
@@ -1389,7 +1389,7 @@ tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion,
                if (tunnel_item->conn) {
                        tunnel_item->state = SOUP_MESSAGE_RUNNING;
                        soup_session_send_queue_item (session, tunnel_item,
-                                                     tunnel_message_completed);
+                                                     (SoupMessageIOCompletionFn)tunnel_message_completed);
                        soup_message_io_run (msg, !tunnel_item->async);
                        return;
                }
@@ -1443,7 +1443,7 @@ tunnel_connect (SoupMessageQueueItem *item)
        g_signal_emit (session, signals[TUNNELING], 0, tunnel_item->conn);
 
        soup_session_send_queue_item (session, tunnel_item,
-                                     tunnel_message_completed);
+                                     (SoupMessageIOCompletionFn)tunnel_message_completed);
        soup_message_io_run (msg, !item->async);
        g_object_unref (msg);
 }
@@ -1709,7 +1709,8 @@ soup_session_process_queue_item (SoupSession          *session,
 
                        item->state = SOUP_MESSAGE_RUNNING;
 
-                       soup_session_send_queue_item (session, item, message_completed);
+                       soup_session_send_queue_item (session, item,
+                                                     (SoupMessageIOCompletionFn)message_completed);
 
                        if (item->async)
                                async_send_request_running (session, item);
diff --git a/libsoup/soup-status.c b/libsoup/soup-status.c
index d4ed5c41..5eb1f635 100644
--- a/libsoup/soup-status.c
+++ b/libsoup/soup-status.c
@@ -304,3 +304,11 @@ soup_status_proxify (guint status_code)
 }
 
 G_DEFINE_QUARK (soup-http-error-quark, soup_http_error)
+
+/**
+ * SoupHTTPVersion:
+ * @SOUP_HTTP_1_0: HTTP 1.0 (RFC 1945)
+ * @SOUP_HTTP_1_1: HTTP 1.1 (RFC 2616)
+ *
+ * Indicates the HTTP protocol version being used.
+ */
diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h
index 040a858c..cc7512f9 100644
--- a/libsoup/soup-status.h
+++ b/libsoup/soup-status.h
@@ -93,6 +93,11 @@ typedef enum {
        SOUP_STATUS_NOT_EXTENDED                    = 510  /* RFC 2774 */
 } SoupStatus;
 
+typedef enum {
+        SOUP_HTTP_1_0 = 0, /*< nick=http-1-0 >*/
+        SOUP_HTTP_1_1 = 1  /*< nick=http-1-1 >*/
+} SoupHTTPVersion;
+
 SOUP_AVAILABLE_IN_2_4
 const char *soup_status_get_phrase (guint status_code);
 SOUP_AVAILABLE_IN_2_26
diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h
index abd7ea21..49a6c9c4 100644
--- a/libsoup/soup-types.h
+++ b/libsoup/soup-types.h
@@ -25,6 +25,7 @@ typedef struct _SoupMessage             SoupMessage;
 typedef struct _SoupRequest             SoupRequest;
 typedef struct _SoupRequestHTTP         SoupRequestHTTP;
 typedef struct _SoupServer              SoupServer;
+typedef struct _SoupServerMessage       SoupServerMessage;
 typedef struct _SoupSession             SoupSession;
 typedef struct _SoupSessionFeature      SoupSessionFeature;
 typedef struct _SoupSocket              SoupSocket;
diff --git a/libsoup/soup.h b/libsoup/soup.h
index 36485dc2..bd95488e 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -45,6 +45,7 @@ extern "C" {
 #include "server/soup-auth-domain-basic.h"
 #include "server/soup-auth-domain-digest.h"
 #include "server/soup-server.h"
+#include "server/soup-server-message.h"
 #include "soup-session.h"
 #include "soup-session-feature.h"
 #include "soup-socket.h"
diff --git a/libsoup/websocket/soup-websocket.c b/libsoup/websocket/soup-websocket.c
index e7f29f49..48f9a7bd 100644
--- a/libsoup/websocket/soup-websocket.c
+++ b/libsoup/websocket/soup-websocket.c
@@ -27,6 +27,7 @@
 #include "soup-websocket.h"
 #include "soup-headers.h"
 #include "soup-message-private.h"
+#include "soup-server-message.h"
 #include "soup-websocket-extension.h"
 
 #define FIXED_DIGEST_LEN 20
@@ -205,9 +206,9 @@ compute_accept_key (const char *key)
 }
 
 static gboolean
-choose_subprotocol (SoupMessage  *msg,
-                   const char  **server_protocols,
-                   const char  **chosen_protocol)
+choose_subprotocol (SoupServerMessage *msg,
+                   const char       **server_protocols,
+                   const char       **chosen_protocol)
 {
        const char *client_protocols_str;
        char **client_protocols;
@@ -219,8 +220,9 @@ choose_subprotocol (SoupMessage  *msg,
        if (!server_protocols)
                return TRUE;
 
-       client_protocols_str = soup_message_headers_get_one (msg->request_headers,
-                                                            "Sec-Websocket-Protocol");
+       client_protocols_str =
+               soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
+                                             "Sec-Websocket-Protocol");
        if (!client_protocols_str)
                return TRUE;
 
@@ -406,10 +408,10 @@ soup_websocket_client_prepare_handshake_with_extensions (SoupMessage *msg,
  * Since: 2.50
  */
 gboolean
-soup_websocket_server_check_handshake (SoupMessage  *msg,
-                                      const char   *expected_origin,
-                                      char        **protocols,
-                                      GError      **error)
+soup_websocket_server_check_handshake (SoupServerMessage *msg,
+                                      const char        *expected_origin,
+                                      char             **protocols,
+                                      GError           **error)
 {
        return soup_websocket_server_check_handshake_with_extensions (msg, expected_origin, protocols, NULL, 
error);
 }
@@ -460,15 +462,15 @@ extract_extension_names_from_request (SoupMessage *msg)
 }
 
 static gboolean
-process_extensions (SoupMessage *msg,
-                    const char  *extensions,
-                    gboolean     is_server,
+process_extensions (const char  *extensions,
+                   SoupMessage *msg,
                     GPtrArray   *supported_extensions,
                     GList      **accepted_extensions,
                     GError     **error)
 {
         GSList *extension_list, *l;
         GHashTable *requested_extensions = NULL;
+       gboolean is_server = msg == NULL;
 
         if (!supported_extensions || supported_extensions->len == 0) {
                 if (is_server)
@@ -608,7 +610,7 @@ process_extensions (SoupMessage *msg,
 
 /**
  * soup_websocket_server_check_handshake_with_extensions:
- * @msg: #SoupMessage containing the client side of a WebSocket handshake
+ * @msg: #SoupServerMessage containing the client side of a WebSocket handshake
  * @origin: (nullable): expected Origin header
  * @protocols: (nullable) (array zero-terminated=1): allowed WebSocket
  *   protocols.
@@ -640,19 +642,20 @@ process_extensions (SoupMessage *msg,
  * Since: 2.68
  */
 gboolean
-soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
-                                                       const char   *expected_origin,
-                                                       char        **protocols,
-                                                       GPtrArray   *supported_extensions,
-                                                       GError      **error)
+soup_websocket_server_check_handshake_with_extensions (SoupServerMessage *msg,
+                                                       const char        *expected_origin,
+                                                       char             **protocols,
+                                                       GPtrArray         *supported_extensions,
+                                                       GError           **error)
 {
        const char *origin;
        const char *key;
        const char *extensions;
+       SoupMessageHeaders *request_headers;
 
-       g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
+       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), FALSE);
 
-       if (msg->method != SOUP_METHOD_GET) {
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
                g_set_error_literal (error,
                                     SOUP_WEBSOCKET_ERROR,
                                     SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET,
@@ -660,8 +663,9 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
                return FALSE;
        }
 
-       if (!soup_message_headers_header_equals (msg->request_headers, "Upgrade", "websocket") ||
-           !soup_message_headers_header_contains (msg->request_headers, "Connection", "upgrade")) {
+       request_headers = soup_server_message_get_request_headers (msg);
+       if (!soup_message_headers_header_equals (request_headers, "Upgrade", "websocket") ||
+           !soup_message_headers_header_contains (request_headers, "Connection", "upgrade")) {
                g_set_error_literal (error,
                                     SOUP_WEBSOCKET_ERROR,
                                     SOUP_WEBSOCKET_ERROR_NOT_WEBSOCKET,
@@ -669,7 +673,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
                return FALSE;
        }
 
-       if (!soup_message_headers_header_equals (msg->request_headers, "Sec-WebSocket-Version", "13")) {
+       if (!soup_message_headers_header_equals (request_headers, "Sec-WebSocket-Version", "13")) {
                g_set_error_literal (error,
                                     SOUP_WEBSOCKET_ERROR,
                                     SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE,
@@ -677,7 +681,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
                return FALSE;
        }
 
-       key = soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key");
+       key = soup_message_headers_get_one (request_headers, "Sec-WebSocket-Key");
        if (key == NULL || !validate_key (key)) {
                g_set_error_literal (error,
                                     SOUP_WEBSOCKET_ERROR,
@@ -687,7 +691,7 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
        }
 
        if (expected_origin) {
-               origin = soup_message_headers_get_one (msg->request_headers, "Origin");
+               origin = soup_message_headers_get_one (request_headers, "Origin");
                if (!origin || g_ascii_strcasecmp (origin, expected_origin) != 0) {
                        g_set_error (error,
                                     SOUP_WEBSOCKET_ERROR,
@@ -705,9 +709,9 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
                return FALSE;
        }
 
-       extensions = soup_message_headers_get_list (msg->request_headers, "Sec-WebSocket-Extensions");
+       extensions = soup_message_headers_get_list (request_headers, "Sec-WebSocket-Extensions");
        if (extensions && *extensions) {
-               if (!process_extensions (msg, extensions, TRUE, supported_extensions, NULL, error))
+               if (!process_extensions (extensions, NULL, supported_extensions, NULL, error))
                        return FALSE;
        }
 
@@ -718,32 +722,35 @@ soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
        "<body>Received invalid WebSocket request</body></html>\r\n"
 
 static void
-respond_handshake_forbidden (SoupMessage *msg)
+respond_handshake_forbidden (SoupServerMessage *msg)
 {
-       soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
-       soup_message_headers_append (msg->response_headers, "Connection", "close");
-       soup_message_set_response (msg, "text/html", SOUP_MEMORY_COPY,
-                                  RESPONSE_FORBIDDEN, strlen (RESPONSE_FORBIDDEN));
+       soup_server_message_set_status (msg, SOUP_STATUS_FORBIDDEN, NULL);
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
+                                    "Connection", "close");
+       soup_server_message_set_response (msg, "text/html", SOUP_MEMORY_COPY,
+                                         RESPONSE_FORBIDDEN, strlen (RESPONSE_FORBIDDEN));
 }
 
 #define RESPONSE_BAD "<html><head><title>400 Bad Request</title></head>\r\n" \
        "<body>Received invalid WebSocket request: %s</body></html>\r\n"
 
 static void
-respond_handshake_bad (SoupMessage *msg, const char *why)
+respond_handshake_bad (SoupServerMessage *msg,
+                      const char        *why)
 {
        char *text;
 
        text = g_strdup_printf (RESPONSE_BAD, why);
-       soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
-       soup_message_headers_append (msg->response_headers, "Connection", "close");
-       soup_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE,
-                                  text, strlen (text));
+       soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
+                                    "Connection", "close");
+       soup_server_message_set_response (msg, "text/html", SOUP_MEMORY_TAKE,
+                                         text, strlen (text));
 }
 
 /**
  * soup_websocket_server_process_handshake:
- * @msg: #SoupMessage containing the client side of a WebSocket handshake
+ * @msg: #SoupServerMessage containing the client side of a WebSocket handshake
  * @expected_origin: (allow-none): expected Origin header
  * @protocols: (allow-none) (array zero-terminated=1): allowed WebSocket
  *   protocols.
@@ -773,16 +780,16 @@ respond_handshake_bad (SoupMessage *msg, const char *why)
  * Since: 2.50
  */
 gboolean
-soup_websocket_server_process_handshake (SoupMessage  *msg,
-                                        const char   *expected_origin,
-                                        char        **protocols)
+soup_websocket_server_process_handshake (SoupServerMessage *msg,
+                                        const char        *expected_origin,
+                                        char             **protocols)
 {
        return soup_websocket_server_process_handshake_with_extensions (msg, expected_origin, protocols, 
NULL, NULL);
 }
 
 /**
  * soup_websocket_server_process_handshake_with_extensions:
- * @msg: #SoupMessage containing the client side of a WebSocket handshake
+ * @msg: #SoupServerMessage containing the client side of a WebSocket handshake
  * @expected_origin: (nullable): expected Origin header
  * @protocols: (nullable) (array zero-terminated=1): allowed WebSocket
  *   protocols.
@@ -813,18 +820,21 @@ soup_websocket_server_process_handshake (SoupMessage  *msg,
  * Since: 2.68
  */
 gboolean
-soup_websocket_server_process_handshake_with_extensions (SoupMessage  *msg,
-                                                         const char   *expected_origin,
-                                                         char        **protocols,
-                                                         GPtrArray    *supported_extensions,
-                                                         GList       **accepted_extensions)
+soup_websocket_server_process_handshake_with_extensions (SoupServerMessage *msg,
+                                                         const char        *expected_origin,
+                                                         char             **protocols,
+                                                         GPtrArray         *supported_extensions,
+                                                         GList            **accepted_extensions)
 {
        const char *chosen_protocol = NULL;
        const char *key;
        const char *extensions;
        char *accept_key;
        GError *error = NULL;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
 
+       g_return_val_if_fail (SOUP_IS_SERVER_MESSAGE (msg), FALSE);
        g_return_val_if_fail (accepted_extensions == NULL || *accepted_extensions == NULL, FALSE);
 
        if (!soup_websocket_server_check_handshake_with_extensions (msg, expected_origin, protocols, 
supported_extensions, &error)) {
@@ -838,25 +848,27 @@ soup_websocket_server_process_handshake_with_extensions (SoupMessage  *msg,
                return FALSE;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS);
-       soup_message_headers_replace (msg->response_headers, "Upgrade", "websocket");
-       soup_message_headers_append (msg->response_headers, "Connection", "Upgrade");
+       soup_server_message_set_status (msg, SOUP_STATUS_SWITCHING_PROTOCOLS, NULL);
+       response_headers = soup_server_message_get_response_headers (msg);
+       soup_message_headers_replace (response_headers, "Upgrade", "websocket");
+       soup_message_headers_append (response_headers, "Connection", "Upgrade");
 
-       key = soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Key");
+       request_headers = soup_server_message_get_request_headers (msg);
+       key = soup_message_headers_get_one (request_headers, "Sec-WebSocket-Key");
        accept_key = compute_accept_key (key);
-       soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Accept", accept_key);
+       soup_message_headers_append (response_headers, "Sec-WebSocket-Accept", accept_key);
        g_free (accept_key);
 
        choose_subprotocol (msg, (const char **) protocols, &chosen_protocol);
        if (chosen_protocol)
-               soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Protocol", 
chosen_protocol);
+               soup_message_headers_append (response_headers, "Sec-WebSocket-Protocol", chosen_protocol);
 
-       extensions = soup_message_headers_get_list (msg->request_headers, "Sec-WebSocket-Extensions");
+       extensions = soup_message_headers_get_list (request_headers, "Sec-WebSocket-Extensions");
        if (extensions && *extensions) {
                GList *websocket_extensions = NULL;
                GList *l;
 
-               process_extensions (msg, extensions, TRUE, supported_extensions, &websocket_extensions, NULL);
+               process_extensions (extensions, NULL, supported_extensions, &websocket_extensions, NULL);
                if (websocket_extensions) {
                        GString *response_extensions;
 
@@ -878,11 +890,11 @@ soup_websocket_server_process_handshake_with_extensions (SoupMessage  *msg,
                        }
 
                        if (response_extensions->len > 0) {
-                               soup_message_headers_replace (msg->response_headers,
+                               soup_message_headers_replace (response_headers,
                                                              "Sec-WebSocket-Extensions",
                                                              response_extensions->str);
                        } else {
-                               soup_message_headers_remove (msg->response_headers,
+                               soup_message_headers_remove (response_headers,
                                                             "Sec-WebSocket-Extensions");
                        }
                        g_string_free (response_extensions, TRUE);
@@ -1009,7 +1021,7 @@ soup_websocket_client_verify_handshake_with_extensions (SoupMessage *msg,
 
        extensions = soup_message_headers_get_list (msg->response_headers, "Sec-WebSocket-Extensions");
        if (extensions && *extensions) {
-               if (!process_extensions (msg, extensions, FALSE, supported_extensions, accepted_extensions, 
error))
+               if (!process_extensions (extensions, msg, supported_extensions, accepted_extensions, error))
                        return FALSE;
        }
 
diff --git a/libsoup/websocket/soup-websocket.h b/libsoup/websocket/soup-websocket.h
index 9f265e0a..d347c213 100644
--- a/libsoup/websocket/soup-websocket.h
+++ b/libsoup/websocket/soup-websocket.h
@@ -87,28 +87,28 @@ gboolean soup_websocket_client_verify_handshake_with_extensions (SoupMessage *ms
                                                                  GError     **error);
 
 SOUP_AVAILABLE_IN_2_50
-gboolean soup_websocket_server_check_handshake   (SoupMessage  *msg,
-                                                 const char   *origin,
-                                                 char        **protocols,
-                                                 GError      **error);
+gboolean soup_websocket_server_check_handshake   (SoupServerMessage *msg,
+                                                 const char        *origin,
+                                                 char             **protocols,
+                                                 GError           **error);
 SOUP_AVAILABLE_IN_2_68
 gboolean
-soup_websocket_server_check_handshake_with_extensions (SoupMessage  *msg,
-                                                       const char   *origin,
-                                                       char        **protocols,
-                                                       GPtrArray    *supported_extensions,
-                                                       GError      **error);
+soup_websocket_server_check_handshake_with_extensions (SoupServerMessage *msg,
+                                                       const char        *origin,
+                                                       char             **protocols,
+                                                       GPtrArray         *supported_extensions,
+                                                       GError           **error);
 
 SOUP_AVAILABLE_IN_2_50
-gboolean soup_websocket_server_process_handshake (SoupMessage  *msg,
-                                                 const char   *expected_origin,
-                                                 char        **protocols);
+gboolean soup_websocket_server_process_handshake (SoupServerMessage *msg,
+                                                 const char        *expected_origin,
+                                                 char             **protocols);
 SOUP_AVAILABLE_IN_2_68
 gboolean
-soup_websocket_server_process_handshake_with_extensions (SoupMessage  *msg,
-                                                         const char   *expected_origin,
-                                                         char        **protocols,
-                                                         GPtrArray    *supported_extensions,
-                                                         GList       **accepted_extensions);
+soup_websocket_server_process_handshake_with_extensions (SoupServerMessage *msg,
+                                                         const char        *expected_origin,
+                                                         char             **protocols,
+                                                         GPtrArray         *supported_extensions,
+                                                         GList            **accepted_extensions);
 
 G_END_DECLS
diff --git a/tests/auth-test.c b/tests/auth-test.c
index 9f7720ab..1f3ba064 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -838,19 +838,24 @@ select_auth_test_one (SoupURI *uri,
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "OK\r\n", 4);
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "OK\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static gboolean
-server_basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
-                           const char *username, const char *password, gpointer data)
+server_basic_auth_callback (SoupAuthDomain    *auth_domain,
+                           SoupServerMessage *msg,
+                           const char        *username,
+                           const char        *password,
+                           gpointer           data)
 {
        if (strcmp (username, "user") != 0)
                return FALSE;
@@ -858,8 +863,10 @@ server_basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
 }
 
 static char *
-server_digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
-                            const char *username, gpointer data)
+server_digest_auth_callback (SoupAuthDomain    *auth_domain,
+                            SoupServerMessage *msg,
+                            const char        *username,
+                            gpointer           data)
 {
        if (strcmp (username, "user") != 0)
                return NULL;
@@ -974,19 +981,21 @@ do_select_auth_test (void)
 }
 
 static void
-sneakily_close_connection (SoupMessage *msg, gpointer user_data)
+sneakily_close_connection (SoupServerMessage *msg,
+                          gpointer           user_data)
 {
        /* Sneakily close the connection after the response, by
         * tricking soup-message-io into thinking that had been
         * the plan all along.
         */
-       soup_message_headers_append (msg->response_headers,
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
                                     "Connection", "close");
 }
 
 static void
-auth_close_request_started (SoupServer *server, SoupMessage *msg,
-                           SoupClientContext *client, gpointer user_data)
+auth_close_request_started (SoupServer        *server,
+                           SoupServerMessage *msg,
+                           gpointer           user_data)
 {
        g_signal_connect (msg, "wrote-headers",
                          G_CALLBACK (sneakily_close_connection), NULL);
@@ -1112,13 +1121,20 @@ do_infinite_auth_test (void)
 }
 
 static void
-disappear_request_read (SoupServer *server, SoupMessage *msg,
-                       SoupClientContext *context, gpointer user_data)
+disappear_request_read (SoupServer        *server,
+                       SoupServerMessage *msg,
+                       gpointer           user_data)
 {
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+
+       request_headers = soup_server_message_get_request_headers (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+
        /* Remove the WWW-Authenticate header if this was a failed attempt */
-       if (soup_message_headers_get_one (msg->request_headers, "Authorization") &&
-           msg->status_code == SOUP_STATUS_UNAUTHORIZED)
-               soup_message_headers_remove (msg->response_headers, "WWW-Authenticate");
+       if (soup_message_headers_get_one (request_headers, "Authorization") &&
+           soup_server_message_get_status (msg, NULL) == SOUP_STATUS_UNAUTHORIZED)
+               soup_message_headers_remove (response_headers, "WWW-Authenticate");
 }
 
 static void
diff --git a/tests/cache-test.c b/tests/cache-test.c
index 34e26be8..a048db57 100644
--- a/tests/cache-test.c
+++ b/tests/cache-test.c
@@ -6,53 +6,62 @@
 #include "test-utils.h"
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        const char *last_modified, *etag;
        const char *header;
+       const char *method;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
        guint status = SOUP_STATUS_OK;
 
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       method = soup_server_message_get_method (msg);
+
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       request_headers = soup_server_message_get_request_headers (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+       header = soup_message_headers_get_one (request_headers,
                                               "Test-Set-Expires");
        if (header) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Expires",
                                             header);
        }
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (request_headers,
                                               "Test-Set-Cache-Control");
        if (header) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Cache-Control",
                                             header);
        }
 
-       last_modified = soup_message_headers_get_one (msg->request_headers,
+       last_modified = soup_message_headers_get_one (request_headers,
                                                      "Test-Set-Last-Modified");
        if (last_modified) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Last-Modified",
                                             last_modified);
        }
 
-       etag = soup_message_headers_get_one (msg->request_headers,
+       etag = soup_message_headers_get_one (request_headers,
                                             "Test-Set-ETag");
        if (etag) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "ETag",
                                             etag);
        }
 
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (request_headers,
                                               "If-Modified-Since");
        if (header && last_modified) {
                GDateTime *modified_date, *header_date;
@@ -67,17 +76,17 @@ server_callback (SoupServer *server, SoupMessage *msg,
                 g_date_time_unref (header_date);
        }
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (request_headers,
                                               "If-None-Match");
        if (header && etag) {
                if (!strcmp (header, etag))
                        status = SOUP_STATUS_NOT_MODIFIED;
        }
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (request_headers,
                                               "Test-Set-My-Header");
        if (header) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "My-Header",
                                             header);
        }
@@ -93,12 +102,12 @@ server_callback (SoupServer *server, SoupMessage *msg,
                if (etag)
                        g_checksum_update (sum, (guchar *)etag, strlen (etag));
                body = g_checksum_get_string (sum);
-               soup_message_set_response (msg, "text/plain",
-                                          SOUP_MEMORY_COPY,
-                                          body, strlen (body) + 1);
+               soup_server_message_set_response (msg, "text/plain",
+                                                 SOUP_MEMORY_COPY,
+                                                 body, strlen (body) + 1);
                g_checksum_free (sum);
        }
-       soup_message_set_status (msg, status);
+       soup_server_message_set_status (msg, status, NULL);
 }
 
 static gboolean
diff --git a/tests/coding-test.c b/tests/coding-test.c
index 696f46f5..75233351 100644
--- a/tests/coding-test.c
+++ b/tests/coding-test.c
@@ -10,26 +10,34 @@ SoupServer *server;
 SoupURI *base_uri;
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        const char *accept_encoding, *options;
        GSList *codings;
        GBytes *response = NULL;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *response_body;
 
-       options = soup_message_headers_get_one (msg->request_headers,
+       request_headers = soup_server_message_get_request_headers (msg);
+       options = soup_message_headers_get_one (request_headers,
                                                "X-Test-Options");
        if (!options)
                options = "";
 
-       accept_encoding = soup_message_headers_get_list (msg->request_headers,
+       accept_encoding = soup_message_headers_get_list (request_headers,
                                                         "Accept-Encoding");
        if (accept_encoding && !soup_header_contains (options, "force-encode"))
                codings = soup_header_parse_quality_list (accept_encoding, NULL);
        else
                codings = NULL;
 
+       response_headers = soup_server_message_get_response_headers (msg);
+
        if (codings) {
                gboolean claim_deflate, claim_gzip;
                const char *extension = NULL, *encoding = NULL;
@@ -58,7 +66,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                        response = soup_test_load_resource (resource, NULL);
 
                        if (response) {
-                               soup_message_headers_append (msg->response_headers,
+                               soup_message_headers_append (response_headers,
                                                             "Content-Encoding",
                                                             encoding);
                        }
@@ -75,7 +83,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                 * the error with "Content-Encoding: gzip" but there's
                 * no body, so, eh.
                 */
-               soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                return;
        }
 
@@ -86,34 +94,35 @@ server_callback (SoupServer *server, SoupMessage *msg,
                    soup_header_contains (options, "prefer-deflate-raw"))
                        encoding = "deflate";
 
-               soup_message_headers_replace (msg->response_headers,
+               soup_message_headers_replace (response_headers,
                                              "Content-Encoding",
                                              encoding);
        }
 
        /* Content-Type matches the "real" format, not the sent format */
        if (g_str_has_suffix (path, ".gz")) {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type",
                                             "application/gzip");
        } else {
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type",
                                             "text/plain");
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_headers_set_encoding (msg->response_headers, SOUP_ENCODING_CHUNKED);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_message_headers_set_encoding (response_headers, SOUP_ENCODING_CHUNKED);
 
+       response_body = soup_server_message_get_response_body (msg);
        if (!soup_header_contains (options, "empty"))
-               soup_message_body_append_bytes (msg->response_body, response);
+               soup_message_body_append_bytes (response_body, response);
        g_bytes_unref (response);
 
        if (soup_header_contains (options, "trailing-junk")) {
-               soup_message_body_append (msg->response_body, SOUP_MEMORY_COPY,
+               soup_message_body_append (response_body, SOUP_MEMORY_COPY,
                                          options, strlen (options));
        }
-       soup_message_body_complete (msg->response_body);
+       soup_message_body_complete (response_body);
 }
 
 typedef struct {
diff --git a/tests/connection-test.c b/tests/connection-test.c
index d267c3ec..f694bf94 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -7,7 +7,7 @@
 
 #include "soup-connection.h"
 #include "soup-socket-private.h"
-#include "soup-server-private.h"
+#include "soup-server-message-private.h"
 
 #include <gio/gnetworking.h>
 
@@ -16,15 +16,17 @@ SoupURI *base_uri;
 GMutex server_mutex;
 
 static void
-forget_close (SoupMessage *msg, gpointer user_data)
+forget_close (SoupServerMessage *msg,
+             gpointer           user_data)
 {
-       soup_message_headers_remove (msg->response_headers, "Connection");
+       soup_message_headers_remove (soup_server_message_get_response_headers (msg),
+                                    "Connection");
 }
 
 static void
-close_socket (SoupMessage *msg, gpointer user_data)
+close_socket (SoupServerMessage *msg,
+             SoupSocket        *sock)
 {
-       SoupSocket *sock = user_data;
         GSocket *gsocket;
        int sockfd;
 
@@ -42,7 +44,8 @@ close_socket (SoupMessage *msg, gpointer user_data)
        /* Then add the missing data to the message now, so SoupServer
         * can clean up after itself properly.
         */
-       soup_message_body_append (msg->response_body, SOUP_MEMORY_STATIC,
+       soup_message_body_append (soup_server_message_get_response_body (msg),
+                                 SOUP_MEMORY_STATIC,
                                  "foo", 3);
 }
 
@@ -53,8 +56,9 @@ timeout_socket (SoupSocket *sock, gpointer user_data)
 }
 
 static void
-timeout_request_started (SoupServer *server, SoupMessage *msg,
-                        SoupClientContext *client, gpointer user_data)
+timeout_request_started (SoupServer        *server,
+                        SoupServerMessage *msg,
+                        gpointer           user_data)
 {
        SoupSocket *sock;
        GMainContext *context = g_main_context_get_thread_default ();
@@ -62,7 +66,7 @@ timeout_request_started (SoupServer *server, SoupMessage *msg,
 
        g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL);
 
-       sock = soup_client_context_get_soup_socket (client);
+       sock = soup_server_message_get_soup_socket (msg);
        readable = g_signal_connect (sock, "readable",
                                    G_CALLBACK (timeout_socket), NULL);
 
@@ -104,10 +108,14 @@ setup_timeout_persistent (SoupServer *server, SoupSocket *sock)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
+       const char *method;
+
        /* The way this gets used in the tests, we don't actually
         * need to hold it through the whole function, so it's simpler
         * to just release it right away.
@@ -115,21 +123,25 @@ server_callback (SoupServer *server, SoupMessage *msg,
        g_mutex_lock (&server_mutex);
        g_mutex_unlock (&server_mutex);
 
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       method = soup_server_message_get_method (msg);
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
        if (g_str_has_prefix (path, "/content-length/")) {
                gboolean too_long = strcmp (path, "/content-length/long") == 0;
                gboolean no_close = strcmp (path, "/content-length/noclose") == 0;
+               SoupMessageHeaders *response_headers;
+
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+               soup_server_message_set_response (msg, "text/plain",
+                                                 SOUP_MEMORY_STATIC, "foobar", 6);
 
-               soup_message_set_status (msg, SOUP_STATUS_OK);
-               soup_message_set_response (msg, "text/plain",
-                                          SOUP_MEMORY_STATIC, "foobar", 6);
+               response_headers = soup_server_message_get_response_headers (msg);
                if (too_long)
-                       soup_message_headers_set_content_length (msg->response_headers, 9);
-               soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_set_content_length (response_headers, 9);
+               soup_message_headers_append (response_headers,
                                             "Connection", "close");
 
                if (too_long) {
@@ -140,7 +152,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                         * the declared Content-Length. Instead, we
                         * forcibly close the socket at that point.
                         */
-                       sock = soup_client_context_get_soup_socket (context);
+                       sock = soup_server_message_get_soup_socket (msg);
                        g_signal_connect (msg, "wrote-chunk",
                                          G_CALLBACK (close_socket), sock);
                } else if (no_close) {
@@ -158,13 +170,13 @@ server_callback (SoupServer *server, SoupMessage *msg,
        if (!strcmp (path, "/timeout-persistent")) {
                SoupSocket *sock;
 
-               sock = soup_client_context_get_soup_socket (context);
+               sock = soup_server_message_get_soup_socket (msg);
                setup_timeout_persistent (server, sock);
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC, "index", 5);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC, "index", 5);
        return;
 }
 
diff --git a/tests/context-test.c b/tests/context-test.c
index a47a3142..e8c55dc8 100644
--- a/tests/context-test.c
+++ b/tests/context-test.c
@@ -10,12 +10,13 @@ static char *base_uri;
 
 typedef struct {
        SoupServer *server;
-       SoupMessage *msg;
+       SoupServerMessage *msg;
        GSource *timeout;
 } SlowData;
 
 static void
-request_finished (SoupMessage *msg, gpointer data)
+request_finished (SoupServerMessage *msg,
+                 gpointer           data)
 {
        SlowData *sd = data;
 
@@ -28,10 +29,12 @@ static gboolean
 add_body_chunk (gpointer data)
 {
        SlowData *sd = data;
+       SoupMessageBody *response_body;
 
-       soup_message_body_append (sd->msg->response_body,
+       response_body = soup_server_message_get_response_body (sd->msg);
+       soup_message_body_append (response_body,
                                  SOUP_MEMORY_STATIC, "OK\r\n", 4);
-       soup_message_body_complete (sd->msg->response_body);
+       soup_message_body_complete (response_body);
        soup_server_unpause_message (sd->server, sd->msg);
        g_object_unref (sd->msg);
 
@@ -39,25 +42,29 @@ add_body_chunk (gpointer data)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        SlowData *sd;
+       SoupMessageHeaders *response_headers;
 
-       if (msg->method != SOUP_METHOD_GET) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
        if (!strcmp (path, "/fast")) {
-               soup_message_set_response (msg, "text/plain",
-                                          SOUP_MEMORY_STATIC, "OK\r\n", 4);
+               soup_server_message_set_response (msg, "text/plain",
+                                                 SOUP_MEMORY_STATIC, "OK\r\n", 4);
                return;
        }
 
-       soup_message_headers_set_encoding (msg->response_headers,
+       response_headers = soup_server_message_get_response_headers (msg);
+       soup_message_headers_set_encoding (response_headers,
                                           SOUP_ENCODING_CHUNKED);
        g_object_ref (msg);
        soup_server_pause_message (server, msg);
diff --git a/tests/continue-test.c b/tests/continue-test.c
index 85d1a4e6..2b63b1b7 100644
--- a/tests/continue-test.c
+++ b/tests/continue-test.c
@@ -14,37 +14,65 @@ static SoupURI *base_uri;
 static GSList *events;
 
 static void
-event (SoupMessage *msg, const char *side, const char *message)
+client_event (SoupMessage *msg,
+             const char  *message)
 {
-       char *data = g_strdup_printf ("%s-%s", side, message);
+       char *data = g_strdup_printf ("client-%s", message);
+
+       debug_printf (2, "  %s", data);
+       debug_printf (2, "\n");
+
+       events = g_slist_append (events, data);
+}
+
+static void
+server_event (SoupServerMessage *msg,
+             const char        *message)
+{
+       char *data = g_strdup_printf ("server-%s", message);
        gboolean record_status =
                (!strcmp (data, "server-wrote_headers") ||
                 !strcmp (data, "server-wrote_informational"));
+       const char *reason_phrase;
+       guint status_code = soup_server_message_get_status (msg, &reason_phrase);
 
        debug_printf (2, "  %s", data);
        if (record_status)
-               debug_printf (2, " (%s)", msg->reason_phrase);
+               debug_printf (2, " (%s)", reason_phrase);
        debug_printf (2, "\n");
 
        events = g_slist_append (events, data);
        if (record_status)
-               events = g_slist_append (events, GUINT_TO_POINTER (msg->status_code));
+               events = g_slist_append (events, GUINT_TO_POINTER (status_code));
 }
 
-#define EVENT_HANDLER(name)                    \
+#define CLIENT_EVENT_HANDLER(name)             \
 static void                                    \
-name (SoupMessage *msg, gpointer side)         \
+client_##name (SoupMessage *msg, gpointer side)        \
 {                                              \
-       event (msg, side, #name);               \
+       client_event (msg, #name);              \
 }
 
-EVENT_HANDLER (got_informational)
-EVENT_HANDLER (got_headers)
-EVENT_HANDLER (got_body)
-EVENT_HANDLER (wrote_informational)
-EVENT_HANDLER (wrote_headers)
-EVENT_HANDLER (wrote_body)
-EVENT_HANDLER (finished)
+#define SERVER_EVENT_HANDLER(name)             \
+static void                                    \
+server_##name (SoupServerMessage *msg, gpointer side)  \
+{                                              \
+       server_event (msg, #name);              \
+}
+
+CLIENT_EVENT_HANDLER (got_informational)
+CLIENT_EVENT_HANDLER (got_headers)
+CLIENT_EVENT_HANDLER (got_body)
+CLIENT_EVENT_HANDLER (wrote_headers)
+CLIENT_EVENT_HANDLER (wrote_body)
+CLIENT_EVENT_HANDLER (finished)
+
+SERVER_EVENT_HANDLER (got_headers)
+SERVER_EVENT_HANDLER (got_body)
+SERVER_EVENT_HANDLER (wrote_informational)
+SERVER_EVENT_HANDLER (wrote_headers)
+SERVER_EVENT_HANDLER (wrote_body)
+SERVER_EVENT_HANDLER (finished)
 
 static void
 restarted (SoupMessage *msg,
@@ -76,7 +104,6 @@ do_message (const char *path, gboolean long_body,
        }
        soup_uri_set_path (uri, path);
        msg = soup_message_new_from_uri ("POST", uri);
-       g_print ("DBG: soup_message_new_from_uri: %p\n", msg);
        soup_uri_free (uri);
 
        body = long_body ? LONG_BODY : SHORT_BODY;
@@ -88,20 +115,18 @@ do_message (const char *path, gboolean long_body,
                                                       SOUP_EXPECTATION_CONTINUE);
        }
 
-       g_signal_connect (msg, "got_informational",
-                         G_CALLBACK (got_informational), "client");
-       g_signal_connect (msg, "got_headers",
-                         G_CALLBACK (got_headers), "client");
-       g_signal_connect (msg, "got_body",
-                         G_CALLBACK (got_body), "client");
-       g_signal_connect (msg, "wrote_informational",
-                         G_CALLBACK (wrote_informational), "client");
-       g_signal_connect (msg, "wrote_headers",
-                         G_CALLBACK (wrote_headers), "client");
-       g_signal_connect (msg, "wrote_body",
-                         G_CALLBACK (wrote_body), "client");
+       g_signal_connect (msg, "got-informational",
+                         G_CALLBACK (client_got_informational), NULL);
+       g_signal_connect (msg, "got-headers",
+                         G_CALLBACK (client_got_headers), NULL);
+       g_signal_connect (msg, "got-body",
+                         G_CALLBACK (client_got_body), NULL);
+       g_signal_connect (msg, "wrote-headers",
+                         G_CALLBACK (client_wrote_headers), NULL);
+       g_signal_connect (msg, "wrote-body",
+                         G_CALLBACK (client_wrote_body), NULL);
        g_signal_connect (msg, "finished",
-                         G_CALLBACK (finished), "client");
+                         G_CALLBACK (client_finished), NULL);
        g_signal_connect (msg, "restarted",
                          G_CALLBACK (restarted), request_body);
 
@@ -401,47 +426,54 @@ do_test_auth_long_expect_pass (void)
 /* SERVER */
 
 static void
-server_got_headers (SoupMessage *msg, gpointer server)
+_server_got_headers (SoupServerMessage *msg,
+                    gpointer           server)
 {
+       guint status_code;
+       SoupMessageHeaders *request_headers;
+
+       status_code = soup_server_message_get_status (msg, NULL);
        /* FIXME */
-       if (msg->status_code != SOUP_STATUS_CONTINUE &&
-           msg->status_code != 0)
+       if (status_code != SOUP_STATUS_CONTINUE && status_code != 0)
                return;
 
-       if (soup_message_headers_get_expectations (msg->request_headers) &
+       request_headers = soup_server_message_get_request_headers (msg);
+       if (soup_message_headers_get_expectations (request_headers) &
            SOUP_EXPECTATION_CONTINUE) {
                const char *length;
 
-               length = soup_message_headers_get_one (msg->request_headers,
+               length = soup_message_headers_get_one (request_headers,
                                                       "Content-Length");
                if (length && atoi (length) > MAX_POST_LENGTH) {
-                       soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
-                       soup_message_headers_append (msg->response_headers, "Connection", "close");
+                       SoupMessageHeaders *response_headers;
+
+                       response_headers = soup_server_message_get_response_headers (msg);
+                       soup_server_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE, NULL);
+                       soup_message_headers_append (response_headers, "Connection", "close");
                }
        }
-}      
+}
 
 static void
-request_started (SoupServer *server, SoupMessage *msg,
-                SoupClientContext *client, gpointer user_data)
+request_started (SoupServer        *server,
+                SoupServerMessage *msg,
+                gpointer           user_data)
 {
-       g_signal_connect (msg, "got_headers",
-                         G_CALLBACK (server_got_headers), server);
-
-       g_signal_connect (msg, "got_informational",
-                         G_CALLBACK (got_informational), "server");
-       g_signal_connect (msg, "got_headers",
-                         G_CALLBACK (got_headers), "server");
-       g_signal_connect (msg, "got_body",
-                         G_CALLBACK (got_body), "server");
-       g_signal_connect (msg, "wrote_informational",
-                         G_CALLBACK (wrote_informational), "server");
-       g_signal_connect (msg, "wrote_headers",
-                         G_CALLBACK (wrote_headers), "server");
-       g_signal_connect (msg, "wrote_body",
-                         G_CALLBACK (wrote_body), "server");
+       g_signal_connect (msg, "got-headers",
+                         G_CALLBACK (_server_got_headers), server);
+
+       g_signal_connect (msg, "got-headers",
+                         G_CALLBACK (server_got_headers), NULL);
+       g_signal_connect (msg, "got-body",
+                         G_CALLBACK (server_got_body), NULL);
+       g_signal_connect (msg, "wrote-informational",
+                         G_CALLBACK (server_wrote_informational), NULL);
+       g_signal_connect (msg, "wrote-headers",
+                         G_CALLBACK (server_wrote_headers), NULL);
+       g_signal_connect (msg, "wrote-body",
+                         G_CALLBACK (server_wrote_body), NULL);
        g_signal_connect (msg, "finished",
-                         G_CALLBACK (finished), "server");
+                         G_CALLBACK (server_finished), NULL);
 }
 
 static gboolean
@@ -452,18 +484,25 @@ auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       if (msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
-               soup_message_headers_append (msg->response_headers, "Connection", "close");
-       } else if (msg->request_body->length > MAX_POST_LENGTH) {
-               soup_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE);
-               soup_message_headers_append (msg->response_headers, "Connection", "close");
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *request_body;
+
+       response_headers = soup_server_message_get_response_headers (msg);
+       request_body = soup_server_message_get_request_body (msg);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
+               soup_message_headers_append (response_headers, "Connection", "close");
+       } else if (request_body->length > MAX_POST_LENGTH) {
+               soup_server_message_set_status (msg, SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE, NULL);
+               soup_message_headers_append (response_headers, "Connection", "close");
        } else
-               soup_message_set_status (msg, SOUP_STATUS_CREATED);
+               soup_server_message_set_status (msg, SOUP_STATUS_CREATED, NULL);
 }
 
 static SoupServer *
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index 63c9e711..163f4f42 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -11,27 +11,34 @@ const char *first_party = "http://127.0.0.1/";;
 const char *third_party = "http://localhost/";;
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeaders *request_headers;
+
+       response_headers = soup_server_message_get_response_headers (msg);
+       request_headers = soup_server_message_get_request_headers (msg);
        if (g_str_equal (path, "/index.html")) {
-               soup_message_headers_replace (msg->response_headers,
+               soup_message_headers_replace (response_headers,
                                              "Set-Cookie",
                                              "foo=bar");
        } else if (g_str_equal (path, "/foo.jpg")) {
-               soup_message_headers_replace (msg->response_headers,
+               soup_message_headers_replace (response_headers,
                                              "Set-Cookie",
                                              "baz=qux");
-       } else if (soup_message_headers_get_one (msg->request_headers,
+       } else if (soup_message_headers_get_one (request_headers,
                                                 "Echo-Set-Cookie")) {
-               soup_message_headers_replace (msg->response_headers,
+               soup_message_headers_replace (response_headers,
                                              "Set-Cookie",
-                                             soup_message_headers_get_one (msg->request_headers,
+                                             soup_message_headers_get_one (request_headers,
                                                                            "Echo-Set-Cookie"));
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 typedef struct {
diff --git a/tests/forms-test.c b/tests/forms-test.c
index 1a07fa00..9d069a4c 100644
--- a/tests/forms-test.c
+++ b/tests/forms-test.c
@@ -250,16 +250,20 @@ do_form_decode_test (void)
 }
 
 static void
-hello_callback (SoupServer *server, SoupMessage *msg,
-               const char *path, GHashTable *query,
-               SoupClientContext *context, gpointer data)
+hello_callback (SoupServer        *server,
+               SoupServerMessage *msg,
+               const char        *path,
+               GHashTable        *query,
+               gpointer           data)
 {
        char *title, *name, *fmt;
        const char *content_type;
        GString *buf;
+       const char *method;
 
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       method = soup_server_message_get_method (msg);
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_HEAD) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
@@ -296,17 +300,19 @@ hello_callback (SoupServer *server, SoupMessage *msg,
                }
        }
 
-       soup_message_set_response (msg, content_type,
-                                  SOUP_MEMORY_TAKE,
-                                  buf->str, buf->len);
+       soup_server_message_set_response (msg, content_type,
+                                         SOUP_MEMORY_TAKE,
+                                         buf->str, buf->len);
        g_string_free (buf, FALSE);
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static void
-md5_get_callback (SoupServer *server, SoupMessage *msg,
-                 const char *path, GHashTable *query,
-                 SoupClientContext *context, gpointer data)
+md5_get_callback (SoupServer        *server,
+                 SoupServerMessage *msg,
+                 const char        *path,
+                 GHashTable        *query,
+                 gpointer           data)
 {
        const char *file = NULL, *md5sum = NULL, *fmt;
        const char *content_type;
@@ -340,17 +346,19 @@ md5_get_callback (SoupServer *server, SoupMessage *msg,
                        g_string_append_printf (buf, "%s", md5sum);
        }
 
-       soup_message_set_response (msg, content_type,
-                                  SOUP_MEMORY_TAKE,
-                                  buf->str, buf->len);
+       soup_server_message_set_response (msg, content_type,
+                                         SOUP_MEMORY_TAKE,
+                                         buf->str, buf->len);
        g_string_free (buf, FALSE);
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static void
-md5_post_callback (SoupServer *server, SoupMessage *msg,
-                  const char *path, GHashTable *query,
-                  SoupClientContext *context, gpointer data)
+md5_post_callback (SoupServer        *server,
+                  SoupServerMessage *msg,
+                  const char        *path,
+                  GHashTable        *query,
+                  gpointer           data)
 {
        const char *content_type;
        GHashTable *params;
@@ -358,17 +366,23 @@ md5_post_callback (SoupServer *server, SoupMessage *msg,
        char *filename, *md5sum, *redirect_uri;
        GBytes *file;
        SoupURI *uri;
+       SoupMultipart *multipart;
+       GBytes *body;
+       SoupMessageHeaders *request_headers;
 
-       content_type = soup_message_headers_get_content_type (msg->request_headers, NULL);
+       request_headers = soup_server_message_get_request_headers (msg);
+       content_type = soup_message_headers_get_content_type (request_headers, NULL);
        if (!content_type || strcmp (content_type, "multipart/form-data") != 0) {
-               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+               soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                return;
        }
 
-       params = soup_form_decode_multipart (msg, "file",
-                                            &filename, NULL, &file);
+       body = soup_message_body_flatten (soup_server_message_get_request_body (msg));
+       multipart = soup_multipart_new_from_message (request_headers, body);
+       g_bytes_unref (body);
+       params = multipart ? soup_form_decode_multipart (multipart, "file", &filename, NULL, &file) : NULL;
        if (!params) {
-               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+               soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                return;
        }
        fmt = g_hash_table_lookup (params, "fmt");
@@ -376,7 +390,7 @@ md5_post_callback (SoupServer *server, SoupMessage *msg,
        md5sum = g_compute_checksum_for_bytes (G_CHECKSUM_MD5, file);
        g_bytes_unref (file);
 
-       uri = soup_uri_copy (soup_message_get_uri (msg));
+       uri = soup_uri_copy (soup_server_message_get_uri (msg));
        soup_uri_set_query_from_fields (uri,
                                        "file", filename ? filename : "",
                                        "md5sum", md5sum,
@@ -384,7 +398,7 @@ md5_post_callback (SoupServer *server, SoupMessage *msg,
                                        NULL);
        redirect_uri = soup_uri_to_string (uri, FALSE);
 
-       soup_message_set_redirect (msg, SOUP_STATUS_SEE_OTHER, redirect_uri);
+       soup_server_message_set_redirect (msg, SOUP_STATUS_SEE_OTHER, redirect_uri);
 
        g_free (redirect_uri);
        soup_uri_free (uri);
@@ -394,16 +408,22 @@ md5_post_callback (SoupServer *server, SoupMessage *msg,
 }
 
 static void
-md5_callback (SoupServer *server, SoupMessage *msg,
-             const char *path, GHashTable *query,
-             SoupClientContext *context, gpointer data)
+md5_callback (SoupServer        *server,
+             SoupServerMessage *msg,
+             const char        *path,
+             GHashTable        *query,
+             gpointer           data)
 {
-       if (msg->method == SOUP_METHOD_GET || msg->method == SOUP_METHOD_HEAD)
-               md5_get_callback (server, msg, path, query, context, data);
-       else if (msg->method == SOUP_METHOD_POST)
-               md5_post_callback (server, msg, path, query, context, data);
+       const char *method;
+
+       method = soup_server_message_get_method (msg);
+
+       if (method == SOUP_METHOD_GET || method == SOUP_METHOD_HEAD)
+               md5_get_callback (server, msg, path, query, data);
+       else if (method == SOUP_METHOD_POST)
+               md5_post_callback (server, msg, path, query, data);
        else
-               soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+               soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL);
 }
 
 static gboolean run_tests = TRUE;
diff --git a/tests/hsts-db-test.c b/tests/hsts-db-test.c
index f45e355d..2adc05b7 100644
--- a/tests/hsts-db-test.c
+++ b/tests/hsts-db-test.c
@@ -13,42 +13,47 @@ SoupURI *https_uri;
    test the Soup HSTS feature.
  */
 static void
-server_callback  (SoupServer *server, SoupMessage *msg,
-                 const char *path, GHashTable *query,
-                 SoupClientContext *context, gpointer data)
+server_callback  (SoupServer        *server,
+                  SoupServerMessage *msg,
+                 const char        *path,
+                  GHashTable        *query,
+                 gpointer           data)
 {
+        SoupMessageHeaders *response_headers;
        const char *server_protocol = data;
 
+        response_headers = soup_server_message_get_response_headers (msg);
+
        if (strcmp (server_protocol, "http") == 0) {
                char *uri_string;
                SoupURI *uri = soup_uri_new ("https://localhost";);
                soup_uri_set_path (uri, path);
                uri_string = soup_uri_to_string (uri, FALSE);
                fprintf (stderr, "server is redirecting to HTTPS\n");
-               soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, uri_string);
+               soup_server_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, uri_string);
                soup_uri_free (uri);
                g_free (uri_string);
        } else if (strcmp (server_protocol, "https") == 0) {
-               soup_message_set_status (msg, SOUP_STATUS_OK);
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
                if (strcmp (path, "/long-lasting") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000");
                } else if (strcmp (path, "/two-seconds") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=2");
                } else if (strcmp (path, "/delete") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=0");
                } else if (strcmp (path, "/subdomains") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000; includeSubDomains");
                }
                 else if (strcmp (path, "/very-long-lasting") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=631138519");
                }
diff --git a/tests/hsts-test.c b/tests/hsts-test.c
index e8c7d877..9bb36edf 100644
--- a/tests/hsts-test.c
+++ b/tests/hsts-test.c
@@ -13,88 +13,93 @@ SoupURI *https_uri;
    test the Soup HSTS feature.
  */
 static void
-server_callback  (SoupServer *server, SoupMessage *msg,
-                 const char *path, GHashTable *query,
-                 SoupClientContext *context, gpointer data)
+server_callback  (SoupServer        *server,
+                 SoupServerMessage *msg,
+                 const char        *path,
+                 GHashTable        *query,
+                 gpointer           data)
 {
+       SoupMessageHeaders *response_headers;
        const char *server_protocol = data;
 
+       response_headers = soup_server_message_get_response_headers (msg);
+
        if (strcmp (server_protocol, "http") == 0) {
                if (strcmp (path, "/insecure") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000");
-                       soup_message_set_status (msg, SOUP_STATUS_OK);
+                       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
                } else {
                        char *uri_string;
                        SoupURI *uri = soup_uri_new ("https://localhost";);
                        soup_uri_set_path (uri, path);
                        uri_string = soup_uri_to_string (uri, FALSE);
-                       soup_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, uri_string);
+                       soup_server_message_set_redirect (msg, SOUP_STATUS_MOVED_PERMANENTLY, uri_string);
                        soup_uri_free (uri);
                        g_free (uri_string);
                }
        } else if (strcmp (server_protocol, "https") == 0) {
-               soup_message_set_status (msg, SOUP_STATUS_OK);
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
                if (strcmp (path, "/long-lasting") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000");
                } else if (strcmp (path, "/two-seconds") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=2");
                } else if (strcmp (path, "/three-seconds") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=3");
                } else if (strcmp (path, "/delete") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=0");
                } else if (strcmp (path, "/subdomains") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000; includeSubDomains");
                } else if (strcmp (path, "/no-sts-header") == 0) {
                        /* Do not add anything */
                } else if (strcmp (path, "/multiple-headers") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=31536000; includeSubDomains");
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=1; includeSubDomains");
                } else if (strcmp (path, "/missing-values") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "");
                } else if (strcmp (path, "/invalid-values") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=foo");
                } else if (strcmp (path, "/extra-values-0") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=3600; foo");
                } else if (strcmp (path, "/extra-values-1") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     " max-age=3600; includeDomains; foo");
                } else if (strcmp (path, "/duplicated-directives") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=3600; includeDomains; includeDomains");
                } else if (strcmp (path, "/case-insensitive-header") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "STRICT-TRANSPORT-SECURITY",
                                                     "max-age=3600");
                } else if (strcmp (path, "/case-insensitive-directives") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "MAX-AGE=3600; includesubdomains");
                } else if (strcmp (path, "/optional-quotations") == 0) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Strict-Transport-Security",
                                                     "max-age=\"31536000\"");
                }
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 759c2abf..beb586ba 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -27,29 +27,37 @@ timeout_finish_message (gpointer msg)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
-{
-       SoupURI *uri = soup_message_get_uri (msg);
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
+{
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       const char *method = soup_server_message_get_method (msg);
+       SoupURI *uri = soup_server_message_get_uri (msg);
        const char *server_protocol = data;
 
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
        if (!strcmp (path, "/redirect")) {
-               soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
+               soup_server_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
                return;
        }
 
+       request_headers = soup_server_message_get_request_headers (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+
        if (!strcmp (path, "/alias-redirect")) {
                SoupURI *redirect_uri;
                char *redirect_string;
                const char *redirect_protocol;
 
-               redirect_protocol = soup_message_headers_get_one (msg->request_headers, 
"X-Redirect-Protocol");
+               redirect_protocol = soup_message_headers_get_one (request_headers, "X-Redirect-Protocol");
 
                redirect_uri = soup_uri_copy (uri);
                soup_uri_set_scheme (redirect_uri, "foo");
@@ -60,13 +68,13 @@ server_callback (SoupServer *server, SoupMessage *msg,
                soup_uri_set_path (redirect_uri, "/alias-redirected");
                redirect_string = soup_uri_to_string (redirect_uri, FALSE);
 
-               soup_message_set_redirect (msg, SOUP_STATUS_FOUND, redirect_string);
+               soup_server_message_set_redirect (msg, SOUP_STATUS_FOUND, redirect_string);
                g_free (redirect_string);
                soup_uri_free (redirect_uri);
                return;
        } else if (!strcmp (path, "/alias-redirected")) {
-               soup_message_set_status (msg, SOUP_STATUS_OK);
-               soup_message_headers_append (msg->response_headers,
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+               soup_message_headers_append (response_headers,
                                             "X-Redirected-Protocol",
                                             server_protocol);
                return;
@@ -79,14 +87,14 @@ server_callback (SoupServer *server, SoupMessage *msg,
                                  1000, timeout_finish_message, msg);
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
        if (!strcmp (uri->host, "foo")) {
-               soup_message_set_response (msg, "text/plain",
-                                          SOUP_MEMORY_STATIC, "foo-index", 9);
+               soup_server_message_set_response (msg, "text/plain",
+                                                 SOUP_MEMORY_STATIC, "foo-index", 9);
                return;
        } else {
-               soup_message_set_response (msg, "text/plain",
-                                          SOUP_MEMORY_STATIC, "index", 5);
+               soup_server_message_set_response (msg, "text/plain",
+                                                 SOUP_MEMORY_STATIC, "index", 5);
                return;
        }
 }
diff --git a/tests/multipart-test.c b/tests/multipart-test.c
index c0c09dc8..d9a00ae1 100644
--- a/tests/multipart-test.c
+++ b/tests/multipart-test.c
@@ -58,26 +58,33 @@ const char *payload = \
         "\r\n--cut-here--";
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       if (msg->method != SOUP_METHOD_GET) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *response_body;
+
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 
-       soup_message_headers_append (msg->response_headers,
+       response_headers = soup_server_message_get_response_headers (msg);
+       soup_message_headers_append (response_headers,
                                     "Content-Type", "multipart/x-mixed-replace; boundary=cut-here");
 
-       soup_message_body_append (msg->response_body,
+       response_body = soup_server_message_get_response_body (msg);
+       soup_message_body_append (response_body,
                                  SOUP_MEMORY_STATIC,
                                  payload,
                                  strlen (payload));
 
-       soup_message_body_complete (msg->response_body);
+       soup_message_body_complete (response_body);
 }
 
 static void
diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c
index b92dd2c2..02bec973 100644
--- a/tests/no-ssl-test.c
+++ b/tests/no-ssl-test.c
@@ -38,16 +38,15 @@ do_ssl_tests (gconstpointer data)
 
 static void
 server_handler (SoupServer        *server,
-               SoupMessage       *msg,
+               SoupServerMessage *msg,
                const char        *path,
                GHashTable        *query,
-               SoupClientContext *client,
                gpointer           user_data)
 {
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "ok\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "ok\r\n", 4);
 }
 
 int
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index 0a980f9b..c3d69c9b 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -55,19 +55,22 @@ clear_state (gpointer connections, GObject *ex_connection)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *client, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        TestServer *ts = data;
        GSocket *socket;
        const char *auth;
+       SoupMessageHeaders *request_headers;
        NTLMServerState state, required_user = 0;
        gboolean auth_required, not_found = FALSE;
        gboolean basic_allowed = TRUE, ntlm_allowed = TRUE;
 
-       if (msg->method != SOUP_METHOD_GET) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
@@ -86,10 +89,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
        if (strstr (path, "/404"))
                not_found = TRUE;
 
-       socket = soup_client_context_get_socket (client);
+       socket = soup_server_message_get_socket (msg);
        state = GPOINTER_TO_INT (g_hash_table_lookup (ts->connections, socket));
-       auth = soup_message_headers_get_one (msg->request_headers,
-                                            "Authorization");
+       request_headers = soup_server_message_get_request_headers (msg);
+       auth = soup_message_headers_get_one (request_headers, "Authorization");
 
        if (auth) {
                if (!strncmp (auth, "NTLM ", 5)) {
@@ -126,33 +129,36 @@ server_callback (SoupServer *server, SoupMessage *msg,
                auth_required = FALSE;
 
        if (auth_required) {
-               soup_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED);
+               SoupMessageHeaders *response_headers;
 
+               soup_server_message_set_status (msg, SOUP_STATUS_UNAUTHORIZED, NULL);
+
+               response_headers = soup_server_message_get_response_headers (msg);
                if (basic_allowed && state != NTLM_RECEIVED_REQUEST) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "WWW-Authenticate",
                                                     "Basic realm=\"ntlm-test\"");
                }
 
                if (ntlm_allowed && state == NTLM_RECEIVED_REQUEST) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "WWW-Authenticate",
                                                     ts->ntlmssp ? ("NTLM " NTLMSSP_CHALLENGE) : ts->ntlmv2 ? 
("NTLM " NTLMV2_CHALLENGE) : ("NTLM " NTLMV1_CHALLENGE));
                        state = NTLM_SENT_CHALLENGE;
                } else if (ntlm_allowed) {
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "WWW-Authenticate", "NTLM");
-                       soup_message_headers_append (msg->response_headers,
+                       soup_message_headers_append (response_headers,
                                                     "Connection", "close");
                }
        } else {
                if (not_found)
-                       soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+                       soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                else {
-                       soup_message_set_response (msg, "text/plain",
-                                                  SOUP_MEMORY_STATIC,
-                                                  "OK\r\n", 4);
-                       soup_message_set_status (msg, SOUP_STATUS_OK);
+                       soup_server_message_set_response (msg, "text/plain",
+                                                         SOUP_MEMORY_STATIC,
+                                                         "OK\r\n", 4);
+                       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
                }
        }
 
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index a17671d4..03c79377 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -238,13 +238,15 @@ do_async_proxy_test (gconstpointer data)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       SoupURI *uri = soup_message_get_uri (msg);
+       SoupURI *uri = soup_server_message_get_uri (msg);
 
-       soup_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, uri->fragment ? SOUP_STATUS_BAD_REQUEST : SOUP_STATUS_OK, NULL);
 }
 
 static void
diff --git a/tests/pull-api-test.c b/tests/pull-api-test.c
index f73d8cee..36d44a5e 100644
--- a/tests/pull-api-test.c
+++ b/tests/pull-api-test.c
@@ -52,8 +52,6 @@ typedef struct {
 } FullyAsyncData;
 
 static void fully_async_got_headers (SoupMessage *msg, gpointer user_data);
-static void fully_async_got_chunk   (SoupMessage *msg, GBytes *chunk,
-                                    gpointer user_data);
 static gboolean fully_async_request_chunk (gpointer user_data);
 
 static void
@@ -99,12 +97,6 @@ do_fully_async_test (SoupSession *session,
        ad.read_so_far = 0;
        ad.expected_status = expected_status;
 
-       /* Since we aren't going to look at the final value of
-        * msg->response_body, we tell libsoup to not even bother
-        * generating it.
-        */
-       soup_message_body_set_accumulate (msg->response_body, FALSE);
-
        /* Connect to "got_headers", from which we'll decide where to
         * go next.
         */
@@ -187,44 +179,10 @@ fully_async_got_headers (SoupMessage *msg, gpointer user_data)
         * until one is requested.
         */
        ad->chunks_ready = TRUE;
-       g_signal_connect (msg, "got_chunk",
-                         G_CALLBACK (fully_async_got_chunk), ad);
        if (!ad->chunk_wanted)
                soup_session_pause_message (ad->session, msg);
 }
 
-static void
-fully_async_got_chunk (SoupMessage *msg, GBytes *chunk, gpointer user_data)
-{
-       FullyAsyncData *ad = user_data;
-
-       debug_printf (2, "  got chunk from %lu - %lu\n",
-                     (unsigned long) ad->read_so_far,
-                     (unsigned long) ad->read_so_far + g_bytes_get_size (chunk));
-
-       /* We've got a chunk, let's process it. In the case of the
-        * test program, that means comparing it against
-        * correct_response to make sure that we got the right data.
-        */
-        gsize chunk_length = g_bytes_get_size (chunk);
-       g_assert_cmpint (ad->read_so_far + chunk_length, <=, g_bytes_get_size (correct_response));
-       soup_assert_cmpmem (g_bytes_get_data (chunk, NULL), chunk_length,
-                           (guchar*)g_bytes_get_data (correct_response, NULL) + ad->read_so_far,
-                           chunk_length);
-       ad->read_so_far += chunk_length;
-
-       /* Now pause I/O, and prepare to read another chunk later.
-        * (Again, the timeout just abstractly represents the idea of
-        * the application requesting another chunk at some random
-        * point in the future. You wouldn't be using a timeout in a
-        * real program.)
-        */
-       soup_session_pause_message (ad->session, msg);
-       ad->chunk_wanted = FALSE;
-
-       ad->timeout = g_timeout_add (10, fully_async_request_chunk, ad);
-}
-
 static void
 do_fast_async_test (gconstpointer data)
 {
diff --git a/tests/range-test.c b/tests/range-test.c
index ce8a744b..bf97f958 100644
--- a/tests/range-test.c
+++ b/tests/range-test.c
@@ -349,15 +349,14 @@ do_apache_range_test (void)
 
 static void
 server_handler (SoupServer        *server,
-               SoupMessage       *msg, 
+               SoupServerMessage *msg,
                const char        *path,
                GHashTable        *query,
-               SoupClientContext *client,
                gpointer           user_data)
 {
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_body_append_bytes (msg->response_body,
-                                        full_response);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_message_body_append_bytes (soup_server_message_get_response_body (msg),
+                                       full_response);
 }
 
 static void
diff --git a/tests/redirect-test.c b/tests/redirect-test.c
index d360065a..9ecc2e2d 100644
--- a/tests/redirect-test.c
+++ b/tests/redirect-test.c
@@ -287,72 +287,82 @@ do_async_req_api_test (gconstpointer test)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        char *remainder;
        guint status_code;
+       SoupMessageHeaders *response_headers;
+       const char *method;
 
        /* Make sure that a HTTP/1.0 redirect doesn't cause an
         * HTTP/1.0 re-request. (#521848)
         */
-       if (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) {
-               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+       if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) {
+               soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                return;
        }
 
+       method = soup_server_message_get_method (msg);
+       response_headers = soup_server_message_get_response_headers (msg);
+
        if (g_str_has_prefix (path, "/bad")) {
                if (!strcmp (path, "/bad")) {
-                       soup_message_set_status (msg, SOUP_STATUS_FOUND);
-                       soup_message_headers_replace (msg->response_headers,
+                       soup_server_message_set_status (msg, SOUP_STATUS_FOUND, NULL);
+                       soup_message_headers_replace (response_headers,
                                                      "Location",
                                                      "/bad with spaces");
                } else if (!strcmp (path, "/bad-recursive")) {
-                       soup_message_set_status (msg, SOUP_STATUS_FOUND);
-                       soup_message_headers_replace (msg->response_headers,
+                       soup_server_message_set_status (msg, SOUP_STATUS_FOUND, NULL);
+                       soup_message_headers_replace (response_headers,
                                                      "Location",
                                                      "/bad-recursive");
                } else if (!strcmp (path, "/bad-no-host")) {
-                       soup_message_set_status (msg, SOUP_STATUS_FOUND);
-                       soup_message_headers_replace (msg->response_headers,
+                       soup_server_message_set_status (msg, SOUP_STATUS_FOUND, NULL);
+                       soup_message_headers_replace (response_headers,
                                                      "Location",
                                                      "about:blank");
                } else if (!strcmp (path, "/bad with spaces"))
-                       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_NOT_FOUND);
+                       soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                return;
        } else if (!strcmp (path, "/server2")) {
-               soup_message_set_status (msg, SOUP_STATUS_FOUND);
-               soup_message_headers_replace (msg->response_headers,
+               soup_server_message_set_status (msg, SOUP_STATUS_FOUND, NULL);
+               soup_message_headers_replace (response_headers,
                                              "Location",
                                              server2_uri);
                return;
        } else if (!strcmp (path, "/")) {
-               if (msg->method != SOUP_METHOD_GET &&
-                   msg->method != SOUP_METHOD_HEAD) {
-                       soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+               SoupMessageBody *request_body;
+
+               if (method != SOUP_METHOD_GET &&
+                   method != SOUP_METHOD_HEAD) {
+                       soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL);
                        return;
                }
 
                /* Make sure that redirecting a POST clears the body */
-               if (msg->request_body->length) {
-                       soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+               request_body = soup_server_message_get_request_body (msg);
+               if (request_body->length) {
+                       soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
                        return;
                }
 
-               soup_message_set_status (msg, SOUP_STATUS_OK);
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 
                /* FIXME: this is wrong, though it doesn't matter for
                 * the purposes of this test, and to do the right
                 * thing currently we'd have to set Content-Length by
                 * hand.
                 */
-               if (msg->method != SOUP_METHOD_HEAD) {
-                       soup_message_set_response (msg, "text/plain",
-                                                  SOUP_MEMORY_STATIC,
-                                                  "OK\r\n", 4);
+               if (method != SOUP_METHOD_HEAD) {
+                       soup_server_message_set_response (msg, "text/plain",
+                                                         SOUP_MEMORY_STATIC,
+                                                         "OK\r\n", 4);
                }
                return;
        }
@@ -360,7 +370,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
        status_code = strtoul (path + 1, &remainder, 10);
        if (!SOUP_STATUS_IS_REDIRECTION (status_code) ||
            (*remainder && *remainder != '/')) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                return;
        }
 
@@ -369,18 +379,20 @@ server_callback (SoupServer *server, SoupMessage *msg,
         * the rest of the time.
         */
        if (*remainder == '/')
-               soup_message_set_http_version (msg, SOUP_HTTP_1_0);
+               soup_server_message_set_http_version (msg, SOUP_HTTP_1_0);
 
-       soup_message_set_redirect (msg, status_code,
-                                  *remainder ? remainder : "/");
+       soup_server_message_set_redirect (msg, status_code,
+                                         *remainder ? remainder : "/");
 }
 
 static void
-server2_callback (SoupServer *server, SoupMessage *msg,
-                 const char *path, GHashTable *query,
-                 SoupClientContext *context, gpointer data)
+server2_callback (SoupServer        *server,
+                 SoupServerMessage *msg,
+                 const char        *path,
+                 GHashTable        *query,
+                 gpointer           data)
 {
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 int
diff --git a/tests/request-body-test.c b/tests/request-body-test.c
index fb5cd31d..fad90c7f 100644
--- a/tests/request-body-test.c
+++ b/tests/request-body-test.c
@@ -138,30 +138,32 @@ do_request_test (gconstpointer data)
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                 const char *path, GHashTable *query,
-                 SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                 const char        *path,
+                GHashTable        *query,
+                 gpointer           data)
 {
         SoupMessageBody *md5_body;
         char *md5;
 
         if (g_str_has_prefix (path, "/redirect")) {
-                soup_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
+                soup_server_message_set_redirect (msg, SOUP_STATUS_FOUND, "/");
                 return;
         }
 
-        if (msg->method == SOUP_METHOD_PUT) {
-                soup_message_set_status (msg, SOUP_STATUS_CREATED);
-                md5_body = msg->request_body;
+        if (soup_server_message_get_method (msg) == SOUP_METHOD_PUT) {
+                soup_server_message_set_status (msg, SOUP_STATUS_CREATED, NULL);
+                md5_body = soup_server_message_get_request_body (msg);
         } else {
-                soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+                soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL);
                 return;
         }
 
         md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
                                            (guchar *)md5_body->data,
                                            md5_body->length);
-        soup_message_headers_append (msg->response_headers,
+        soup_message_headers_append (soup_server_message_get_response_headers (msg),
                                      "Content-MD5", md5);
         g_free (md5);
 }
diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c
index dd92f1a6..54910967 100644
--- a/tests/server-auth-test.c
+++ b/tests/server-auth-test.c
@@ -227,15 +227,20 @@ do_server_auth_test (gconstpointer data)
 }
 
 static gboolean
-basic_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
-                    const char *username, const char *password, gpointer data)
+basic_auth_callback (SoupAuthDomain    *auth_domain,
+                    SoupServerMessage *msg,
+                    const char        *username,
+                    const char        *password,
+                    gpointer           data)
 {
        return !strcmp (username, "user") && !strcmp (password, "password");
 }
 
 static char *
-digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
-                     const char *username, gpointer data)
+digest_auth_callback (SoupAuthDomain    *auth_domain,
+                     SoupServerMessage *msg,
+                     const char        *username,
+                     gpointer           data)
 {
        if (strcmp (username, "user") != 0)
                return NULL;
@@ -251,27 +256,33 @@ digest_auth_callback (SoupAuthDomain *auth_domain, SoupMessage *msg,
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_HEAD) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       const char *method;
+
+       method = soup_server_message_get_method (msg);
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_HEAD) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "OK\r\n", 4);
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "OK\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static void
-got_headers_callback (SoupMessage *msg, gpointer data)
+got_headers_callback (SoupServerMessage *msg,
+                     gpointer           data)
 {
        const char *header;
 
-       header = soup_message_headers_get_one (msg->request_headers,
+       header = soup_message_headers_get_one (soup_server_message_get_request_headers (msg),
                                               "Authorization");
        if (header) {
                if (strstr (header, "Basic "))
@@ -282,11 +293,12 @@ got_headers_callback (SoupMessage *msg, gpointer data)
 }
 
 static void
-wrote_headers_callback (SoupMessage *msg, gpointer data)
+wrote_headers_callback (SoupServerMessage *msg,
+                       gpointer           data)
 {
        const char *header;
 
-       header = soup_message_headers_get_list (msg->response_headers,
+       header = soup_message_headers_get_list (soup_server_message_get_response_headers (msg),
                                                "WWW-Authenticate");
        if (header) {
                if (strstr (header, "Basic "))
@@ -297,12 +309,13 @@ wrote_headers_callback (SoupMessage *msg, gpointer data)
 }
 
 static void
-request_started_callback (SoupServer *server, SoupMessage *msg,
-                         SoupClientContext *client, gpointer data)
+request_started_callback (SoupServer        *server,
+                         SoupServerMessage *msg,
+                         gpointer           data)
 {
-       g_signal_connect (msg, "got_headers",
+       g_signal_connect (msg, "got-headers",
                          G_CALLBACK (got_headers_callback), NULL);
-       g_signal_connect (msg, "wrote_headers",
+       g_signal_connect (msg, "wrote-headers",
                          G_CALLBACK (wrote_headers_callback), NULL);
 }
 
diff --git a/tests/server-test.c b/tests/server-test.c
index 3c5a1cab..8b18f8a7 100644
--- a/tests/server-test.c
+++ b/tests/server-test.c
@@ -14,27 +14,32 @@ typedef struct {
 } ServerData;
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
-       soup_message_headers_append (msg->response_headers,
+       const char *method;
+
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
                                     "X-Handled-By", "server_callback");
 
        if (!strcmp (path, "*")) {
                soup_test_assert (FALSE, "default server_callback got request for '*'");
-               soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+               soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL);
                return;
        }
 
-       if (msg->method != SOUP_METHOD_GET && msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       method = soup_server_message_get_method (msg);
+       if (method != SOUP_METHOD_GET && method != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC, "index", 5);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC, "index", 5);
 }
 
 static void
@@ -90,25 +95,27 @@ server_teardown (ServerData *sd, gconstpointer test_data)
 }
 
 static void
-server_star_callback (SoupServer *server, SoupMessage *msg,
-                     const char *path, GHashTable *query,
-                     SoupClientContext *context, gpointer data)
+server_star_callback (SoupServer        *server,
+                     SoupServerMessage *msg,
+                     const char        *path,
+                     GHashTable        *query,
+                     gpointer           data)
 {
-       soup_message_headers_append (msg->response_headers,
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
                                     "X-Handled-By", "star_callback");
 
        if (strcmp (path, "*") != 0) {
                soup_test_assert (FALSE, "server_star_callback got request for '%s'", path);
-               soup_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR);
+               soup_server_message_set_status (msg, SOUP_STATUS_INTERNAL_SERVER_ERROR, NULL);
                return;
        }
 
-       if (msg->method != SOUP_METHOD_OPTIONS) {
-               soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_OPTIONS) {
+               soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL);
                return;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 /* Server handlers for "*" work but are separate from handlers for
@@ -345,26 +352,28 @@ do_dot_dot_test (ServerData *sd, gconstpointer test_data)
 }
 
 static void
-ipv6_server_callback (SoupServer *server, SoupMessage *msg,
-                     const char *path, GHashTable *query,
-                     SoupClientContext *context, gpointer data)
+ipv6_server_callback (SoupServer        *server,
+                     SoupServerMessage *msg,
+                     const char        *path,
+                     GHashTable        *query,
+                     gpointer           data)
 {
        const char *host;
        GSocketAddress *addr;
        char expected_host[128];
 
-       addr = soup_client_context_get_local_address (context);
+       addr = soup_server_message_get_local_address (msg);
        g_snprintf (expected_host, sizeof (expected_host),
                    "[::1]:%d",
                    g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)));
 
-       host = soup_message_headers_get_one (msg->request_headers, "Host");
+       host = soup_message_headers_get_one (soup_server_message_get_request_headers (msg), "Host");
        g_assert_cmpstr (host, ==, expected_host);
 
        if (g_test_failed ())
-               soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
+               soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
        else
-               soup_message_set_status (msg, SOUP_STATUS_OK);
+               soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static void
@@ -413,19 +422,21 @@ do_ipv6_test (ServerData *sd, gconstpointer test_data)
 }
 
 static void
-multi_server_callback (SoupServer *server, SoupMessage *msg,
-                      const char *path, GHashTable *query,
-                      SoupClientContext *context, gpointer data)
+multi_server_callback (SoupServer        *server,
+                      SoupServerMessage *msg,
+                      const char        *path,
+                      GHashTable        *query,
+                      gpointer           data)
 {
        GSocketAddress *addr;
        GInetSocketAddress *iaddr;
        SoupURI *uri;
        char *uristr, *addrstr;
 
-       addr = soup_client_context_get_local_address (context);
+       addr = soup_server_message_get_local_address (msg);
        iaddr = G_INET_SOCKET_ADDRESS (addr);
 
-       uri = soup_message_get_uri (msg);
+       uri = soup_server_message_get_uri (msg);
        uristr = soup_uri_to_string (uri, FALSE);
 
        addrstr = g_inet_address_to_string (g_inet_socket_address_get_address (iaddr));
@@ -436,9 +447,9 @@ multi_server_callback (SoupServer *server, SoupMessage *msg,
 
        /* FIXME ssl */
 
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_TAKE, uristr, strlen (uristr));
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_TAKE, uristr, strlen (uristr));
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 }
 
 static void
@@ -794,27 +805,29 @@ g_test_io_stream_new (GInputStream *input, GOutputStream *output)
 }
 
 static void
-mem_server_callback (SoupServer *server, SoupMessage *msg,
-                    const char *path, GHashTable *query,
-                    SoupClientContext *context, gpointer data)
+mem_server_callback (SoupServer        *server,
+                    SoupServerMessage *msg,
+                    const char        *path,
+                    GHashTable        *query,
+                    gpointer           data)
 {
        GSocketAddress *addr;
        GSocket *sock;
        const char *host;
 
-       addr = soup_client_context_get_local_address (context);
+       addr = soup_server_message_get_local_address (msg);
        g_assert_nonnull (addr);
 
-       addr = soup_client_context_get_remote_address (context);
+       addr = soup_server_message_get_remote_address (msg);
        g_assert_nonnull (addr);
 
-       sock = soup_client_context_get_socket (context);
+       sock = soup_server_message_get_socket (msg);
        g_assert_null (sock);
 
-       host = soup_client_context_get_host (context);
+       host = soup_server_message_get_remote_host (msg);
        g_assert_cmpstr (host, ==, "127.0.0.1");
 
-       server_callback (server, msg, path, query, context, data);
+       server_callback (server, msg, path, query, data);
 }
 
 static void
@@ -858,7 +871,7 @@ do_iostream_accept_test (void)
 
 typedef struct {
        SoupServer *server;
-       SoupMessage *smsg;
+       SoupServerMessage *smsg;
        gboolean handler_called;
        gboolean paused;
 } UnhandledServerData;
@@ -873,15 +886,17 @@ idle_unpause_message (gpointer user_data)
 }
 
 static void
-unhandled_server_callback (SoupServer *server, SoupMessage *msg,
-                          const char *path, GHashTable *query,
-                          SoupClientContext *context, gpointer data)
+unhandled_server_callback (SoupServer        *server,
+                          SoupServerMessage *msg,
+                          const char        *path,
+                          GHashTable        *query,
+                          gpointer           data)
 {
        UnhandledServerData *usd = data;
 
        usd->handler_called = TRUE;
 
-       if (soup_message_headers_get_one (msg->request_headers, "X-Test-Server-Pause")) {
+       if (soup_message_headers_get_one (soup_server_message_get_request_headers (msg), 
"X-Test-Server-Pause")) {
                usd->paused = TRUE;
                usd->server = server;
                usd->smsg = msg;
@@ -946,7 +961,9 @@ do_fail_500_test (ServerData *sd, gconstpointer pause)
 }
 
 static void
-stream_got_chunk (SoupMessage *msg, GBytes *chunk, gpointer user_data)
+stream_got_chunk (SoupServerMessage *msg,
+                 GBytes            *chunk,
+                 gpointer           user_data)
 {
        GChecksum *checksum = user_data;
 
@@ -954,26 +971,29 @@ stream_got_chunk (SoupMessage *msg, GBytes *chunk, gpointer user_data)
 }
 
 static void
-stream_got_body (SoupMessage *msg, gpointer user_data)
+stream_got_body (SoupServerMessage *msg,
+                gpointer           user_data)
 {
        GChecksum *checksum = user_data;
        const char *md5 = g_checksum_get_string (checksum);
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY,
-                                  md5, strlen (md5));
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain", SOUP_MEMORY_COPY,
+                                         md5, strlen (md5));
        g_checksum_free (checksum);
 }
 
 static void
-early_stream_callback (SoupServer *server, SoupMessage *msg,
-                      const char *path, GHashTable *query,
-                      SoupClientContext *context, gpointer data)
+early_stream_callback (SoupServer        *server,
+                      SoupServerMessage *msg,
+                      const char        *path,
+                      GHashTable        *query,
+                      gpointer           data)
 {
        GChecksum *checksum;
 
-       if (msg->method != SOUP_METHOD_POST) {
-               soup_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_POST) {
+               soup_server_message_set_status (msg, SOUP_STATUS_METHOD_NOT_ALLOWED, NULL);
                return;
        }
 
@@ -983,7 +1003,7 @@ early_stream_callback (SoupServer *server, SoupMessage *msg,
        g_signal_connect (msg, "got-body",
                          G_CALLBACK (stream_got_body), checksum);
 
-       soup_message_body_set_accumulate (msg->request_body, TRUE);
+       soup_message_body_set_accumulate (soup_server_message_get_request_body (msg), TRUE);
 }
 
 static void
@@ -1016,12 +1036,14 @@ do_early_stream_test (ServerData *sd, gconstpointer test_data)
 }
 
 static void
-early_respond_callback (SoupServer *server, SoupMessage *msg,
-                       const char *path, GHashTable *query,
-                       SoupClientContext *context, gpointer data)
+early_respond_callback (SoupServer        *server,
+                       SoupServerMessage *msg,
+                       const char        *path,
+                       GHashTable        *query,
+                       gpointer           data)
 {
        if (!strcmp (path, "/"))
-               soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
+               soup_server_message_set_status (msg, SOUP_STATUS_FORBIDDEN, NULL);
 }
 
 static void
@@ -1040,7 +1062,6 @@ do_early_respond_test (ServerData *sd, gconstpointer test_data)
        msg = soup_message_new_from_uri ("GET", sd->base_uri);
        soup_test_session_send_message (session, msg);
        soup_test_assert_message_status (msg, SOUP_STATUS_FORBIDDEN);
-       g_assert_cmpint (msg->response_body->length, ==, 0);
        g_object_unref (msg);
 
        /* The early handler will ignore this one */
@@ -1057,11 +1078,13 @@ do_early_respond_test (ServerData *sd, gconstpointer test_data)
 }
 
 static void
-early_multi_callback (SoupServer *server, SoupMessage *msg,
-                     const char *path, GHashTable *query,
-                     SoupClientContext *context, gpointer data)
+early_multi_callback (SoupServer        *server,
+                     SoupServerMessage *msg,
+                     const char        *path,
+                     GHashTable        *query,
+                     gpointer           data)
 {
-       soup_message_headers_append (msg->response_headers, "X-Early", "yes");
+       soup_message_headers_append (soup_server_message_get_response_headers (msg), "X-Early", "yes");
 }
 
 static void
@@ -1147,8 +1170,7 @@ typedef struct {
 
 typedef struct {
        SoupServer *self;
-       SoupMessage *msg;
-       SoupClientContext *context;
+       SoupServerMessage *msg;
        GCancellable *cancellable;
 
        TunnelEnd client, server;
@@ -1272,11 +1294,12 @@ tunnel_read_cb (GObject      *object,
 }
 
 static void
-start_tunnel (SoupMessage *msg, gpointer user_data)
+start_tunnel (SoupServerMessage *msg,
+             gpointer           user_data)
 {
        Tunnel *tunnel = user_data;
 
-       tunnel->client.iostream = soup_client_context_steal_connection (tunnel->context);
+       tunnel->client.iostream = soup_server_message_steal_connection (msg);
        tunnel->client.istream = g_io_stream_get_input_stream (tunnel->client.iostream);
        tunnel->client.ostream = g_io_stream_get_output_stream (tunnel->client.iostream);
        g_clear_object (&tunnel->self);
@@ -1309,10 +1332,10 @@ tunnel_connected_cb (GObject      *object,
        tunnel->server.iostream = (GIOStream *)
                g_socket_client_connect_to_host_finish (G_SOCKET_CLIENT (object), result, &error);
        if (!tunnel->server.iostream) {
-               soup_message_set_status (tunnel->msg, SOUP_STATUS_BAD_GATEWAY);
-               soup_message_set_response (tunnel->msg, "text/plain",
-                                          SOUP_MEMORY_COPY,
-                                          error->message, strlen (error->message));
+               soup_server_message_set_status (tunnel->msg, SOUP_STATUS_BAD_GATEWAY, NULL);
+               soup_server_message_set_response (tunnel->msg, "text/plain",
+                                                 SOUP_MEMORY_COPY,
+                                                 error->message, strlen (error->message));
                g_error_free (error);
                soup_server_unpause_message (tunnel->self, tunnel->msg);
                tunnel_close (tunnel);
@@ -1322,23 +1345,25 @@ tunnel_connected_cb (GObject      *object,
        tunnel->server.istream = g_io_stream_get_input_stream (tunnel->server.iostream);
        tunnel->server.ostream = g_io_stream_get_output_stream (tunnel->server.iostream);
 
-       soup_message_set_status (tunnel->msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (tunnel->msg, SOUP_STATUS_OK, NULL);
        soup_server_unpause_message (tunnel->self, tunnel->msg);
        g_signal_connect (tunnel->msg, "wrote-body",
                          G_CALLBACK (start_tunnel), tunnel);
 }
 
 static void
-proxy_server_callback (SoupServer *server, SoupMessage *msg,
-                      const char *path, GHashTable *query,
-                      SoupClientContext *context, gpointer data)
+proxy_server_callback (SoupServer        *server,
+                      SoupServerMessage *msg,
+                      const char        *path,
+                      GHashTable        *query,
+                      gpointer           data)
 {
        GSocketClient *sclient;
        SoupURI *dest_uri;
        Tunnel *tunnel;
 
-       if (msg->method != SOUP_METHOD_CONNECT) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_CONNECT) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
@@ -1347,9 +1372,8 @@ proxy_server_callback (SoupServer *server, SoupMessage *msg,
        tunnel = g_new0 (Tunnel, 1);
        tunnel->self = g_object_ref (server);
        tunnel->msg = g_object_ref (msg);
-       tunnel->context = context;
 
-       dest_uri = soup_message_get_uri (msg);
+       dest_uri = soup_server_message_get_uri (msg);
        sclient = g_socket_client_new ();
        g_socket_client_connect_to_host_async (sclient, dest_uri->host, dest_uri->port,
                                               NULL, tunnel_connected_cb, tunnel);
diff --git a/tests/session-test.c b/tests/session-test.c
index a3952f7d..be85d669 100644
--- a/tests/session-test.c
+++ b/tests/session-test.c
@@ -18,10 +18,9 @@ timeout_cb (gpointer user_data)
 
 static void
 server_handler (SoupServer        *server,
-               SoupMessage       *msg, 
+               SoupServerMessage *msg,
                const char        *path,
                GHashTable        *query,
-               SoupClientContext *client,
                gpointer           user_data)
 {
        if (!strcmp (path, "/request-timeout")) {
@@ -35,10 +34,10 @@ server_handler (SoupServer        *server,
        } else
                server_processed_message = TRUE;
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "ok\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "ok\r\n", 4);
 }
 
 static void
diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c
index 98547a34..e81811b0 100644
--- a/tests/sniffing-test.c
+++ b/tests/sniffing-test.c
@@ -10,27 +10,32 @@ SoupURI *base_uri;
 SoupMessageBody *chunk_data;
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        GError *error = NULL;
        char *query_key;
        GBytes *response = NULL;
        gsize offset;
+       SoupMessageHeaders *response_headers;
+       SoupMessageBody *response_body;
        gboolean empty_response = FALSE;
 
-       if (msg->method != SOUP_METHOD_GET) {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED);
+       if (soup_server_message_get_method (msg) != SOUP_METHOD_GET) {
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_IMPLEMENTED, NULL);
                return;
        }
 
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 
+       response_headers = soup_server_message_get_response_headers (msg);
        if (query) {
                query_key = g_hash_table_lookup (query, "chunked");
                if (query_key && g_str_equal (query_key, "yes")) {
-                       soup_message_headers_set_encoding (msg->response_headers,
+                       soup_message_headers_set_encoding (response_headers,
                                                           SOUP_ENCODING_CHUNKED);
                }
 
@@ -45,7 +50,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                        g_assert_no_error (error);
                }
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "text/plain");
        }
 
@@ -56,10 +61,10 @@ server_callback (SoupServer *server, SoupMessage *msg,
                g_assert_no_error (error);
                g_free (base_name);
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "X-Content-Type-Options", "nosniff");
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "no/sniffing-allowed");
        }
 
@@ -70,7 +75,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                g_assert_no_error (error);
                g_free (base_name);
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "text/plain");
        }
 
@@ -81,7 +86,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                g_assert_no_error (error);
                g_free (base_name);
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "UNKNOWN/unknown");
        }
 
@@ -99,7 +104,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                ptr = g_strrstr (components[2], "_");
                *ptr = '/';
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", components[2]);
                g_strfreev (components);
        }
@@ -111,24 +116,25 @@ server_callback (SoupServer *server, SoupMessage *msg,
                g_assert_no_error (error);
                g_free (base_name);
 
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "text/xml");
-               soup_message_headers_append (msg->response_headers,
+               soup_message_headers_append (response_headers,
                                             "Content-Type", "text/plain");
        }
 
+       response_body = soup_server_message_get_response_body (msg);
        if (response) {
-                gsize response_size = g_bytes_get_size (response);                
+                gsize response_size = g_bytes_get_size (response);
                for (offset = 0; offset < response_size; offset += 500) {
                         GBytes *chunk = g_bytes_new_from_bytes (response, offset, MIN (500, response_size - 
offset));
-                        soup_message_body_append_bytes (msg->response_body, chunk);
+                        soup_message_body_append_bytes (response_body, chunk);
                         g_bytes_unref (chunk);
                }
 
                g_bytes_unref (response);
        }
 
-       soup_message_body_complete (msg->response_body);
+       soup_message_body_complete (response_body);
 }
 
 static gboolean
@@ -229,7 +235,9 @@ do_signals_test (gboolean should_content_sniff,
 
        soup_message_set_uri (msg, uri);
 
+#if 0
        soup_message_body_set_accumulate (msg->response_body, should_accumulate);
+#endif
 
        g_object_connect (msg,
                          "signal::got-headers", got_headers, GINT_TO_POINTER (should_pause),
@@ -258,8 +266,10 @@ do_signals_test (gboolean should_content_sniff,
 
        if (!should_accumulate && chunk_data)
                body = soup_message_body_flatten (chunk_data);
+#if 0
        else if (msg->response_body)
                body = soup_message_body_flatten (msg->response_body);
+#endif
 
        if (body) {
                 //g_message ("|||body (%zu): %s", g_bytes_get_size (body), (char*)g_bytes_get_data (body, 
NULL));
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 1422834a..c314b2d7 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -238,16 +238,15 @@ do_tls_interaction_test (void)
 
 static void
 server_handler (SoupServer        *server,
-               SoupMessage       *msg, 
+               SoupServerMessage *msg,
                const char        *path,
                GHashTable        *query,
-               SoupClientContext *client,
                gpointer           user_data)
 {
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "ok\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "ok\r\n", 4);
 }
 
 int
diff --git a/tests/streaming-test.c b/tests/streaming-test.c
index b1a10d54..a9280cb8 100644
--- a/tests/streaming-test.c
+++ b/tests/streaming-test.c
@@ -11,16 +11,20 @@ GBytes *full_response;
 char *full_response_md5;
 
 static void
-write_next_chunk (SoupMessage *msg, gpointer user_data)
+write_next_chunk (SoupServerMessage *msg,
+                 gpointer           user_data)
 {
        gsize *offset = user_data;
        gsize chunk_length;
+       SoupMessageBody *response_body;
+
+       response_body = soup_server_message_get_response_body (msg);
 
        chunk_length = MIN (RESPONSE_CHUNK_SIZE, g_bytes_get_size (full_response) - *offset);
        if (chunk_length > 0) {
                debug_printf (2, "  writing chunk\n");
                 GBytes *chunk = g_bytes_new_from_bytes (full_response, *offset, chunk_length);
-                soup_message_body_append_bytes (msg->response_body, chunk);
+                soup_message_body_append_bytes (response_body, chunk);
                 g_bytes_unref (chunk);
                *offset += chunk_length;
        } else {
@@ -29,44 +33,49 @@ write_next_chunk (SoupMessage *msg, gpointer user_data)
                 * cases, but it's harmless in the content-length
                 * case.
                 */
-               soup_message_body_complete (msg->response_body);
+               soup_message_body_complete (response_body);
        }
 }
 
 static void
-free_offset (SoupMessage *msg, gpointer offset)
+free_offset (SoupServerMessage *msg,
+            gpointer           offset)
 {
        g_free (offset);
 }
 
 static void
-server_callback (SoupServer *server, SoupMessage *msg,
-                const char *path, GHashTable *query,
-                SoupClientContext *context, gpointer data)
+server_callback (SoupServer        *server,
+                SoupServerMessage *msg,
+                const char        *path,
+                GHashTable        *query,
+                gpointer           data)
 {
        gsize *offset;
+       SoupMessageHeaders *response_headers;
 
+       response_headers = soup_server_message_get_response_headers (msg);
        if (!strcmp (path, "/chunked")) {
-               soup_message_headers_set_encoding (msg->response_headers,
+               soup_message_headers_set_encoding (response_headers,
                                                   SOUP_ENCODING_CHUNKED);
        } else if (!strcmp (path, "/content-length")) {
-               soup_message_headers_set_encoding (msg->response_headers,
+               soup_message_headers_set_encoding (response_headers,
                                                   SOUP_ENCODING_CONTENT_LENGTH);
-               soup_message_headers_set_content_length (msg->response_headers,
+               soup_message_headers_set_content_length (response_headers,
                                                         g_bytes_get_size (full_response));
        } else if (!strcmp (path, "/eof")) {
-               soup_message_headers_set_encoding (msg->response_headers,
+               soup_message_headers_set_encoding (response_headers,
                                                   SOUP_ENCODING_EOF);
        } else {
-               soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+               soup_server_message_set_status (msg, SOUP_STATUS_NOT_FOUND, NULL);
                return;
        }
-       soup_message_set_status (msg, SOUP_STATUS_OK);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
 
        offset = g_new0 (gsize, 1);
-       g_signal_connect (msg, "wrote_headers",
+       g_signal_connect (msg, "wrote-headers",
                          G_CALLBACK (write_next_chunk), offset);
-       g_signal_connect (msg, "wrote_chunk",
+       g_signal_connect (msg, "wrote-chunk",
                          G_CALLBACK (write_next_chunk), offset);
        g_signal_connect (msg, "finished",
                          G_CALLBACK (free_offset), offset);
diff --git a/tests/timeout-test.c b/tests/timeout-test.c
index 17977434..b3325e42 100644
--- a/tests/timeout-test.c
+++ b/tests/timeout-test.c
@@ -287,16 +287,15 @@ timeout_finish_message (gpointer msg)
 
 static void
 server_handler (SoupServer        *server,
-               SoupMessage       *msg, 
+               SoupServerMessage *msg,
                const char        *path,
                GHashTable        *query,
-               SoupClientContext *client,
                gpointer           user_data)
 {
-       soup_message_set_status (msg, SOUP_STATUS_OK);
-       soup_message_set_response (msg, "text/plain",
-                                  SOUP_MEMORY_STATIC,
-                                  "ok\r\n", 4);
+       soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL);
+       soup_server_message_set_response (msg, "text/plain",
+                                         SOUP_MEMORY_STATIC,
+                                         "ok\r\n", 4);
 
        if (!strcmp (path, "/slow")) {
                soup_server_pause_message (server, msg);
diff --git a/tests/websocket-test.c b/tests/websocket-test.c
index 5e40cf36..c3a401ce 100644
--- a/tests/websocket-test.c
+++ b/tests/websocket-test.c
@@ -19,7 +19,7 @@
  */
 
 #include "test-utils.h"
-
+#include "soup-server-message-private.h"
 #include <zlib.h>
 
 typedef struct {
@@ -291,9 +291,9 @@ client_connect (Test *test,
 
 static void
 got_server_connection (SoupServer              *server,
-                      SoupWebsocketConnection *connection,
+                      SoupServerMessage       *msg,
                       const char              *path,
-                      SoupClientContext       *client,
+                      SoupWebsocketConnection *connection,
                       gpointer                 user_data)
 {
        Test *test = user_data;
@@ -415,10 +415,12 @@ test_handshake (Test *test,
 }
 
 static void
-websocket_server_request_started (SoupServer *server, SoupMessage *msg,
-                                 SoupClientContext *client, gpointer user_data)
+websocket_server_request_started (SoupServer        *server,
+                                 SoupServerMessage *msg,
+                                 gpointer           user_data)
 {
-       soup_message_headers_append (msg->response_headers, "Sec-WebSocket-Extensions", "x-foo");
+       soup_message_headers_append (soup_server_message_get_response_headers (msg),
+                                    "Sec-WebSocket-Extensions", "x-foo");
 }
 
 static void
@@ -639,6 +641,11 @@ test_protocol_negotiate_direct (Test *test,
                                gconstpointer unused)
 {
        SoupMessage *msg;
+       SoupServerMessage *server_msg;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
        gboolean ok;
        const char *protocol;
        GError *error = NULL;
@@ -647,16 +654,28 @@ test_protocol_negotiate_direct (Test *test,
        soup_websocket_client_prepare_handshake (msg, NULL,
                                                 (char **) negotiate_client_protocols);
 
-       ok = soup_websocket_server_check_handshake (msg, NULL,
+       server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+       soup_server_message_set_method (server_msg, msg->method);
+       soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+       request_headers = soup_server_message_get_request_headers (server_msg);
+       soup_message_headers_iter_init (&iter, msg->request_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (request_headers, name, value);
+       ok = soup_websocket_server_check_handshake (server_msg, NULL,
                                                    (char **) negotiate_server_protocols,
                                                    &error);
        g_assert_no_error (error);
        g_assert_true (ok);
 
-       ok = soup_websocket_server_process_handshake (msg, NULL,
+       ok = soup_websocket_server_process_handshake (server_msg, NULL,
                                                      (char **) negotiate_server_protocols);
        g_assert_true (ok);
 
+       msg->status_code = soup_server_message_get_status (server_msg, NULL);
+       response_headers = soup_server_message_get_response_headers (server_msg);
+       soup_message_headers_iter_init (&iter, response_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (msg->response_headers, name, value);
        protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
        g_assert_cmpstr (protocol, ==, negotiated_protocol);
 
@@ -665,6 +684,7 @@ test_protocol_negotiate_direct (Test *test,
        g_assert_true (ok);
 
        g_object_unref (msg);
+       g_object_unref (server_msg);
 }
 
 static void
@@ -689,6 +709,11 @@ test_protocol_mismatch_direct (Test *test,
                               gconstpointer unused)
 {
        SoupMessage *msg;
+       SoupServerMessage *server_msg;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
        gboolean ok;
        const char *protocol;
        GError *error = NULL;
@@ -697,18 +722,30 @@ test_protocol_mismatch_direct (Test *test,
        soup_websocket_client_prepare_handshake (msg, NULL,
                                                 (char **) mismatch_client_protocols);
 
-       ok = soup_websocket_server_check_handshake (msg, NULL,
+       server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+       soup_server_message_set_method (server_msg, msg->method);
+       soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+       request_headers = soup_server_message_get_request_headers (server_msg);
+       soup_message_headers_iter_init (&iter, msg->request_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (request_headers, name, value);
+       ok = soup_websocket_server_check_handshake (server_msg, NULL,
                                                    (char **) mismatch_server_protocols,
                                                    &error);
        g_assert_error (error, SOUP_WEBSOCKET_ERROR, SOUP_WEBSOCKET_ERROR_BAD_HANDSHAKE);
        g_clear_error (&error);
        g_assert_false (ok);
 
-       ok = soup_websocket_server_process_handshake (msg, NULL,
+       ok = soup_websocket_server_process_handshake (server_msg, NULL,
                                                      (char **) mismatch_server_protocols);
        g_assert_false (ok);
+       msg->status_code = soup_server_message_get_status (server_msg, NULL);
        soup_test_assert_message_status (msg, SOUP_STATUS_BAD_REQUEST);
 
+       response_headers = soup_server_message_get_response_headers (server_msg);
+       soup_message_headers_iter_init (&iter, response_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (msg->response_headers, name, value);
        protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
        g_assert_cmpstr (protocol, ==, NULL);
 
@@ -718,6 +755,7 @@ test_protocol_mismatch_direct (Test *test,
        g_assert_false (ok);
 
        g_object_unref (msg);
+       g_object_unref (server_msg);
 }
 
 static void
@@ -738,6 +776,11 @@ test_protocol_server_any_direct (Test *test,
                                 gconstpointer unused)
 {
        SoupMessage *msg;
+       SoupServerMessage *server_msg;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
        gboolean ok;
        const char *protocol;
        GError *error = NULL;
@@ -745,13 +788,25 @@ test_protocol_server_any_direct (Test *test,
        msg = soup_message_new ("GET", "http://127.0.0.1";);
        soup_websocket_client_prepare_handshake (msg, NULL, (char **) all_protocols);
 
-       ok = soup_websocket_server_check_handshake (msg, NULL, NULL, &error);
+       server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+       soup_server_message_set_method (server_msg, msg->method);
+       soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+       request_headers = soup_server_message_get_request_headers (server_msg);
+       soup_message_headers_iter_init (&iter, msg->request_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (request_headers, name, value);
+       ok = soup_websocket_server_check_handshake (server_msg, NULL, NULL, &error);
        g_assert_no_error (error);
        g_assert_true (ok);
 
-       ok = soup_websocket_server_process_handshake (msg, NULL, NULL);
+       ok = soup_websocket_server_process_handshake (server_msg, NULL, NULL);
        g_assert_true (ok);
 
+       msg->status_code = soup_server_message_get_status (server_msg, NULL);
+       response_headers = soup_server_message_get_response_headers (server_msg);
+       soup_message_headers_iter_init (&iter, response_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (msg->response_headers, name, value);
        protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
        g_assert_cmpstr (protocol, ==, NULL);
 
@@ -760,6 +815,7 @@ test_protocol_server_any_direct (Test *test,
        g_assert_true (ok);
 
        g_object_unref (msg);
+       g_object_unref (server_msg);
 }
 
 static void
@@ -782,6 +838,11 @@ test_protocol_client_any_direct (Test *test,
                                 gconstpointer unused)
 {
        SoupMessage *msg;
+       SoupServerMessage *server_msg;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
        gboolean ok;
        const char *protocol;
        GError *error = NULL;
@@ -789,13 +850,25 @@ test_protocol_client_any_direct (Test *test,
        msg = soup_message_new ("GET", "http://127.0.0.1";);
        soup_websocket_client_prepare_handshake (msg, NULL, NULL);
 
-       ok = soup_websocket_server_check_handshake (msg, NULL, (char **) all_protocols, &error);
+       server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+       soup_server_message_set_method (server_msg, msg->method);
+       soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+       request_headers = soup_server_message_get_request_headers (server_msg);
+       soup_message_headers_iter_init (&iter, msg->request_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (request_headers, name, value);
+       ok = soup_websocket_server_check_handshake (server_msg, NULL, (char **) all_protocols, &error);
        g_assert_no_error (error);
        g_assert_true (ok);
 
-       ok = soup_websocket_server_process_handshake (msg, NULL, (char **) all_protocols);
+       ok = soup_websocket_server_process_handshake (server_msg, NULL, (char **) all_protocols);
        g_assert_true (ok);
 
+       msg->status_code = soup_server_message_get_status (server_msg, NULL);
+       response_headers = soup_server_message_get_response_headers (server_msg);
+       soup_message_headers_iter_init (&iter, response_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (msg->response_headers, name, value);
        protocol = soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Protocol");
        g_assert_cmpstr (protocol, ==, NULL);
 
@@ -804,6 +877,7 @@ test_protocol_client_any_direct (Test *test,
        g_assert_true (ok);
 
        g_object_unref (msg);
+       g_object_unref (server_msg);
 }
 
 static void
@@ -1431,9 +1505,9 @@ test_server_receive_unmasked_frame (Test *test,
 
 static void
 test_client_context_got_server_connection (SoupServer              *server,
-                                          SoupWebsocketConnection *connection,
+                                          SoupServerMessage       *msg,
                                           const char              *path,
-                                          SoupClientContext       *client,
+                                          SoupWebsocketConnection *connection,
                                           gpointer                 user_data)
 {
        Test *test = user_data;
@@ -1442,7 +1516,7 @@ test_client_context_got_server_connection (SoupServer              *server,
        char *str;
        const char *remote_ip;
 
-       addr = soup_client_context_get_local_address (client);
+       addr = soup_server_message_get_local_address (msg);
        iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
        str = g_inet_address_to_string (iaddr);
        if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4)
@@ -1451,7 +1525,7 @@ test_client_context_got_server_connection (SoupServer              *server,
                g_assert_cmpstr (str, ==, "::1");
        g_free (str);
 
-       addr = soup_client_context_get_remote_address (client);
+       addr = soup_server_message_get_remote_address (msg);
        iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr));
        str = g_inet_address_to_string (iaddr);
        if (g_inet_address_get_family (iaddr) == G_SOCKET_FAMILY_IPV4)
@@ -1459,7 +1533,7 @@ test_client_context_got_server_connection (SoupServer              *server,
        else
                g_assert_cmpstr (str, ==, "::1");
 
-       remote_ip = soup_client_context_get_host (client);
+       remote_ip = soup_server_message_get_remote_host (msg);
        g_assert_cmpstr (remote_ip, ==, str);
        g_free (str);
 
@@ -1610,6 +1684,11 @@ test_deflate_negotiate_direct (Test *test,
 
        for (i = 0; i < G_N_ELEMENTS (deflate_negotiate_tests); i++) {
                SoupMessage *msg;
+               SoupServerMessage *server_msg;
+               SoupMessageHeaders *request_headers;
+               SoupMessageHeaders *response_headers;
+               SoupMessageHeadersIter iter;
+               const char *name, *value;
                gboolean result;
                GList *accepted_extensions = NULL;
                GError *error = NULL;
@@ -1618,7 +1697,15 @@ test_deflate_negotiate_direct (Test *test,
 
                soup_websocket_client_prepare_handshake (msg, NULL, NULL);
                soup_message_headers_append (msg->request_headers, "Sec-WebSocket-Extensions", 
deflate_negotiate_tests[i].client_extension);
-               result = soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL,
+
+               server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+               soup_server_message_set_method (server_msg, msg->method);
+               soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+               request_headers = soup_server_message_get_request_headers (server_msg);
+               soup_message_headers_iter_init (&iter, msg->request_headers);
+               while (soup_message_headers_iter_next (&iter, &name, &value))
+                       soup_message_headers_append (request_headers, name, value);
+               result = soup_websocket_server_check_handshake_with_extensions (server_msg, NULL, NULL,
                                                                                
deflate_negotiate_tests[i].server_supports_extensions ?
                                                                                supported_extensions : NULL,
                                                                                &error);
@@ -1630,11 +1717,17 @@ test_deflate_negotiate_direct (Test *test,
                        g_clear_error (&error);
                }
 
-               result = soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL,
+               result = soup_websocket_server_process_handshake_with_extensions (server_msg, NULL, NULL,
                                                                                  
deflate_negotiate_tests[i].server_supports_extensions ?
                                                                                  supported_extensions : NULL,
                                                                                  &accepted_extensions);
                g_assert (result == deflate_negotiate_tests[i].expected_check_result);
+
+               msg->status_code = soup_server_message_get_status (server_msg, NULL);
+               response_headers = soup_server_message_get_response_headers (server_msg);
+               soup_message_headers_iter_init (&iter, response_headers);
+                while (soup_message_headers_iter_next (&iter, &name, &value))
+                        soup_message_headers_append (msg->response_headers, name, value);
                if (deflate_negotiate_tests[i].expected_accepted_extension) {
                        const char *extension;
 
@@ -1668,6 +1761,7 @@ test_deflate_negotiate_direct (Test *test,
                 }
 
                g_object_unref (msg);
+               g_object_unref (server_msg);
         }
 
        g_ptr_array_unref (supported_extensions);
@@ -1678,6 +1772,11 @@ test_deflate_disabled_in_message_direct (Test *test,
                                         gconstpointer unused)
 {
        SoupMessage *msg;
+       SoupServerMessage *server_msg;
+       SoupMessageHeaders *request_headers;
+       SoupMessageHeaders *response_headers;
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
        GPtrArray *supported_extensions;
        GList *accepted_extensions = NULL;
        GError *error = NULL;
@@ -1690,11 +1789,24 @@ test_deflate_disabled_in_message_direct (Test *test,
        soup_websocket_client_prepare_handshake_with_extensions (msg, NULL, NULL, supported_extensions);
        g_assert_cmpstr (soup_message_headers_get_one (msg->request_headers, "Sec-WebSocket-Extensions"), ==, 
NULL);
 
-       g_assert_true (soup_websocket_server_check_handshake_with_extensions (msg, NULL, NULL, 
supported_extensions, &error));
+       server_msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL);
+       soup_server_message_set_method (server_msg, msg->method);
+       soup_server_message_set_uri (server_msg, soup_message_get_uri (msg));
+       request_headers = soup_server_message_get_request_headers (server_msg);
+       soup_message_headers_iter_init (&iter, msg->request_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (request_headers, name, value);
+
+       g_assert_true (soup_websocket_server_check_handshake_with_extensions (server_msg, NULL, NULL, 
supported_extensions, &error));
        g_assert_no_error (error);
 
-       g_assert_true (soup_websocket_server_process_handshake_with_extensions (msg, NULL, NULL, 
supported_extensions, &accepted_extensions));
+       g_assert_true (soup_websocket_server_process_handshake_with_extensions (server_msg, NULL, NULL, 
supported_extensions, &accepted_extensions));
        g_assert_null (accepted_extensions);
+       msg->status_code = soup_server_message_get_status (server_msg, NULL);
+       response_headers = soup_server_message_get_response_headers (server_msg);
+       soup_message_headers_iter_init (&iter, response_headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               soup_message_headers_append (msg->response_headers, name, value);
        g_assert_cmpstr (soup_message_headers_get_one (msg->response_headers, "Sec-WebSocket-Extensions"), 
==, NULL);
 
        g_assert_true (soup_websocket_client_verify_handshake_with_extensions (msg, supported_extensions, 
&accepted_extensions, &error));
@@ -1702,6 +1814,7 @@ test_deflate_disabled_in_message_direct (Test *test,
        g_assert_null (accepted_extensions);
 
        g_object_unref (msg);
+       g_object_unref (server_msg);
        g_ptr_array_unref (supported_extensions);
 }
 
@@ -1828,10 +1941,12 @@ test_cookies_in_request (Test *test,
 }
 
 static void
-cookies_test_websocket_server_request_started (SoupServer *server, SoupMessage *msg,
-                                               SoupClientContext *client, gpointer user_data)
+cookies_test_websocket_server_request_started (SoupServer        *server,
+                                              SoupServerMessage *msg,
+                                               gpointer           user_data)
 {
-        soup_message_headers_append (msg->response_headers, "Set-Cookie", "foo=bar; Path=/");
+        soup_message_headers_append (soup_server_message_get_response_headers (msg),
+                                    "Set-Cookie", "foo=bar; Path=/");
 }
 
 static void



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