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;
		}
	}
}
Beispiel #3
0
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);
}
Beispiel #4
0
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;
}
Beispiel #5
0
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;
}
Beispiel #6
0
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);
}
Beispiel #7
0
/**
 * 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;
}