diff options
Diffstat (limited to 'libsoup/soup-session.c')
-rw-r--r-- | libsoup/soup-session.c | 309 |
1 files changed, 80 insertions, 229 deletions
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: * |