[libsoup/wip/server] SoupServer: add exciting new APIs, deprecate musty old ones



commit d834e1e5d8e41f7fa1c082a7ab1ab9ce0335daed
Author: Dan Winship <danw gnome org>
Date:   Tue Nov 6 10:37:54 2012 -0500

    SoupServer: add exciting new APIs, deprecate musty old ones
    
    FIXME: server-howto

 docs/reference/libsoup-2.4-docs.sgml    |    1 +
 docs/reference/libsoup-2.4-sections.txt |   45 +-
 docs/reference/server-howto.xml         |   96 ++-
 examples/simple-httpd.c                 |   68 +-
 examples/simple-proxy.c                 |   32 +-
 libsoup/libsoup-2.4.sym                 |   10 +
 libsoup/soup-address.c                  |   11 +
 libsoup/soup-message-private.h          |    1 +
 libsoup/soup-message-server-io.c        |   18 +-
 libsoup/soup-misc-private.h             |    3 +
 libsoup/soup-server.c                   | 1040 ++++++++++++++++++++++++++-----
 libsoup/soup-server.h                   |  130 +++--
 libsoup/soup-socket.c                   |   26 +
 po/POTFILES.in                          |    1 +
 tests/auth-test.c                       |   16 +-
 tests/cache-test.c                      |    3 +-
 tests/chunk-io-test.c                   |    6 +-
 tests/chunk-test.c                      |   13 +-
 tests/coding-test.c                     |    5 +-
 tests/connection-test.c                 |   13 +-
 tests/context-test.c                    |   10 +-
 tests/continue-test.c                   |   49 +-
 tests/cookies-test.c                    |   11 +-
 tests/forms-test.c                      |   18 +-
 tests/misc-test.c                       |   54 +-
 tests/multipart-test.c                  |    5 +-
 tests/no-ssl-test.c                     |   17 +-
 tests/ntlm-test.c                       |   11 +-
 tests/proxy-test.c                      |    5 +-
 tests/range-test.c                      |   14 +-
 tests/redirect-test.c                   |   21 +-
 tests/requester-test.c                  |   54 +-
 tests/server-auth-test.c                |    9 +-
 tests/server-test.c                     |   42 +-
 tests/session-test.c                    |   41 +-
 tests/sniffing-test.c                   |    5 +-
 tests/ssl-test.c                        |   21 +-
 tests/streaming-test.c                  |    7 +-
 tests/test-utils.c                      |  224 ++++++--
 tests/test-utils.h                      |   13 +-
 tests/timeout-test.c                    |   47 +-
 tests/xmlrpc-server-test.c              |   14 +-
 42 files changed, 1604 insertions(+), 626 deletions(-)
---
diff --git a/docs/reference/libsoup-2.4-docs.sgml b/docs/reference/libsoup-2.4-docs.sgml
index ca92b86..1554c0e 100644
--- a/docs/reference/libsoup-2.4-docs.sgml
+++ b/docs/reference/libsoup-2.4-docs.sgml
@@ -34,6 +34,7 @@
     <xi:include href="xml/soup-request-file.xml"/>
     <xi:include href="xml/soup-request-data.xml"/>
     <xi:include href="xml/soup-server.xml"/>
+    <xi:include href="xml/soup-server-deprecated.xml"/>
     <xi:include href="xml/soup-session.xml"/>
     <xi:include href="xml/soup-session-async.xml"/>
     <xi:include href="xml/soup-session-sync.xml"/>
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 6b19310..998794d 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -214,25 +214,25 @@ SoupKnownStatusCode
 <TITLE>SoupServer</TITLE>
 SoupServer
 soup_server_new
-soup_server_is_https
-soup_server_get_port
-soup_server_get_listener
-soup_server_run
-soup_server_run_async
-soup_server_quit
+<SUBSECTION>
+SoupServerListenOptions
+soup_server_listen
+soup_server_listen_all
+soup_server_listen_local
 soup_server_disconnect
-soup_server_get_async_context
+soup_server_is_https
 <SUBSECTION>
 SoupServerCallback
 soup_server_add_handler
 soup_server_remove_handler
 <SUBSECTION>
 SoupClientContext
-soup_client_context_get_socket
-soup_client_context_get_address
+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_gsocket
 <SUBSECTION>
 soup_server_add_auth_domain
 soup_server_remove_auth_domain
@@ -240,12 +240,9 @@ soup_server_remove_auth_domain
 soup_server_pause_message
 soup_server_unpause_message
 <SUBSECTION>
-SOUP_SERVER_PORT
-SOUP_SERVER_INTERFACE
-SOUP_SERVER_SSL_CERT_FILE
-SOUP_SERVER_SSL_KEY_FILE
 SOUP_SERVER_TLS_CERTIFICATE
-SOUP_SERVER_ASYNC_CONTEXT
+SOUP_SERVER_TLS_CERT_FILE
+SOUP_SERVER_TLS_KEY_FILE
 SOUP_SERVER_RAW_PATHS
 SOUP_SERVER_SERVER_HEADER
 <SUBSECTION Standard>
@@ -262,6 +259,26 @@ soup_client_context_get_type
 </SECTION>
 
 <SECTION>
+<FILE>soup-server-deprecated</FILE>
+<TITLE>SoupServer deprecated API</TITLE>
+soup_server_get_port
+soup_server_get_listener
+soup_server_run
+soup_server_run_async
+soup_server_quit
+soup_server_get_async_context
+<SUBSECTION>
+soup_client_context_get_socket
+soup_client_context_get_address
+<SUBSECTION>
+SOUP_SERVER_PORT
+SOUP_SERVER_INTERFACE
+SOUP_SERVER_SSL_CERT_FILE
+SOUP_SERVER_SSL_KEY_FILE
+SOUP_SERVER_ASYNC_CONTEXT
+</SECTION>
+
+<SECTION>
 <FILE>soup-auth-domain</FILE>
 <TITLE>SoupAuthDomain</TITLE>
 SoupAuthDomain
diff --git a/docs/reference/server-howto.xml b/docs/reference/server-howto.xml
index 76c1918..79f4c28 100644
--- a/docs/reference/server-howto.xml
+++ b/docs/reference/server-howto.xml
@@ -25,54 +25,35 @@ linkend="SoupServer"><type>SoupServer</type></link>.
 You create the server with <link
 linkend="soup-server-new"><function>soup_server_new</function></link>,
 and as with the <type>SoupSession</type> constructor, you can specify
-various additional options:
+a few additional options:
 </para>
 
 <variablelist>
     <varlistentry>
-       <term><link linkend="SOUP-SERVER-PORT:CAPS"><literal>SOUP_SERVER_PORT</literal></link></term>
+       <term><link 
linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link></term>
        <listitem><para>
-           The TCP port to listen on. If <literal>0</literal> (or
-           left unspecified), some unused port will be selected for
-           you. (You can find out what port by calling <link
-           linkend="soup-server-get-port"><function>soup_server_get_port</function></link>.
+           A <link
+           linkend="GTlsCertificate"><type>GTlsCertificate</type></link>
+           (containing a private key) that will be used when handling
+           HTTPS requests on the server.
        </para></listitem>
     </varlistentry>
     <varlistentry>
-       <term><link 
linkend="SOUP-SERVER-INTERFACE:CAPS"><literal>SOUP_SERVER_INTERFACE</literal></link></term>
+       <term><link 
linkend="SOUP-SERVER-TLS-CERT-FILE:CAPS"><literal>SOUP_SERVER_TLS_CERT_FILE</literal></link></term>
        <listitem><para>
-           A <link linkend="SoupAddress"><type>SoupAddress</type></link>,
-           specifying the IP address of the network interface to run
-           the server on. If <literal>NULL</literal> (or left
-           unspecified), the server will listen on all interfaces.
+           Points to a file containing a TLS (aka SSL) certificate to
+           use when handling HTTPS requests.
        </para></listitem>
     </varlistentry>
     <varlistentry>
-       <term><link 
linkend="SOUP-SERVER-SSL-CERT-FILE:CAPS"><literal>SOUP_SERVER_SSL_CERT_FILE</literal></link></term>
-       <listitem><para>
-           Points to a file containing an SSL certificate to use. If
-           this is set, then the server will speak HTTPS; otherwise
-           it will speak HTTP.
-       </para></listitem>
-    </varlistentry>
-    <varlistentry>
-       <term><link 
linkend="SOUP-SERVER-SSL-KEY-FILE:CAPS"><literal>SOUP_SERVER_SSL_KEY_FILE</literal></link></term>
+       <term><link 
linkend="SOUP-SERVER-TLS-KEY-FILE:CAPS"><literal>SOUP_SERVER_TLS_KEY_FILE</literal></link></term>
        <listitem><para>
            Points to a file containing the private key for the
-           <literal>SOUP_SERVER_SSL_CERT_FILE</literal>. (It may
+           <literal>SOUP_SERVER_TLS_CERT_FILE</literal>. (It may
            point to the same file.)
        </para></listitem>
     </varlistentry>
     <varlistentry>
-       <term><link 
linkend="SOUP-SERVER-ASYNC-CONTEXT:CAPS"><literal>SOUP_SERVER_ASYNC_CONTEXT</literal></link></term>
-       <listitem><para>
-           A <link linkend="GMainContext"><type>GMainContext</type></link> which
-           the server will use for asynchronous operations. This can
-           be set if you want to use a SoupServer in a thread
-           other than the main thread.
-       </para></listitem>
-    </varlistentry>
-    <varlistentry>
        <term><link 
linkend="SOUP-SERVER-RAW-PATHS:CAPS"><literal>SOUP_SERVER_RAW_PATHS</literal></link></term>
        <listitem><para>
            Set this to <literal>TRUE</literal> if you don't want
@@ -84,6 +65,61 @@ various additional options:
     </varlistentry>
 </variablelist>
 
+<para>
+  If you are using <literal>SOUP_SERVER_TLS_CERT_FILE</literal> and
+  <literal>SOUP_SERVER_TLS_KEY_FILE</literal>, you should create the
+  server with <link
+  linkend="g-initable-new"><function>g_initable_new</function></link>
+  rather than <link
+  linkend="soup-server-new"><function>soup_server_new</function></link>,
+  so that you can get back a proper error if the specified files can't
+  be read.
+</para>
+
+</refsect2>
+
+<refsect2>
+<title>Adding Listening Sockets</title>
+
+<para>
+  To tell the server where to listen, call <link
+  linkend="soup-server-listen"><function>soup_server_listen</function></link>
+  (to listen on a specific <link
+  linkend="GSocketAddress"><type>GSocketAddress</type></link>), <link
+  linkend="soup-server-listen-all"><function>soup_server_listen_all</function></link>
+  (to listen on a given port on all network interfaces), or <link
+  linkend="soup-server-listen-local"><function>soup_server_listen_local</function></link>
+  (to listen to a given port on the loopback interface only). You can
+  call any of these functions multiple times, to set up multiple
+  listening sockets.
+</para>
+
+<para>
+  To set up an HTTPS server, you must have set <link
+  linkend="SOUP-SERVER-TLS-CERT-FILE:CAPS"><literal>SOUP_SERVER_TLS_CERT_FILE</literal></link>
+  or <link
+  linkend="SOUP-SERVER-TLS-CERTIFICATE:CAPS"><literal>SOUP_SERVER_TLS_CERTIFICATE</literal></link>,
+  and then you can pass <link
+  linkend="SOUP-SERVER-LISTEN-HTTPS:CAPS"><literal>SOUP_SERVER_LISTEN_HTTPS</literal></link>
+  as an option to <link
+  linkend="soup-server-listen"><function>soup_server_listen</function></link>,
+  etc.
+</para>
+
+<para>
+  By default, servers listen for both IPv4 and IPv6 connections; if
+  you don't want this, use the <link
+  linkend="SOUP-SERVER-LISTEN-IPV4-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV4_ONLY</literal></link>
+  or <link
+  linkend="SOUP-SERVER-LISTEN-IPV6-ONLY:CAPS"><literal>SOUP_SERVER_LISTEN_IPV6_ONLY</literal></link>
+  options.
+</para>
+
+<para>
+  The server runs asynchronously, in the thread-default
+  <link linkend="GMainContext"><type>GMainContext</type></link> of the
+  thread in which the "listen" calls were made.
+</para>
 </refsect2>
 
 <refsect2>
diff --git a/examples/simple-httpd.c b/examples/simple-httpd.c
index 71ff874..f3be82e 100644
--- a/examples/simple-httpd.c
+++ b/examples/simple-httpd.c
@@ -107,6 +107,7 @@ do_get (SoupServer *server, SoupMessage *msg, const char *path)
                soup_message_set_response (msg, "text/html",
                                           SOUP_MEMORY_TAKE,
                                           listing->str, listing->len);
+               soup_message_set_status (msg, SOUP_STATUS_OK);
                g_string_free (listing, FALSE);
                return;
        }
@@ -213,22 +214,19 @@ quit (int sig)
        exit (0);
 }
 
-static int port, ssl_port;
-static const char *ssl_cert_file, *ssl_key_file;
+static int port;
+static const char *tls_cert_file, *tls_key_file;
 
 static GOptionEntry entries[] = {
        { "cert-file", 'c', 0,
-         G_OPTION_ARG_STRING, &ssl_cert_file,
+         G_OPTION_ARG_STRING, &tls_cert_file,
          "Use FILE as the TLS certificate file", "FILE" },
        { "key-file", 'k', 0,
-         G_OPTION_ARG_STRING, &ssl_key_file,
+         G_OPTION_ARG_STRING, &tls_key_file,
          "Use FILE as the TLS private key file", "FILE" },
        { "port", 'p', 0,
          G_OPTION_ARG_INT, &port,
          "Port to listen on", NULL },
-       { "ssl-port", 's', 0,
-         G_OPTION_ARG_INT, &port,
-         "Port to listen on for TLS traffic", NULL },
        { NULL }
 };
 
@@ -237,7 +235,10 @@ main (int argc, char **argv)
 {
        GOptionContext *opts;
        GMainLoop *loop;
-       SoupServer *server, *ssl_server;
+       SoupServer *server;
+       GSList *uris, *u;
+       char *str;
+       GTlsCertificate *cert;
        GError *error = NULL;
 
        opts = g_option_context_new (NULL);
@@ -258,36 +259,35 @@ main (int argc, char **argv)
 
        signal (SIGINT, quit);
 
-       server = soup_server_new (SOUP_SERVER_PORT, port,
-                                 SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
-                                 NULL);
-       if (!server) {
-               g_printerr ("Unable to bind to server port %d\n", port);
-               exit (1);
+       if (tls_cert_file && tls_key_file) {
+               cert = g_tls_certificate_new_from_files (tls_cert_file, tls_key_file, &error);
+               if (error) {
+                       g_printerr ("Unable to create server: %s\n", error->message);
+                       exit (1);
+               }
+               server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
+                                         SOUP_SERVER_TLS_CERTIFICATE, cert,
+                                         NULL);
+               g_object_unref (cert);
+
+               soup_server_listen_all (server, port, SOUP_SERVER_LISTEN_HTTPS, &error);
+       } else {
+               server = soup_server_new (SOUP_SERVER_SERVER_HEADER, "simple-httpd ",
+                                         NULL);
+               soup_server_listen_all (server, port, 0, &error);
        }
+
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-       g_print ("\nStarting Server on port %d\n",
-                soup_server_get_port (server));
-       soup_server_run_async (server);
-
-       if (ssl_cert_file && ssl_key_file) {
-               ssl_server = soup_server_new (
-                       SOUP_SERVER_PORT, ssl_port,
-                       SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
-                       SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
-                       NULL);
-
-               if (!ssl_server) {
-                       g_printerr ("Unable to bind to SSL server port %d\n", ssl_port);
-                       exit (1);
-               }
-               soup_server_add_handler (ssl_server, NULL,
-                                        server_callback, NULL, NULL);
-               g_print ("Starting SSL Server on port %d\n", 
-                        soup_server_get_port (ssl_server));
-               soup_server_run_async (ssl_server);
+
+       uris = soup_server_get_uris (server);
+       for (u = uris; u; u = u->next) {
+               str = soup_uri_to_string (u->data, FALSE);
+               g_print ("Listening on %s\n", str);
+               g_free (str);
+               soup_uri_free (u->data);
        }
+       g_slist_free (uris);
 
        g_print ("\nWaiting for requests...\n");
 
diff --git a/examples/simple-proxy.c b/examples/simple-proxy.c
index 6623166..8833257 100644
--- a/examples/simple-proxy.c
+++ b/examples/simple-proxy.c
@@ -141,6 +141,8 @@ main (int argc, char **argv)
 {
        GOptionContext *opts;
        GMainLoop *loop;
+       GSList *uris, *u;
+       char *str;
        GError *error = NULL;
 
        opts = g_option_context_new (NULL);
@@ -152,22 +154,11 @@ main (int argc, char **argv)
                            g_option_context_get_help (opts, TRUE, NULL));
                exit (1);
        }
-
-       if (argc != 2) {
-               g_printerr ("%s",
-                           g_option_context_get_help (opts, TRUE, NULL));
-               exit (1);
-       }
        g_option_context_free (opts);
 
        signal (SIGINT, quit);
 
-       server = soup_server_new (SOUP_SERVER_PORT, port,
-                                 NULL);
-       if (!server) {
-               g_printerr ("Unable to bind to server port %d\n", port);
-               exit (1);
-       }
+       server = g_object_new (SOUP_TYPE_SERVER, NULL);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
        if (require_auth) {
@@ -182,9 +173,20 @@ main (int argc, char **argv)
                g_object_unref (auth_domain);
        }
 
-       g_print ("\nStarting proxy on port %d\n",
-                soup_server_get_port (server));
-       soup_server_run_async (server);
+       soup_server_listen_all (server, port, 0, &error);
+       if (error) {
+               g_printerr ("Unable to create server: %s\n", error->message);
+               exit (1);
+       }
+
+       uris = soup_server_get_uris (server);
+       for (u = uris; u; u = u->next) {
+               str = soup_uri_to_string (u->data, FALSE);
+               g_print ("Listening on %s\n", str);
+               g_free (str);
+               soup_uri_free (u->data);
+       }
+       g_slist_free (uris);
 
        session = soup_session_async_new ();
 
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index be4cd7f..39a5574 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -87,7 +87,10 @@ soup_check_version
 soup_client_context_get_address
 soup_client_context_get_auth_domain
 soup_client_context_get_auth_user
+soup_client_context_get_gsocket
 soup_client_context_get_host
+soup_client_context_get_local_address
+soup_client_context_get_remote_address
 soup_client_context_get_socket
 soup_client_context_get_type
 soup_connection_state_get_type
@@ -344,9 +347,16 @@ soup_server_add_handler
 soup_server_disconnect
 soup_server_get_async_context
 soup_server_get_listener
+soup_server_get_listeners
+soup_server_get_gsocket
 soup_server_get_port
 soup_server_get_type
+soup_server_get_uris
 soup_server_is_https
+soup_server_listen
+soup_server_listen_all
+soup_server_listen_local
+soup_server_listen_options_get_type
 soup_server_new
 soup_server_pause_message
 soup_server_quit
diff --git a/libsoup/soup-address.c b/libsoup/soup-address.c
index b2d1647..ecbd9a7 100644
--- a/libsoup/soup-address.c
+++ b/libsoup/soup-address.c
@@ -384,6 +384,17 @@ soup_address_new_from_sockaddr (struct sockaddr *sa, int len)
                             NULL);
 }
 
+SoupAddress *
+soup_address_new_from_gsockaddr (GSocketAddress *addr)
+{
+       struct sockaddr_storage sa;
+
+       g_socket_address_to_native (addr, &sa, sizeof (sa), NULL);
+       return g_object_new (SOUP_TYPE_ADDRESS,
+                            SOUP_ADDRESS_SOCKADDR, &sa,
+                            NULL);
+}
+
 /**
  * SoupAddressFamily:
  * @SOUP_ADDRESS_FAMILY_INVALID: an invalid %SoupAddress
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 35cc988..c29d73e 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -68,6 +68,7 @@ void soup_message_send_request (SoupMessageQueueItem      *item,
                                gpointer                   user_data);
 void soup_message_read_request (SoupMessage               *msg,
                                SoupSocket                *sock,
+                               gboolean                   use_thread_context,
                                SoupMessageCompletionFn    completion_cb,
                                gpointer                   user_data);
 
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 2647b81..53c0ba3 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -252,17 +252,22 @@ get_response_headers (SoupMessage *msg, GString *headers,
 void
 soup_message_read_request (SoupMessage               *msg,
                           SoupSocket                *sock,
+                          gboolean                   use_thread_context,
                           SoupMessageCompletionFn    completion_cb,
                           gpointer                   user_data)
 {
        GMainContext *async_context;
        GIOStream *iostream;
 
-       g_object_get (sock,
-                     SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
-                     NULL);
-       if (!async_context)
-               async_context = g_main_context_ref (g_main_context_default ());
+       if (use_thread_context)
+               async_context = g_main_context_ref_thread_default ();
+       else {
+               g_object_get (sock,
+                             SOUP_SOCKET_ASYNC_CONTEXT, &async_context,
+                             NULL);
+               if (!async_context)
+                       async_context = g_main_context_ref (g_main_context_default ());
+       }
 
        iostream = soup_socket_get_iostream (sock);
 
@@ -271,6 +276,5 @@ soup_message_read_request (SoupMessage               *msg,
                                parse_request_headers,
                                sock,
                                completion_cb, user_data);
-       if (async_context)
-               g_main_context_unref (async_context);
+       g_main_context_unref (async_context);
 }
diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h
index 50a3b98..e3cc7b6 100644
--- a/libsoup/soup-misc-private.h
+++ b/libsoup/soup-misc-private.h
@@ -46,6 +46,7 @@ GIOStream *soup_socket_get_iostream   (SoupSocket *sock);
 
 #define SOUP_SOCKET_CLEAN_DISPOSE "clean-dispose"
 #define SOUP_SOCKET_PROXY_RESOLVER "proxy-resolver"
+#define SOUP_SOCKET_IPV6_ONLY     "ipv6-only"
 SoupURI *soup_socket_get_http_proxy_uri (SoupSocket *sock);
 
 /* At some point it might be possible to mark additional methods
@@ -73,4 +74,6 @@ guint soup_message_headers_get_ranges_internal (SoupMessageHeaders  *hdrs,
                                                SoupRange          **ranges,
                                                int                 *length);
 
+SoupAddress *soup_address_new_from_gsockaddr (GSocketAddress *addr);
+
 #endif /* SOUP_MISC_PRIVATE_H */
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index 07d801d..8afd7bd 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -11,6 +11,8 @@
 
 #include <string.h>
 
+#include <glib/gi18n-lib.h>
+
 #include "soup-server.h"
 #include "soup.h"
 #include "soup-message-private.h"
@@ -44,16 +46,28 @@
  * Additional processing options are available via #SoupServer's
  * signals; Connect to #SoupServer::request-started to be notified
  * every time a new request is being processed. (This gives you a
- * chance to connect to the #SoupMessage "got-" signals in case you
- * want to do processing before the body has been fully read.)
+ * chance to connect to the #SoupMessage "<literal>got-</literal>"
+ * signals in case you want to do processing before the body has been
+ * fully read.)
  * 
- * Once the server is set up, start it processing connections by
- * calling soup_server_run_async() or soup_server_run(). #SoupServer
- * runs via the glib main loop; if you need to have a server that runs
- * in another thread (or merely isn't bound to the default main loop),
- * create a #GMainContext for it to use, and set that via the
- * #SOUP_SERVER_ASYNC_CONTEXT property.
- **/
+ * If you want to process https connections in addition to (or instead
+ * of) http connections, you can either set the
+ * %SOUP_SERVER_TLS_CERTIFICATE property when creating the server, or
+ * else call soup_server_set_ssl_certificate() after creating it.
+
+ * Once the server is set up, make one or more calls to
+ * soup_server_listen(), soup_server_listen_local(), or
+ * soup_server_listen_all() to tell it where to listen for
+ * connections. (All ports on a #SoupServer use the same handlers; if
+ * you need to handle some ports differently, such as returning
+ * different data for http and https, you'll need to create multiple
+ * #SoupServers, or else check the passed-in URI in the handler
+ * function.).
+ *
+ * #SoupServer will begin processing connections as soon as you return
+ * to (or start) the main loop for the current thread-default
+ * #GMainContext.
+ */
 
 G_DEFINE_TYPE (SoupServer, soup_server, G_TYPE_OBJECT)
 
@@ -74,6 +88,9 @@ struct SoupClientContext {
        SoupAuthDomain *auth_domain;
        char           *auth_user;
 
+       GSocketAddress *remote_addr;
+       GSocketAddress *local_addr;
+
        int             ref_count;
 };
 
@@ -86,28 +103,30 @@ typedef struct {
 } SoupServerHandler;
 
 typedef struct {
-       SoupAddress       *iface;
-       guint              port;
+       GSList            *listeners;
+       GSList            *clients;
 
        char              *ssl_cert_file, *ssl_key_file;
-       GTlsCertificate   *ssl_cert;
+       GTlsCertificate   *tls_cert;
 
        char              *server_header;
 
+       GMainContext      *async_context;
        GMainLoop         *loop;
 
-       SoupSocket        *listen_sock;
-       GSList            *clients;
-
        gboolean           raw_paths;
        SoupPathMap       *handlers;
        SoupServerHandler *default_handler;
        
        GSList            *auth_domains;
 
-       GMainContext      *async_context;
-
        char             **http_aliases, **https_aliases;
+
+       SoupAddress       *legacy_iface;
+       int                legacy_port;
+
+       gboolean           disposed;
+
 } SoupServerPrivate;
 #define SOUP_SERVER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SERVER, SoupServerPrivate))
 
@@ -120,6 +139,8 @@ enum {
        PROP_INTERFACE,
        PROP_SSL_CERT_FILE,
        PROP_SSL_KEY_FILE,
+       PROP_TLS_CERT_FILE,
+       PROP_TLS_KEY_FILE,
        PROP_TLS_CERTIFICATE,
        PROP_ASYNC_CONTEXT,
        PROP_RAW_PATHS,
@@ -150,6 +171,20 @@ soup_server_init (SoupServer *server)
        priv->http_aliases = g_new (char *, 2);
        priv->http_aliases[0] = (char *)g_intern_string ("*");
        priv->http_aliases[1] = NULL;
+
+       priv->legacy_port = -1;
+}
+
+static void
+soup_server_dispose (GObject *object)
+{
+       SoupServer *server = SOUP_SERVER (object);
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       priv->disposed = TRUE;
+       soup_server_disconnect (server);
+
+       G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
 }
 
 static void
@@ -158,16 +193,14 @@ soup_server_finalize (GObject *object)
        SoupServer *server = SOUP_SERVER (object);
        SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       g_clear_object (&priv->iface);
+       g_clear_object (&priv->legacy_iface);
 
        g_free (priv->ssl_cert_file);
        g_free (priv->ssl_key_file);
-       g_clear_object (&priv->ssl_cert);
+       g_clear_object (&priv->tls_cert);
 
        g_free (priv->server_header);
 
-       g_clear_object (&priv->listen_sock);
-
        while (priv->clients) {
                SoupClientContext *client = priv->clients->data;
                SoupSocket *sock = g_object_ref (client->sock);
@@ -206,6 +239,34 @@ soup_server_finalize (GObject *object)
        G_OBJECT_CLASS (soup_server_parent_class)->finalize (object);
 }
 
+static gboolean
+soup_server_ensure_listening (SoupServer *server)
+{
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+       SoupSocket *listener;
+
+       if (priv->listeners)
+               return TRUE;
+
+       if (!priv->legacy_iface) {
+               priv->legacy_iface =
+                       soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
+                                             priv->legacy_port);
+       }
+
+       listener = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->legacy_iface,
+                                   SOUP_SOCKET_SSL_CREDENTIALS, priv->tls_cert,
+                                   SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
+                                   NULL);
+       if (!soup_socket_listen (listener)) {
+               g_object_unref (listener);
+               return FALSE;
+       }
+
+       priv->listeners = g_slist_prepend (priv->listeners, listener);
+       return TRUE;
+}
+
 static GObject *
 soup_server_constructor (GType                  type,
                         guint                  n_construct_properties,
@@ -213,6 +274,7 @@ soup_server_constructor (GType                  type,
 {
        GObject *server;
        SoupServerPrivate *priv;
+       gboolean legacy_port_set;
 
        server = G_OBJECT_CLASS (soup_server_parent_class)->constructor (
                type, n_construct_properties, construct_properties);
@@ -220,20 +282,19 @@ soup_server_constructor (GType                  type,
                return NULL;
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       if (!priv->iface) {
-               priv->iface =
-                       soup_address_new_any (SOUP_ADDRESS_FAMILY_IPV4,
-                                             priv->port);
-       }
-
+       /* For backward compatibility, we have to process the
+        * :ssl-cert-file, :ssl-key-file, :interface, and :port
+        * properties now, and return NULL if they are
+        * invalid/unsatisfiable.
+        */
        if (priv->ssl_cert_file && priv->ssl_key_file) {
                GError *error = NULL;
 
-               if (priv->ssl_cert)
-                       g_object_unref (priv->ssl_cert);
-               priv->ssl_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file, priv->ssl_key_file, 
&error);
-               if (!priv->ssl_cert) {
-                       g_warning ("Could not read SSL certificate from '%s': %s",
+               if (priv->tls_cert)
+                       g_object_unref (priv->tls_cert);
+               priv->tls_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file, priv->ssl_key_file, 
&error);
+               if (!priv->tls_cert) {
+                       g_warning ("Could not read TLS certificate from '%s': %s",
                                   priv->ssl_cert_file, error->message);
                        g_error_free (error);
                        g_object_unref (server);
@@ -241,23 +302,33 @@ soup_server_constructor (GType                  type,
                }
        }
 
-       priv->listen_sock =
-               soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, priv->iface,
-                                SOUP_SOCKET_SSL_CREDENTIALS, priv->ssl_cert,
-                                SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context,
-                                NULL);
-       if (!soup_socket_listen (priv->listen_sock)) {
-               g_object_unref (server);
-               return NULL;
+       if (priv->legacy_port != -1)
+               legacy_port_set = TRUE;
+       else {
+               legacy_port_set = FALSE;
+               priv->legacy_port = 0;
        }
 
-       /* Re-resolve the interface address, in particular in case
-        * the passed-in address had SOUP_ADDRESS_ANY_PORT.
-        */
-       g_object_unref (priv->iface);
-       priv->iface = soup_socket_get_local_address (priv->listen_sock);
-       g_object_ref (priv->iface);
-       priv->port = soup_address_get_port (priv->iface);
+       if (legacy_port_set || priv->legacy_iface) {
+               if (!soup_server_ensure_listening (SOUP_SERVER (server))) {
+                       g_object_unref (server);
+                       return NULL;
+               }
+       } else {
+               /* If neither port nor iface was specified, then
+                * either: (a) the caller is planning to use the new
+                * listen APIs, so we don't have to do anything now,
+                * or (b) the caller is using the legacy APIs but
+                * wants the default values for interface and port
+                * (address 0.0.0.0, port 0), in which case a later
+                * call to soup_server_ensure_listening() will set it
+                * up just-in-time; we don't have to worry about it
+                * failing in that case, because it can't (unless you
+                * have no IPv4 addresses configured [even localhost],
+                * or there are already listeners on all 65,535 ports.
+                * We assume neither of these will happen.)
+                */
+       }
 
        return server;
 }
@@ -294,27 +365,28 @@ soup_server_set_property (GObject *object, guint prop_id,
 
        switch (prop_id) {
        case PROP_PORT:
-               priv->port = g_value_get_uint (value);
+               if (g_value_get_uint (value) != 0)
+                       priv->legacy_port = g_value_get_uint (value);
                break;
        case PROP_INTERFACE:
-               if (priv->iface)
-                       g_object_unref (priv->iface);
-               priv->iface = g_value_get_object (value);
-               if (priv->iface)
-                       g_object_ref (priv->iface);
+               if (priv->legacy_iface)
+                       g_object_unref (priv->legacy_iface);
+               priv->legacy_iface = g_value_get_object (value);
+               if (priv->legacy_iface)
+                       g_object_ref (priv->legacy_iface);
                break;
        case PROP_SSL_CERT_FILE:
-               priv->ssl_cert_file =
-                       g_strdup (g_value_get_string (value));
+               g_free (priv->ssl_cert_file);
+               priv->ssl_cert_file = g_value_dup_string (value);
                break;
        case PROP_SSL_KEY_FILE:
-               priv->ssl_key_file =
-                       g_strdup (g_value_get_string (value));
+               g_free (priv->ssl_key_file);
+               priv->ssl_key_file = g_value_dup_string (value);
                break;
        case PROP_TLS_CERTIFICATE:
-               if (priv->ssl_cert)
-                       g_object_unref (priv->ssl_cert);
-               priv->ssl_cert = g_value_dup_object (value);
+               if (priv->tls_cert)
+                       g_object_unref (priv->tls_cert);
+               priv->tls_cert = g_value_dup_object (value);
                break;
        case PROP_ASYNC_CONTEXT:
                priv->async_context = g_value_get_pointer (value);
@@ -355,14 +427,17 @@ static void
 soup_server_get_property (GObject *object, guint prop_id,
                          GValue *value, GParamSpec *pspec)
 {
-       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (object);
+       SoupServer *server = SOUP_SERVER (object);
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
 
        switch (prop_id) {
        case PROP_PORT:
-               g_value_set_uint (value, priv->port);
+               soup_server_ensure_listening (server);
+               g_value_set_uint (value, priv->legacy_port > 0 ? priv->legacy_port : 0);
                break;
        case PROP_INTERFACE:
-               g_value_set_object (value, priv->iface);
+               soup_server_ensure_listening (server);
+               g_value_set_object (value, priv->legacy_iface);
                break;
        case PROP_SSL_CERT_FILE:
                g_value_set_string (value, priv->ssl_cert_file);
@@ -371,7 +446,7 @@ soup_server_get_property (GObject *object, guint prop_id,
                g_value_set_string (value, priv->ssl_key_file);
                break;
        case PROP_TLS_CERTIFICATE:
-               g_value_set_object (value, priv->ssl_cert);
+               g_value_set_object (value, priv->tls_cert);
                break;
        case PROP_ASYNC_CONTEXT:
                g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : 
NULL);
@@ -403,6 +478,7 @@ soup_server_class_init (SoupServerClass *server_class)
 
        /* virtual method override */
        object_class->constructor = soup_server_constructor;
+       object_class->dispose = soup_server_dispose;
        object_class->finalize = soup_server_finalize;
        object_class->set_property = soup_server_set_property;
        object_class->get_property = soup_server_get_property;
@@ -514,92 +590,143 @@ soup_server_class_init (SoupServerClass *server_class)
 
        /* properties */
        /**
+        * SoupServer:port:
+        *
+        * The port the server is listening on, if you are using the
+        * old #SoupServer API. (This will not be set if you use
+        * soup_server_listen(), etc.)
+        *
+        * Deprecated: #SoupServers can listen on multiple interfaces
+        * at once now. Use soup_server_listen(), etc, to listen on a
+        * port, and soup_server_get_uris() to see what ports are
+        * being listened on.
+        */
+       /**
         * SOUP_SERVER_PORT:
         *
-        * Alias for the #SoupServer:port property. (The port the
-        * server listens on.)
+        * Alias for the deprecated #SoupServer:port property, qv.
+        *
+        * Deprecated: #SoupServers can listen on multiple interfaces
+        * at once now. Use soup_server_listen(), etc, to listen on a
+        * port, and soup_server_get_uris() to see what ports are
+        * being listened on.
         **/
        g_object_class_install_property (
                object_class, PROP_PORT,
                g_param_spec_uint (SOUP_SERVER_PORT,
                                   "Port",
-                                  "Port to listen on",
+                                  "Port to listen on (Deprecated)",
                                   0, 65536, 0,
-                                  G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                  G_PARAM_READWRITE |
+                                  G_PARAM_CONSTRUCT_ONLY |
+                                  G_PARAM_DEPRECATED));
+       /**
+        * SoupServer:interface:
+        *
+        * The address of the network interface the server is
+        * listening on, if you are using the old #SoupServer API.
+        * (This will not be set if you use soup_server_listen(),
+        * etc.)
+        *
+        * Deprecated: #SoupServers can listen on multiple interfaces
+        * at once now. Use soup_server_listen(), etc, to listen on an
+        * interface, and soup_server_get_uris() to see what addresses
+        * are being listened on.
+        */
        /**
         * SOUP_SERVER_INTERFACE:
         *
-        * Alias for the #SoupServer:interface property. (The address
-        * of the network interface the server listens on.)
+        * Alias for the #SoupServer:interface property, qv.
+        *
+        * Deprecated: #SoupServers can listen on multiple interfaces
+        * at once now. Use soup_server_listen(), etc, to listen on an
+        * interface, and soup_server_get_uris() to see what addresses
+        * are being listened on.
         **/
        g_object_class_install_property (
                object_class, PROP_INTERFACE,
                g_param_spec_object (SOUP_SERVER_INTERFACE,
                                     "Interface",
-                                    "Address of interface to listen on",
+                                    "Address of interface to listen on (Deprecated)",
                                     SOUP_TYPE_ADDRESS,
-                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_CONSTRUCT_ONLY |
+                                    G_PARAM_DEPRECATED));
        /**
         * SOUP_SERVER_SSL_CERT_FILE:
         *
         * Alias for the #SoupServer:ssl-cert-file property, qv.
+        *
+        * Deprecated: use #SoupServer:tls-certificate or
+        * soup_server_set_ssl_certificate().
         */
        /**
         * SoupServer:ssl-cert-file:
         *
-        * Path to a file containing a PEM-encoded certificate. If
-        * this and #SoupServer:ssl-key-file are both set, then the
-        * server will speak https rather than plain http.
+        * Path to a file containing a PEM-encoded certificate.
         *
-        * Alternatively, you can use #SoupServer:tls-certificate
-        * to provide an arbitrary #GTlsCertificate.
+        * If you set this property and #SoupServer:ssl-key-file at
+        * construct time, then soup_server_new() will try to read the
+        * files; if it cannot, it will return %NULL, with no explicit
+        * indication of what went wrong (and logging a warning with
+        * newer versions of glib, since returning %NULL from a
+        * constructor is illegal).
+        *
+        * Deprecated: use #SoupServer:tls-certificate or
+        * soup_server_set_ssl_certificate().
         */
        g_object_class_install_property (
                object_class, PROP_SSL_CERT_FILE,
                g_param_spec_string (SOUP_SERVER_SSL_CERT_FILE,
-                                    "SSL certificate file",
-                                    "File containing server SSL certificate",
+                                    "TLS (aka SSL) certificate file",
+                                    "File containing server TLS (aka SSL) certificate",
                                     NULL,
-                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_CONSTRUCT_ONLY));
        /**
         * SOUP_SERVER_SSL_KEY_FILE:
         *
         * Alias for the #SoupServer:ssl-key-file property, qv.
+        *
+        * Deprecated: use #SoupServer:tls-certificate or
+        * soup_server_set_ssl_certificate().
         */
        /**
         * SoupServer:ssl-key-file:
         *
-        * Path to a file containing a PEM-encoded private key. If
-        * this and #SoupServer:ssl-key-file are both set, then the
-        * server will speak https rather than plain http. Note that
-        * you are allowed to set them to the same value, if you have
-        * a single file containing both the certificate and the key.
+        * Path to a file containing a PEM-encoded private key. See
+        * #SoupServer:ssl-cert-file for more information about how this
+        * is used.
         *
-        * Alternatively, you can use #SoupServer:tls-certificate
-        * to provide an arbitrary #GTlsCertificate.
+        * Deprecated: use #SoupServer:tls-certificate or
+        * soup_server_set_ssl_certificate().
         */
        g_object_class_install_property (
                object_class, PROP_SSL_KEY_FILE,
                g_param_spec_string (SOUP_SERVER_SSL_KEY_FILE,
-                                    "SSL key file",
-                                    "File containing server SSL key",
+                                    "TLS (aka SSL) key file",
+                                    "File containing server TLS (aka SSL) key",
                                     NULL,
-                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                    G_PARAM_READWRITE |
+                                    G_PARAM_CONSTRUCT_ONLY));
        /**
         * SOUP_SERVER_TLS_CERTIFICATE:
         *
         * Alias for the #SoupServer:tls-certificate property, qv.
+        *
+        * Since: 2.38
         */
        /**
         * SoupServer:tls-certificate:
         *
         * A #GTlsCertificate that has a #GTlsCertificate:private-key
-        * set. If this is set, then the server will speak https
-        * rather than plain http.
+        * set. If this is set, then the server will be able to speak
+        * https in addition to (or instead of) plain http.
         *
-        * Alternatively, you can use #SoupServer:ssl-cert-file and
-        * #SoupServer:ssl-key-file properties, to have #SoupServer
-        * read in a a certificate from a file.
+        * Alternatively, you can call soup_server_set_ssl_cert_file()
+        * to have #SoupServer read in a a certificate from a file.
+        *
+        * Since: 2.38
         */
        g_object_class_install_property (
                object_class, PROP_TLS_CERTIFICATE,
@@ -609,17 +736,33 @@ soup_server_class_init (SoupServerClass *server_class)
                                     G_TYPE_TLS_CERTIFICATE,
                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
        /**
+        * SoupServer:async-context:
+        *
+        * The server's #GMainContext, if you are using the old API.
+        * Servers created using soup_server_listen() will listen on
+        * the #GMainContext that was the thread-default context at
+        * the time soup_server_listen() was called.
+        *
+        * Deprecated: The new API uses the thread-default #GMainContext
+        * rather than having an explicitly-specified one.
+        */
+       /**
         * SOUP_SERVER_ASYNC_CONTEXT:
         *
-        * Alias for the #SoupServer:async-context property. (The
-        * server's #GMainContext.)
+        * Alias for the deprecated #SoupServer:async-context
+        * property, qv.
+        *
+        * Deprecated: The new API uses the thread-default #GMainContext
+        * rather than having an explicitly-specified one.
         **/
        g_object_class_install_property (
                object_class, PROP_ASYNC_CONTEXT,
                g_param_spec_pointer (SOUP_SERVER_ASYNC_CONTEXT,
                                      "Async GMainContext",
                                      "The GMainContext to dispatch async I/O in",
-                                     G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+                                     G_PARAM_READWRITE |
+                                     G_PARAM_CONSTRUCT_ONLY |
+                                     G_PARAM_DEPRECATED));
        /**
         * SOUP_SERVER_RAW_PATHS:
         *
@@ -744,9 +887,11 @@ soup_server_class_init (SoupServerClass *server_class)
  * @optname1: name of first property to set
  * @...: value of @optname1, followed by additional property/value pairs
  *
- * Creates a new #SoupServer.
+ * Creates a new #SoupServer. This is exactly equivalent to calling
+ * g_object_new() and specifying %SOUP_TYPE_SERVER as the type.
  *
- * Return value: a new #SoupServer
+ * Return value: a new #SoupServer. If you are using certain legacy
+ * properties, this may also return %NULL if an error occurs.
  **/
 SoupServer *
 soup_server_new (const char *optname1, ...)
@@ -766,32 +911,86 @@ soup_server_new (const char *optname1, ...)
  * soup_server_get_port:
  * @server: a #SoupServer
  *
- * Gets the TCP port that @server is listening on. This is most useful
- * when you did not request a specific port (or explicitly requested
- * %SOUP_ADDRESS_ANY_PORT).
+ * Gets the TCP port that @server is listening on, if you are using
+ * the old API.
  *
  * Return value: the port @server is listening on.
+ *
+ * Deprecated: If you are using soup_server_listen(), etc, then use
+ * soup_server_get_uris() to get a list of all listening addresses.
  **/
 guint
 soup_server_get_port (SoupServer *server)
 {
+       SoupServerPrivate *priv;
+
        g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       soup_server_ensure_listening (server);
+       g_return_val_if_fail (priv->legacy_iface != NULL, 0);
+
+       return priv->legacy_port;
+}
+
+/**
+ * soup_server_set_ssl_cert_file:
+ * @server: a #SoupServer
+ * @ssl_cert_file: path to a file containing a PEM-encoded SSL/TLS
+ *   certificate.
+ * @ssl_key_file: path to a file containing a PEM-encoded private key.
+ * @error: return location for a #GError
+ *
+ * Sets @server up to do https, using the SSL/TLS certificate
+ * specified by @ssl_cert_file and @ssl_key_file (which may point to
+ * the same file).
+ *
+ * Alternatively, you can set the #SoupServer:tls-certificate property
+ * at construction time, if you already have a #GTlsCertificate.
+ *
+ * Return value: success or failure.
+ *
+ * Since: 2.46
+ */
+gboolean
+soup_server_set_ssl_cert_file  (SoupServer  *server,
+                               const char  *ssl_cert_file,
+                               const char  *ssl_key_file,
+                               GError     **error)
+{
+       SoupServerPrivate *priv;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE);
+       priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       return SOUP_SERVER_GET_PRIVATE (server)->port;
+       if (priv->tls_cert)
+               g_object_unref (priv->tls_cert);
+       priv->tls_cert = g_tls_certificate_new_from_files (priv->ssl_cert_file,
+                                                          priv->ssl_key_file,
+                                                          error);
+       return priv->tls_cert != NULL;
 }
 
 /**
  * soup_server_is_https:
  * @server: a #SoupServer
  *
- * Checks whether @server is running plain http or https.
+ * Checks whether @server is capable of https.
  *
- * In order for a server to run https, you must set the
- * %SOUP_SERVER_SSL_CERT_FILE and %SOUP_SERVER_SSL_KEY_FILE properties
- * or %SOUP_SERVER_TLS_CERTIFICATE property to provide it with an SSL
+ * In order for a server to run https, you must call
+ * soup_server_set_ssl_cert_file(), or set the
+ * #SoupServer:tls-certificate property, to provide it with a
  * certificate to use.
  *
- * Return value: %TRUE if @server is serving https.
+ * If you are using the deprecated single-listener APIs, then a return
+ * value of %TRUE indicates that the #SoupServer serves https
+ * exclusively. If you are using soup_server_listen(), etc, then a
+ * %TRUE return value merely indicates that the server is
+ * <emphasis>able</emphasis> to do https, regardless of whether it
+ * actually currently is or not. Use soup_server_get_uris() to see if
+ * it currently has any https listeners.
+ *
+ * Return value: %TRUE if @server is configured to serve https.
  **/
 gboolean
 soup_server_is_https (SoupServer *server)
@@ -801,18 +1000,23 @@ soup_server_is_https (SoupServer *server)
        g_return_val_if_fail (SOUP_IS_SERVER (server), 0);
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       return priv->ssl_cert != NULL;
+       return priv->tls_cert != NULL;
 }
 
 /**
  * soup_server_get_listener:
  * @server: a #SoupServer
  *
- * Gets @server's listening socket. You should treat this as
- * read-only; writing to it or modifiying it may cause @server to
- * malfunction.
+ * Gets @server's listening socket, if you are using the old API.
+ *
+ * You should treat this socket as read-only; writing to it or
+ * modifiying it may cause @server to malfunction.
  *
  * Return value: (transfer none): the listening socket.
+ *
+ * Deprecated: If you are using soup_server_listen(), etc, then use
+ * soup_server_get_listeners() to get a list of all listening sockets,
+ * but note that that function returns #GSockets, not #SoupSockets.
  **/
 SoupSocket *
 soup_server_get_listener (SoupServer *server)
@@ -822,7 +1026,45 @@ soup_server_get_listener (SoupServer *server)
        g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       return priv->listen_sock;
+       soup_server_ensure_listening (server);
+       g_return_val_if_fail (priv->legacy_iface != NULL, NULL);
+
+       return priv->listeners ? priv->listeners->data : NULL;
+}
+
+/**
+ * soup_server_get_listeners:
+ * @server: a #SoupServer
+ *
+ * Gets @server's list of listening sockets.
+ *
+ * You should treat these sockets as read-only; writing to or
+ * modifiying any of these sockets may cause @server to malfunction.
+ *
+ * (Beware that in contrast to the old soup_server_get_listener(), this
+ * function returns #GSockets, not #SoupSockets.)
+ *
+ * Return value: (transfer container) (element-type Gio.Socket): a
+ * list of listening sockets.
+ **/
+GSList *
+soup_server_get_listeners (SoupServer *server)
+{
+       SoupServerPrivate *priv;
+       GSList *listeners, *iter;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       listeners = NULL;
+       for (iter = priv->listeners; iter; iter = iter->next)
+               listeners = g_slist_prepend (listeners, soup_socket_get_gsocket (iter->data));
+
+       /* priv->listeners has the sockets in reverse order from how
+        * they were added, so listeners now has them back in the
+        * original order.
+        */
+       return listeners;
 }
 
 static void start_request (SoupServer *, SoupClientContext *);
@@ -842,14 +1084,11 @@ soup_client_context_new (SoupServer *server, SoupSocket *sock)
 static void
 soup_client_context_cleanup (SoupClientContext *client)
 {
-       if (client->auth_domain) {
-               g_object_unref (client->auth_domain);
-               client->auth_domain = NULL;
-       }
-       if (client->auth_user) {
-               g_free (client->auth_user);
-               client->auth_user = NULL;
-       }
+       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;
 }
 
@@ -927,8 +1166,8 @@ got_headers (SoupMessage *msg, SoupClientContext *client)
        char *auth_user;
 
        uri = soup_message_get_uri (msg);
-       if ((soup_server_is_https (server) && !soup_uri_is_https (uri, priv->https_aliases)) ||
-           (!soup_server_is_https (server) && !soup_uri_is_http (uri, priv->http_aliases))) {
+       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);
                return;
        }
@@ -1054,12 +1293,9 @@ start_request (SoupServer *server, SoupClientContext *client)
 
        g_object_ref (client->sock);
 
-       if (priv->async_context)
-               g_main_context_push_thread_default (priv->async_context);
        soup_message_read_request (msg, client->sock,
+                                  priv->legacy_iface == NULL,
                                   request_finished, client);
-       if (priv->async_context)
-               g_main_context_pop_thread_default (priv->async_context);
 }
 
 static void
@@ -1073,7 +1309,7 @@ socket_disconnected (SoupSocket *sock, SoupClientContext *client)
 }
 
 static void
-new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
+new_connection (SoupSocket *listener, SoupSocket *sock, gpointer user_data)
 {
        SoupServer *server = user_data;
        SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
@@ -1090,24 +1326,33 @@ new_connection (SoupSocket *listner, SoupSocket *sock, gpointer user_data)
  * soup_server_run_async:
  * @server: a #SoupServer
  *
- * Starts @server, causing it to listen for and process incoming
- * connections.
+ * Starts @server, if you are using the old API, causing it to listen
+ * for and process incoming connections.
  *
- * The server actually runs in @server's #GMainContext. It will not
- * actually perform any processing unless the appropriate main loop is
- * running. In the simple case where you did not set the server's
+ * The server runs in @server's #GMainContext. It will not actually
+ * perform any processing unless the appropriate main loop is running.
+ * In the simple case where you did not set the server's
  * %SOUP_SERVER_ASYNC_CONTEXT property, this means the server will run
  * whenever the glib main loop is running.
+ *
+ * Deprecated: When using soup_server_listen(), etc, the server will
+ * always listen for connections, and will process them whenever the
+ * thread-default #GMainContext is running.
  **/
 void
 soup_server_run_async (SoupServer *server)
 {
        SoupServerPrivate *priv;
+       SoupSocket *listener;
 
        g_return_if_fail (SOUP_IS_SERVER (server));
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       if (!priv->listen_sock) {
+       soup_server_ensure_listening (server);
+
+       g_return_if_fail (priv->legacy_iface != NULL);
+
+       if (!priv->listeners) {
                if (priv->loop) {
                        g_main_loop_unref (priv->loop);
                        priv->loop = NULL;
@@ -1115,21 +1360,26 @@ soup_server_run_async (SoupServer *server)
                return;
        }
 
-       g_signal_connect (priv->listen_sock, "new_connection",
+       listener = priv->listeners->data;
+       g_signal_connect (listener, "new_connection",
                          G_CALLBACK (new_connection), server);
 
        return;
-
 }
 
 /**
  * soup_server_run:
  * @server: a #SoupServer
  *
- * Starts @server, causing it to listen for and process incoming
- * connections. Unlike soup_server_run_async(), this creates a
- * #GMainLoop and runs it, and it will not return until someone calls
- * soup_server_quit() to stop the server.
+ * Starts @server, if you are using the old API, causing it to listen
+ * for and process incoming connections. Unlike
+ * soup_server_run_async(), this creates a #GMainLoop and runs it, and
+ * it will not return until someone calls soup_server_quit() to stop
+ * the server.
+ *
+ * Deprecated: When using soup_server_listen(), etc, the server will
+ * always listen for connections, and will process them whenever the
+ * thread-default #GMainContext is running.
  **/
 void
 soup_server_run (SoupServer *server)
@@ -1141,7 +1391,9 @@ soup_server_run (SoupServer *server)
 
        if (!priv->loop) {
                priv->loop = g_main_loop_new (priv->async_context, TRUE);
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
                soup_server_run_async (server);
+               G_GNUC_END_IGNORE_DEPRECATIONS;
        }
 
        if (priv->loop)
@@ -1152,21 +1404,34 @@ soup_server_run (SoupServer *server)
  * soup_server_quit:
  * @server: a #SoupServer
  *
- * Stops processing for @server. Call this to clean up after
- * soup_server_run_async(), or to terminate a call to soup_server_run().
+ * Stops processing for @server, if you are using the old API. Call
+ * this to clean up after soup_server_run_async(), or to terminate a
+ * call to soup_server_run().
+ *
+ * Note that messages currently in progress will continue to be
+ * handled, if the main loop associated with the server is resumed or
+ * kept running.
  *
  * @server is still in a working state after this call; you can start
  * and stop a server as many times as you want.
+ *
+ * Deprecated: When using soup_server_listen(), etc, the server will
+ * always listen for connections, and will process them whenever the
+ * thread-default #GMainContext is running.
  **/
 void
 soup_server_quit (SoupServer *server)
 {
        SoupServerPrivate *priv;
+       SoupSocket *listener;
 
        g_return_if_fail (SOUP_IS_SERVER (server));
        priv = SOUP_SERVER_GET_PRIVATE (server);
+       g_return_if_fail (priv->legacy_iface != NULL);
+       g_return_if_fail (priv->listeners != NULL);
 
-       g_signal_handlers_disconnect_by_func (priv->listen_sock,
+       listener = priv->listeners->data;
+       g_signal_handlers_disconnect_by_func (listener,
                                              G_CALLBACK (new_connection),
                                              server);
        if (priv->loop)
@@ -1177,43 +1442,408 @@ soup_server_quit (SoupServer *server)
  * soup_server_disconnect:
  * @server: a #SoupServer
  *
- * Stops processing for @server and closes its socket. This implies
- * the effects of soup_server_quit(), but additionally closes the
- * listening socket.  Note that messages currently in progress will
- * continue to be handled, if the main loop associated with the
- * server is resumed or kept running.
+ * Closes and frees @server's listening sockets. If you are using the
+ * old #SoupServer APIs, this also includes the effect of
+ * soup_server_quit().
  *
- * After calling this function, @server is no longer functional, so it
- * has nearly the same effect as destroying @server entirely. The
- * function is thus useful mainly for language bindings without
- * explicit control over object lifetime.
+ * Note that if there are currently requests in progress on @server,
+ * that they will continue to be processed if @server's #GMainContext
+ * is still running.
+ *
+ * You can call soup_server_listen(), etc, after calling this function
+ * if you want to start listening again.
  **/
 void
 soup_server_disconnect (SoupServer *server)
 {
        SoupServerPrivate *priv;
+       GSList *listeners, *iter;
+       SoupSocket *listener;
 
        g_return_if_fail (SOUP_IS_SERVER (server));
        priv = SOUP_SERVER_GET_PRIVATE (server);
 
-       soup_server_quit (server);
+       if (priv->legacy_iface) {
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+               soup_server_quit (server);
+               G_GNUC_END_IGNORE_DEPRECATIONS;
+       }
 
-       if (priv->listen_sock) {
-               soup_socket_disconnect (priv->listen_sock);
-               g_object_unref (priv->listen_sock);
-               priv->listen_sock = NULL;
+       listeners = priv->listeners;
+       priv->listeners = NULL;
+       for (iter = listeners; iter; iter = iter->next) {
+               listener = iter->data;
+               soup_socket_disconnect (listener);
+               g_object_unref (listener);
        }
+       g_slist_free (listeners);
+}
+
+static gboolean 
+soup_server_listen_ipv4_ipv6 (SoupServer *server,
+                             GInetAddress *iaddr4,
+                             GInetAddress *iaddr6,
+                             guint port,
+                             SoupServerListenOptions options,
+                             GError **error)
+{
+       SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
+       GSocketAddress *addr4, *addr6;
+       GError *my_error = NULL;
+       SoupSocket *v4sock;
+       guint v4port;
+
+       options &= ~(SOUP_SERVER_LISTEN_IPV4_ONLY | SOUP_SERVER_LISTEN_IPV6_ONLY);
+
+       if (iaddr4) {
+               addr4 = g_inet_socket_address_new (iaddr4, port);
+               if (!soup_server_listen (server, addr4, options, error)) {
+                       g_object_unref (addr4);
+                       return FALSE;
+               }
+               g_object_unref (addr4);
+
+               v4sock = priv->listeners->data;
+               v4port = soup_address_get_port (soup_socket_get_local_address (v4sock));
+       } else {
+               v4sock = NULL;
+               v4port = port;
+       }
+
+       if (iaddr6) {
+               addr6 = g_inet_socket_address_new (iaddr6, v4port);
+               if (soup_server_listen (server, addr6, options, &my_error)) {
+                       g_object_unref (addr6);
+                       return TRUE;
+               }
+               g_object_unref (addr6);
+
+               /* FIXME: this isn't right. Need to translate EAFNOSUPPORT */
+               if (!g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED)) {
+                       g_propagate_error (error, my_error);
+
+                       /* Close the IPv4 socket if IPv6 failed */
+                       if (v4sock) {
+                               priv->listeners = g_slist_remove (priv->listeners, v4sock);
+                               soup_socket_disconnect (v4sock);
+                               g_object_unref (v4sock);
+                       }
+
+                       return FALSE;
+               }
+
+               g_error_free (my_error);
+       }
+
+       return TRUE;
+}
+
+/**
+ * SoupServerListenOptions:
+ * @SOUP_SERVER_LISTEN_HTTPS: Listen for https connections rather
+ *   than plain http.
+ * @SOUP_SERVER_LISTEN_IPV4_ONLY: Only listen on IPv4 interfaces.
+ * @SOUP_SERVER_LISTEN_IPV6_ONLY: Only listen on IPv6 interfaces.
+ *
+ * Options to pass to soup_server_listen(), etc.
+ *
+ * %SOUP_SERVER_LISTEN_IPV4_ONLY and %SOUP_SERVER_LISTEN_IPV6_ONLY
+ * only make sense with soup_server_listen_all() and
+ * soup_server_listen_local(), not plain soup_server_listen() (which
+ * simply listens on whatever kind of socket you give it). And you
+ * cannot specify both of them in a single call.
+ *
+ * Since: 2.46
+ */
+
+/**
+ * soup_server_listen_all:
+ * @server: a #SoupServer
+ * @port: the port to listen on, or 0
+ * @options: listening options for this server
+ * @error: return location for a #GError
+ *
+ * This attempts to set up @server to listen for connections on all
+ * interfaces on the system. (That is, it listens on the addresses
+ * <literal>0.0.0.0</literal> and/or <literal>::</literal>, depending
+ * on whether @options includes %SOUP_SERVER_LISTEN_IPV4_ONLY,
+ * %SOUP_SERVER_LISTEN_IPV6_ONLY, or neither.) If @port is specified,
+ * @server will listen on that port. If it is 0, @server will find an
+ * unused port to listen on. (In that case, you can use
+ * soup_server_get_uris() to find out what port it ended up choosing.)
+ *
+ * If @options includes %SOUP_SERVER_LISTEN_HTTPS, and @server has
+ * been configured for TLS, then @server will listen for https
+ * connections on these ports. Otherwise it will listen for plain
+ * http.
+ *
+ * You may call this method (along with soup_server_listen_local() and
+ * soup_server_listen()) any number of times on a server, if you want
+ * to listen on multiple ports, or set up both http and https service.
+ *
+ * After calling this method, @server will begin accepting and
+ * processing connections as soon as the appropriate #GMainContext is
+ * run.
+ *
+ * Return value: %TRUE on success, %FALSE if @port could not be bound
+ * or any other error occurred (in which case @error will be set).
+ *
+ * Since: 2.46
+ **/
+gboolean 
+soup_server_listen_all (SoupServer *server, guint port,
+                       SoupServerListenOptions options,
+                       GError **error)
+{
+       GInetAddress *iaddr4, *iaddr6;
+       gboolean success;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE);
+       g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) ||
+                             !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE);
+
+       if (options & SOUP_SERVER_LISTEN_IPV6_ONLY)
+               iaddr4 = NULL;
+       else
+               iaddr4 = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
+
+       if (options & SOUP_SERVER_LISTEN_IPV4_ONLY)
+               iaddr6 = NULL;
+       else
+               iaddr6 = g_inet_address_new_any (G_SOCKET_FAMILY_IPV6);
+
+       success = soup_server_listen_ipv4_ipv6 (server, iaddr4, iaddr6,
+                                               port, options, error);
+
+       g_clear_object (&iaddr4);
+       g_clear_object (&iaddr6);
+
+       return success;
+}
+
+/**
+ * soup_server_listen_local:
+ * @server: a #SoupServer
+ * @port: the port to listen on, or 0
+ * @options: listening options for this server
+ * @error: return location for a #GError
+ *
+ * This attempts to set up @server to listen for connections on
+ * "localhost" (that is, <literal>127.0.0.1</literal> and/or
+ * <literal>::1</literal>, depending on whether @options includes
+ * %SOUP_SERVER_LISTEN_IPV4_ONLY, %SOUP_SERVER_LISTEN_IPV6_ONLY, or
+ * neither). If @port is specified, @server will listen on that port.
+ * If it is 0, @server will find an unused port to listen on. (In that
+ * case, you can use soup_server_get_uris() to find out what port it
+ * ended up choosing.)
+ *
+ * If @options includes %SOUP_SERVER_LISTEN_HTTPS, and @server has
+ * been configured for TLS, then @server will listen for https
+ * connections on these ports. Otherwise it will listen for plain
+ * http.
+ *
+ * You may call this method (along with soup_server_listen_all() and
+ * soup_server_listen()) any number of times on a server, if you want
+ * to listen on multiple ports, or set up both http and https service.
+ *
+ * After calling this method, @server will begin accepting and
+ * processing connections as soon as the appropriate #GMainContext is
+ * run.
+ *
+ * Return value: %TRUE on success, %FALSE if @port could not be bound
+ * or any other error occurred (in which case @error will be set).
+ *
+ * Since: 2.46
+ **/
+gboolean
+soup_server_listen_local (SoupServer *server, guint port,
+                         SoupServerListenOptions options,
+                         GError **error)
+{
+       GInetAddress *iaddr4, *iaddr6;
+       gboolean success;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE);
+       g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) ||
+                             !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE);
+
+       if (options & SOUP_SERVER_LISTEN_IPV6_ONLY)
+               iaddr4 = NULL;
+       else
+               iaddr4 = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
+
+       if (options & SOUP_SERVER_LISTEN_IPV4_ONLY)
+               iaddr6 = NULL;
+       else
+               iaddr6 = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV6);
+
+       success = soup_server_listen_ipv4_ipv6 (server, iaddr4, iaddr6,
+                                               port, options, error);
+
+       g_clear_object (&iaddr4);
+       g_clear_object (&iaddr6);
+
+       return success;
+}
+
+/**
+ * soup_server_listen:
+ * @server: a #SoupServer
+ * @address: the address of the interface to listen on
+ * @options: listening options for this server
+ * @error: return location for a #GError
+ *
+ * This attempts to set up @server to listen for connections on
+ * @address.
+ *
+ * If @options includes %SOUP_SERVER_LISTEN_HTTPS, and @server has
+ * been configured for TLS, then @server will listen for https
+ * connections on this port. Otherwise it will listen for plain http.
+ *
+ * You may call this method (along with soup_server_listen_all() and
+ * soup_server_listen_local()) any number of times on a server, if you
+ * want to listen on multiple ports, or set up both http and https
+ * service.n
+ *
+ * After calling this method, @server will begin accepting and
+ * processing connections as soon as the appropriate #GMainContext is
+ * run.
+ *
+ * Note that #SoupServer never makes use of dual IPv4/IPv6 sockets; if
+ * @address is an IPv6 address, it will only accept IPv6 connections.
+ * You must configure IPv4 listening separately.
+ *
+ * Return value: %TRUE on success, %FALSE if @address could not be
+ * bound or any other error occurred (in which case @error will be
+ * set).
+ *
+ * Since: 2.46
+ **/
+gboolean
+soup_server_listen (SoupServer *server, GSocketAddress *address,
+                   SoupServerListenOptions options,
+                   GError **error)
+{
+       SoupServerPrivate *priv;
+       SoupSocket *listener;
+       SoupAddress *saddr;
+       GTlsCertificate *tls_creds;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE);
+       g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) &&
+                             !(options & SOUP_SERVER_LISTEN_IPV6_ONLY), FALSE);
+
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+       g_return_val_if_fail (priv->disposed == FALSE, FALSE);
+
+       if (options & SOUP_SERVER_LISTEN_HTTPS) {
+               if (!priv->tls_cert) {
+                       g_set_error_literal (error,
+                                            G_IO_ERROR,
+                                            G_IO_ERROR_INVALID_ARGUMENT,
+                                            _("Can't create a TLS server without a TLS certificate"));
+                       return FALSE;
+               }
+               tls_creds = priv->tls_cert;
+       } else
+               tls_creds = NULL;
+
+       saddr = soup_address_new_from_gsockaddr (address);
+       listener = soup_socket_new (SOUP_SOCKET_LOCAL_ADDRESS, saddr,
+                                   SOUP_SOCKET_SSL_CREDENTIALS, tls_creds,
+                                   SOUP_SOCKET_USE_THREAD_CONTEXT, TRUE,
+                                   SOUP_SOCKET_IPV6_ONLY, TRUE,
+                                   NULL);
+
+       if (!soup_socket_listen (listener)) {
+               g_set_error (error,
+                            G_IO_ERROR,
+                            G_IO_ERROR_FAILED,
+                            _("Could not listen on address %s, port %d"),
+                            soup_address_get_physical (saddr),
+                            soup_address_get_port (saddr));
+               g_object_unref (saddr);
+               g_object_unref (listener);
+               return FALSE;
+       }
+
+       g_object_unref (saddr);
+
+       g_signal_connect (listener, "new_connection",
+                         G_CALLBACK (new_connection), server);
+
+       /* Note: soup_server_listen_ipv4_ipv6() above relies on the
+        * fact that this does g_slist_prepend().
+        */
+       priv->listeners = g_slist_prepend (priv->listeners, listener);
+       return TRUE;
+}
+
+/**
+ * soup_server_get_uris:
+ * @server: a #SoupServer
+ *
+ * Gets a list of URIs corresponding to the interfaces @server is
+ * listening on. These will contain IP addresses, not hostnames, and
+ * will also indicate whether the given listener is http or https.
+ *
+ * Note that if you used soup_server_listen_all(), the returned URIs
+ * will use the addresses <literal>0.0.0.0</literal> and
+ * <literal>::</literal>, rather than actually returning separate URIs
+ * for each interface on the system.
+ *
+ * Return value: (transfer full) (element-type Soup.URI): a list of
+ * #SoupURIs, which you must free when you are done with it.
+ *
+ * Since: 2.46
+ */
+GSList *
+soup_server_get_uris (SoupServer *server)
+{
+       SoupServerPrivate *priv;
+       GSList *uris, *l;
+       SoupSocket *listener;
+       SoupAddress *addr;
+       SoupURI *uri;
+       gpointer creds;
+
+       g_return_val_if_fail (SOUP_IS_SERVER (server), NULL);
+       priv = SOUP_SERVER_GET_PRIVATE (server);
+
+       for (l = priv->listeners, uris = NULL; l; l = l->next) {
+               listener = l->data;
+               addr = soup_socket_get_local_address (listener);
+               g_object_get (G_OBJECT (listener), SOUP_SOCKET_SSL_CREDENTIALS, &creds, NULL);
+
+               uri = soup_uri_new (NULL);
+               soup_uri_set_scheme (uri, creds ? "https" : "http");
+               soup_uri_set_host (uri, soup_address_get_physical (addr));
+               soup_uri_set_port (uri, soup_address_get_port (addr));
+               soup_uri_set_path (uri, "/");
+
+               uris = g_slist_prepend (uris, uri);
+       }
+
+       return uris;
 }
 
 /**
  * soup_server_get_async_context:
  * @server: a #SoupServer
  *
- * Gets @server's async_context. This does not add a ref to the
- * context, so you will need to ref it yourself if you want it to
- * outlive its server.
+ * Gets @server's async_context, if you are using the old API. (With
+ * the new API, the server runs in the thread's thread-default
+ * #GMainContext, regardless of what this method returns.)
+ *
+ * This does not add a ref to the context, so you will need to ref it
+ * yourself if you want it to outlive its server.
  *
- * Return value: (transfer none): @server's #GMainContext, which may be %NULL
+ * Return value: (transfer none): @server's #GMainContext, which may
+ * be %NULL
+ *
+ * Deprecated: If you are using soup_server_listen(), etc, then
+ * the server listens on the thread-default #GMainContext, and this
+ * property is ignored.
  **/
 GMainContext *
 soup_server_get_async_context (SoupServer *server)
@@ -1235,9 +1865,9 @@ soup_server_get_async_context (SoupServer *server)
  * soup_client_context_get_auth_user() to determine if HTTP
  * authentication was used successfully.
  *
- * soup_client_context_get_address() and/or
+ * 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
+ * logging or debugging purposes. soup_client_context_get_gsocket() may
  * also be of use in some situations (eg, tracking when multiple
  * requests are made on the same connection).
  **/
@@ -1259,6 +1889,9 @@ G_DEFINE_BOXED_TYPE (SoupClientContext, soup_client_context, soup_client_context
  *
  * Return value: (transfer none): the #SoupSocket that @client is
  * associated with.
+ *
+ * Deprecated: use soup_client_context_get_gsocket(), which returns
+ * a #GSocket.
  **/
 SoupSocket *
 soup_client_context_get_socket (SoupClientContext *client)
@@ -1269,6 +1902,32 @@ soup_client_context_get_socket (SoupClientContext *client)
 }
 
 /**
+ * soup_client_context_get_gsocket:
+ * @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: (transfer none): the #GSocket that @client is
+ * associated with.
+ *
+ * Since: 2.46
+ **/
+GSocket *
+soup_client_context_get_gsocket (SoupClientContext *client)
+{
+       g_return_val_if_fail (client != NULL, NULL);
+
+       return soup_socket_get_gsocket (client->sock);
+}
+
+/**
  * soup_client_context_get_address:
  * @client: a #SoupClientContext
  *
@@ -1277,6 +1936,9 @@ soup_client_context_get_socket (SoupClientContext *client)
  *
  * Return value: (transfer none): the #SoupAddress associated with the
  * remote end of a connection.
+ *
+ * Deprecated: Use soup_client_context_get_remote_address(), which returns
+ * a #GSocketAddress.
  **/
 SoupAddress *
 soup_client_context_get_address (SoupClientContext *client)
@@ -1287,13 +1949,55 @@ soup_client_context_get_address (SoupClientContext *client)
 }
 
 /**
+ * soup_client_context_get_remote_address:
+ * @client: a #SoupClientContext
+ *
+ * Retrieves the #GSocketAddress associated with the remote end
+ * of a connection.
+ *
+ * Return value: (transfer none): the #GSocketAddress associated with
+ * the remote end of a connection.
+ *
+ * Since: 2.46
+ **/
+GSocketAddress *
+soup_client_context_get_remote_address (SoupClientContext *client)
+{
+       g_return_val_if_fail (client != NULL, NULL);
+
+       if (!client->remote_addr)
+               client->remote_addr = g_socket_get_remote_address (soup_client_context_get_gsocket (client), 
NULL);
+       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: (transfer none): the #GSocketAddress associated with
+ * the local end of a connection.
+ *
+ * Since: 2.46
+ **/
+GSocketAddress *
+soup_client_context_get_local_address (SoupClientContext *client)
+{
+       g_return_val_if_fail (client != NULL, NULL);
+
+       if (!client->local_addr)
+               client->local_addr = g_socket_get_local_address (soup_client_context_get_gsocket (client), 
NULL);
+       return client->local_addr;
+}
+
+/**
  * soup_client_context_get_host:
  * @client: a #SoupClientContext
  *
  * Retrieves the IP address associated with the remote end of a
- * connection. (If you want the actual hostname, you'll have to call
- * soup_client_context_get_address() and then call the appropriate
- * #SoupAddress method to resolve it.)
+ * connection.
  *
  * Return value: the IP address associated with the remote end of a
  * connection.
@@ -1303,7 +2007,9 @@ soup_client_context_get_host (SoupClientContext *client)
 {
        SoupAddress *address;
 
+       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
        address = soup_client_context_get_address (client);
+       G_GNUC_END_IGNORE_DEPRECATIONS;
        return soup_address_get_physical (address);
 }
 
diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h
index 0d09322..d9429d6 100644
--- a/libsoup/soup-server.h
+++ b/libsoup/soup-server.h
@@ -22,6 +22,12 @@ typedef struct SoupClientContext SoupClientContext;
 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),
+       SOUP_SERVER_LISTEN_IPV6_ONLY = (1 << 2)
+} SoupServerListenOptions;
+
 struct _SoupServer {
        GObject parent;
 
@@ -56,61 +62,109 @@ typedef void (*SoupServerCallback) (SoupServer        *server,
                                    SoupClientContext *client,
                                    gpointer           user_data);
 
-#define SOUP_SERVER_PORT            "port"
-#define SOUP_SERVER_INTERFACE       "interface"
-#define SOUP_SERVER_SSL_CERT_FILE   "ssl-cert-file"
-#define SOUP_SERVER_SSL_KEY_FILE    "ssl-key-file"
 #define SOUP_SERVER_TLS_CERTIFICATE "tls-certificate"
-#define SOUP_SERVER_ASYNC_CONTEXT   "async-context"
 #define SOUP_SERVER_RAW_PATHS       "raw-paths"
 #define SOUP_SERVER_SERVER_HEADER   "server-header"
 #define SOUP_SERVER_HTTP_ALIASES    "http-aliases"
 #define SOUP_SERVER_HTTPS_ALIASES   "https-aliases"
 
-SoupServer        *soup_server_new            (const char            *optname1,
-                                              ...) G_GNUC_NULL_TERMINATED;
-
-gboolean           soup_server_is_https       (SoupServer            *server);
-guint              soup_server_get_port       (SoupServer            *server);
-
-SoupSocket        *soup_server_get_listener   (SoupServer            *server);
-
-void               soup_server_run            (SoupServer            *server);
-void               soup_server_run_async      (SoupServer            *server);
-void               soup_server_quit           (SoupServer            *server);
-void               soup_server_disconnect     (SoupServer            *server);
+SoupServer     *soup_server_new                (const char               *optname1,
+                                               ...) G_GNUC_NULL_TERMINATED;
+
+SOUP_AVAILABLE_IN_2_46
+gboolean        soup_server_set_ssl_cert_file  (SoupServer               *server,
+                                               const char               *ssl_cert_file,
+                                               const char               *ssl_key_file,
+                                               GError                  **error);
+
+SOUP_AVAILABLE_IN_2_46
+gboolean        soup_server_listen_all         (SoupServer               *server,
+                                               guint                     port,
+                                               SoupServerListenOptions   options,
+                                               GError                  **error);
+SOUP_AVAILABLE_IN_2_46
+gboolean        soup_server_listen_local       (SoupServer               *server,
+                                               guint                     port,
+                                               SoupServerListenOptions   options,
+                                               GError                  **error);
+SOUP_AVAILABLE_IN_2_46
+gboolean        soup_server_listen             (SoupServer               *server,
+                                               GSocketAddress           *interface,
+                                               SoupServerListenOptions   options,
+                                               GError                  **error);
+SOUP_AVAILABLE_IN_2_46
+GSList         *soup_server_get_uris           (SoupServer               *server);
+SOUP_AVAILABLE_IN_2_46
+GSList         *soup_server_get_listeners      (SoupServer               *server);
+
+void            soup_server_disconnect         (SoupServer               *server);
 
-GMainContext      *soup_server_get_async_context (SoupServer         *server);
 
 /* Handlers and auth */
 
-void               soup_server_add_handler    (SoupServer            *server,
-                                              const char            *path,
-                                              SoupServerCallback     callback,
-                                              gpointer               user_data,
-                                              GDestroyNotify         destroy);
-void               soup_server_remove_handler (SoupServer            *server,
-                                              const char            *path);
+void            soup_server_add_handler        (SoupServer         *server,
+                                               const char         *path,
+                                               SoupServerCallback  callback,
+                                               gpointer            user_data,
+                                               GDestroyNotify      destroy);
+void            soup_server_remove_handler     (SoupServer         *server,
+                                               const char         *path);
 
-void               soup_server_add_auth_domain    (SoupServer     *server,
-                                                  SoupAuthDomain *auth_domain);
-void               soup_server_remove_auth_domain (SoupServer     *server,
-                                                  SoupAuthDomain *auth_domain);
+void            soup_server_add_auth_domain    (SoupServer         *server,
+                                               SoupAuthDomain     *auth_domain);
+void            soup_server_remove_auth_domain (SoupServer         *server,
+                                               SoupAuthDomain     *auth_domain);
 
 /* I/O */
 
-void               soup_server_pause_message   (SoupServer           *server,
-                                               SoupMessage          *msg);
-void               soup_server_unpause_message (SoupServer           *server,
-                                               SoupMessage          *msg);
+void            soup_server_pause_message   (SoupServer  *server,
+                                            SoupMessage *msg);
+void            soup_server_unpause_message (SoupServer  *server,
+                                            SoupMessage *msg);
 
 /* Client context */
 
-SoupSocket     *soup_client_context_get_socket      (SoupClientContext *client);
-SoupAddress    *soup_client_context_get_address     (SoupClientContext *client);
-const char     *soup_client_context_get_host        (SoupClientContext *client);
-SoupAuthDomain *soup_client_context_get_auth_domain (SoupClientContext *client);
-const char     *soup_client_context_get_auth_user   (SoupClientContext *client);
+SOUP_AVAILABLE_IN_2_46
+GSocket        *soup_client_context_get_gsocket        (SoupClientContext *client);
+SOUP_AVAILABLE_IN_2_46
+GSocketAddress *soup_client_context_get_local_address  (SoupClientContext *client);
+SOUP_AVAILABLE_IN_2_46
+GSocketAddress *soup_client_context_get_remote_address (SoupClientContext *client);
+const char     *soup_client_context_get_host           (SoupClientContext *client);
+SoupAuthDomain *soup_client_context_get_auth_domain    (SoupClientContext *client);
+const char     *soup_client_context_get_auth_user      (SoupClientContext *client);
+
+
+/* Legacy API */
+
+#define SOUP_SERVER_PORT          "port"
+#define SOUP_SERVER_INTERFACE     "interface"
+#define SOUP_SERVER_ASYNC_CONTEXT "async-context"
+#define SOUP_SERVER_SSL_CERT_FILE "ssl-cert-file"
+#define SOUP_SERVER_SSL_KEY_FILE  "ssl-key-file"
+
+SOUP_DEPRECATED_IN_2_46
+gboolean      soup_server_is_https            (SoupServer        *server);
+SOUP_DEPRECATED_IN_2_46
+guint         soup_server_get_port            (SoupServer        *server);
+
+SOUP_DEPRECATED_IN_2_46
+SoupSocket   *soup_server_get_listener        (SoupServer        *server);
+
+SOUP_DEPRECATED_IN_2_46
+GMainContext *soup_server_get_async_context   (SoupServer        *server);
+
+SOUP_DEPRECATED_IN_2_46
+void          soup_server_run                 (SoupServer        *server);
+SOUP_DEPRECATED_IN_2_46
+void          soup_server_run_async           (SoupServer        *server);
+SOUP_DEPRECATED_IN_2_46
+void          soup_server_quit                (SoupServer        *server);
+
+SOUP_DEPRECATED_IN_2_46
+SoupAddress  *soup_client_context_get_address (SoupClientContext *client);
+SOUP_DEPRECATED_IN_2_46
+SoupSocket   *soup_client_context_get_socket  (SoupClientContext *client);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index e0748fd..abde9b4 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -48,6 +48,7 @@ enum {
        PROP_LOCAL_ADDRESS,
        PROP_REMOTE_ADDRESS,
        PROP_NON_BLOCKING,
+       PROP_IPV6_ONLY,
        PROP_IS_SERVER,
        PROP_SSL_CREDENTIALS,
        PROP_SSL_STRICT,
@@ -74,6 +75,7 @@ typedef struct {
        GProxyResolver *proxy_resolver;
 
        guint non_blocking:1;
+       guint ipv6_only:1;
        guint is_server:1;
        guint ssl:1;
        guint ssl_strict:1;
@@ -201,6 +203,9 @@ soup_socket_set_property (GObject *object, guint prop_id,
        case PROP_NON_BLOCKING:
                priv->non_blocking = g_value_get_boolean (value);
                break;
+       case PROP_IPV6_ONLY:
+               priv->ipv6_only = g_value_get_boolean (value);
+               break;
        case PROP_SSL_CREDENTIALS:
                priv->ssl_creds = g_value_get_pointer (value);
                break;
@@ -251,6 +256,9 @@ soup_socket_get_property (GObject *object, guint prop_id,
        case PROP_NON_BLOCKING:
                g_value_set_boolean (value, priv->non_blocking);
                break;
+       case PROP_IPV6_ONLY:
+               g_value_set_boolean (value, priv->ipv6_only);
+               break;
        case PROP_IS_SERVER:
                g_value_set_boolean (value, priv->is_server);
                break;
@@ -463,6 +471,13 @@ soup_socket_class_init (SoupSocketClass *socket_class)
                                      "Whether or not the socket uses non-blocking I/O",
                                      TRUE,
                                      G_PARAM_READWRITE));
+       g_object_class_install_property (
+               object_class, PROP_IPV6_ONLY,
+               g_param_spec_boolean (SOUP_SOCKET_IPV6_ONLY,
+                                     "IPv6 only",
+                                     "IPv6 only",
+                                     FALSE,
+                                     G_PARAM_READWRITE));
        /**
         * SOUP_SOCKET_IS_SERVER:
         *
@@ -1056,6 +1071,17 @@ soup_socket_listen (SoupSocket *sock)
                goto cant_listen;
        finish_socket_setup (priv);
 
+#if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY)
+       if (priv->ipv6_only) {
+               int fd, v6_only;
+
+               fd = g_socket_get_fd (priv->gsock);
+               v6_only = TRUE;
+               setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY,
+                           &v6_only, sizeof (v6_only));
+       }
+#endif
+
        /* Bind */
        if (!g_socket_bind (priv->gsock, addr, TRUE, NULL))
                goto cant_listen;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 21c70d4..7bcbd50 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,5 +5,6 @@ libsoup/soup-message-client-io.c
 libsoup/soup-message-io.c
 libsoup/soup-message-server-io.c
 libsoup/soup-request.c
+libsoup/soup-server.c
 libsoup/soup-session.c
 libsoup/soup-tld.c
diff --git a/tests/auth-test.c b/tests/auth-test.c
index 992e3d5..3efd066 100644
--- a/tests/auth-test.c
+++ b/tests/auth-test.c
@@ -886,12 +886,10 @@ do_select_auth_test (void)
         * side of this scenario correctly, because we test it against
         * curl in server-auth-test.
         */
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-
-       uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (uri, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
 
        basic_auth_domain = soup_auth_domain_basic_new (
                SOUP_AUTH_DOMAIN_REALM, "auth-test",
@@ -1036,12 +1034,12 @@ do_auth_close_test (void)
 
        debug_printf (1, "\nTesting auth when server times out connection:\n");
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
 
-       uri = soup_uri_new ("http://127.0.0.1/close";);
-       soup_uri_set_port (uri, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       soup_uri_set_path (uri, "/close");
 
        basic_auth_domain = soup_auth_domain_basic_new (
                SOUP_AUTH_DOMAIN_REALM, "auth-test",
@@ -1160,9 +1158,7 @@ do_disappearing_auth_test (void)
        server = soup_test_server_new (FALSE);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-
-       uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (uri, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
 
        auth_domain = soup_auth_domain_basic_new (
                                                  SOUP_AUTH_DOMAIN_REALM, "auth-test",
diff --git a/tests/cache-test.c b/tests/cache-test.c
index a19e974..8a7a401 100644
--- a/tests/cache-test.c
+++ b/tests/cache-test.c
@@ -729,8 +729,7 @@ main (int argc, char **argv)
 
        server = soup_test_server_new (TRUE);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        do_basics_test (base_uri);
        do_cancel_test (base_uri);
diff --git a/tests/chunk-io-test.c b/tests/chunk-io-test.c
index f1f96b0..ea1002c 100644
--- a/tests/chunk-io-test.c
+++ b/tests/chunk-io-test.c
@@ -10,7 +10,6 @@ force_io_streams_init (void)
 {
        SoupServer *server;
        SoupSession *session;
-       guint port;
        SoupURI *base_uri;
        SoupMessage *msg;
 
@@ -20,10 +19,7 @@ force_io_streams_init (void)
         */
 
        server = soup_test_server_new (TRUE);
-       port =  soup_server_get_port (server);
-
-       base_uri = soup_uri_new ("http://127.0.0.1";);
-       soup_uri_set_port (base_uri, port);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
        msg = soup_message_new_from_uri ("POST", base_uri);
diff --git a/tests/chunk-test.c b/tests/chunk-test.c
index ce92956..050deb0 100644
--- a/tests/chunk-test.c
+++ b/tests/chunk-test.c
@@ -506,22 +506,19 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        SoupServer *server;
-       guint port;
-       SoupURI *base_uri;
+       SoupURI *uri;
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-       port =  soup_server_get_port (server);
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       base_uri = soup_uri_new ("http://127.0.0.1";);
-       soup_uri_set_port (base_uri, port);
-       do_chunk_tests (base_uri);
-       soup_uri_free (base_uri);
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       do_chunk_tests (uri);
+       soup_uri_free (uri);
 
        g_main_loop_unref (loop);
        soup_test_server_quit_unref (server);
diff --git a/tests/coding-test.c b/tests/coding-test.c
index 1bffbc4..1bde4e5 100644
--- a/tests/coding-test.c
+++ b/tests/coding-test.c
@@ -563,10 +563,9 @@ main (int argc, char **argv)
 {
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        do_coding_test ();
        do_coding_req_test ();
diff --git a/tests/connection-test.c b/tests/connection-test.c
index fd7179f..d9c38a2 100644
--- a/tests/connection-test.c
+++ b/tests/connection-test.c
@@ -51,10 +51,12 @@ timeout_request_started (SoupServer *server, SoupMessage *msg,
                         SoupClientContext *client, gpointer user_data)
 {
        SoupSocket *sock;
-       GMainContext *context = soup_server_get_async_context (server);
+       GMainContext *context = g_main_context_get_thread_default ();
        guint readable;
 
+       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
        sock = soup_client_context_get_socket (client);
+       G_GNUC_END_IGNORE_DEPRECATIONS;
        readable = g_signal_connect (sock, "readable",
                                    G_CALLBACK (timeout_socket), NULL);
        while (soup_socket_is_connected (sock))
@@ -130,7 +132,9 @@ server_callback (SoupServer *server, SoupMessage *msg,
                         * the declared Content-Length. Instead, we
                         * forcibly close the socket at that point.
                         */
+                       G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
                        sock = soup_client_context_get_socket (context);
+                       G_GNUC_END_IGNORE_DEPRECATIONS;
                        g_signal_connect (msg, "wrote-chunk",
                                          G_CALLBACK (close_socket), sock);
                } else if (no_close) {
@@ -148,7 +152,9 @@ server_callback (SoupServer *server, SoupMessage *msg,
        if (!strcmp (path, "/timeout-persistent")) {
                SoupSocket *sock;
 
+               G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
                sock = soup_client_context_get_socket (context);
+               G_GNUC_END_IGNORE_DEPRECATIONS;
                setup_timeout_persistent (server, sock);
        }
 
@@ -956,10 +962,9 @@ main (int argc, char **argv)
        apache_init ();
 #endif
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, "http", NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        do_content_length_framing_test ();
        do_persistent_connection_timeout_test ();
diff --git a/tests/context-test.c b/tests/context-test.c
index 97cd2c0..74bb1ea 100644
--- a/tests/context-test.c
+++ b/tests/context-test.c
@@ -65,7 +65,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
        sd->server = server;
        sd->msg = msg;
        sd->timeout = soup_add_timeout (
-               soup_server_get_async_context (server),
+               g_main_context_get_thread_default (),
                200, add_body_chunk, sd);
        g_signal_connect (msg, "finished",
                          G_CALLBACK (request_failed), sd);
@@ -364,13 +364,15 @@ int
 main (int argc, char **argv)
 {
        SoupServer *server;
+       SoupURI *uri;
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
-                                   soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       base_uri = soup_uri_to_string (uri, FALSE);
+       soup_uri_free (uri);
 
        do_test1 (1, FALSE);
        do_test1 (2, TRUE);
diff --git a/tests/continue-test.c b/tests/continue-test.c
index 7cc9f69..99c6f6a 100644
--- a/tests/continue-test.c
+++ b/tests/continue-test.c
@@ -10,7 +10,7 @@
 
 #define MAX_POST_LENGTH (sizeof (SHORT_BODY))
 
-static int port;
+static SoupURI *base_uri;
 static GSList *events;
 
 static void
@@ -54,24 +54,27 @@ do_message (const char *path, gboolean long_body,
        SoupSession *session;
        SoupMessage *msg;
        const char *body;
-       char *uri;
+       SoupURI *uri;
        va_list ap;
        const char *expected_event;
        char *actual_event;
        int expected_status, actual_status;
        static int count = 1;
 
-       debug_printf (1, "%d. /%s, %s body, %sExpect, %s password\n",
+       debug_printf (1, "%d. %s, %s body, %sExpect, %s password\n",
                      count++, path,
                      long_body ? "long" : "short",
                      expect_continue ? "" : "no ",
                      auth ? "with" : "without");
 
-       uri = g_strdup_printf ("http://%s127.0.0.1:%d/%s";,
-                              auth ? "user:pass@" : "",
-                              port, path);
-       msg = soup_message_new ("POST", uri);
-       g_free (uri);
+       uri = soup_uri_copy (base_uri);
+       if (auth) {
+               soup_uri_set_user (uri, "user");
+               soup_uri_set_password (uri, "pass");
+       }
+       soup_uri_set_path (uri, path);
+       msg = soup_message_new_from_uri ("POST", uri);
+       soup_uri_free (uri);
 
        body = long_body ? LONG_BODY : SHORT_BODY;
        soup_message_set_request (msg, "text/plain", SOUP_MEMORY_STATIC,
@@ -159,7 +162,7 @@ do_message (const char *path, gboolean long_body,
 static void
 run_tests (void)
 {
-       do_message ("unauth", FALSE, FALSE, FALSE,
+       do_message ("/unauth", FALSE, FALSE, FALSE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -171,7 +174,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("unauth", TRUE, FALSE, FALSE,
+       do_message ("/unauth", TRUE, FALSE, FALSE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -183,7 +186,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("unauth", FALSE, TRUE, FALSE,
+       do_message ("/unauth", FALSE, TRUE, FALSE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_informational", SOUP_STATUS_CONTINUE,
@@ -197,7 +200,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("unauth", TRUE, TRUE, FALSE,
+       do_message ("/unauth", TRUE, TRUE, FALSE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_REQUEST_ENTITY_TOO_LARGE,
@@ -208,7 +211,7 @@ run_tests (void)
                    "client-finished",
                    NULL);
 
-       do_message ("auth", FALSE, FALSE, FALSE,
+       do_message ("/auth", FALSE, FALSE, FALSE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -220,7 +223,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", TRUE, FALSE, FALSE,
+       do_message ("/auth", TRUE, FALSE, FALSE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -232,7 +235,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", FALSE, TRUE, FALSE,
+       do_message ("/auth", FALSE, TRUE, FALSE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
@@ -242,7 +245,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", TRUE, TRUE, FALSE,
+       do_message ("/auth", TRUE, TRUE, FALSE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
@@ -253,7 +256,7 @@ run_tests (void)
                    "client-finished",
                    NULL);
 
-       do_message ("auth", FALSE, FALSE, TRUE,
+       do_message ("/auth", FALSE, FALSE, TRUE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -274,7 +277,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", TRUE, FALSE, TRUE,
+       do_message ("/auth", TRUE, FALSE, TRUE,
                    "client-wrote_headers",
                    "client-wrote_body",
                    "server-got_headers",
@@ -295,7 +298,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", FALSE, TRUE, TRUE,
+       do_message ("/auth", FALSE, TRUE, TRUE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
@@ -316,7 +319,7 @@ run_tests (void)
                    "client-got_body",
                    "client-finished",
                    NULL);
-       do_message ("auth", TRUE, TRUE, TRUE,
+       do_message ("/auth", TRUE, TRUE, TRUE,
                    "client-wrote_headers",
                    "server-got_headers",
                    "server-wrote_headers", SOUP_STATUS_UNAUTHORIZED,
@@ -410,7 +413,7 @@ setup_server (void)
        SoupServer *server;
        SoupAuthDomain *auth_domain;
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
 
        g_signal_connect (server, "request-started",
                          G_CALLBACK (request_started), NULL);
@@ -438,11 +441,13 @@ main (int argc, char **argv)
        test_init (argc, argv, NULL);
 
        server = setup_server ();
-       port = soup_server_get_port (server);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        run_tests ();
 
        soup_test_server_quit_unref (server);
+       soup_uri_free (base_uri);
+
        test_cleanup ();
        return errors != 0;
 }
diff --git a/tests/cookies-test.c b/tests/cookies-test.c
index 58f8052..37a0b6b 100644
--- a/tests/cookies-test.c
+++ b/tests/cookies-test.c
@@ -203,20 +203,25 @@ do_cookies_parsing_test (void)
 int
 main (int argc, char **argv)
 {
+       SoupURI *server_uri;
+
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
+       server_uri = soup_test_server_get_uri (server, "http", NULL);
+
        first_party_uri = soup_uri_new (first_party);
        third_party_uri = soup_uri_new (third_party);
-       soup_uri_set_port (first_party_uri, soup_server_get_port (server));
-       soup_uri_set_port (third_party_uri, soup_server_get_port (server));
+       soup_uri_set_port (first_party_uri, server_uri->port);
+       soup_uri_set_port (third_party_uri, server_uri->port);
 
        do_cookies_accept_policy_test ();
        do_cookies_parsing_test ();
 
        soup_uri_free (first_party_uri);
        soup_uri_free (third_party_uri);
+       soup_uri_free (server_uri);
        soup_test_server_quit_unref (server);
 
        test_cleanup ();
diff --git a/tests/forms-test.c b/tests/forms-test.c
index 3b6e5c1..bd9bec3 100644
--- a/tests/forms-test.c
+++ b/tests/forms-test.c
@@ -417,38 +417,44 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        SoupServer *server;
-       guint port;
+       SoupURI *base_uri, *uri;
        char *uri_str;
 
        test_init (argc, argv, no_test_entry);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, "/hello",
                                 hello_callback, NULL, NULL);
        soup_server_add_handler (server, "/md5",
                                 md5_callback, NULL, NULL);
-       port = soup_server_get_port (server);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        loop = g_main_loop_new (NULL, TRUE);
 
        if (run_tests) {
-               uri_str = g_strdup_printf ("http://127.0.0.1:%u/hello";, port);
+               uri = soup_uri_new_with_base (base_uri, "/hello");
+               uri_str = soup_uri_to_string (uri, FALSE);
                do_hello_tests (uri_str);
+               soup_uri_free (uri);
                g_free (uri_str);
 
-               uri_str = g_strdup_printf ("http://127.0.0.1:%u/md5";, port);
+               uri = soup_uri_new_with_base (base_uri, "/md5");
+               uri_str = soup_uri_to_string (uri, FALSE);
                do_md5_tests (uri_str);
+               soup_uri_free (uri);
                g_free (uri_str);
 
                do_form_decode_test ();
        } else {
-               g_print ("Listening on port %d\n", port);
+               g_print ("Listening on port %d\n", base_uri->port);
                g_main_loop_run (loop);
        }
 
        g_main_loop_unref (loop);
 
        soup_test_server_quit_unref (server);
+       soup_uri_free (base_uri);
+
        if (run_tests)
                test_cleanup ();
        return errors != 0;
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 2aaa2cd..96777b4 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -73,7 +73,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
        if (!strcmp (path, "/slow")) {
                soup_server_pause_message (server, msg);
                g_object_set_data (G_OBJECT (msg), "server", server);
-               soup_add_timeout (soup_server_get_async_context (server),
+               soup_add_timeout (g_main_context_get_thread_default (),
                                  1000, timeout_finish_message, msg);
        }
 
@@ -174,35 +174,28 @@ static void
 do_callback_unref_test (void)
 {
        SoupServer *bad_server;
-       SoupAddress *addr;
        SoupSession *session;
        SoupMessage *one, *two;
        GMainLoop *loop;
-       char *bad_uri;
+       SoupURI *bad_uri;
 
        debug_printf (1, "\nCallback unref handling (msg api)\n");
 
        /* Get a guaranteed-bad URI */
-       addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
-       soup_address_resolve_sync (addr, NULL);
-       bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
-                                     NULL);
-       g_object_unref (addr);
-
-       bad_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
-                                  soup_server_get_port (bad_server));
-       g_object_unref (bad_server);
+       bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
+       bad_uri = soup_test_server_get_uri (bad_server, "http", NULL);
+       soup_test_server_quit_unref (bad_server);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
        g_object_add_weak_pointer (G_OBJECT (session), (gpointer *)&session);
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       one = soup_message_new ("GET", bad_uri);
+       one = soup_message_new_from_uri ("GET", bad_uri);
        g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
-       two = soup_message_new ("GET", bad_uri);
+       two = soup_message_new_from_uri ("GET", bad_uri);
        g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
-       g_free (bad_uri);
+       soup_uri_free (bad_uri);
 
        soup_session_queue_message (session, one, cu_one_completed, loop);
        soup_session_queue_message (session, two, cu_two_completed, loop);
@@ -283,24 +276,17 @@ static void
 do_callback_unref_req_test (void)
 {
        SoupServer *bad_server;
-       SoupAddress *addr;
        SoupSession *session;
        SoupRequest *one, *two;
        GMainLoop *loop;
-       char *bad_uri;
+       SoupURI *bad_uri;
 
        debug_printf (1, "\nCallback unref handling (request api)\n");
 
        /* Get a guaranteed-bad URI */
-       addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
-       soup_address_resolve_sync (addr, NULL);
-       bad_server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
-                                     NULL);
-       g_object_unref (addr);
-
-       bad_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
-                                  soup_server_get_port (bad_server));
-       g_object_unref (bad_server);
+       bad_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
+       bad_uri = soup_test_server_get_uri (bad_server, "http", NULL);
+       soup_test_server_quit_unref (bad_server);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
                                         SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
@@ -309,11 +295,11 @@ do_callback_unref_req_test (void)
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       one = soup_session_request (session, bad_uri, NULL);
+       one = soup_session_request_uri (session, bad_uri, NULL);
        g_object_add_weak_pointer (G_OBJECT (one), (gpointer *)&one);
-       two = soup_session_request (session, bad_uri, NULL);
+       two = soup_session_request_uri (session, bad_uri, NULL);
        g_object_add_weak_pointer (G_OBJECT (two), (gpointer *)&two);
-       g_free (bad_uri);
+       soup_uri_free (bad_uri);
 
        soup_request_send_async (one, NULL, cur_one_completed, session);
        g_object_unref (one);
@@ -1022,10 +1008,9 @@ main (int argc, char **argv)
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, "http", NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        auth_domain = soup_auth_domain_basic_new (
                SOUP_AUTH_DOMAIN_REALM, "misc-test",
@@ -1036,10 +1021,9 @@ main (int argc, char **argv)
        g_object_unref (auth_domain);
 
        if (tls_available) {
-               ssl_server = soup_test_server_new_ssl (TRUE);
+               ssl_server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
                soup_server_add_handler (ssl_server, NULL, server_callback, "https", NULL);
-               ssl_base_uri = soup_uri_new ("https://127.0.0.1/";);
-               soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server));
+               ssl_base_uri = soup_test_server_get_uri (ssl_server, "https", "127.0.0.1");
        }
 
        do_host_test ();
diff --git a/tests/multipart-test.c b/tests/multipart-test.c
index bfe4f5b..d44effa 100644
--- a/tests/multipart-test.c
+++ b/tests/multipart-test.c
@@ -588,10 +588,9 @@ main (int argc, char **argv)
 
        buffer = g_malloc (READ_BUFFER_SIZE);
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
        base_uri_string = soup_uri_to_string (base_uri, FALSE);
 
        /* FIXME: I had to raise the number of connections allowed here, otherwise I
diff --git a/tests/no-ssl-test.c b/tests/no-ssl-test.c
index ab1bdc3..235ec09 100644
--- a/tests/no-ssl-test.c
+++ b/tests/no-ssl-test.c
@@ -3,13 +3,13 @@
 #include "test-utils.h"
 
 static void
-do_ssl_test_for_session (SoupSession *session, char *uri)
+do_ssl_test_for_session (SoupSession *session, SoupURI *uri)
 {
        SoupMessage *msg;
        GTlsCertificate *cert;
        GTlsCertificateFlags flags;
 
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != SOUP_STATUS_SSL_FAILED) {
                debug_printf (1, "    Unexpected status: %d %s\n",
@@ -34,7 +34,7 @@ do_ssl_test_for_session (SoupSession *session, char *uri)
 }
 
 static void
-do_ssl_tests (char *uri)
+do_ssl_tests (SoupURI *uri)
 {
        SoupSession *session;
 
@@ -167,7 +167,8 @@ int
 main (int argc, char **argv)
 {
        SoupServer *server;
-       char *uri;
+       SoupURI *uri;
+       guint port;
 
        /* Force this test to use the dummy TLS backend */
        g_setenv ("GIO_USE_TLS", "dummy", TRUE);
@@ -180,13 +181,15 @@ main (int argc, char **argv)
         */
        server = soup_test_server_new (TRUE);
        soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-       uri = g_strdup_printf ("https://127.0.0.1:%u/";,
-                              soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       port = uri->port;
+       soup_uri_set_scheme (uri, SOUP_URI_SCHEME_HTTPS);
+       soup_uri_set_port (uri, port);
 
        do_session_property_tests ();
        do_ssl_tests (uri);
 
-       g_free (uri);
+       soup_uri_free (uri);
        soup_test_server_quit_unref (server);
 
        test_cleanup ();
diff --git a/tests/ntlm-test.c b/tests/ntlm-test.c
index f67a3d1..6753835 100644
--- a/tests/ntlm-test.c
+++ b/tests/ntlm-test.c
@@ -42,7 +42,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
                 SoupClientContext *client, gpointer data)
 {
        GHashTable *connections = data;
-       SoupSocket *socket;
+       GSocket *socket;
        const char *auth;
        NTLMServerState state, required_user = 0;
        gboolean auth_required, not_found = FALSE;
@@ -68,7 +68,7 @@ server_callback (SoupServer *server, SoupMessage *msg,
        if (strstr (path, "/404"))
                not_found = TRUE;
 
-       socket = soup_client_context_get_socket (client);
+       socket = soup_client_context_get_gsocket (client);
        state = GPOINTER_TO_INT (g_hash_table_lookup (connections, socket));
        auth = soup_message_headers_get_one (msg->request_headers,
                                             "Authorization");
@@ -555,13 +555,12 @@ main (int argc, char **argv)
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        connections = g_hash_table_new (NULL, NULL);
        soup_server_add_handler (server, NULL,
                                 server_callback, connections, NULL);
 
-       uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (uri, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
 
        /* Built-in NTLM auth support. (We set SOUP_NTLM_AUTH_DEBUG to
         * an empty string to ensure that the built-in support is
@@ -593,8 +592,6 @@ main (int argc, char **argv)
        debug_printf (1, "\nRetrying on failed password\n");
        do_retrying_test (uri);
 
-       soup_uri_free (uri);
-
        soup_test_server_quit_unref (server);
        test_cleanup ();
        g_hash_table_destroy (connections);
diff --git a/tests/proxy-test.c b/tests/proxy-test.c
index 682b537..e981901 100644
--- a/tests/proxy-test.c
+++ b/tests/proxy-test.c
@@ -401,10 +401,9 @@ main (int argc, char **argv)
                        g_simple_proxy_resolver_new (proxies[i], (char **) ignore_hosts);
        }
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        do_proxy_tests ();
        do_proxy_fragment_test (base_uri);
diff --git a/tests/range-test.c b/tests/range-test.c
index 00b8567..f4fc399 100644
--- a/tests/range-test.c
+++ b/tests/range-test.c
@@ -399,7 +399,8 @@ main (int argc, char **argv)
 {
        SoupSession *session;
        SoupServer *server;
-       char *base_uri;
+       SoupURI *base_uri;
+       char *base_uri_str;
 
        test_init (argc, argv, NULL);
        apache_init ();
@@ -417,12 +418,13 @@ main (int argc, char **argv)
 #endif
 
        debug_printf (1, "\n2. Testing against SoupServer\n");
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-       base_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
-                                   soup_server_get_port (server));
-       do_range_test (session, base_uri, TRUE, TRUE);
-       g_free (base_uri);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
+       base_uri_str = soup_uri_to_string (base_uri, FALSE);
+       do_range_test (session, base_uri_str, TRUE, TRUE);
+       soup_uri_free (base_uri);
+       g_free (base_uri_str);
        soup_test_server_quit_unref (server);
 
        soup_test_session_abort_unref (session);
diff --git a/tests/redirect-test.c b/tests/redirect-test.c
index 2b4fb5e..87d13f1 100644
--- a/tests/redirect-test.c
+++ b/tests/redirect-test.c
@@ -564,37 +564,36 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        SoupServer *server, *server2;
-       guint port;
-       SoupURI *base_uri;
+       SoupURI *base_uri, *uri2;
 
        test_init (argc, argv, no_test_entry);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-       port = soup_server_get_port (server);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
-       server2 = soup_test_server_new (TRUE);
+       server2 = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server2, NULL,
                                 server2_callback, NULL, NULL);
-       server2_uri = g_strdup_printf ("http://127.0.0.1:%d/on-server2";,
-                                      soup_server_get_port (server2));
+       uri2 = soup_test_server_get_uri (server2, "http", NULL);
+       soup_uri_set_path (uri2, "/on-server2");
+       server2_uri = soup_uri_to_string (uri2, FALSE);
+       soup_uri_free (uri2);
 
        loop = g_main_loop_new (NULL, TRUE);
 
        if (run_tests) {
-               base_uri = soup_uri_new ("http://127.0.0.1";);
-               soup_uri_set_port (base_uri, port);
                do_redirect_tests (base_uri);
                do_connection_test (base_uri);
-               soup_uri_free (base_uri);
        } else {
-               g_print ("Listening on port %d\n", port);
+               g_print ("Listening on port %d\n", base_uri->port);
                g_main_loop_run (loop);
        }
 
        g_main_loop_unref (loop);
        g_free (server2_uri);
+       soup_uri_free (base_uri);
        soup_test_server_quit_unref (server);
        soup_test_server_quit_unref (server2);
 
diff --git a/tests/requester-test.c b/tests/requester-test.c
index a202c16..acb1e74 100644
--- a/tests/requester-test.c
+++ b/tests/requester-test.c
@@ -351,7 +351,7 @@ do_async_test (SoupSession *session, SoupURI *uri,
 }
 
 static void
-do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
+do_test_for_thread_and_context (SoupSession *session, SoupURI *base_uri)
 {
        SoupRequester *requester;
        SoupURI *uri;
@@ -364,39 +364,33 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
        soup_session_add_feature_by_type (session, SOUP_TYPE_CONTENT_SNIFFER);
 
        debug_printf (1, "    basic test\n");
-       uri = soup_uri_new (base_uri);
-       do_async_test (session, uri, test_sent,
+       do_async_test (session, base_uri, test_sent,
                       SOUP_STATUS_OK, response,
                       TRUE, FALSE);
-       soup_uri_free (uri);
 
        debug_printf (1, "    chunked test\n");
-       uri = soup_uri_new (base_uri);
-       soup_uri_set_path (uri, "/chunked");
+       uri = soup_uri_new_with_base (base_uri, "/chunked");
        do_async_test (session, uri, test_sent,
                       SOUP_STATUS_OK, response,
                       TRUE, FALSE);
        soup_uri_free (uri);
 
        debug_printf (1, "    auth test\n");
-       uri = soup_uri_new (base_uri);
-       soup_uri_set_path (uri, "/auth");
+       uri = soup_uri_new_with_base (base_uri, "/auth");
        do_async_test (session, uri, auth_test_sent,
                       SOUP_STATUS_UNAUTHORIZED, auth_response,
                       TRUE, FALSE);
        soup_uri_free (uri);
 
        debug_printf (1, "    non-persistent test\n");
-       uri = soup_uri_new (base_uri);
-       soup_uri_set_path (uri, "/non-persistent");
+       uri = soup_uri_new_with_base (base_uri, "/non-persistent");
        do_async_test (session, uri, test_sent,
                       SOUP_STATUS_OK, response,
                       FALSE, FALSE);
        soup_uri_free (uri);
 
        debug_printf (1, "    cancellation test\n");
-       uri = soup_uri_new (base_uri);
-       soup_uri_set_path (uri, "/");
+       uri = soup_uri_new_with_base (base_uri, "/");
        do_async_test (session, uri, test_sent,
                       SOUP_STATUS_FORBIDDEN, NULL,
                       FALSE, TRUE);
@@ -404,7 +398,7 @@ do_test_for_thread_and_context (SoupSession *session, const char *base_uri)
 }
 
 static void
-do_simple_tests (const char *uri)
+do_simple_tests (SoupURI *uri)
 {
        SoupSession *session;
 
@@ -424,7 +418,7 @@ do_simple_tests (const char *uri)
 }
 
 static void
-do_test_with_context_and_type (const char *uri, gboolean plain_session)
+do_test_with_context_and_type (SoupURI *uri, gboolean plain_session)
 {
        GMainContext *async_context;
        SoupSession *session;
@@ -461,7 +455,7 @@ do_plain_test_with_context (gpointer uri)
 }
 
 static void
-do_context_tests (const char *uri)
+do_context_tests (SoupURI *uri)
 {
        debug_printf (1, "\nStreaming with a non-default-context\n");
 
@@ -470,7 +464,7 @@ do_context_tests (const char *uri)
 }
 
 static void
-do_thread_tests (const char *uri)
+do_thread_tests (SoupURI *uri)
 {
        GThread *thread;
 
@@ -478,12 +472,12 @@ do_thread_tests (const char *uri)
 
        thread = g_thread_new ("do_test_with_context",
                               do_plain_test_with_context,
-                              (gpointer)uri);
+                              uri);
        g_thread_join (thread);
 
        thread = g_thread_new ("do_test_with_context",
                               do_test_with_context,
-                              (gpointer)uri);
+                              uri);
        g_thread_join (thread);
 }
 
@@ -602,7 +596,7 @@ do_sync_request (SoupSession *session, SoupRequest *request,
 }
 
 static void
-do_sync_tests_for_session (SoupSession *session, const char *uri_string)
+do_sync_tests_for_session (SoupSession *session, SoupURI *base_uri)
 {
        SoupRequester *requester;
        SoupRequest *request;
@@ -610,7 +604,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string)
 
        requester = SOUP_REQUESTER (soup_session_get_feature (session, SOUP_TYPE_REQUESTER));
 
-       uri = soup_uri_new (uri_string);
+       uri = soup_uri_copy (base_uri);
 
        debug_printf (1, "    basic test\n");
        if (requester)
@@ -670,7 +664,7 @@ do_sync_tests_for_session (SoupSession *session, const char *uri_string)
 }
 
 static void
-do_sync_tests (const char *uri_string)
+do_sync_tests (SoupURI *uri)
 {
        SoupSession *session;
        SoupRequester *requester;
@@ -679,7 +673,7 @@ do_sync_tests (const char *uri_string)
 
        debug_printf (1, "  SoupSession\n");
        session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
-       do_sync_tests_for_session (session, uri_string);
+       do_sync_tests_for_session (session, uri);
        soup_test_session_abort_unref (session);
 
        debug_printf (1, "  SoupSessionSync\n");
@@ -687,7 +681,7 @@ do_sync_tests (const char *uri_string)
        requester = soup_requester_new ();
        soup_session_add_feature (session, SOUP_SESSION_FEATURE (requester));
        g_object_unref (requester);
-       do_sync_tests_for_session (session, uri_string);
+       do_sync_tests_for_session (session, uri);
        soup_test_session_abort_unref (session);
 }
 
@@ -874,15 +868,14 @@ do_close_test_for_session (SoupSession *session,
 }
 
 static void
-do_close_tests (const char *uri)
+do_close_tests (SoupURI *uri)
 {
        SoupSession *session;
        SoupURI *slow_uri;
 
        debug_printf (1, "\nClosing stream before end should cancel\n");
 
-       slow_uri = soup_uri_new (uri);
-       soup_uri_set_path (slow_uri, "/slow");
+       slow_uri = soup_uri_new_with_base (uri, "/slow");
 
        debug_printf (1, "  SoupSessionAsync\n");
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
@@ -904,15 +897,16 @@ do_close_tests (const char *uri)
 int
 main (int argc, char **argv)
 {
-       char *uri;
+       SoupURI *uri;
 
        test_init (argc, argv, NULL);
        get_index ();
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
 
-       uri = g_strdup_printf ("http://127.0.0.1:%u/foo";, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       soup_uri_set_path (uri, "/foo");
 
        do_simple_tests (uri);
        do_thread_tests (uri);
@@ -921,7 +915,7 @@ main (int argc, char **argv)
        do_null_char_tests ();
        do_close_tests (uri);
 
-       g_free (uri);
+       soup_uri_free (uri);
        soup_buffer_free (response);
        soup_buffer_free (auth_response);
        soup_test_server_quit_unref (server);
diff --git a/tests/server-auth-test.c b/tests/server-auth-test.c
index 33cff45..3f4d73c 100644
--- a/tests/server-auth-test.c
+++ b/tests/server-auth-test.c
@@ -318,7 +318,7 @@ main (int argc, char **argv)
 
        test_init (argc, argv, no_test_entry);
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        g_signal_connect (server, "request_started",
                          G_CALLBACK (request_started_callback), NULL);
        soup_server_add_handler (server, NULL,
@@ -346,15 +346,14 @@ main (int argc, char **argv)
 
        loop = g_main_loop_new (NULL, TRUE);
 
+       uri = soup_test_server_get_uri (server, "http", NULL);
        if (run_tests) {
-               uri = soup_uri_new ("http://127.0.0.1";);
-               soup_uri_set_port (uri, soup_server_get_port (server));
                do_auth_tests (uri);
-               soup_uri_free (uri);
        } else {
-               g_print ("Listening on port %d\n", soup_server_get_port (server));
+               g_print ("Listening on port %d\n", uri->port);
                g_main_loop_run (loop);
        }
+       soup_uri_free (uri);
 
        g_main_loop_unref (loop);
        soup_test_server_quit_unref (server);
diff --git a/tests/server-test.c b/tests/server-test.c
index 3ff7cad..2bd8708 100644
--- a/tests/server-test.c
+++ b/tests/server-test.c
@@ -5,7 +5,7 @@
 
 #include "test-utils.h"
 
-SoupServer *server, *ssl_server;
+SoupServer *server;
 SoupURI *base_uri, *ssl_base_uri;
 
 static void
@@ -255,6 +255,7 @@ ipv6_server_callback (SoupServer *server, SoupMessage *msg,
                      SoupClientContext *context, gpointer data)
 {
        const char *host;
+       GSocketAddress *addr;
        char expected_host[128];
 
        host = soup_message_headers_get_one (msg->request_headers, "Host");
@@ -265,8 +266,10 @@ ipv6_server_callback (SoupServer *server, SoupMessage *msg,
                return;
        }
 
+       addr = soup_client_context_get_local_address (context);
        g_snprintf (expected_host, sizeof (expected_host),
-                   "[::1]:%d", soup_server_get_port (server));
+                   "[::1]:%d",
+                   g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr)));
 
        if (strcmp (host, expected_host) == 0)
                soup_message_set_status (msg, SOUP_STATUS_OK);
@@ -282,27 +285,16 @@ do_ipv6_test (void)
 {
        SoupServer *ipv6_server;
        SoupURI *ipv6_uri;
-       SoupAddress *ipv6_addr;
        SoupSession *session;
        SoupMessage *msg;
 
-       debug_printf (1, "\nIPv6 server test\n");
+       // FIXME: deal with lack of IPv6 support
 
-       ipv6_addr = soup_address_new ("::1", SOUP_ADDRESS_ANY_PORT);
-       soup_address_resolve_sync (ipv6_addr, NULL);
-       ipv6_server = soup_server_new (SOUP_SERVER_INTERFACE, ipv6_addr,
-                                      NULL);
-       g_object_unref (ipv6_addr);
-       if (!ipv6_server) {
-               debug_printf (1, "  skipping due to lack of IPv6 support\n");
-               return;
-       }
+       debug_printf (1, "\nIPv6 server test\n");
 
+       ipv6_server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (ipv6_server, NULL, ipv6_server_callback, NULL, NULL);
-       soup_server_run_async (ipv6_server);
-
-       ipv6_uri = soup_uri_new ("http://[::1]/";);
-       soup_uri_set_port (ipv6_uri, soup_server_get_port (ipv6_server));
+       ipv6_uri = soup_test_server_get_uri (server, "http", "::1");
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
 
@@ -340,21 +332,17 @@ main (int argc, char **argv)
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        g_object_set (G_OBJECT (server),
                      SOUP_SERVER_HTTP_ALIASES, http_aliases,
                      NULL);
 
        if (tls_available) {
-               ssl_server = soup_test_server_new_ssl (TRUE);
-               soup_server_add_handler (ssl_server, NULL, server_callback, NULL, NULL);
-               ssl_base_uri = soup_uri_new ("https://127.0.0.1/";);
-               soup_uri_set_port (ssl_base_uri, soup_server_get_port (ssl_server));
-               g_object_set (G_OBJECT (ssl_server),
+               ssl_base_uri = soup_test_server_get_uri (server, "https", NULL);
+               g_object_set (G_OBJECT (server),
                              SOUP_SERVER_HTTPS_ALIASES, https_aliases,
                              NULL);
        }
@@ -367,10 +355,8 @@ main (int argc, char **argv)
        soup_uri_free (base_uri);
        soup_test_server_quit_unref (server);
 
-       if (tls_available) {
+       if (tls_available)
                soup_uri_free (ssl_base_uri);
-               soup_test_server_quit_unref (ssl_server);
-       }
 
        test_cleanup ();
        return errors != 0;
diff --git a/tests/session-test.c b/tests/session-test.c
index 205885d..d149d0f 100644
--- a/tests/session-test.c
+++ b/tests/session-test.c
@@ -25,7 +25,7 @@ server_handler (SoupServer        *server,
                gpointer           user_data)
 {
        if (!strcmp (path, "/request-timeout")) {
-               GMainContext *context = soup_server_get_async_context (server);
+               GMainContext *context = g_main_context_get_thread_default ();
                GSource *timer;
 
                timer = g_timeout_source_new (100);
@@ -58,7 +58,7 @@ cancel_message_cb (SoupMessage *msg, gpointer session)
 
 static void
 do_test_for_session (SoupSession *session,
-                    const char *uri, const char *timeout_uri,
+                    SoupURI *uri, SoupURI *timeout_uri,
                     gboolean queue_is_async,
                     gboolean send_is_blocking,
                     gboolean cancel_is_immediate)
@@ -69,11 +69,11 @@ do_test_for_session (SoupSession *session,
 
        debug_printf (1, "  queue_message\n");
        debug_printf (2, "    requesting timeout\n");
-       msg = soup_message_new ("GET", timeout_uri);
+       msg = soup_message_new_from_uri ("GET", timeout_uri);
        soup_session_send_message (session, msg);
        g_object_unref (msg);
 
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        server_processed_message = timeout = finished = FALSE;
        soup_session_queue_message (session, msg, finished_cb, &finished);
        while (!timeout)
@@ -107,7 +107,7 @@ do_test_for_session (SoupSession *session,
        }
 
        debug_printf (1, "  send_message\n");
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        server_processed_message = local_timeout = FALSE;
        timeout_id = g_idle_add_full (G_PRIORITY_HIGH, timeout_cb, &local_timeout, NULL);
        soup_session_send_message (session, msg);
@@ -136,7 +136,7 @@ do_test_for_session (SoupSession *session,
                return;
 
        debug_printf (1, "  cancel_message\n");
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        g_object_ref (msg);
        finished = FALSE;
        soup_session_queue_message (session, msg, finished_cb, &finished);
@@ -173,7 +173,7 @@ do_test_for_session (SoupSession *session,
 }
 
 static void
-do_plain_tests (char *uri, char *timeout_uri)
+do_plain_tests (SoupURI *uri, SoupURI *timeout_uri)
 {
        SoupSession *session;
 
@@ -185,7 +185,7 @@ do_plain_tests (char *uri, char *timeout_uri)
 }
 
 static void
-do_async_tests (char *uri, char *timeout_uri)
+do_async_tests (SoupURI *uri, SoupURI *timeout_uri)
 {
        SoupSession *session;
 
@@ -197,7 +197,7 @@ do_async_tests (char *uri, char *timeout_uri)
 }
 
 static void
-do_sync_tests (char *uri, char *timeout_uri)
+do_sync_tests (SoupURI *uri, SoupURI *timeout_uri)
 {
        SoupSession *session;
 
@@ -226,7 +226,7 @@ priority_test_finished_cb (SoupSession *session, SoupMessage *msg, gpointer user
 }
 
 static void
-do_priority_tests (char *uri)
+do_priority_tests (SoupURI *uri)
 {
        SoupSession *session;
        int i, finished_count = 0;
@@ -245,12 +245,14 @@ do_priority_tests (char *uri)
        expected_priorities[2] = SOUP_MESSAGE_PRIORITY_LOW;
 
        for (i = 0; i < 3; i++) {
-               char *msg_uri;
+               SoupURI *msg_uri;
                SoupMessage *msg;
+               char buf[5];
 
-               msg_uri = g_strdup_printf ("%s/%d", uri, i);
-               msg = soup_message_new ("GET", uri);
-               g_free (msg_uri);
+               g_snprintf (buf, sizeof (buf), "%d", i);
+               msg_uri = soup_uri_new_with_base (uri, buf);
+               msg = soup_message_new_from_uri ("GET", msg_uri);
+               soup_uri_free (msg_uri);
 
                soup_message_set_priority (msg, priorities[i]);
                soup_session_queue_message (session, msg, priority_test_finished_cb, &finished_count);
@@ -401,15 +403,14 @@ int
 main (int argc, char **argv)
 {
        SoupServer *server;
-       char *uri, *timeout_uri;
+       SoupURI *uri, *timeout_uri;
 
        test_init (argc, argv, NULL);
 
        server = soup_test_server_new (TRUE);
        soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-       uri = g_strdup_printf ("http://127.0.0.1:%u";,
-                              soup_server_get_port (server));
-       timeout_uri = g_strdup_printf ("%s/request-timeout", uri);
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       timeout_uri = soup_uri_new_with_base (uri, "/request-timeout");
 
        do_plain_tests (uri, timeout_uri);
        do_async_tests (uri, timeout_uri);
@@ -417,8 +418,8 @@ main (int argc, char **argv)
        do_priority_tests (uri);
        do_property_tests ();
 
-       g_free (uri);
-       g_free (timeout_uri);
+       soup_uri_free (uri);
+       soup_uri_free (timeout_uri);
        soup_test_server_quit_unref (server);
 
        test_cleanup ();
diff --git a/tests/sniffing-test.c b/tests/sniffing-test.c
index cbebaba..cf3d934 100644
--- a/tests/sniffing-test.c
+++ b/tests/sniffing-test.c
@@ -468,10 +468,9 @@ main (int argc, char **argv)
 
        test_init (argc, argv, NULL);
 
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
-       base_uri = soup_uri_new ("http://127.0.0.1/";);
-       soup_uri_set_port (base_uri, soup_server_get_port (server));
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
 
        session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC,
                                         SOUP_SESSION_USE_THREAD_CONTEXT, TRUE,
diff --git a/tests/ssl-test.c b/tests/ssl-test.c
index 7716f30..a3228fd 100644
--- a/tests/ssl-test.c
+++ b/tests/ssl-test.c
@@ -3,13 +3,13 @@
 #include "test-utils.h"
 
 static void
-do_properties_test_for_session (SoupSession *session, char *uri)
+do_properties_test_for_session (SoupSession *session, SoupURI *uri)
 {
        SoupMessage *msg;
        GTlsCertificate *cert;
        GTlsCertificateFlags flags;
 
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != SOUP_STATUS_OK) {
                debug_printf (1, "    FAILED: %d %s\n",
@@ -40,7 +40,7 @@ do_properties_test_for_session (SoupSession *session, char *uri)
 }
 
 static void
-do_properties_tests (char *uri)
+do_properties_tests (SoupURI *uri)
 {
        SoupSession *session;
 
@@ -66,7 +66,7 @@ do_properties_tests (char *uri)
 }
 
 static void
-do_one_strict_test (SoupSession *session, char *uri,
+do_one_strict_test (SoupSession *session, SoupURI *uri,
                    gboolean strict, gboolean with_ca_list,
                    guint expected_status)
 {
@@ -84,7 +84,7 @@ do_one_strict_test (SoupSession *session, char *uri,
        /* Close existing connections with old params */
        soup_session_abort (session);
 
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
        soup_session_send_message (session, msg);
        if (msg->status_code != expected_status) {
                debug_printf (1, "      FAILED: %d %s (expected %d %s)\n",
@@ -118,7 +118,7 @@ do_one_strict_test (SoupSession *session, char *uri,
 }
 
 static void
-do_strict_tests (char *uri)
+do_strict_tests (SoupURI *uri)
 {
        SoupSession *session;
 
@@ -323,21 +323,20 @@ int
 main (int argc, char **argv)
 {
        SoupServer *server;
-       char *uri;
+       SoupURI *uri;
 
        test_init (argc, argv, NULL);
 
        if (tls_available) {
-               server = soup_test_server_new_ssl (TRUE);
+               server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
                soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-               uri = g_strdup_printf ("https://127.0.0.1:%u/";,
-                                      soup_server_get_port (server));
+               uri = soup_test_server_get_uri (server, "https", "127.0.0.1");
 
                do_session_property_tests ();
                do_strict_tests (uri);
                do_properties_tests (uri);
 
-               g_free (uri);
+               soup_uri_free (uri);
                soup_test_server_quit_unref (server);
        }
 
diff --git a/tests/streaming-test.c b/tests/streaming-test.c
index 239e0ce..af84915 100644
--- a/tests/streaming-test.c
+++ b/tests/streaming-test.c
@@ -153,21 +153,18 @@ main (int argc, char **argv)
 {
        GMainLoop *loop;
        SoupServer *server;
-       guint port;
        SoupURI *base_uri;
 
        test_init (argc, argv, NULL);
        get_full_response ();
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (server, NULL,
                                 server_callback, NULL, NULL);
-       port =  soup_server_get_port (server);
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       base_uri = soup_uri_new ("http://127.0.0.1";);
-       soup_uri_set_port (base_uri, port);
+       base_uri = soup_test_server_get_uri (server, "http", NULL);
        do_tests (base_uri);
        soup_uri_free (base_uri);
 
diff --git a/tests/test-utils.c b/tests/test-utils.c
index f4632a9..650a5eb 100644
--- a/tests/test-utils.c
+++ b/tests/test-utils.c
@@ -268,77 +268,209 @@ soup_test_session_abort_unref (SoupSession *session)
        }
 }
 
-static gpointer run_server_thread (gpointer user_data);
+static void
+server_listen (SoupServer *server)
+{
+       GError *error = NULL;
+       SoupServerListenOptions options =
+               GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (server), "listen-options"));
 
-static SoupServer *
-test_server_new (gboolean in_own_thread, gboolean ssl)
+       soup_server_listen_local (server, 0, options, &error);
+       if (error) {
+               g_printerr ("Unable to create server: %s\n", error->message);
+               exit (1);
+       }
+}
+
+static GMutex server_start_mutex;
+static GCond server_start_cond;
+
+static gpointer
+run_server_thread (gpointer user_data)
 {
-       SoupServer *server;
-       GMainContext *async_context;
-       const char *ssl_cert_file, *ssl_key_file;
-       SoupAddress *addr;
+       SoupServer *server = user_data;
+       GMainContext *context;
+       GMainLoop *loop;
 
-       async_context = in_own_thread ? g_main_context_new () : NULL;
+       context = g_main_context_new ();
+       g_main_context_push_thread_default (context);
+       loop = g_main_loop_new (context, FALSE);
+       g_object_set_data (G_OBJECT (server), "GMainLoop", loop);
 
-       if (ssl) {
-               ssl_cert_file = SRCDIR "/test-cert.pem";
-               ssl_key_file = SRCDIR "/test-key.pem";
-       } else
-               ssl_cert_file = ssl_key_file = NULL;
+       server_listen (server);
 
-       addr = soup_address_new ("127.0.0.1", SOUP_ADDRESS_ANY_PORT);
-       soup_address_resolve_sync (addr, NULL);
+       g_mutex_lock (&server_start_mutex);
+       g_cond_signal (&server_start_cond);
+       g_mutex_unlock (&server_start_mutex);
 
-       server = soup_server_new (SOUP_SERVER_INTERFACE, addr,
-                                 SOUP_SERVER_ASYNC_CONTEXT, async_context,
-                                 SOUP_SERVER_SSL_CERT_FILE, ssl_cert_file,
-                                 SOUP_SERVER_SSL_KEY_FILE, ssl_key_file,
-                                 NULL);
-       g_object_unref (addr);
-       if (async_context)
-               g_main_context_unref (async_context);
+       g_main_loop_run (loop);
+       g_main_loop_unref (loop);
 
-       if (!server) {
-               g_printerr ("Unable to create server\n");
-               exit (1);
+       soup_server_disconnect (server);
+
+       g_main_context_pop_thread_default (context);
+       g_main_context_unref (context);
+
+       return NULL;
+}
+
+SoupServer *
+soup_test_server_new (SoupTestServerOptions options)
+{
+       SoupServer *server;
+       GTlsCertificate *cert = NULL;
+       GError *error = NULL;
+
+       if (tls_available) {
+               cert = g_tls_certificate_new_from_files (SRCDIR "/test-cert.pem",
+                                                        SRCDIR "/test-key.pem",
+                                                        &error);
+               if (error) {
+                       g_printerr ("Unable to create server: %s\n", error->message);
+                       exit (1);
+               }
        }
+       
+       server = soup_server_new (SOUP_SERVER_TLS_CERTIFICATE, cert,
+                                 NULL);
+       g_clear_object (&cert);
 
-       if (in_own_thread) {
+       g_object_set_data (G_OBJECT (server), "options", GUINT_TO_POINTER (options));
+
+       if (options & SOUP_TEST_SERVER_IN_THREAD) {
                GThread *thread;
 
+               g_mutex_lock (&server_start_mutex);
+
                thread = g_thread_new ("server_thread", run_server_thread, server);
                g_object_set_data (G_OBJECT (server), "thread", thread);
+
+               /* We have to call soup_server_listen() from the server's
+                * thread, but want to be sure we don't return from here
+                * until it happens, hence the locking.
+                */
+               g_cond_wait (&server_start_cond, &server_start_mutex);
+               g_mutex_unlock (&server_start_mutex);
        } else
-               soup_server_run_async (server);
+               server_listen (server);
 
        return server;
 }
 
-SoupServer *
-soup_test_server_new (gboolean in_own_thread)
+static SoupURI *
+find_server_uri (SoupServer *server, const char *scheme, const char *host)
+{
+       GSList *uris, *u;
+       SoupURI *uri, *ret_uri = NULL;
+
+       uris = soup_server_get_uris (server);
+       for (u = uris; u; u = u->next) {
+               uri = u->data;
+
+               if (scheme && strcmp (uri->scheme, scheme) != 0)
+                       continue;
+               if (host && strcmp (uri->host, host) != 0)
+                       continue;
+
+               ret_uri = soup_uri_copy (uri);
+               break;
+       }
+       g_slist_free_full (uris, (GDestroyNotify)soup_uri_free);
+
+       return ret_uri;
+}
+
+static SoupURI *
+add_listener (SoupServer *server, const char *scheme, const char *host)
 {
-       return test_server_new (in_own_thread, FALSE);
+       SoupServerListenOptions options = 0;
+       GError *error = NULL;
+
+       if (!g_strcmp0 (scheme, SOUP_URI_SCHEME_HTTPS))
+               options |= SOUP_SERVER_LISTEN_HTTPS;
+       if (!g_strcmp0 (host, "127.0.0.1"))
+               options |= SOUP_SERVER_LISTEN_IPV4_ONLY;
+       else if (!g_strcmp0 (host, "::1"))
+               options |= SOUP_SERVER_LISTEN_IPV6_ONLY;
+
+       soup_server_listen_local (server, 0, options, &error);
+       g_assert_no_error (error);
+
+       return find_server_uri (server, scheme, host);
 }
 
-SoupServer *
-soup_test_server_new_ssl (gboolean in_own_thread)
+typedef struct {
+       GMutex mutex;
+       GCond cond;
+
+       SoupServer *server;
+       const char *scheme;
+       const char *host;
+
+       SoupURI *uri;
+} AddListenerData;
+
+static gboolean
+add_listener_in_thread (gpointer user_data)
 {
-       return test_server_new (in_own_thread, TRUE);
+       AddListenerData *data = user_data;
+
+       data->uri = add_listener (data->server, data->scheme, data->host);
+       g_mutex_lock (&data->mutex);
+       g_cond_signal (&data->cond);
+       g_mutex_unlock (&data->mutex);
+
+       return FALSE;
 }
 
-static gpointer
-run_server_thread (gpointer user_data)
+SoupURI *
+soup_test_server_get_uri (SoupServer    *server,
+                         const char    *scheme,
+                         const char    *host)
 {
-       SoupServer *server = user_data;
+       SoupURI *uri;
+       GMainLoop *loop;
 
-       soup_server_run (server);
-       return NULL;
+       uri = find_server_uri (server, scheme, host);
+       if (uri)
+               return uri;
+
+       /* Need to add a new listener */
+       uri = soup_uri_new (NULL);
+       soup_uri_set_scheme (uri, scheme);
+       soup_uri_set_host (uri, host);
+
+       loop = g_object_get_data (G_OBJECT (server), "GMainLoop");
+       if (loop) {
+               GMainContext *context = g_main_loop_get_context (loop);
+               AddListenerData data;
+
+               g_mutex_init (&data.mutex);
+               g_cond_init (&data.cond);
+               data.server = server;
+               data.scheme = scheme;
+               data.host = host;
+               data.uri = NULL;
+
+               g_mutex_lock (&data.mutex);
+               soup_add_completion (context, add_listener_in_thread, &data);
+
+               while (!data.uri)
+                       g_cond_wait (&data.cond, &data.mutex);
+
+               g_mutex_clear (&data.mutex);
+               g_cond_clear (&data.cond);
+               uri = data.uri;
+       } else
+               uri = add_listener (server, scheme, host);
+
+       return uri;
 }
 
 static gboolean
-idle_quit_server (gpointer server)
+idle_quit_server (gpointer loop)
 {
-       soup_server_quit (server);
+       g_main_loop_quit (loop);
        return FALSE;
 }
 
@@ -352,11 +484,15 @@ soup_test_server_quit_unref (SoupServer *server)
 
        thread = g_object_get_data (G_OBJECT (server), "thread");
        if (thread) {
-               soup_add_completion (soup_server_get_async_context (server),
-                                    idle_quit_server, server);
+               GMainLoop *loop;
+               GMainContext *context;
+
+               loop = g_object_get_data (G_OBJECT (server), "GMainLoop");
+               context = g_main_loop_get_context (loop);
+               soup_add_completion (context, idle_quit_server, loop);
                g_thread_join (thread);
        } else
-               soup_server_quit (server);
+               soup_server_disconnect (server);
        g_object_unref (server);
 
        if (server) {
diff --git a/tests/test-utils.h b/tests/test-utils.h
index c85103d..e686720 100644
--- a/tests/test-utils.h
+++ b/tests/test-utils.h
@@ -36,9 +36,16 @@ typedef enum {
 SoupSession *soup_test_session_new         (GType type, ...);
 void         soup_test_session_abort_unref (SoupSession *session);
 
-SoupServer  *soup_test_server_new        (gboolean in_own_thread);
-SoupServer  *soup_test_server_new_ssl    (gboolean in_own_thread);
-void         soup_test_server_quit_unref (SoupServer *server);
+typedef enum {
+  SOUP_TEST_SERVER_DEFAULT   = 0,
+  SOUP_TEST_SERVER_IN_THREAD = (1 << 0)
+} SoupTestServerOptions;
+
+SoupServer  *soup_test_server_new            (SoupTestServerOptions  options);
+SoupURI     *soup_test_server_get_uri        (SoupServer            *server,
+                                             const char            *scheme,
+                                             const char            *host);
+void         soup_test_server_quit_unref     (SoupServer            *server);
 
 GInputStream *soup_test_request_send         (SoupRequest  *req,
                                              GCancellable *cancellable,
diff --git a/tests/timeout-test.c b/tests/timeout-test.c
index 5903069..f98fead 100644
--- a/tests/timeout-test.c
+++ b/tests/timeout-test.c
@@ -20,7 +20,7 @@ request_started_cb (SoupSession *session, SoupMessage *msg,
 }
 
 static void
-do_message_to_session (SoupSession *session, const char *uri,
+do_message_to_session (SoupSession *session, SoupURI *uri,
                       const char *comment, guint expected_status)
 {
        SoupMessage *msg;
@@ -28,7 +28,7 @@ do_message_to_session (SoupSession *session, const char *uri,
 
        if (comment)
                debug_printf (1, "    msg %s\n", comment);
-       msg = soup_message_new ("GET", uri);
+       msg = soup_message_new_from_uri ("GET", uri);
 
        g_signal_connect (msg, "finished",
                          G_CALLBACK (message_finished), &finished);
@@ -63,7 +63,7 @@ static void
 do_msg_tests_for_session (SoupSession *timeout_session,
                          SoupSession *idle_session,
                          SoupSession *plain_session,
-                         char *fast_uri, char *slow_uri)
+                         SoupURI *fast_uri, SoupURI *slow_uri)
 {
        SoupSocket *ret, *idle_first, *idle_second;
        SoupSocket *plain_first, *plain_second;
@@ -115,7 +115,7 @@ do_msg_tests_for_session (SoupSession *timeout_session,
 }
 
 static void
-do_request_to_session (SoupSession *session, const char *uri,
+do_request_to_session (SoupSession *session, SoupURI *uri,
                       const char *comment, gboolean expect_timeout)
 {
        SoupRequest *req;
@@ -125,7 +125,7 @@ do_request_to_session (SoupSession *session, const char *uri,
        gboolean finished = FALSE;
 
        debug_printf (1, "    req %s\n", comment);
-       req = soup_session_request (session, uri, NULL);
+       req = soup_session_request_uri (session, uri, NULL);
        msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (req));
 
        g_signal_connect (msg, "finished",
@@ -189,7 +189,7 @@ static void
 do_req_tests_for_session (SoupSession *timeout_session,
                          SoupSession *idle_session,
                          SoupSession *plain_session,
-                         char *fast_uri, char *slow_uri)
+                         SoupURI *fast_uri, SoupURI *slow_uri)
 {
        SoupSocket *ret, *idle_first, *idle_second;
        SoupSocket *plain_first, *plain_second;
@@ -243,7 +243,7 @@ do_req_tests_for_session (SoupSession *timeout_session,
 }
 
 static void
-do_timeout_tests (char *fast_uri, char *slow_uri, gboolean extra_slow)
+do_timeout_tests (SoupURI *fast_uri, SoupURI *slow_uri, gboolean extra_slow)
 {
        SoupSession *timeout_session, *idle_session, *plain_session;
 
@@ -311,7 +311,7 @@ server_handler (SoupServer        *server,
        if (!strcmp (path, "/slow")) {
                soup_server_pause_message (server, msg);
                g_object_set_data (G_OBJECT (msg), "server", server);
-               soup_add_timeout (soup_server_get_async_context (server),
+               soup_add_timeout (g_main_context_get_thread_default (),
                                  4000, timeout_finish_message, msg);
        }
 }
@@ -320,21 +320,19 @@ int
 main (int argc, char **argv)
 {
        SoupServer *server;
-       char *fast_uri, *slow_uri;
+       SoupURI *fast_uri, *slow_uri;
 
        test_init (argc, argv, NULL);
 
        debug_printf (1, "http\n");
-       server = soup_test_server_new (TRUE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
        soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-       fast_uri = g_strdup_printf ("http://127.0.0.1:%u/";,
-                                   soup_server_get_port (server));
-       slow_uri = g_strdup_printf ("http://127.0.0.1:%u/slow";,
-                                   soup_server_get_port (server));
+
+       fast_uri = soup_test_server_get_uri (server, "http", NULL);
+       slow_uri = soup_uri_new_with_base (fast_uri, "/slow");
        do_timeout_tests (fast_uri, slow_uri, FALSE);
-       g_free (fast_uri);
-       g_free (slow_uri);
-       soup_test_server_quit_unref (server);
+       soup_uri_free (fast_uri);
+       soup_uri_free (slow_uri);
 
        if (tls_available) {
                SoupSession *test_session;
@@ -342,12 +340,8 @@ main (int argc, char **argv)
                gint64 start, end;
 
                debug_printf (1, "\nhttps\n");
-               server = soup_test_server_new_ssl (TRUE);
-               soup_server_add_handler (server, NULL, server_handler, NULL, NULL);
-               fast_uri = g_strdup_printf ("https://127.0.0.1:%u/";,
-                                           soup_server_get_port (server));
-               slow_uri = g_strdup_printf ("https://127.0.0.1:%u/slow";,
-                                           soup_server_get_port (server));
+               fast_uri = soup_test_server_get_uri (server, "https", "127.0.0.1");
+               slow_uri = soup_uri_new_with_base (fast_uri, "/slow");
 
                /* The 1-second timeouts are too fast for some machines... */
                test_session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
@@ -365,11 +359,12 @@ main (int argc, char **argv)
                }
 
                do_timeout_tests (fast_uri, slow_uri, extra_slow);
-               g_free (fast_uri);
-               g_free (slow_uri);
-               soup_test_server_quit_unref (server);
+               soup_uri_free (fast_uri);
+               soup_uri_free (slow_uri);
        }
 
+       soup_test_server_quit_unref (server);
+
        test_cleanup ();
        return errors != 0;
 }
diff --git a/tests/xmlrpc-server-test.c b/tests/xmlrpc-server-test.c
index 421348b..d89ba3d 100644
--- a/tests/xmlrpc-server-test.c
+++ b/tests/xmlrpc-server-test.c
@@ -325,19 +325,19 @@ main (int argc, char **argv)
 
        test_init (argc, argv, no_test_entry);
 
-       server = soup_test_server_new (FALSE);
+       server = soup_test_server_new (SOUP_TEST_SERVER_DEFAULT);
        soup_server_add_handler (server, "/xmlrpc-server.php",
                                 server_callback, NULL, NULL);
 
        loop = g_main_loop_new (NULL, TRUE);
 
-       if (run_tests) {
-               uri = soup_uri_new ("http://127.0.0.1/xmlrpc-server.php";);
-               soup_uri_set_port (uri, soup_server_get_port (server));
+       uri = soup_test_server_get_uri (server, "http", NULL);
+       soup_uri_set_path (uri, "/xmlrpc-server.php");
+       if (run_tests)
                do_xmlrpc_tests (uri);
-               soup_uri_free (uri);
-       } else
-               g_print ("Listening on port %d\n", soup_server_get_port (server));
+       else
+               g_print ("Listening on port %d\n", uri->port);
+       soup_uri_free (uri);
 
        g_main_loop_run (loop);
        g_main_loop_unref (loop);



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