[libsoup/carlosgc/thread-safe: 3/19] Move connection handling from SoupSession to new object SoupConnectionManager




commit 2a03ccac8f37e39cdb82818b1707daef4bb6e077
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Thu Mar 31 11:33:04 2022 +0200

    Move connection handling from SoupSession to new object SoupConnectionManager
    
    Also reuse the host address for connections to the same host.

 libsoup/meson.build               |   1 +
 libsoup/soup-connection-manager.c | 472 +++++++++++++++++++++++++++++++
 libsoup/soup-connection-manager.h |  36 +++
 libsoup/soup-session-private.h    |  11 +
 libsoup/soup-session.c            | 565 ++++----------------------------------
 tests/misc-test.c                 |   4 +-
 6 files changed, 570 insertions(+), 519 deletions(-)
---
diff --git a/libsoup/meson.build b/libsoup/meson.build
index f3291968..332aceef 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -61,6 +61,7 @@ soup_sources = [
   'soup-client-input-stream.c',
   'soup-client-message-io.c',
   'soup-connection.c',
+  'soup-connection-manager.c',
   'soup-date-utils.c',
   'soup-filter-input-stream.c',
   'soup-form.c',
diff --git a/libsoup/soup-connection-manager.c b/libsoup/soup-connection-manager.c
new file mode 100644
index 00000000..b160bee3
--- /dev/null
+++ b/libsoup/soup-connection-manager.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2022 Igalia S.L.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-connection-manager.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-session-private.h"
+#include "soup-uri-utils-private.h"
+#include "soup.h"
+
+struct _SoupConnectionManager {
+        SoupSession *session;
+
+        GSocketConnectable *remote_connectable;
+        guint max_conns;
+        guint max_conns_per_host;
+        guint num_conns;
+
+        GHashTable *http_hosts;
+        GHashTable *https_hosts;
+        GHashTable *conns;
+
+        guint64 last_connection_id;
+};
+
+typedef struct {
+        GUri *uri;
+        GHashTable *owner_map;
+        GNetworkAddress *addr;
+
+        GList *conns;
+        guint  num_conns;
+
+        GSource *keep_alive_src;
+        SoupConnectionManager *conn_manager;
+} SoupHost;
+
+#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
+
+static SoupHost *
+soup_host_new (GUri       *uri,
+               GHashTable *owner_map)
+{
+        SoupHost *host;
+        const char *scheme = g_uri_get_scheme (uri);
+
+        host = g_new0 (SoupHost, 1);
+        host->owner_map = owner_map;
+        if (g_strcmp0 (scheme, "http") != 0 && g_strcmp0 (scheme, "https") != 0) {
+                host->uri = soup_uri_copy (uri,
+                                           SOUP_URI_SCHEME, soup_uri_is_https (uri) ? "https" : "http",
+                                           SOUP_URI_NONE);
+        } else
+                host->uri = g_uri_ref (uri);
+
+        host->addr = g_object_new (G_TYPE_NETWORK_ADDRESS,
+                                   "hostname", g_uri_get_host (host->uri),
+                                   "port", g_uri_get_port (host->uri),
+                                   "scheme", g_uri_get_scheme (host->uri),
+                                   NULL);
+
+        g_hash_table_insert (host->owner_map, host->uri, host);
+
+        return host;
+}
+
+static void
+soup_host_free (SoupHost *host)
+{
+        g_warn_if_fail (host->conns == NULL);
+
+        if (host->keep_alive_src) {
+                g_source_destroy (host->keep_alive_src);
+                g_source_unref (host->keep_alive_src);
+        }
+
+        g_uri_unref (host->uri);
+        g_object_unref (host->addr);
+        g_free (host);
+}
+
+/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
+ * because we want to ignore the protocol; http://example.com and
+ * webcal://example.com are the same host.
+ */
+static guint
+soup_host_uri_hash (gconstpointer key)
+{
+        GUri *uri = (GUri*)key;
+
+        g_warn_if_fail (uri != NULL && g_uri_get_host (uri) != NULL);
+
+        return g_uri_get_port (uri) + soup_str_case_hash (g_uri_get_host (uri));
+}
+
+static gboolean
+soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
+{
+        GUri *one = (GUri*)v1;
+        GUri *two = (GUri*)v2;
+
+        g_warn_if_fail (one != NULL && two != NULL);
+
+        const char *one_host = g_uri_get_host (one);
+        const char *two_host = g_uri_get_host (two);
+        g_warn_if_fail (one_host != NULL && two_host != NULL);
+
+        if (g_uri_get_port (one) != g_uri_get_port (two))
+                return FALSE;
+
+        return g_ascii_strcasecmp (one_host, two_host) == 0;
+}
+
+static gboolean
+free_unused_host (gpointer user_data)
+{
+        SoupHost *host = (SoupHost *)user_data;
+
+        if (host->conns)
+                return FALSE;
+
+        /* This will free the host in addition to removing it from the hash table */
+        g_hash_table_remove (host->owner_map, host->uri);
+
+        return FALSE;
+}
+
+static void
+soup_host_add_connection (SoupHost       *host,
+                          SoupConnection *conn)
+{
+        host->conns = g_list_prepend (host->conns, conn);
+        host->num_conns++;
+
+        if (host->keep_alive_src) {
+                g_source_destroy (host->keep_alive_src);
+                g_source_unref (host->keep_alive_src);
+                host->keep_alive_src = NULL;
+        }
+}
+
+static void
+soup_host_remove_connection (SoupHost       *host,
+                             SoupConnection *conn)
+{
+        host->conns = g_list_remove (host->conns, conn);
+        host->num_conns--;
+
+        /* Free the SoupHost (and its GNetworkAddress) if there
+         * has not been any new connection to the host during
+         * the last HOST_KEEP_ALIVE msecs.
+         */
+        if (host->num_conns == 0) {
+                g_assert (host->keep_alive_src == NULL);
+                host->keep_alive_src = soup_add_timeout (g_main_context_get_thread_default (),
+                                                         HOST_KEEP_ALIVE,
+                                                         free_unused_host,
+                                                         host);
+        }
+}
+
+static SoupHost *
+soup_connection_manager_get_host_for_message (SoupConnectionManager *manager,
+                                              SoupMessage           *msg)
+{
+        GUri *uri = soup_message_get_uri (msg);
+        SoupHost *host;
+        GHashTable *map;
+
+        map = soup_uri_is_https (uri) ?  manager->https_hosts : manager->http_hosts;
+        host = g_hash_table_lookup (map, uri);
+        if (!host)
+                host = soup_host_new (uri, map);
+
+        return host;
+}
+
+SoupConnectionManager *
+soup_connection_manager_new (SoupSession *session,
+                             guint        max_conns,
+                             guint        max_conns_per_host)
+{
+        SoupConnectionManager *manager;
+
+        manager = g_new0 (SoupConnectionManager, 1);
+        manager->session = session;
+        manager->max_conns = max_conns;
+        manager->max_conns_per_host = max_conns_per_host;
+        manager->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
+                                                     soup_host_uri_equal,
+                                                     NULL,
+                                                     (GDestroyNotify)soup_host_free);
+        manager->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
+                                                      soup_host_uri_equal,
+                                                      NULL,
+                                                      (GDestroyNotify)soup_host_free);
+        manager->conns = g_hash_table_new (NULL, NULL);
+
+        return manager;
+}
+
+void
+soup_connection_manager_free (SoupConnectionManager *manager)
+{
+        g_clear_object (&manager->remote_connectable);
+        g_hash_table_destroy (manager->http_hosts);
+        g_hash_table_destroy (manager->https_hosts);
+        g_hash_table_destroy (manager->conns);
+
+        g_free (manager);
+}
+
+void
+soup_connection_manager_set_max_conns (SoupConnectionManager *manager,
+                                       guint                  max_conns)
+{
+        g_assert (manager->num_conns == 0);
+        manager->max_conns = max_conns;
+}
+
+guint
+soup_connection_manager_get_max_conns (SoupConnectionManager *manager)
+{
+        return manager->max_conns;
+}
+
+void
+soup_connection_manager_set_max_conns_per_host (SoupConnectionManager *manager,
+                                                guint                  max_conns_per_host)
+{
+        g_assert (manager->num_conns == 0);
+        manager->max_conns_per_host = max_conns_per_host;
+}
+
+guint
+soup_connection_manager_get_max_conns_per_host (SoupConnectionManager *manager)
+{
+        return manager->max_conns_per_host;
+}
+
+void
+soup_connection_manager_set_remote_connectable (SoupConnectionManager *manager,
+                                                GSocketConnectable    *connectable)
+{
+        g_assert (manager->num_conns == 0);
+        manager->remote_connectable = connectable ? g_object_ref (connectable) : NULL;
+}
+
+GSocketConnectable *
+soup_connection_manager_get_remote_connectable (SoupConnectionManager *manager)
+{
+        return manager->remote_connectable;
+}
+
+guint
+soup_connection_manager_get_num_conns (SoupConnectionManager *manager)
+{
+        return manager->num_conns;
+}
+
+static void
+soup_connection_manager_drop_connection (SoupConnectionManager *manager,
+                                         SoupConnection        *conn)
+{
+        g_signal_handlers_disconnect_by_data (conn, manager);
+        manager->num_conns--;
+
+        g_object_unref (conn);
+}
+
+static void
+connection_disconnected (SoupConnection        *conn,
+                         SoupConnectionManager *manager)
+{
+        SoupHost *host = NULL;
+
+        g_hash_table_steal_extended (manager->conns, conn, NULL, (gpointer *)&host);
+        if (host)
+                soup_host_remove_connection (host, conn);
+        soup_connection_manager_drop_connection (manager, conn);
+
+        soup_session_kick_queue (manager->session);
+}
+
+static void
+connection_state_changed (SoupConnection        *conn,
+                          GParamSpec            *param,
+                          SoupConnectionManager *manager)
+{
+        if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE && soup_connection_is_idle_open (conn))
+                soup_session_kick_queue (manager->session);
+}
+
+SoupConnection *
+soup_connection_manager_get_connection (SoupConnectionManager *manager,
+                                        SoupMessageQueueItem  *item)
+{
+        SoupMessage *msg = item->msg;
+        gboolean need_new_connection;
+        SoupConnection *conn;
+        SoupSocketProperties *socket_props;
+        SoupHost *host;
+        guint8 force_http_version;
+        GList *l;
+        GSocketConnectable *remote_connectable;
+        gboolean try_cleanup = TRUE;
+
+        soup_connection_manager_cleanup (manager, FALSE);
+
+        conn = soup_message_get_connection (msg);
+        if (conn) {
+                g_warn_if_fail (soup_connection_get_state (conn) != SOUP_CONNECTION_DISCONNECTED);
+                return conn;
+        }
+
+        need_new_connection =
+                (soup_message_query_flags (msg, SOUP_MESSAGE_NEW_CONNECTION)) ||
+                (soup_message_is_misdirected_retry (msg)) ||
+                (!soup_message_query_flags (msg, SOUP_MESSAGE_IDEMPOTENT) &&
+                 !SOUP_METHOD_IS_IDEMPOTENT (soup_message_get_method (msg)));
+
+        host = soup_connection_manager_get_host_for_message (manager, msg);
+
+        force_http_version = g_getenv ("SOUP_FORCE_HTTP1") ? SOUP_HTTP_1_1 : 
soup_message_get_force_http_version (msg);
+        while (TRUE) {
+                for (l = host->conns; l && l->data; l = g_list_next (l)) {
+                        SoupHTTPVersion http_version;
+
+                        conn = (SoupConnection *)l->data;
+
+                        http_version = soup_connection_get_negotiated_protocol (conn);
+                        if (force_http_version <= SOUP_HTTP_2_0 && http_version != force_http_version)
+                                continue;
+
+                        switch (soup_connection_get_state (conn)) {
+                        case SOUP_CONNECTION_IN_USE:
+                                if (!need_new_connection && http_version == SOUP_HTTP_2_0 && 
soup_connection_is_reusable (conn))
+                                        return conn;
+                                break;
+                        case SOUP_CONNECTION_IDLE:
+                                if (!need_new_connection && soup_connection_is_idle_open (conn))
+                                        return conn;
+                                break;
+                        case SOUP_CONNECTION_CONNECTING:
+                                if (soup_session_steal_preconnection (item->session, item, conn))
+                                        return conn;
+
+                                /* Always wait if we have a pending connection as it may be
+                                 * an h2 connection which will be shared. http/1.x connections
+                                 * will only be slightly delayed. */
+                                if (force_http_version > SOUP_HTTP_1_1 && !need_new_connection && 
!item->connect_only)
+                                        return NULL;
+                        default:
+                                break;
+                        }
+                }
+
+                if (host->num_conns >= manager->max_conns_per_host) {
+                        if (need_new_connection && try_cleanup) {
+                                try_cleanup = FALSE;
+                                if (soup_connection_manager_cleanup (manager, TRUE))
+                                        continue;
+                        }
+
+                        return NULL;
+                }
+
+                if (manager->num_conns >= manager->max_conns) {
+                        if (try_cleanup) {
+                                try_cleanup = FALSE;
+                                if (soup_connection_manager_cleanup (manager, TRUE))
+                                        continue;
+                        }
+
+                        return NULL;
+                }
+
+                break;
+        }
+
+        /* Create a new connection */
+        remote_connectable = manager->remote_connectable ? manager->remote_connectable : 
G_SOCKET_CONNECTABLE (host->addr);
+        socket_props = soup_session_ensure_socket_props (item->session);
+        conn = g_object_new (SOUP_TYPE_CONNECTION,
+                             "id", ++manager->last_connection_id,
+                             "remote-connectable", remote_connectable,
+                             "ssl", soup_uri_is_https (host->uri),
+                             "socket-properties", socket_props,
+                             "force-http-version", force_http_version,
+                             NULL);
+
+        g_signal_connect (conn, "disconnected",
+                          G_CALLBACK (connection_disconnected),
+                          manager);
+        g_signal_connect (conn, "notify::state",
+                          G_CALLBACK (connection_state_changed),
+                          manager);
+
+        g_hash_table_insert (manager->conns, conn, host);
+
+        manager->num_conns++;
+        soup_host_add_connection (host, conn);
+
+        return conn;
+}
+
+gboolean
+soup_connection_manager_cleanup (SoupConnectionManager *manager,
+                                 gboolean               cleanup_idle)
+{
+        GList *conns = NULL;
+        GHashTableIter iter;
+        SoupConnection *conn;
+        SoupHost *host;
+        SoupConnectionState state;
+        GList *c;
+
+        g_hash_table_iter_init (&iter, manager->conns);
+        while (g_hash_table_iter_next (&iter, (gpointer *)&conn, (gpointer *)&host)) {
+                state = soup_connection_get_state (conn);
+                if (state == SOUP_CONNECTION_IDLE && (cleanup_idle || !soup_connection_is_idle_open (conn))) 
{
+                        conns = g_list_prepend (conns, g_object_ref (conn));
+                        g_hash_table_iter_remove (&iter);
+                        soup_host_remove_connection (host, conn);
+                        soup_connection_manager_drop_connection (manager, conn);
+                }
+        }
+
+        if (!conns)
+                return FALSE;
+
+        for (c = conns; c; c = g_list_next (c)) {
+                conn = (SoupConnection *)c->data;
+                soup_connection_disconnect (conn);
+                g_object_unref (conn);
+        }
+        g_list_free (conns);
+
+        return TRUE;
+
+}
+
+GIOStream *
+soup_connection_manager_steal_connection (SoupConnectionManager *manager,
+                                          SoupMessage           *msg)
+{
+        SoupConnection *conn;
+        SoupHost *host;
+        GIOStream *stream;
+
+        conn = soup_message_get_connection (msg);
+        if (!conn || soup_connection_get_state (conn) != SOUP_CONNECTION_IN_USE)
+                return NULL;
+
+        g_object_ref (conn);
+        host = soup_connection_manager_get_host_for_message (manager, msg);
+        g_hash_table_remove (manager->conns, conn);
+        soup_host_remove_connection (host, conn);
+        soup_connection_manager_drop_connection (manager, conn);
+
+        stream = soup_connection_steal_iostream (conn);
+        soup_message_set_connection (msg, NULL);
+        g_object_unref (conn);
+
+        return stream;
+}
diff --git a/libsoup/soup-connection-manager.h b/libsoup/soup-connection-manager.h
new file mode 100644
index 00000000..d5efa947
--- /dev/null
+++ b/libsoup/soup-connection-manager.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2022 Igalia S.L.
+ */
+
+#ifndef __SOUP_CONNECTION_MANAGER_H__
+#define __SOUP_CONNECTION_MANAGER_H__ 1
+
+#include "soup-connection.h"
+#include "soup-message-queue-item.h"
+#include <gio/gio.h>
+
+typedef struct _SoupConnectionManager SoupConnectionManager;
+
+SoupConnectionManager *soup_connection_manager_new                    (SoupSession           *session,
+                                                                       guint                  max_conns,
+                                                                       guint                  
max_conns_per_host);
+void                   soup_connection_manager_free                   (SoupConnectionManager *manager);
+void                   soup_connection_manager_set_max_conns          (SoupConnectionManager *manager,
+                                                                       guint                  max_conns);
+guint                  soup_connection_manager_get_max_conns          (SoupConnectionManager *manager);
+void                   soup_connection_manager_set_max_conns_per_host (SoupConnectionManager *manager,
+                                                                       guint                  
max_conns_per_host);
+guint                  soup_connection_manager_get_max_conns_per_host (SoupConnectionManager *manager);
+void                   soup_connection_manager_set_remote_connectable (SoupConnectionManager *manager,
+                                                                       GSocketConnectable    *connectable);
+GSocketConnectable    *soup_connection_manager_get_remote_connectable (SoupConnectionManager *manager);
+guint                  soup_connection_manager_get_num_conns          (SoupConnectionManager *manager);
+SoupConnection        *soup_connection_manager_get_connection         (SoupConnectionManager *manager,
+                                                                       SoupMessageQueueItem  *item);
+gboolean               soup_connection_manager_cleanup                (SoupConnectionManager *manager,
+                                                                       gboolean               cleanup_idle);
+GIOStream             *soup_connection_manager_steal_connection       (SoupConnectionManager *manager,
+                                                                       SoupMessage           *msg);
+
+#endif /* __SOUP_CONNECTION_MANAGER_H__ */
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index d27b8955..3ed380d1 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -7,7 +7,10 @@
 #define __SOUP_SESSION_PRIVATE_H__ 1
 
 #include "soup-session.h"
+#include "soup-connection.h"
 #include "soup-content-processor.h"
+#include "soup-message-queue-item.h"
+#include "soup-socket-properties.h"
 
 G_BEGIN_DECLS
 
@@ -31,6 +34,14 @@ GInputStream *soup_session_setup_message_body_input_stream (SoupSession        *
 GSList       *soup_session_get_features                    (SoupSession        *session,
                                                            GType               feature_type);
 
+gboolean soup_session_steal_preconnection (SoupSession          *session,
+                                           SoupMessageQueueItem *item,
+                                           SoupConnection       *conn);
+
+void     soup_session_kick_queue (SoupSession *session);
+
+SoupSocketProperties *soup_session_ensure_socket_props (SoupSession *session);
+
 G_END_DECLS
 
 #endif /* __SOUP_SESSION_PRIVATE_H__ */
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 534e3317..9f40f8f8 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -16,7 +16,7 @@
 #include "auth/soup-auth-manager.h"
 #include "auth/soup-auth-ntlm.h"
 #include "cache/soup-cache-private.h"
-#include "soup-connection.h"
+#include "soup-connection-manager.h"
 #include "soup-message-private.h"
 #include "soup-message-headers-private.h"
 #include "soup-misc.h"
@@ -29,9 +29,6 @@
 #include "websocket/soup-websocket-connection.h"
 #include "websocket/soup-websocket-extension-manager-private.h"
 
-#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
-
-
 /**
  * SoupSession:
  *
@@ -71,19 +68,6 @@
  * context at the time of the function call.
  **/
 
-typedef struct {
-       GUri            *uri;
-       GNetworkAddress *addr;
-
-       GSList      *connections;      /* CONTAINS: SoupConnection */
-       guint        num_conns;
-
-       GSource     *keep_alive_src;
-       SoupSession *session;
-} SoupSessionHost;
-static guint soup_host_uri_hash (gconstpointer key);
-static gboolean soup_host_uri_equal (gconstpointer v1, gconstpointer v2);
-
 typedef struct {
        gboolean disposed;
 
@@ -108,36 +92,19 @@ typedef struct {
        char *accept_language;
        gboolean accept_language_auto;
 
-       GSocketConnectable *remote_connectable;
-
        GSList *features;
        GHashTable *features_cache;
 
-       GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
-       GHashTable *conns; /* SoupConnection -> SoupSessionHost */
-       guint num_conns;
-       guint max_conns, max_conns_per_host;
-        guint64 last_connection_id;
+        SoupConnectionManager *conn_manager;
 } SoupSessionPrivate;
 
-static void free_host (SoupSessionHost *host);
-static void connection_state_changed (GObject *object, GParamSpec *param,
-                                     gpointer user_data);
-static void connection_disconnected (SoupConnection *conn, gpointer user_data);
-static void drop_connection (SoupSession *session, SoupSessionHost *host,
-                            SoupConnection *conn);
-
 static void async_run_queue (SoupSession *session);
 
 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
 
-static void soup_session_kick_queue (SoupSession *session);
-
-static void
-soup_session_process_queue_item (SoupSession          *session,
-                                SoupMessageQueueItem *item,
-                                gboolean             *should_cleanup,
-                                gboolean              loop);
+static void soup_session_process_queue_item (SoupSession          *session,
+                                             SoupMessageQueueItem *item,
+                                             gboolean              loop);
 
 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
@@ -238,16 +205,9 @@ soup_session_init (SoupSession *session)
        g_source_attach (priv->queue_source, g_main_context_get_thread_default ());
         priv->io_timeout = priv->idle_timeout = 60;
 
-       priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
-                                                 soup_host_uri_equal,
-                                                 NULL, (GDestroyNotify)free_host);
-       priv->https_hosts = g_hash_table_new_full (soup_host_uri_hash,
-                                                  soup_host_uri_equal,
-                                                  NULL, (GDestroyNotify)free_host);
-       priv->conns = g_hash_table_new (NULL, NULL);
-
-       priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
-       priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
+        priv->conn_manager = soup_connection_manager_new (session,
+                                                          SOUP_SESSION_MAX_CONNS_DEFAULT,
+                                                          SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT);
 
        priv->features_cache = g_hash_table_new (NULL, NULL);
 
@@ -277,7 +237,7 @@ soup_session_dispose (GObject *object)
 
        priv->disposed = TRUE;
        soup_session_abort (session);
-       g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
+       g_warn_if_fail (soup_connection_manager_get_num_conns (priv->conn_manager) == 0);
 
        while (priv->features)
                soup_session_remove_feature (session, priv->features->data);
@@ -297,11 +257,7 @@ soup_session_finalize (GObject *object)
        g_queue_free (priv->queue);
        g_source_unref (priv->queue_source);
 
-       g_clear_object (&priv->remote_connectable);
-
-       g_hash_table_destroy (priv->http_hosts);
-       g_hash_table_destroy (priv->https_hosts);
-       g_hash_table_destroy (priv->conns);
+        g_clear_pointer (&priv->conn_manager, soup_connection_manager_free);
 
        g_free (priv->user_agent);
        g_free (priv->accept_language);
@@ -320,13 +276,13 @@ soup_session_finalize (GObject *object)
        G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
 }
 
-static void
-ensure_socket_props (SoupSession *session)
+SoupSocketProperties *
+soup_session_ensure_socket_props (SoupSession *session)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
 
        if (priv->socket_props)
-               return;
+               return priv->socket_props;
 
        priv->socket_props = soup_socket_properties_new (priv->local_addr,
                                                         priv->tls_interaction,
@@ -336,6 +292,8 @@ ensure_socket_props (SoupSession *session)
                soup_socket_properties_set_proxy_resolver (priv->socket_props, priv->proxy_resolver);
        if (!priv->tlsdb_use_default)
                soup_socket_properties_set_tls_database (priv->socket_props, priv->tlsdb);
+
+        return priv->socket_props;
 }
 
 static void
@@ -348,7 +306,7 @@ socket_props_changed (SoupSession *session)
 
        soup_socket_properties_unref (priv->socket_props);
        priv->socket_props = NULL;
-       ensure_socket_props (session);
+       soup_session_ensure_socket_props (session);
 }
 
 static void
@@ -367,10 +325,10 @@ soup_session_set_property (GObject *object, guint prop_id,
                soup_session_set_proxy_resolver (session, g_value_get_object (value));
                break;
        case PROP_MAX_CONNS:
-               priv->max_conns = g_value_get_int (value);
+                soup_connection_manager_set_max_conns (priv->conn_manager, g_value_get_int (value));
                break;
        case PROP_MAX_CONNS_PER_HOST:
-               priv->max_conns_per_host = g_value_get_int (value);
+                soup_connection_manager_set_max_conns_per_host (priv->conn_manager, g_value_get_int (value));
                break;
        case PROP_TLS_DATABASE:
                soup_session_set_tls_database (session, g_value_get_object (value));
@@ -391,7 +349,7 @@ soup_session_set_property (GObject *object, guint prop_id,
                soup_session_set_accept_language_auto (session, g_value_get_boolean (value));
                break;
        case PROP_REMOTE_CONNECTABLE:
-               priv->remote_connectable = g_value_dup_object (value);
+                soup_connection_manager_set_remote_connectable (priv->conn_manager, g_value_get_object 
(value));
                break;
        case PROP_IDLE_TIMEOUT:
                soup_session_set_idle_timeout (session, g_value_get_uint (value));
@@ -524,7 +482,7 @@ soup_session_get_max_conns (SoupSession *session)
        g_return_val_if_fail (SOUP_IS_SESSION (session), 0);
 
        priv = soup_session_get_instance_private (session);
-       return priv->max_conns;
+       return soup_connection_manager_get_max_conns (priv->conn_manager);
 }
 
 /**
@@ -544,7 +502,7 @@ soup_session_get_max_conns_per_host (SoupSession *session)
        g_return_val_if_fail (SOUP_IS_SESSION (session), 0);
 
        priv = soup_session_get_instance_private (session);
-       return priv->max_conns_per_host;
+       return soup_connection_manager_get_max_conns_per_host (priv->conn_manager);
 }
 
 /**
@@ -989,122 +947,7 @@ soup_session_get_remote_connectable (SoupSession *session)
        g_return_val_if_fail (SOUP_IS_SESSION (session), NULL);
 
        priv = soup_session_get_instance_private (session);
-       return priv->remote_connectable;
-}
-
-/* Hosts */
-
-/* Note that we can't use soup_uri_host_hash() and soup_uri_host_equal()
- * because we want to ignore the protocol; http://example.com and
- * webcal://example.com are the same host.
- */
-static guint
-soup_host_uri_hash (gconstpointer key)
-{
-       GUri *uri = (GUri*)key;
-
-       g_return_val_if_fail (uri != NULL && g_uri_get_host (uri) != NULL, 0);
-
-       return g_uri_get_port (uri) + soup_str_case_hash (g_uri_get_host (uri));
-}
-
-static gboolean
-soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
-{
-       GUri *one = (GUri*)v1;
-       GUri *two = (GUri*)v2;
-
-       g_return_val_if_fail (one != NULL && two != NULL, one == two);
-
-        const char *one_host = g_uri_get_host (one);
-        const char *two_host = g_uri_get_host (two);
-       g_return_val_if_fail (one_host != NULL && two_host != NULL, one_host == two_host);
-
-       if (g_uri_get_port (one) != g_uri_get_port (two))
-               return FALSE;
-
-       return g_ascii_strcasecmp (one_host, two_host) == 0;
-}
-
-static SoupSessionHost *
-soup_session_host_new (SoupSession *session, GUri *uri)
-{
-       SoupSessionHost *host;
-        const char *scheme = g_uri_get_scheme (uri);
-
-       host = g_slice_new0 (SoupSessionHost);
-       if (g_strcmp0 (scheme, "http") &&
-           g_strcmp0 (scheme, "https")) {
-               host->uri = soup_uri_copy (uri,
-                                          SOUP_URI_SCHEME, soup_uri_is_https (uri) ?
-                                          "https" : "http",
-                                          SOUP_URI_NONE);
-       } else
-                host->uri = g_uri_ref (uri);
-
-       host->addr = g_object_new (G_TYPE_NETWORK_ADDRESS,
-                                  "hostname", g_uri_get_host (host->uri),
-                                  "port", g_uri_get_port (host->uri),
-                                  "scheme", g_uri_get_scheme (host->uri),
-                                  NULL);
-       host->keep_alive_src = NULL;
-       host->session = session;
-
-       return host;
-}
-
-static SoupSessionHost *
-get_host_for_uri (SoupSession *session, GUri *uri)
-{
-       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       SoupSessionHost *host;
-       gboolean https;
-       GUri *uri_tmp = NULL;
-
-       https = soup_uri_is_https (uri);
-       if (https)
-               host = g_hash_table_lookup (priv->https_hosts, uri);
-       else
-               host = g_hash_table_lookup (priv->http_hosts, uri);
-       if (host)
-               return host;
-
-       if (!soup_uri_is_http (uri) && !soup_uri_is_https (uri)) {
-               uri = uri_tmp = soup_uri_copy (uri,
-                                              SOUP_URI_SCHEME, https ? "https" : "http",
-                                              SOUP_URI_NONE);
-       }
-       host = soup_session_host_new (session, uri);
-       if (uri_tmp)
-               g_uri_unref (uri_tmp);
-
-       if (https)
-               g_hash_table_insert (priv->https_hosts, host->uri, host);
-       else
-               g_hash_table_insert (priv->http_hosts, host->uri, host);
-
-       return host;
-}
-
-static SoupSessionHost *
-get_host_for_message (SoupSession *session, SoupMessage *msg)
-{
-       return get_host_for_uri (session, soup_message_get_uri (msg));
-}
-
-static void
-free_host (SoupSessionHost *host)
-{
-       g_warn_if_fail (host->connections == NULL);
-
-       if (host->keep_alive_src) {
-               g_source_destroy (host->keep_alive_src);
-               g_source_unref (host->keep_alive_src);
-       }
-
-       g_uri_unref (host->uri);
-       g_object_unref (host->addr);
-       g_slice_free (SoupSessionHost, host);
+       return soup_connection_manager_get_remote_connectable (priv->conn_manager);
 }
 
 static SoupMessageQueueItem *
@@ -1440,115 +1283,6 @@ soup_session_send_queue_item (SoupSession *session,
                 soup_message_send_item (item->msg, item, completion_cb, item);
 }
 
-static gboolean
-soup_session_cleanup_connections (SoupSession *session,
-                                 gboolean     cleanup_idle)
-{
-       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       GSList *conns = NULL, *c;
-       GHashTableIter iter;
-       gpointer conn, host;
-       SoupConnectionState state;
-
-       g_hash_table_iter_init (&iter, priv->conns);
-       while (g_hash_table_iter_next (&iter, &conn, &host)) {
-               state = soup_connection_get_state (conn);
-                if (state == SOUP_CONNECTION_IDLE &&
-                    (cleanup_idle || !soup_connection_is_idle_open (conn))) {
-                       conns = g_slist_prepend (conns, g_object_ref (conn));
-                       g_hash_table_iter_remove (&iter);
-                       drop_connection (session, host, conn);
-               }
-       }
-
-       if (!conns)
-               return FALSE;
-
-       for (c = conns; c; c = c->next) {
-               conn = c->data;
-               soup_connection_disconnect (conn);
-               g_object_unref (conn);
-       }
-       g_slist_free (conns);
-
-       return TRUE;
-}
-
-static gboolean
-free_unused_host (gpointer user_data)
-{
-       SoupSessionHost *host = (SoupSessionHost *) user_data;
-       SoupSessionPrivate *priv = soup_session_get_instance_private (host->session);
-       GUri *uri = host->uri;
-
-       if (host->connections)
-               return FALSE;
-
-       /* This will free the host in addition to removing it from the
-        * hash table
-        */
-       if (soup_uri_is_https (uri))
-               g_hash_table_remove (priv->https_hosts, uri);
-       else
-               g_hash_table_remove (priv->http_hosts, uri);
-
-       return FALSE;
-}
-
-static void
-drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
-{
-       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-
-       if (host) {
-               host->connections = g_slist_remove (host->connections, conn);
-               host->num_conns--;
-
-               /* Free the SoupHost (and its GNetworkAddress) if there
-                * has not been any new connection to the host during
-                * the last HOST_KEEP_ALIVE msecs.
-                */
-               if (host->num_conns == 0) {
-                       g_assert (host->keep_alive_src == NULL);
-                       host->keep_alive_src = soup_add_timeout (g_main_context_get_thread_default (),
-                                                                HOST_KEEP_ALIVE,
-                                                                free_unused_host,
-                                                                host);
-               }
-       }
-
-       g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
-       g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
-       priv->num_conns--;
-
-       g_object_unref (conn);
-}
-
-static void
-connection_disconnected (SoupConnection *conn, gpointer user_data)
-{
-       SoupSession *session = user_data;
-       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       SoupSessionHost *host;
-
-       host = g_hash_table_lookup (priv->conns, conn);
-       if (host)
-               g_hash_table_remove (priv->conns, conn);
-       drop_connection (session, host, conn);
-
-       soup_session_kick_queue (session);
-}
-
-static void
-connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data)
-{
-       SoupSession *session = user_data;
-       SoupConnection *conn = SOUP_CONNECTION (object);
-
-       if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE && soup_connection_is_idle_open (conn))
-               soup_session_kick_queue (session);
-}
-
 static void
 soup_session_unqueue_item (SoupSession          *session,
                           SoupMessageQueueItem *item)
@@ -1603,7 +1337,7 @@ message_completed (SoupMessage *msg, SoupMessageIOCompletion completion, gpointe
                item->state = SOUP_MESSAGE_FINISHING;
 
                if (!item->async)
-                       soup_session_process_queue_item (item->session, item, NULL, TRUE);
+                       soup_session_process_queue_item (item->session, item, TRUE);
        }
 }
 
@@ -1754,7 +1488,7 @@ connect_async_complete (GObject      *object,
                /* Complete the preconnect successfully, since it was stolen. */
                item->state = SOUP_MESSAGE_FINISHING;
                item->related = NULL;
-               soup_session_process_queue_item (item->session, item, NULL, FALSE);
+               soup_session_process_queue_item (item->session, item, FALSE);
                soup_message_queue_item_unref (item);
 
                item = new_item;
@@ -1770,10 +1504,10 @@ connect_async_complete (GObject      *object,
        soup_message_queue_item_unref (item);
 }
 
-static gboolean
-steal_preconnection (SoupSession          *session,
-                     SoupMessageQueueItem *item,
-                     SoupConnection       *conn)
+gboolean
+soup_session_steal_preconnection (SoupSession          *session,
+                                  SoupMessageQueueItem *item,
+                                  SoupConnection       *conn)
 {
         SoupMessageQueueItem *preconnect_item;
 
@@ -1797,153 +1531,16 @@ steal_preconnection (SoupSession          *session,
         return TRUE;
 }
 
-static SoupConnection *
-get_connection_for_host (SoupSession *session,
-                        SoupMessageQueueItem *item,
-                        SoupSessionHost *host,
-                        gboolean need_new_connection,
-                        gboolean *try_cleanup)
-{
-       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       GSocketConnectable *remote_connectable;
-        guint8 force_http_version;
-       SoupConnection *conn;
-       GSList *conns;
-
-       if (priv->disposed)
-               return NULL;
-
-        conn = soup_message_get_connection (item->msg);
-       if (conn) {
-               g_return_val_if_fail (soup_connection_get_state (conn) != SOUP_CONNECTION_DISCONNECTED, 
FALSE);
-               return conn;
-       }
-
-        force_http_version = g_getenv ("SOUP_FORCE_HTTP1") ? SOUP_HTTP_1_1 : 
soup_message_get_force_http_version (item->msg);
-
-       for (conns = host->connections; conns; conns = conns->next) {
-                SoupHTTPVersion http_version;
-
-               conn = conns->data;
-
-                http_version = soup_connection_get_negotiated_protocol (conn);
-                if (force_http_version <= SOUP_HTTP_2_0 && http_version != force_http_version)
-                        continue;
-
-               switch (soup_connection_get_state (conn)) {
-                case SOUP_CONNECTION_IN_USE:
-                        if (!need_new_connection && http_version == SOUP_HTTP_2_0 && 
soup_connection_is_reusable (conn))
-                                return conn;
-                        break;
-               case SOUP_CONNECTION_IDLE:
-                       if (!need_new_connection && soup_connection_is_idle_open (conn))
-                               return conn;
-                       break;
-               case SOUP_CONNECTION_CONNECTING:
-                       if (steal_preconnection (session, item, conn))
-                               return conn;
-
-                        /* Always wait if we have a pending connection as it may be
-                         * an h2 connection which will be shared. http/1.x connections
-                         * will only be slightly delayed. */
-                        if (force_http_version > SOUP_HTTP_1_1 && !need_new_connection && 
!item->connect_only)
-                                return NULL;
-               default:
-                       break;
-               }
-       }
-
-       if (host->num_conns >= priv->max_conns_per_host) {
-               if (need_new_connection)
-                       *try_cleanup = TRUE;
-               return NULL;
-       }
-
-       if (priv->num_conns >= priv->max_conns) {
-               *try_cleanup = TRUE;
-               return NULL;
-       }
-
-       if (priv->remote_connectable == NULL) {
-               remote_connectable =
-                       g_object_new (G_TYPE_NETWORK_ADDRESS,
-                                     "hostname", g_uri_get_host (host->uri),
-                                     "port", g_uri_get_port (host->uri),
-                                     "scheme", g_uri_get_scheme (host->uri),
-                                     NULL);
-       } else {
-               remote_connectable = g_object_ref (priv->remote_connectable);
-       }
-
-       ensure_socket_props (session);
-       conn = g_object_new (SOUP_TYPE_CONNECTION,
-                             "id", ++priv->last_connection_id,
-                            "remote-connectable", remote_connectable,
-                            "ssl", soup_uri_is_https (host->uri),
-                            "socket-properties", priv->socket_props,
-                             "force-http-version", force_http_version,
-                            NULL);
-       g_object_unref (remote_connectable);
-
-       g_signal_connect (conn, "disconnected",
-                         G_CALLBACK (connection_disconnected),
-                         session);
-       g_signal_connect (conn, "notify::state",
-                         G_CALLBACK (connection_state_changed),
-                         session);
-
-       g_hash_table_insert (priv->conns, conn, host);
-
-       priv->num_conns++;
-       host->num_conns++;
-       host->connections = g_slist_prepend (host->connections, conn);
-
-       if (host->keep_alive_src) {
-               g_source_destroy (host->keep_alive_src);
-               g_source_unref (host->keep_alive_src);
-               host->keep_alive_src = NULL;
-       }
-
-       return conn;
-}
-
 static gboolean
-get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
+soup_session_ensure_item_connection (SoupSession          *session,
+                                     SoupMessageQueueItem *item)
 {
-       SoupSession *session = item->session;
-       SoupSessionHost *host;
-       SoupConnection *conn = NULL;
-       gboolean my_should_cleanup = FALSE;
-       gboolean need_new_connection;
-
-       soup_session_cleanup_connections (session, FALSE);
-
-       need_new_connection =
-               (soup_message_query_flags (item->msg, SOUP_MESSAGE_NEW_CONNECTION)) ||
-                (soup_message_is_misdirected_retry (item->msg)) ||
-               (!soup_message_query_flags (item->msg, SOUP_MESSAGE_IDEMPOTENT) &&
-                !SOUP_METHOD_IS_IDEMPOTENT (soup_message_get_method (item->msg)));
-
-       host = get_host_for_message (session, item->msg);
-       while (TRUE) {
-               conn = get_connection_for_host (session, item, host,
-                                               need_new_connection,
-                                               &my_should_cleanup);
-               if (conn || item->async)
-                       break;
-
-               if (my_should_cleanup) {
-                       soup_session_cleanup_connections (session, TRUE);
-                       my_should_cleanup = FALSE;
-                       continue;
-               }
-       }
+        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
+       SoupConnection *conn;
 
-       if (!conn) {
-               if (should_cleanup)
-                       *should_cleanup = my_should_cleanup;
+        conn = soup_connection_manager_get_connection (priv->conn_manager, item);
+       if (!conn)
                return FALSE;
-       }
 
         soup_message_set_connection (item->msg, conn);
 
@@ -1983,7 +1580,6 @@ get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
 static void
 soup_session_process_queue_item (SoupSession          *session,
                                 SoupMessageQueueItem *item,
-                                gboolean             *should_cleanup,
                                 gboolean              loop)
 {
        g_assert (item->session == session);
@@ -1994,7 +1590,7 @@ soup_session_process_queue_item (SoupSession          *session,
 
                switch (item->state) {
                case SOUP_MESSAGE_STARTING:
-                       if (!get_connection (item, should_cleanup))
+                       if (!soup_session_ensure_item_connection (session, item))
                                return;
                        break;
 
@@ -2064,8 +1660,7 @@ soup_session_process_queue_item (SoupSession          *session,
 }
 
 static void
-process_queue_item (SoupMessageQueueItem *item,
-                    gboolean             *should_cleanup)
+process_queue_item (SoupMessageQueueItem *item)
 {
         if (!item->async)
                 return;
@@ -2074,32 +1669,19 @@ process_queue_item (SoupMessageQueueItem *item,
         if (soup_message_get_method (item->msg) == SOUP_METHOD_CONNECT)
                 return;
 
-        soup_session_process_queue_item (item->session, item, should_cleanup, TRUE);
+        soup_session_process_queue_item (item->session, item, TRUE);
 }
 
 static void
 async_run_queue (SoupSession *session)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       gboolean try_cleanup = TRUE, should_cleanup = FALSE;
 
        g_object_ref (session);
         priv->in_async_run_queue++;
-       soup_session_cleanup_connections (session, FALSE);
-
- try_again:
-        g_queue_foreach (priv->queue, (GFunc)process_queue_item, &should_cleanup);
+       soup_connection_manager_cleanup (priv->conn_manager, FALSE);
 
-       if (try_cleanup && should_cleanup) {
-               /* There is at least one message in the queue that
-                * could be sent if we cleanupd an idle connection from
-                * some other server.
-                */
-               if (soup_session_cleanup_connections (session, TRUE)) {
-                       try_cleanup = should_cleanup = FALSE;
-                       goto try_again;
-               }
-       }
+        g_queue_foreach (priv->queue, (GFunc)process_queue_item, NULL);
 
         priv->in_async_run_queue--;
         if (!priv->in_async_run_queue && priv->needs_queue_sort) {
@@ -2152,7 +1734,7 @@ soup_session_pause_message (SoupSession *session,
                soup_message_io_pause (msg);
 }
 
-static void
+void
 soup_session_kick_queue (SoupSession *session)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
@@ -2216,36 +1798,16 @@ void
 soup_session_abort (SoupSession *session)
 {
        SoupSessionPrivate *priv;
-       GSList *conns, *c;
-       GHashTableIter iter;
-       gpointer conn, host;
 
        g_return_if_fail (SOUP_IS_SESSION (session));
+
        priv = soup_session_get_instance_private (session);
 
        /* Cancel everything */
        g_queue_foreach (priv->queue, (GFunc)soup_message_queue_item_cancel, NULL);
 
        /* Close all idle connections */
-       conns = NULL;
-       g_hash_table_iter_init (&iter, priv->conns);
-       while (g_hash_table_iter_next (&iter, &conn, &host)) {
-               SoupConnectionState state;
-
-               state = soup_connection_get_state (conn);
-               if (state == SOUP_CONNECTION_IDLE) {
-                       conns = g_slist_prepend (conns, g_object_ref (conn));
-                       g_hash_table_iter_remove (&iter);
-                       drop_connection (session, host, conn);
-               }
-       }
-
-       for (c = conns; c; c = c->next) {
-               soup_connection_disconnect (c->data);
-               g_object_unref (c->data);
-       }
-
-       g_slist_free (conns);
+        soup_connection_manager_cleanup (priv->conn_manager, TRUE);
 }
 
 static gboolean
@@ -3081,7 +2643,7 @@ run_until_read_done (SoupMessage          *msg,
                        soup_message_io_finished (msg);
                item->paused = FALSE;
                item->state = SOUP_MESSAGE_FINISHING;
-               soup_session_process_queue_item (item->session, item, NULL, FALSE);
+               soup_session_process_queue_item (item->session, item, FALSE);
        }
        async_send_request_return_result (item, NULL, error);
 }
@@ -3390,7 +2952,7 @@ soup_session_send_finish (SoupSession   *session,
                                 item->state = SOUP_MESSAGE_FINISHING;
 
                         if (item->state != SOUP_MESSAGE_FINISHED)
-                                soup_session_process_queue_item (session, item, NULL, FALSE);
+                                soup_session_process_queue_item (session, item, FALSE);
                 }
        }
 
@@ -3453,7 +3015,7 @@ soup_session_send (SoupSession   *session,
 
        while (!stream) {
                /* Get a connection, etc */
-               soup_session_process_queue_item (session, item, NULL, TRUE);
+               soup_session_process_queue_item (session, item, TRUE);
                if (item->state != SOUP_MESSAGE_RUNNING)
                        break;
 
@@ -3526,7 +3088,7 @@ soup_session_send (SoupSession   *session,
                        item->state = SOUP_MESSAGE_FINISHING;
                item->paused = FALSE;
                if (item->state != SOUP_MESSAGE_FINISHED)
-                       soup_session_process_queue_item (session, item, NULL, TRUE);
+                       soup_session_process_queue_item (session, item, TRUE);
        }
 
        soup_message_queue_item_unref (item);
@@ -3719,27 +3281,6 @@ soup_session_get_async_result_message (SoupSession  *session,
         return item ? item->msg : NULL;
 }
 
-static GIOStream *
-steal_connection (SoupSession          *session,
-                  SoupMessageQueueItem *item)
-{
-        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-        SoupConnection *conn;
-        SoupSessionHost *host;
-        GIOStream *stream;
-
-        conn = g_object_ref (soup_message_get_connection (item->msg));
-        host = get_host_for_message (session, item->msg);
-        g_hash_table_remove (priv->conns, conn);
-        drop_connection (session, host, conn);
-
-       stream = soup_connection_steal_iostream (conn);
-        soup_message_set_connection (item->msg, NULL);
-       g_object_unref (conn);
-
-       return stream;
-}
-
 /**
  * soup_session_steal_connection:
  * @session: a #SoupSession
@@ -3764,19 +3305,9 @@ static GIOStream *
 soup_session_steal_connection (SoupSession *session,
                               SoupMessage *msg)
 {
-       SoupMessageQueueItem *item;
-       GIOStream *stream = NULL;
-        SoupConnection *conn;
-
-       item = soup_session_lookup_queue_item (session, msg);
-       if (!item)
-               return NULL;
-
-        conn = soup_message_get_connection (item->msg);
-       if (conn && soup_connection_get_state (conn) == SOUP_CONNECTION_IN_USE)
-               stream = steal_connection (session, item);
+        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
 
-       return stream;
+        return soup_connection_manager_steal_connection (priv->conn_manager, msg);
 }
 
 static GPtrArray *
diff --git a/tests/misc-test.c b/tests/misc-test.c
index 3ab8fcab..f0a33e96 100644
--- a/tests/misc-test.c
+++ b/tests/misc-test.c
@@ -817,14 +817,14 @@ do_remote_address_test (void)
         g_bytes_unref (body);
         g_object_unref (msg2);
 
-        /* We get a new one if we force a new connection */
+        /* We get the same one if we force a new connection */
         msg2 = soup_message_new_from_uri (SOUP_METHOD_GET, base_uri);
         soup_message_add_flags (msg2, SOUP_MESSAGE_NEW_CONNECTION);
         g_assert_null (soup_message_get_remote_address (msg2));
         body = soup_test_session_async_send (session, msg2, NULL, NULL);
         g_assert_nonnull (soup_message_get_remote_address (msg2));
         g_assert_cmpuint (soup_message_get_connection_id (msg1), !=, soup_message_get_connection_id (msg2));
-        g_assert_false (soup_message_get_remote_address (msg1) == soup_message_get_remote_address (msg2));
+        g_assert_true (soup_message_get_remote_address (msg1) == soup_message_get_remote_address (msg2));
         g_bytes_unref (body);
         g_object_unref (msg2);
 


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