summaryrefslogtreecommitdiff
path: root/libsoup/soup-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-session.c')
-rw-r--r--libsoup/soup-session.c309
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:
*