static void queue_message_restarted (SoupMessage *msg, gpointer user_data) { SoupMessageQueueItem *item = user_data; if (item->proxy_addr) { g_object_unref (item->proxy_addr); item->proxy_addr = NULL; } if (item->proxy_uri) { soup_uri_free (item->proxy_uri); item->proxy_uri = NULL; } if (item->conn && (!soup_message_is_keepalive (msg) || SOUP_STATUS_IS_REDIRECTION (msg->status_code))) { if (soup_connection_get_state (item->conn) == SOUP_CONNECTION_IN_USE) soup_connection_set_state (item->conn, SOUP_CONNECTION_IDLE); g_object_unref (item->conn); item->conn = NULL; } g_cancellable_reset (item->cancellable); item->state = SOUP_MESSAGE_STARTING; }
static void run_queue (SoupSessionAsync *sa) { SoupSession *session = SOUP_SESSION (sa); SoupMessageQueue *queue = soup_session_get_queue (session); SoupMessageQueueItem *item; SoupProxyURIResolver *proxy_resolver; SoupMessage *msg; SoupConnection *conn; gboolean try_pruning = TRUE, should_prune = FALSE; soup_session_cleanup_connections (session, FALSE); try_again: for (item = soup_message_queue_first (queue); item && !should_prune; item = soup_message_queue_next (queue, item)) { msg = item->msg; /* CONNECT messages are handled specially */ if (msg->method == SOUP_METHOD_CONNECT) continue; if (soup_message_io_in_progress (msg)) continue; if (!item->resolved_proxy_addr) { proxy_resolver = (SoupProxyURIResolver *)soup_session_get_feature_for_message (session, SOUP_TYPE_PROXY_URI_RESOLVER, msg); if (proxy_resolver) { resolve_proxy_addr (item, proxy_resolver); continue; } else item->resolved_proxy_addr = TRUE; } conn = soup_session_get_connection (session, item, &should_prune); if (!conn) continue; if (soup_connection_get_state (conn) == SOUP_CONNECTION_NEW) { soup_connection_connect_async (conn, got_connection, session); } else soup_session_send_queue_item (session, item, conn); } if (item) soup_message_queue_item_unref (item); if (try_pruning && should_prune) { /* There is at least one message in the queue that * could be sent if we pruned an idle connection from * some other server. */ if (soup_session_cleanup_connections (session, TRUE)) { try_pruning = should_prune = FALSE; goto try_again; } } }
static void connection_created (SoupSession *session, SoupConnection *conn, gpointer user_data) { SoupConnectionState *state = user_data; *state = soup_connection_get_state (conn); if (*state != SOUP_CONNECTION_NEW) { debug_printf (1, " Unexpected initial state: %d\n", *state); errors++; } g_signal_connect (conn, "notify::state", G_CALLBACK (connection_state_changed), state); }
static void connection_state_changed (GObject *object, GParamSpec *param, gpointer user_data) { SoupConnection *conn = SOUP_CONNECTION (object); SoupConnectionState *state = user_data; SoupConnectionState new_state; new_state = soup_connection_get_state (conn); if (state_transitions[*state] != new_state) { debug_printf (1, " Unexpected transition: %s -> %s\n", state_names[*state], state_names[new_state]); errors++; } else { debug_printf (2, " %s -> %s\n", state_names[*state], state_names[new_state]); } *state = new_state; }
gboolean soup_session_try_prune_connection (SoupSession *session) { SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session); GPtrArray *conns; GHashTableIter iter; gpointer conn, host; int i; conns = g_ptr_array_new (); g_mutex_lock (priv->host_lock); g_hash_table_iter_init (&iter, priv->conns); while (g_hash_table_iter_next (&iter, &conn, &host)) { /* Don't prune a connection that is currently in use, * or hasn't been used yet. */ if (soup_connection_get_state (conn) == SOUP_CONNECTION_IDLE && soup_connection_last_used (conn) > 0) g_ptr_array_add (conns, g_object_ref (conn)); } g_mutex_unlock (priv->host_lock); if (!conns->len) { g_ptr_array_free (conns, TRUE); return FALSE; } for (i = 0; i < conns->len; i++) { soup_connection_disconnect (conns->pdata[i]); g_object_unref (conns->pdata[i]); } g_ptr_array_free (conns, TRUE); return TRUE; }
static void process_queue_item (SoupMessageQueueItem *item, gboolean *should_prune, gboolean loop) { SoupSession *session = item->session; SoupProxyURIResolver *proxy_resolver; do { switch (item->state) { case SOUP_MESSAGE_STARTING: proxy_resolver = (SoupProxyURIResolver *)soup_session_get_feature_for_message (session, SOUP_TYPE_PROXY_URI_RESOLVER, item->msg); if (!proxy_resolver) { item->state = SOUP_MESSAGE_AWAITING_CONNECTION; break; } resolve_proxy_addr (item, proxy_resolver); return; case SOUP_MESSAGE_AWAITING_CONNECTION: if (!soup_session_get_connection (session, item, should_prune)) return; if (soup_connection_get_state (item->conn) != SOUP_CONNECTION_NEW) { item->state = SOUP_MESSAGE_READY; break; } item->state = SOUP_MESSAGE_CONNECTING; soup_message_queue_item_ref (item); g_object_ref (session); soup_connection_connect_async (item->conn, item->cancellable, got_connection, item); return; case SOUP_MESSAGE_READY: item->state = SOUP_MESSAGE_RUNNING; soup_session_send_queue_item (session, item, message_completed); break; case SOUP_MESSAGE_RESTARTING: item->state = SOUP_MESSAGE_STARTING; soup_message_restarted (item->msg); break; case SOUP_MESSAGE_FINISHING: item->state = SOUP_MESSAGE_FINISHED; soup_message_finished (item->msg); if (item->state != SOUP_MESSAGE_FINISHED) break; g_object_ref (session); soup_session_unqueue_item (session, item); if (item->callback) item->callback (session, item->msg, item->callback_data); g_object_unref (item->msg); do_idle_run_queue (session); g_object_unref (session); return; default: /* Nothing to do with this message in any * other state. */ return; } } while (loop && item->state != SOUP_MESSAGE_FINISHED); }
/** * soup_session_get_connection: * @session: a #SoupSession * @item: a #SoupMessageQueueItem * @try_pruning: on return, whether or not to try pruning a connection * * Tries to find or create a connection for @item; this is an internal * method for #SoupSession subclasses. * * If there is an idle connection to the relevant host available, then * that connection will be returned. The connection will be set to * %SOUP_CONNECTION_IN_USE, so the caller must call * soup_connection_set_state() to set it to %SOUP_CONNECTION_IDLE if * it ends up not using the connection right away. * * If there is no idle connection available, but it is possible to * create a new connection, then one will be created and returned * (with state %SOUP_CONNECTION_NEW). The caller MUST then call * soup_connection_connect_sync() or soup_connection_connect_async() * to connect it. If the connection attempt fails, the caller must * call soup_session_connection_failed() to tell the session to free * the connection. * * If no connection is available and a new connection cannot be made, * soup_session_get_connection() will return %NULL. If @session has * the maximum number of open connections open, but does not have the * maximum number of per-host connections open to the relevant host, * then *@try_pruning will be set to %TRUE. In this case, the caller * can call soup_session_try_prune_connection() to close an idle * connection, and then try soup_session_get_connection() again. (If * calling soup_session_try_prune_connection() wouldn't help, then * *@try_pruning is left untouched; it is NOT set to %FALSE.) * * Return value: a #SoupConnection, or %NULL **/ SoupConnection * soup_session_get_connection (SoupSession *session, SoupMessageQueueItem *item, gboolean *try_pruning) { SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session); SoupConnection *conn; SoupSessionHost *host; SoupAddress *remote_addr, *tunnel_addr; SoupSSLCredentials *ssl_creds; GSList *conns; gboolean has_pending = FALSE; SoupURI *uri; g_mutex_lock (priv->host_lock); host = get_host_for_message (session, item->msg); for (conns = host->connections; conns; conns = conns->next) { if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_IDLE) { soup_connection_set_state (conns->data, SOUP_CONNECTION_IN_USE); g_mutex_unlock (priv->host_lock); return conns->data; } else if (soup_connection_get_state (conns->data) == SOUP_CONNECTION_CONNECTING) has_pending = TRUE; } if (has_pending) { /* We've already started one connection to this * address, so don't start another one until it's * done. */ g_mutex_unlock (priv->host_lock); return NULL; } if (host->num_conns >= priv->max_conns_per_host) { g_mutex_unlock (priv->host_lock); return NULL; } if (priv->num_conns >= priv->max_conns) { *try_pruning = TRUE; g_mutex_unlock (priv->host_lock); return NULL; } if (item->proxy_addr) { remote_addr = item->proxy_addr; tunnel_addr = NULL; } else { remote_addr = host->addr; tunnel_addr = NULL; } uri = soup_message_get_uri (item->msg); if (uri->scheme == SOUP_URI_SCHEME_HTTPS) { if (!priv->ssl_creds) priv->ssl_creds = soup_ssl_get_client_credentials (priv->ssl_ca_file); ssl_creds = priv->ssl_creds; if (item->proxy_addr) tunnel_addr = host->addr; } else ssl_creds = NULL; conn = soup_connection_new ( SOUP_CONNECTION_REMOTE_ADDRESS, remote_addr, SOUP_CONNECTION_TUNNEL_ADDRESS, tunnel_addr, SOUP_CONNECTION_PROXY_URI, item->proxy_uri, SOUP_CONNECTION_SSL_CREDENTIALS, ssl_creds, SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context, SOUP_CONNECTION_TIMEOUT, priv->io_timeout, SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout, NULL); g_signal_connect (conn, "disconnected", G_CALLBACK (connection_disconnected), session); g_hash_table_insert (priv->conns, conn, host); priv->num_conns++; host->num_conns++; host->connections = g_slist_prepend (host->connections, conn); g_mutex_unlock (priv->host_lock); return conn; }