diff options
author | Dan Winship <danw@gnome.org> | 2013-12-09 12:16:05 +0100 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2014-12-08 10:28:42 +0100 |
commit | e18257576ee5d2dbb74d85e06e9252bff5ac5f9b (patch) | |
tree | 807e8854c236be38a790f1c9050af8633b09f8e7 | |
parent | eaa46acc264de3a63b2d529b56aeffc5fac042cb (diff) | |
download | libsoup-wip/http2-a.tar.gz |
SoupSessionHost: split from SoupSessionwip/http2-a
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.
-rw-r--r-- | libsoup/Makefile.am | 2 | ||||
-rw-r--r-- | libsoup/soup-session-host.c | 322 | ||||
-rw-r--r-- | libsoup/soup-session-host.h | 59 | ||||
-rw-r--r-- | libsoup/soup-session-private.h | 2 | ||||
-rw-r--r-- | libsoup/soup-session.c | 309 |
5 files changed, 465 insertions, 229 deletions
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index ddf52425..d95e5a54 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -181,6 +181,8 @@ libsoup_2_4_la_SOURCES = \ soup-session.c \ soup-session-async.c \ soup-session-feature.c \ + soup-session-host.c \ + soup-session-host.h \ soup-session-private.h \ soup-session-sync.c \ soup-socket.c \ diff --git a/libsoup/soup-session-host.c b/libsoup/soup-session-host.c new file mode 100644 index 00000000..d062b83a --- /dev/null +++ b/libsoup/soup-session-host.c @@ -0,0 +1,322 @@ +/* -*- 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-private.h" +#include "soup-session-private.h" +#include "soup-socket-private.h" + +G_DEFINE_TYPE (SoupSessionHost, soup_session_host, G_TYPE_OBJECT) + +typedef struct { + GMutex mutex; + + SoupURI *uri; + SoupAddress *addr; + + GSList *connections; /* CONTAINS: SoupConnection */ + guint num_conns; + guint max_conns; + + guint num_messages; + + gboolean ssl_fallback; + + GSource *keep_alive_src; + SoupSession *session; +} SoupSessionHostPrivate; +#define SOUP_SESSION_HOST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SESSION_HOST, SoupSessionHostPrivate)) + +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_PRIVATE (host); + + g_mutex_init (&priv->mutex); +} + +static void +soup_session_host_finalize (GObject *object) +{ + SoupSessionHostPrivate *priv = SOUP_SESSION_HOST_GET_PRIVATE (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); + + g_type_class_add_private (host_class, sizeof (SoupSessionHostClass)); + + 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_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) +{ + return SOUP_SESSION_HOST_GET_PRIVATE (host)->uri; +} + +SoupAddress * +soup_session_host_get_address (SoupSessionHost *host) +{ + return SOUP_SESSION_HOST_GET_PRIVATE (host)->addr; +} + +void +soup_session_host_add_message (SoupSessionHost *host, + SoupMessage *msg) +{ + SOUP_SESSION_HOST_GET_PRIVATE (host)->num_messages++; +} + +void +soup_session_host_remove_message (SoupSessionHost *host, + SoupMessage *msg) +{ + SOUP_SESSION_HOST_GET_PRIVATE (host)->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_PRIVATE (host); + + g_mutex_lock (&priv->mutex); + + if (soup_connection_get_ssl_fallback (conn)) + priv->ssl_fallback = TRUE; + + 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 *try_cleanup) +{ + SoupSessionHostPrivate *priv = SOUP_SESSION_HOST_GET_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 (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_FALLBACK, priv->ssl_fallback, + 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) +{ + return SOUP_SESSION_HOST_GET_PRIVATE (host)->num_conns; +} + +GSList * +soup_session_host_get_connections (SoupSessionHost *host) +{ + SoupSessionHostPrivate *priv = SOUP_SESSION_HOST_GET_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; +} + +gboolean +soup_session_host_cleanup_connections (SoupSessionHost *host, + gboolean cleanup_idle) +{ + SoupSessionHostPrivate *priv = SOUP_SESSION_HOST_GET_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; +} + +gboolean +soup_session_host_get_ssl_fallback (SoupSessionHost *host) +{ + return SOUP_SESSION_HOST_GET_PRIVATE (host)->ssl_fallback; +} + +void +soup_session_host_set_ssl_fallback (SoupSessionHost *host, + gboolean ssl_fallback) +{ + SOUP_SESSION_HOST_GET_PRIVATE (host)->ssl_fallback = ssl_fallback; +} + diff --git a/libsoup/soup-session-host.h b/libsoup/soup-session-host.h new file mode 100644 index 00000000..72f0339a --- /dev/null +++ b/libsoup/soup-session-host.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef SOUP_SESSION_HOST_H +#define SOUP_SESSION_HOST_H 1 + +#include "soup-types.h" + +G_BEGIN_DECLS + +#define SOUP_TYPE_SESSION_HOST (soup_session_host_get_type ()) +#define SOUP_SESSION_HOST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_SESSION_HOST, SoupSessionHost)) +#define SOUP_SESSION_HOST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_SESSION_HOST, SoupSessionHostClass)) +#define SOUP_IS_SESSION_HOST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_SESSION_HOST)) +#define SOUP_IS_SESSION_HOST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_SESSION_HOST)) +#define SOUP_SESSION_HOST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_SESSION_HOST, SoupSessionHostClass)) + +typedef struct { + GObject parent; + +} SoupSessionHost; + +typedef struct { + GObjectClass parent_class; + +} SoupSessionHostClass; + +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 *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); + +gboolean soup_session_host_get_ssl_fallback (SoupSessionHost *host); +void soup_session_host_set_ssl_fallback (SoupSessionHost *host, + gboolean ssl_fallback); + +G_END_DECLS + +#endif /* SOUP_SESSION_HOST_H */ diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h index 7da6781d..d50dc5c0 100644 --- a/libsoup/soup-session-private.h +++ b/libsoup/soup-session-private.h @@ -12,6 +12,8 @@ G_BEGIN_DECLS +#define SOUP_SESSION_SOCKET_PROPERTIES "socket-properties" + /* "protected" methods for subclasses */ SoupMessageQueueItem *soup_session_lookup_queue_item (SoupSession *session, SoupMessage *msg); diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index c97614dc..5833160e 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -20,11 +20,10 @@ #include "soup-misc-private.h" #include "soup-message-queue.h" #include "soup-proxy-resolver-wrapper.h" +#include "soup-session-host.h" #include "soup-session-private.h" #include "soup-socket-private.h" -#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */ - /** * SECTION:soup-session * @short_description: Soup session state object @@ -84,22 +83,9 @@ soup_init (void) #endif } -typedef struct { - SoupURI *uri; - SoupAddress *addr; - - GSList *connections; /* CONTAINS: SoupConnection */ - guint num_conns; - - guint num_messages; - - gboolean ssl_fallback; - - 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 { SoupSession *session; @@ -131,7 +117,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; @@ -157,12 +142,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, @@ -198,6 +181,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, + PROP_SOCKET_PROPERTIES, PROP_PROXY_URI, PROP_PROXY_RESOLVER, PROP_MAX_CONNS, @@ -239,11 +223,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; @@ -332,7 +315,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); @@ -352,7 +334,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); @@ -812,6 +793,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; @@ -987,35 +972,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_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) @@ -1043,9 +999,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; } @@ -1058,21 +1016,6 @@ get_host_for_message (SoupSession *session, SoupMessage *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, gpointer session) @@ -1293,7 +1236,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)) { @@ -1354,94 +1297,68 @@ soup_session_cleanup_connections (SoupSession *session, gboolean cleanup_idle) { SoupSessionPrivate *priv = SOUP_SESSION_GET_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)); - for (c = conns; c; c = c->next) { - conn = c->data; - soup_connection_disconnect (conn); - g_object_unref (conn); + g_mutex_unlock (&priv->conn_lock); + + 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 session) { - SoupSessionHost *host = (SoupSessionHost *) user_data; - SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (host->session); + SoupSessionPrivate *priv = SOUP_SESSION_GET_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_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_reffed (priv->async_context, - HOST_KEEP_ALIVE, - free_unused_host, - host); - } - - if (soup_connection_get_ssl_fallback (conn)) - host->ssl_fallback = TRUE; - } - g_signal_handlers_disconnect_by_func (conn, connection_disconnected, session); g_signal_handlers_disconnect_by_func (conn, connection_state_changed, session); priv->num_conns--; @@ -1454,14 +1371,10 @@ connection_disconnected (SoupConnection *conn, gpointer user_data) { SoupSession *session = user_data; SoupSessionPrivate *priv = SOUP_SESSION_GET_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); @@ -1510,7 +1423,7 @@ soup_session_unqueue_item (SoupSession *session, g_mutex_lock (&priv->conn_lock); host = get_host_for_message (session, item->msg); - host->num_messages--; + soup_session_host_remove_message (host, item->msg); g_cond_broadcast (&priv->conn_cond); g_mutex_unlock (&priv->conn_lock); @@ -1599,8 +1512,8 @@ status_from_connect_error (SoupMessageQueueItem *item, GError *error) g_mutex_lock (&priv->conn_lock); host = get_host_for_message (item->session, item->msg); - if (!host->ssl_fallback) { - host->ssl_fallback = TRUE; + if (!soup_session_host_get_ssl_fallback (host)) { + soup_session_host_set_ssl_fallback (host, TRUE); status = SOUP_STATUS_TRY_AGAIN; } else status = SOUP_STATUS_SSL_FAILED; @@ -1800,74 +1713,33 @@ get_connection_for_host (SoupSession *session, { SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session); SoupConnection *conn; - GSList *conns; - int num_pending = 0; if (priv->disposed) - return FALSE; + 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) - return NULL; - - 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; - } - - ensure_socket_props (session); - conn = g_object_new (SOUP_TYPE_CONNECTION, - SOUP_CONNECTION_REMOTE_URI, host->uri, - SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback, - 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, + 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; @@ -2495,39 +2367,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_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 @@ -2543,7 +2387,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, @@ -3153,6 +2997,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-uri: * |