[libsoup/wip/remove-deprecations: 60/77] SoupSessionHost: split from SoupSession




commit 5defc85d976eb232304ab42dce740ee050659927
Author: Patrick Griffis <pgriffis igalia com>
Date:   Mon Dec 9 12:16:05 2013 +0100

    SoupSessionHost: split from SoupSession
    
    HTTP/2.0 will need to do connection management differently than
    HTTP/1.1, so let's split SoupSessionHost out of soup-session.c in
    preparation for making it more complicated.
    
    Based on work by Dan Winship.

 libsoup/meson.build            |   1 +
 libsoup/soup-session-host.c    | 325 +++++++++++++++++++++++++++++++++++++++
 libsoup/soup-session-host.h    |  42 +++++
 libsoup/soup-session-private.h |   2 +
 libsoup/soup-session.c         | 337 +++++++++++------------------------------
 libsoup/soup-types.h           |   1 +
 6 files changed, 460 insertions(+), 248 deletions(-)
---
diff --git a/libsoup/meson.build b/libsoup/meson.build
index ac7f6b84..ceae8c4c 100644
--- a/libsoup/meson.build
+++ b/libsoup/meson.build
@@ -81,6 +81,7 @@ soup_sources = [
   'soup-session.c',
   'soup-session-async.c',
   'soup-session-feature.c',
+  'soup-session-host.c',
   'soup-session-sync.c',
   'soup-socket.c',
   'soup-socket-properties.c',
diff --git a/libsoup/soup-session-host.c b/libsoup/soup-session-host.c
new file mode 100644
index 00000000..c3d8da7b
--- /dev/null
+++ b/libsoup/soup-session-host.c
@@ -0,0 +1,325 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-session-host.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-session-host.h"
+#include "soup.h"
+#include "soup-connection.h"
+#include "soup-misc.h"
+#include "soup-session-private.h"
+#include "soup-socket-private.h"
+
+struct _SoupSessionHost
+{
+       GObject parent_instance;
+};
+
+typedef struct {
+       GMutex       mutex;
+
+       SoupURI     *uri;
+       SoupAddress *addr;
+
+       GSList      *connections;      /* CONTAINS: SoupConnection */
+       guint        num_conns;
+       guint        max_conns;
+
+       guint        num_messages;
+
+       GSource     *keep_alive_src;
+       SoupSession *session;
+} SoupSessionHostPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (SoupSessionHost, soup_session_host, G_TYPE_OBJECT)
+
+enum {
+       UNUSED,
+       LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
+
+static void
+soup_session_host_init (SoupSessionHost *host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+
+       g_mutex_init (&priv->mutex);
+}
+
+static void
+soup_session_host_finalize (GObject *object)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private ((SoupSessionHost*)object);
+
+       g_warn_if_fail (priv->connections == NULL);
+
+       if (priv->keep_alive_src) {
+               g_source_destroy (priv->keep_alive_src);
+               g_source_unref (priv->keep_alive_src);
+       }
+
+       soup_uri_free (priv->uri);
+       g_object_unref (priv->addr);
+
+       g_mutex_clear (&priv->mutex);
+
+       G_OBJECT_CLASS (soup_session_host_parent_class)->finalize (object);
+}
+
+static void
+soup_session_host_class_init (SoupSessionHostClass *host_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (host_class);
+
+       object_class->finalize = soup_session_host_finalize;
+
+       signals[UNUSED] =
+               g_signal_new ("unused",
+                             G_OBJECT_CLASS_TYPE (object_class),
+                             G_SIGNAL_RUN_LAST,
+                             0,
+                             NULL, NULL,
+                             NULL,
+                             G_TYPE_NONE, 0);
+
+}
+
+SoupSessionHost *
+soup_session_host_new (SoupSession *session,
+                      SoupURI     *uri)
+{
+       SoupSessionHost *host;
+       SoupSessionHostPrivate *priv;
+
+       host = g_object_new (SOUP_TYPE_SESSION_HOST, NULL);
+       priv = soup_session_host_get_instance_private (host);
+
+       priv->uri = soup_uri_copy_host (uri);
+       priv->addr = g_object_new (SOUP_TYPE_ADDRESS,
+                                  SOUP_ADDRESS_NAME, priv->uri->host,
+                                  SOUP_ADDRESS_PORT, priv->uri->port,
+                                  SOUP_ADDRESS_PROTOCOL, priv->uri->scheme,
+                                  NULL);
+       priv->keep_alive_src = NULL;
+       priv->session = session;
+
+       g_object_get (G_OBJECT (session),
+                     SOUP_SESSION_MAX_CONNS_PER_HOST, &priv->max_conns,
+                     NULL);
+
+       return host;
+}
+
+SoupURI *
+soup_session_host_get_uri (SoupSessionHost *host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       return priv->uri;
+}
+
+SoupAddress *
+soup_session_host_get_address (SoupSessionHost *host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       return priv->addr;
+}
+
+void
+soup_session_host_add_message (SoupSessionHost *host,
+                              SoupMessage     *msg)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       priv->num_messages++;
+}
+
+void
+soup_session_host_remove_message (SoupSessionHost *host,
+                                 SoupMessage     *msg)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       priv->num_messages--;
+}
+
+static gboolean
+emit_unused (gpointer host)
+{
+       g_signal_emit (host, signals[UNUSED], 0);
+       return FALSE;
+}
+
+static void
+connection_disconnected (SoupConnection *conn, gpointer host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+
+       g_mutex_lock (&priv->mutex);
+
+       priv->connections = g_slist_remove (priv->connections, conn);
+       priv->num_conns--;
+
+       if (priv->num_conns == 0) {
+               g_assert (priv->keep_alive_src == NULL);
+               priv->keep_alive_src = soup_add_timeout_reffed (soup_session_get_async_context 
(priv->session),
+                                                               HOST_KEEP_ALIVE,
+                                                               emit_unused,
+                                                               host);
+       }
+
+       g_mutex_unlock (&priv->mutex);
+}
+
+SoupConnection *
+soup_session_host_get_connection (SoupSessionHost *host,
+                                 gboolean need_new_connection,
+                                 gboolean at_max_conns,
+                                  gboolean ignore_connection_limits,
+                                 gboolean *try_cleanup)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       SoupConnection *conn;
+       GSList *conns;
+       int num_pending = 0;
+       SoupSocketProperties *socket_props;
+
+       g_mutex_lock (&priv->mutex);
+
+       for (conns = priv->connections; conns; conns = conns->next) {
+               conn = conns->data;
+
+               if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
+                       soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+                       g_mutex_unlock (&priv->mutex);
+                       return conn;
+               } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
+                       num_pending++;
+       }
+
+       /* Limit the number of pending connections; num_messages / 2
+        * is somewhat arbitrary...
+        */
+       if (num_pending > priv->num_messages / 2) {
+               g_mutex_unlock (&priv->mutex);
+               return NULL;
+       }
+
+        if (!ignore_connection_limits) {
+                if (priv->num_conns >= priv->max_conns) {
+                        if (need_new_connection)
+                                *try_cleanup = TRUE;
+                        g_mutex_unlock (&priv->mutex);
+                        return NULL;
+                }
+
+                if (at_max_conns) {
+                        *try_cleanup = TRUE;
+                        g_mutex_unlock (&priv->mutex);
+                        return NULL;
+                }
+        }
+
+       g_object_get (G_OBJECT (priv->session),
+                     SOUP_SESSION_SOCKET_PROPERTIES, &socket_props,
+                     NULL);
+       conn = g_object_new (SOUP_TYPE_CONNECTION,
+                            SOUP_CONNECTION_REMOTE_URI, priv->uri,
+                             SOUP_CONNECTION_SSL, priv->uri->scheme == SOUP_URI_SCHEME_HTTPS,
+                            SOUP_CONNECTION_SOCKET_PROPERTIES, socket_props,
+                            NULL);
+       soup_socket_properties_unref (socket_props);
+
+       priv->num_conns++;
+       priv->connections = g_slist_prepend (priv->connections, conn);
+
+       g_signal_connect (conn, "disconnected",
+                         G_CALLBACK (connection_disconnected),
+                         host);
+
+       if (priv->keep_alive_src) {
+               g_source_destroy (priv->keep_alive_src);
+               g_source_unref (priv->keep_alive_src);
+               priv->keep_alive_src = NULL;
+       }
+
+       g_mutex_unlock (&priv->mutex);
+       return conn;
+}
+
+int
+soup_session_host_get_num_connections (SoupSessionHost *host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       return priv->num_conns;
+}
+
+GSList *
+soup_session_host_get_connections (SoupSessionHost *host)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       GSList *conns, *c;
+
+       g_mutex_lock (&priv->mutex);
+
+       conns = NULL;
+       for (c = priv->connections; c; c = c->next)
+               conns = g_slist_prepend (conns, g_object_ref (c->data));
+       conns = g_slist_reverse (conns);
+
+       g_mutex_unlock (&priv->mutex);
+       return conns;
+}
+
+void
+soup_session_host_steal_connection (SoupSessionHost *host,
+                                    SoupConnection  *conn)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+
+       g_return_if_fail (SOUP_IS_SESSION_HOST (host));
+       g_warn_if_fail (g_slist_find (priv->connections, conn) != NULL);
+
+       priv->connections = g_slist_remove (priv->connections, conn);
+}
+
+gboolean
+soup_session_host_cleanup_connections (SoupSessionHost *host,
+                                      gboolean cleanup_idle)
+{
+       SoupSessionHostPrivate *priv = soup_session_host_get_instance_private (host);
+       GSList *c, *disconnect_conns;
+       SoupConnection *conn;
+       SoupConnectionState state;
+
+       disconnect_conns = NULL;
+
+       g_mutex_lock (&priv->mutex);
+       for (c = priv->connections; c; c = c->next) {
+               conn = c->data;
+               state = soup_connection_get_state (conn);
+               if (state == SOUP_CONNECTION_REMOTE_DISCONNECTED ||
+                   (cleanup_idle && state == SOUP_CONNECTION_IDLE))
+                       disconnect_conns = g_slist_prepend (disconnect_conns, g_object_ref (conn));
+       }
+       g_mutex_unlock (&priv->mutex);
+
+       if (!disconnect_conns)
+               return FALSE;
+
+       for (c = disconnect_conns; c; c = c->next) {
+               conn = c->data;
+               soup_connection_disconnect (conn);
+               g_object_unref (conn);
+       }
+       g_slist_free (disconnect_conns);
+
+       return TRUE;
+}
diff --git a/libsoup/soup-session-host.h b/libsoup/soup-session-host.h
new file mode 100644
index 00000000..10aa43ae
--- /dev/null
+++ b/libsoup/soup-session-host.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#pragma once
+
+#include "soup-types.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_SESSION_HOST (soup_session_host_get_type ())
+G_DECLARE_FINAL_TYPE (SoupSessionHost, soup_session_host, SOUP, SESSION_HOST, GObject)
+
+GType soup_session_host_get_type (void);
+
+SoupSessionHost *soup_session_host_new                 (SoupSession          *session,
+                                                       SoupURI              *uri);
+
+SoupURI         *soup_session_host_get_uri             (SoupSessionHost      *host);
+SoupAddress     *soup_session_host_get_address         (SoupSessionHost      *host);
+
+void             soup_session_host_add_message         (SoupSessionHost      *host,
+                                                       SoupMessage          *msg);
+void             soup_session_host_remove_message      (SoupSessionHost      *host,
+                                                       SoupMessage          *msg);
+
+SoupConnection  *soup_session_host_get_connection      (SoupSessionHost      *host,
+                                                        gboolean              need_new_connection,
+                                                        gboolean              at_max_conns,
+                                                        gboolean              ignore_connection_limits,
+                                                        gboolean             *try_cleanup);
+int              soup_session_host_get_num_connections (SoupSessionHost      *host);
+GSList          *soup_session_host_get_connections     (SoupSessionHost      *host);
+
+gboolean         soup_session_host_cleanup_connections (SoupSessionHost      *host,
+                                                       gboolean              cleanup_idle);
+
+void             soup_session_host_steal_connection    (SoupSessionHost      *host,
+                                                        SoupConnection       *conn);
+
+G_END_DECLS
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index 057b3e48..ae360be9 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -11,6 +11,8 @@
 
 G_BEGIN_DECLS
 
+#define SOUP_SESSION_SOCKET_PROPERTIES "socket-properties"
+
 /* "protected" methods for subclasses */
 SoupMessageQueue     *soup_session_get_queue            (SoupSession          *session);
 
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 0f93c24b..27f9948a 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -19,14 +19,13 @@
 #include "soup-message-private.h"
 #include "soup-misc.h"
 #include "soup-message-queue.h"
+#include "soup-session-host.h"
 #include "soup-session-private.h"
 #include "soup-socket-private.h"
 #include "websocket/soup-websocket.h"
 #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 */
-
 /**
  * SECTION:soup-session
  * @short_description: Soup session state object
@@ -77,20 +76,9 @@
  * subtypes) have a #SoupContentDecoder by default.
  **/
 
-typedef struct {
-       SoupURI     *uri;
-       SoupAddress *addr;
-
-       GSList      *connections;      /* CONTAINS: SoupConnection */
-       guint        num_conns;
-
-       guint        num_messages;
-
-       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);
+static void free_unused_host (SoupSessionHost *host, gpointer session);
 
 typedef struct {
        gboolean disposed;
@@ -120,7 +108,6 @@ typedef struct {
        GHashTable *features_cache;
 
        GHashTable *http_hosts, *https_hosts; /* char* -> SoupSessionHost */
-       GHashTable *conns; /* SoupConnection -> SoupSessionHost */
        guint num_conns;
        guint max_conns, max_conns_per_host;
 
@@ -145,12 +132,10 @@ typedef struct {
 
 #define SOUP_IS_PLAIN_SESSION(o) (G_TYPE_FROM_INSTANCE (o) == SOUP_TYPE_SESSION)
 
-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 drop_connection (SoupSession *session, SoupConnection *conn);
 
 static void auth_manager_authenticate (SoupAuthManager *manager,
                                       SoupMessage *msg, SoupAuth *auth,
@@ -184,6 +169,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 enum {
        PROP_0,
 
+       PROP_SOCKET_PROPERTIES,
        PROP_PROXY_RESOLVER,
        PROP_MAX_CONNS,
        PROP_MAX_CONNS_PER_HOST,
@@ -220,11 +206,10 @@ soup_session_init (SoupSession *session)
        g_cond_init (&priv->conn_cond);
        priv->http_hosts = g_hash_table_new_full (soup_host_uri_hash,
                                                  soup_host_uri_equal,
-                                                 NULL, (GDestroyNotify)free_host);
+                                                 NULL, g_object_unref);
        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);
+                                                  NULL, g_object_unref);
 
        priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
        priv->max_conns_per_host = SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT;
@@ -304,7 +289,6 @@ soup_session_dispose (GObject *object)
 
        priv->disposed = TRUE;
        soup_session_abort (session);
-       g_warn_if_fail (g_hash_table_size (priv->conns) == 0);
 
        while (priv->features)
                soup_session_remove_feature (session, priv->features->data);
@@ -324,7 +308,6 @@ soup_session_finalize (GObject *object)
        g_cond_clear (&priv->conn_cond);
        g_hash_table_destroy (priv->http_hosts);
        g_hash_table_destroy (priv->https_hosts);
-       g_hash_table_destroy (priv->conns);
 
        g_free (priv->user_agent);
        g_free (priv->accept_language);
@@ -697,6 +680,10 @@ soup_session_get_property (GObject *object, guint prop_id,
        GTlsDatabase *tlsdb;
 
        switch (prop_id) {
+       case PROP_SOCKET_PROPERTIES:
+               ensure_socket_props (session);
+               g_value_set_boxed (value, priv->socket_props);
+               break;
        case PROP_LOCAL_ADDRESS:
                g_value_set_object (value, priv->local_addr);
                break;
@@ -865,35 +852,6 @@ soup_host_uri_equal (gconstpointer v1, gconstpointer v2)
        return g_ascii_strcasecmp (one->host, two->host) == 0;
 }
 
-
-static SoupSessionHost *
-soup_session_host_new (SoupSession *session, SoupURI *uri)
-{
-       SoupSessionHost *host;
-
-       host = g_slice_new0 (SoupSessionHost);
-       host->uri = soup_uri_copy_host (uri);
-       if (host->uri->scheme != SOUP_URI_SCHEME_HTTP &&
-           host->uri->scheme != SOUP_URI_SCHEME_HTTPS) {
-               SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-
-               if (soup_uri_is_https (host->uri, priv->https_aliases))
-                       host->uri->scheme = SOUP_URI_SCHEME_HTTPS;
-               else
-                       host->uri->scheme = SOUP_URI_SCHEME_HTTP;
-       }
-
-       host->addr = g_object_new (SOUP_TYPE_ADDRESS,
-                                  SOUP_ADDRESS_NAME, host->uri->host,
-                                  SOUP_ADDRESS_PORT, host->uri->port,
-                                  SOUP_ADDRESS_PROTOCOL, host->uri->scheme,
-                                  NULL);
-       host->keep_alive_src = NULL;
-       host->session = session;
-
-       return host;
-}
-
 /* Requires conn_lock to be locked */
 static SoupSessionHost *
 get_host_for_uri (SoupSession *session, SoupURI *uri)
@@ -921,9 +879,11 @@ get_host_for_uri (SoupSession *session, SoupURI *uri)
                soup_uri_free (uri_tmp);
 
        if (https)
-               g_hash_table_insert (priv->https_hosts, host->uri, host);
+               g_hash_table_insert (priv->https_hosts, soup_session_host_get_uri (host), host);
        else
-               g_hash_table_insert (priv->http_hosts, host->uri, host);
+               g_hash_table_insert (priv->http_hosts, soup_session_host_get_uri (host), host);
+
+       g_signal_connect (host, "unused", G_CALLBACK (free_unused_host), session);
 
        return host;
 }
@@ -935,21 +895,6 @@ 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);
-       }
-
-       soup_uri_free (host->uri);
-       g_object_unref (host->addr);
-       g_slice_free (SoupSessionHost, host);
-}
-
 static void
 auth_manager_authenticate (SoupAuthManager *manager, SoupMessage *msg,
                           SoupAuth *auth, gboolean retrying,
@@ -1173,7 +1118,7 @@ soup_session_append_queue_item (SoupSession *session, SoupMessage *msg,
 
        g_mutex_lock (&priv->conn_lock);
        host = get_host_for_message (session, item->msg);
-       host->num_messages++;
+       soup_session_host_add_message (host, msg);
        g_mutex_unlock (&priv->conn_lock);
 
        if (!(soup_message_get_flags (msg) & SOUP_MESSAGE_NO_REDIRECT)) {
@@ -1237,92 +1182,69 @@ soup_session_cleanup_connections (SoupSession *session,
                                  gboolean     cleanup_idle)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       GSList *conns = NULL, *c;
+       GSList *hosts, *h;
        GHashTableIter iter;
-       gpointer conn, host;
-       SoupConnectionState state;
+       SoupSessionHost *host;
+       gboolean closed_any = FALSE;
 
        g_mutex_lock (&priv->conn_lock);
-       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_REMOTE_DISCONNECTED ||
-                   (cleanup_idle && state == SOUP_CONNECTION_IDLE)) {
-                       conns = g_slist_prepend (conns, g_object_ref (conn));
-                       g_hash_table_iter_remove (&iter);
-                       drop_connection (session, host, conn);
-               }
-       }
-       g_mutex_unlock (&priv->conn_lock);
 
-       if (!conns)
-               return FALSE;
+       hosts = NULL;
+       g_hash_table_iter_init (&iter, priv->http_hosts);
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &host))
+               hosts = g_slist_prepend (hosts, g_object_ref (host));
+       g_hash_table_iter_init (&iter, priv->https_hosts);
+       while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &host))
+               hosts = g_slist_prepend (hosts, g_object_ref (host));
+
+       g_mutex_unlock (&priv->conn_lock);
 
-       for (c = conns; c; c = c->next) {
-               conn = c->data;
-               soup_connection_disconnect (conn);
-               g_object_unref (conn);
+       for (h = hosts; h; h = h->next) {
+               host = h->data;
+               if (soup_session_host_cleanup_connections (host, cleanup_idle))
+                       closed_any = TRUE;
+               g_object_unref (host);
        }
-       g_slist_free (conns);
+       g_slist_free (hosts);
 
-       return TRUE;
+       return closed_any;
 }
 
-static gboolean
-free_unused_host (gpointer user_data)
+static void
+free_unused_host (SoupSessionHost *host,
+                 gpointer         user_data)
 {
-       SoupSessionHost *host = (SoupSessionHost *) user_data;
-       SoupSessionPrivate *priv = soup_session_get_instance_private (host->session);
+       SoupSession *session = user_data;
+       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
+       SoupURI *uri;
 
        g_mutex_lock (&priv->conn_lock);
 
        /* In a multithreaded session, a connection might have been
         * added while we were waiting for conn_lock.
         */
-       if (host->connections) {
+       if (soup_session_host_get_num_connections (host)) {
                g_mutex_unlock (&priv->conn_lock);
-               return FALSE;
+               return;
        }
 
        /* This will free the host in addition to removing it from the
         * hash table
         */
-       if (host->uri->scheme == SOUP_URI_SCHEME_HTTPS)
-               g_hash_table_remove (priv->https_hosts, host->uri);
+       uri = soup_session_host_get_uri (host);
+       if (uri->scheme == SOUP_URI_SCHEME_HTTPS)
+               g_hash_table_remove (priv->https_hosts, uri);
        else
-               g_hash_table_remove (priv->http_hosts, host->uri);
+               g_hash_table_remove (priv->http_hosts, uri);
        g_mutex_unlock (&priv->conn_lock);
-
-       return FALSE;
 }
 
+/* Requires conn_lock to be locked */
 static void
-drop_connection (SoupSession *session, SoupSessionHost *host, SoupConnection *conn)
+drop_connection (SoupSession *session, SoupConnection *conn)
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
 
-       /* Note: caller must hold conn_lock, and must remove @conn
-        * from priv->conns itself.
-        */
-
-       if (host) {
-               host->connections = g_slist_remove (host->connections, conn);
-               host->num_conns--;
-
-               /* Free the SoupHost (and its SoupAddress) 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 (priv->async_context,
-                                                                HOST_KEEP_ALIVE,
-                                                                free_unused_host,
-                                                                host);
-                       host->keep_alive_src = g_source_ref (host->keep_alive_src);
-               }
-       }
-
        g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session);
        g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session);
        priv->num_conns--;
@@ -1335,14 +1257,10 @@ connection_disconnected (SoupConnection *conn, gpointer user_data)
 {
        SoupSession *session = user_data;
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
-       SoupSessionHost *host;
 
        g_mutex_lock (&priv->conn_lock);
 
-       host = g_hash_table_lookup (priv->conns, conn);
-       if (host)
-               g_hash_table_remove (priv->conns, conn);
-       drop_connection (session, host, conn);
+       drop_connection (session, conn);
 
        g_mutex_unlock (&priv->conn_lock);
 
@@ -1393,14 +1311,7 @@ soup_session_unqueue_item (SoupSession          *session,
 
        g_mutex_lock (&priv->conn_lock);
        host = get_host_for_message (session, item->msg);
-       host->num_messages--;
-       if (dedicated_conn) {
-               /* FIXME: Do not drop the connection if current number of connections
-                * is no longer over the limits, just mark it as IDLE so it can be reused.
-                */
-               g_hash_table_remove (priv->conns, dedicated_conn);
-               drop_connection (session, host, dedicated_conn);
-       }
+       soup_session_host_remove_message (host, item->msg);
        g_cond_broadcast (&priv->conn_cond);
        g_mutex_unlock (&priv->conn_lock);
 
@@ -1692,86 +1603,34 @@ get_connection_for_host (SoupSession *session,
 {
        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
        SoupConnection *conn;
-       GSList *conns;
-       guint num_pending = 0;
 
        if (priv->disposed)
                return NULL;
 
        if (item->conn) {
-               g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, 
FALSE);
+               g_return_val_if_fail (soup_connection_get_state (item->conn) != SOUP_CONNECTION_DISCONNECTED, 
NULL);
                return item->conn;
        }
 
-       for (conns = host->connections; conns; conns = conns->next) {
-               conn = conns->data;
-
-               if (!need_new_connection && soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE) {
-                       soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
-                       return conn;
-               } else if (soup_connection_get_state (conn) == SOUP_CONNECTION_CONNECTING)
-                       num_pending++;
-       }
-
-       /* Limit the number of pending connections; num_messages / 2
-        * is somewhat arbitrary...
-        */
-       if (num_pending > host->num_messages / 2) {
-               if (!ignore_connection_limits)
-                       return NULL;
-
-               *is_dedicated_connection = TRUE;
-       }
-
-       if (host->num_conns >= priv->max_conns_per_host) {
-               if (!ignore_connection_limits) {
-                       if (need_new_connection)
-                               *try_cleanup = TRUE;
-                       return NULL;
-               }
-
-               *is_dedicated_connection = TRUE;
-       }
-
-       if (priv->num_conns >= priv->max_conns) {
-               if (!ignore_connection_limits) {
-                       *try_cleanup = TRUE;
-                       return NULL;
-               }
-
-               *is_dedicated_connection = TRUE;
-       }
-
-       ensure_socket_props (session);
-       conn = g_object_new (SOUP_TYPE_CONNECTION,
-                            SOUP_CONNECTION_REMOTE_URI, host->uri,
-                            SOUP_CONNECTION_SSL, soup_uri_is_https (host->uri, priv->https_aliases),
-                            SOUP_CONNECTION_SOCKET_PROPERTIES, priv->socket_props,
-                            NULL);
-
-       g_signal_connect (conn, "disconnected",
-                         G_CALLBACK (connection_disconnected),
-                         session);
-       g_signal_connect (conn, "notify::state",
-                         G_CALLBACK (connection_state_changed),
-                         session);
-
-       /* This is a debugging-related signal, and so can ignore the
-        * usual rule about not emitting signals while holding
-        * conn_lock.
-        */
-       g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
-
-       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;
+       conn = soup_session_host_get_connection (host, need_new_connection,
+                                                 priv->num_conns >= priv->max_conns,
+                                                 ignore_connection_limits,
+                                                 try_cleanup);
+
+       if (conn && soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) {
+               g_signal_connect (conn, "disconnected",
+                                 G_CALLBACK (connection_disconnected),
+                                 session);
+               g_signal_connect (conn, "notify::state",
+                                 G_CALLBACK (connection_state_changed),
+                                 session);
+
+               /* This is a debugging-related signal, and so can ignore the
+                * usual rule about not emitting signals while holding
+                * conn_lock.
+                */
+               g_signal_emit (session, signals[CONNECTION_CREATED], 0, conn);
+               priv->num_conns++;
        }
 
        return conn;
@@ -2432,39 +2291,11 @@ soup_session_real_flush_queue (SoupSession *session)
 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);
 
        SOUP_SESSION_GET_CLASS (session)->flush_queue (session);
 
-       /* Close all idle connections */
-       g_mutex_lock (&priv->conn_lock);
-       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 ||
-                   state == SOUP_CONNECTION_REMOTE_DISCONNECTED) {
-                       conns = g_slist_prepend (conns, g_object_ref (conn));
-                       g_hash_table_iter_remove (&iter);
-                       drop_connection (session, host, conn);
-               }
-       }
-       g_mutex_unlock (&priv->conn_lock);
-
-       for (c = conns; c; c = c->next) {
-               soup_connection_disconnect (c->data);
-               g_object_unref (c->data);
-       }
-
-       g_slist_free (conns);
+       soup_session_cleanup_connections (session, TRUE);
 }
 
 static void
@@ -2480,7 +2311,7 @@ prefetch_uri (SoupSession *session, SoupURI *uri,
 
        g_mutex_lock (&priv->conn_lock);
        host = get_host_for_uri (session, uri);
-       addr = g_object_ref (host->addr);
+       addr = g_object_ref (soup_session_host_get_address (host));
        g_mutex_unlock (&priv->conn_lock);
 
        soup_address_resolve_async (addr,
@@ -3052,6 +2883,13 @@ soup_session_class_init (SoupSessionClass *session_class)
 
 
        /* properties */
+       g_object_class_install_property (
+               object_class, PROP_SOCKET_PROPERTIES,
+               g_param_spec_boxed (SOUP_SESSION_SOCKET_PROPERTIES,
+                                   "Socket properties",
+                                   "Socket properties",
+                                   SOUP_TYPE_SOCKET_PROPERTIES,
+                                   G_PARAM_READABLE));
        /**
         * SoupSession:proxy-resolver:
         *
@@ -4505,20 +4343,23 @@ static GIOStream *
 steal_connection (SoupSession          *session,
                   SoupMessageQueueItem *item)
 {
-        SoupSessionPrivate *priv = soup_session_get_instance_private (session);
+       SoupSessionPrivate *priv = soup_session_get_instance_private (session);
+       SoupSessionHost *host;
         SoupConnection *conn;
         SoupSocket *sock;
-        SoupSessionHost *host;
         GIOStream *stream;
 
+       g_message ("steal_connection");
+
         conn = g_object_ref (item->conn);
         soup_session_set_item_connection (session, item, NULL);
 
-        g_mutex_lock (&priv->conn_lock);
-        host = get_host_for_message (session, item->msg);
-        g_hash_table_remove (priv->conns, conn);
-        drop_connection (session, host, conn);
-        g_mutex_unlock (&priv->conn_lock);
+       g_mutex_lock (&priv->conn_lock);
+       host = get_host_for_message (session, item->msg);
+       g_mutex_unlock (&priv->conn_lock);
+
+       drop_connection (session, conn);
+       soup_session_host_steal_connection (host, conn);
 
         sock = soup_connection_get_socket (conn);
         g_object_set (sock,
diff --git a/libsoup/soup-types.h b/libsoup/soup-types.h
index 89e077a6..069fbb2d 100644
--- a/libsoup/soup-types.h
+++ b/libsoup/soup-types.h
@@ -6,6 +6,7 @@
 #ifndef __SOUP_TYPES_H__
 #define __SOUP_TYPES_H__ 1
 
+#include <glib-object.h>
 #include <gio/gio.h>
 
 #include "soup-version.h"


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