commit d11c6c8e2e74dc0da7cceff64528d18d1e025a85
Author: Dirkjan Ochtman <d.ochtman@activevideo.com>
Date:   Tue Dec 24 15:31:29 2013 +0100

    soup-session: add soup_session_connect{_async,_finish} functions (bug 721343)
    
    New function added to asynchronously establish a connection. This uses
    soup-session mechanics to do proxy/SSL stuff, but returns an established
    connection to the caller as a GIOStream.

diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index be4cd7f..96c2e9e 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -362,6 +362,8 @@ soup_session_async_get_type
 soup_session_async_new
 soup_session_async_new_with_options
 soup_session_cancel_message
+soup_session_connect_async
+soup_session_connect_finish
 soup_session_feature_add_feature
 soup_session_feature_attach
 soup_session_feature_detach
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index f86a129..edc43cd 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -48,13 +48,14 @@ struct _SoupMessageQueueItem {
 	guint new_api           : 1;
 	guint io_started        : 1;
 	guint async             : 1;
+	guint force_tunnel      : 1;
+	guint priority          : 3;
 	guint resend_count      : 28;
 
 	SoupMessageQueueItemState state;
 
 	/*< private >*/
 	guint removed              : 1;
-	guint priority             : 3;
 	guint ref_count            : 28;
 	SoupMessageQueueItem *prev, *next;
 	SoupMessageQueueItem *related;
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index b3081f1..7b3aa37 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -9,6 +9,7 @@
 #include <config.h>
 #endif
 
+#include <stdlib.h>
 #include <glib/gi18n-lib.h>
 
 #include "soup-session.h"
@@ -149,6 +150,8 @@ static void async_run_queue (SoupSession *session);
 
 static void async_send_request_running (SoupSession *session, SoupMessageQueueItem *item);
 
+static void extract_connection (SoupSession *session, SoupMessageQueueItem *item);
+
 #define SOUP_SESSION_MAX_CONNS_DEFAULT 10
 #define SOUP_SESSION_MAX_CONNS_PER_HOST_DEFAULT 2
 
@@ -1571,6 +1574,7 @@ tunnel_complete (SoupMessageQueueItem *tunnel_item,
 
 	if (item->msg->status_code)
 		item->state = SOUP_MESSAGE_FINISHING;
+
 	soup_message_set_https_status (item->msg, item->conn);
 
 	item->error = error;
@@ -1579,8 +1583,9 @@ tunnel_complete (SoupMessageQueueItem *tunnel_item,
 	if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
 		soup_connection_disconnect (item->conn);
 		soup_session_set_item_connection (session, item, NULL);
-		if (!item->new_api || item->msg->status_code == 0)
+		if (!item->new_api || item->msg->status_code == 0) {
 			soup_session_set_item_status (session, item, status, error);
+		}
 	}
 
 	item->state = SOUP_MESSAGE_READY;
@@ -1608,6 +1613,7 @@ tunnel_message_completed (SoupMessage *msg, gpointer user_data)
 	SoupMessageQueueItem *tunnel_item = user_data;
 	SoupMessageQueueItem *item = tunnel_item->related;
 	SoupSession *session = tunnel_item->session;
+	gboolean is_ssl;
 	guint status;
 
 	if (tunnel_item->state == SOUP_MESSAGE_RESTARTING) {
@@ -1631,6 +1637,12 @@ tunnel_message_completed (SoupMessage *msg, gpointer user_data)
 		return;
 	}
 
+	g_object_get (G_OBJECT (item->conn), "ssl", &is_ssl, NULL);
+	if (!is_ssl) {
+		tunnel_complete (tunnel_item, status, NULL);
+		return;
+	}
+
 	if (tunnel_item->async) {
 		soup_connection_start_ssl_async (item->conn, item->cancellable,
 						 tunnel_handshake_complete,
@@ -1901,13 +1913,21 @@ soup_session_process_queue_item (SoupSession          *session,
 			break;
 
 		case SOUP_MESSAGE_CONNECTED:
-			if (soup_connection_is_tunnelled (item->conn))
-				tunnel_connect (item);
-			else
-				item->state = SOUP_MESSAGE_READY;
+			if (soup_connection_is_via_proxy (item->conn)) {
+				if (item->force_tunnel || soup_connection_is_tunnelled (item->conn)) {
+					tunnel_connect (item);
+					break;
+				}
+			}
+			item->state = SOUP_MESSAGE_READY;
 			break;
 
 		case SOUP_MESSAGE_READY:
+			if (item->force_tunnel) {
+				item->state = SOUP_MESSAGE_FINISHING;
+				break;
+			}
+
 			if (item->msg->status_code) {
 				if (item->msg->status_code == SOUP_STATUS_TRY_AGAIN) {
 					soup_message_cleanup_response (item->msg);
@@ -1954,6 +1974,8 @@ soup_session_process_queue_item (SoupSession          *session,
 			}
 
 			soup_message_queue_item_ref (item);
+			if (item->force_tunnel)
+				extract_connection (session, item);
 			soup_session_unqueue_item (session, item);
 			if (item->async && item->callback)
 				item->callback (session, item->msg, item->callback_data);
@@ -4556,3 +4578,91 @@ soup_request_error_quark (void)
 		error = g_quark_from_static_string ("soup_request_error_quark");
 	return error;
 }
+
+void extract_connection (SoupSession *session, SoupMessageQueueItem *item) {
+
+	SoupSocket *sock;
+	SoupURI *uri;
+	SoupSessionHost *host;
+	GIOStream *iostream;
+	GError *error;
+	SoupConnection *soup_conn;
+	SoupSessionPrivate *priv;
+
+	soup_conn = item->conn;
+	item->conn = NULL;
+	error = (!soup_conn && item->error) ? g_error_copy(item->error) : NULL;
+	soup_session_set_item_connection (session, item, NULL);
+
+	if (!soup_conn) {
+		g_task_return_error (item->task, error);
+		return;
+	}
+
+	priv = SOUP_SESSION_GET_PRIVATE (session);
+	uri = soup_message_get_uri (item->msg);
+	host = get_host_for_uri (session, uri);
+
+	g_mutex_lock (&priv->conn_lock);
+	g_hash_table_remove (priv->conns, soup_conn);
+	drop_connection (session, host, soup_conn);
+	g_mutex_unlock (&priv->conn_lock);
+
+	sock = soup_connection_get_socket(soup_conn);
+	g_object_unref (soup_conn);
+
+	iostream = soup_socket_get_connection(sock);
+	g_object_ref (iostream);
+	g_object_set (G_OBJECT (sock), "close-on-dispose", FALSE, NULL);
+	g_object_unref (sock);
+
+	g_task_return_pointer (item->task, iostream, g_object_unref);
+}
+
+/**
+ * soup_session_connect_finish:
+ * @session: a #SoupSession
+ * @result: the #GAsyncResult from the connect operation
+ * @user_data: outparam for possible errors
+ *
+ * Since: 2.46
+ */
+GIOStream* soup_session_connect_finish (SoupSession *session,
+			   GAsyncResult *result,
+			   GError **error) {
+	g_return_val_if_fail (g_task_is_valid (result, session), NULL);
+	return g_task_propagate_pointer ( G_TASK (result), error);
+}
+
+/**
+ * soup_session_connect_async:
+ * @session: a #SoupSession
+ * @destination: a #SoupURI representing the URI/host to connect to
+ * @callback: a callback to call when the connection is established
+ * @user_data: data for @callback
+ *
+ * Sets up a #SoupConnection to the @destination according to the
+ * usual #SoupSession process (i.e. complete with TLS and proxy
+ * setup). The resulting #GIOStream is then passed to the callback
+ * after the associated #SoupConnection and #SoupSocket are freed.
+ *
+ * Since: 2.46
+ */
+void soup_session_connect_async (SoupSession *session,
+			   SoupURI *destination,
+			   GAsyncReadyCallback callback,
+			   gpointer user_data) {
+	SoupMessage *msg;
+	SoupMessageQueueItem *item;
+
+	msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, destination);
+	item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
+					       NULL, NULL);
+	soup_message_queue_item_ref (item);
+
+	item->new_api = TRUE;
+	item->force_tunnel = TRUE;
+	item->task = g_task_new (session, NULL, callback, user_data);
+	g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
+	soup_session_kick_queue (session);
+}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 67a59ea..933cca8 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -198,6 +198,15 @@ SOUP_AVAILABLE_IN_2_42
 GQuark soup_request_error_quark (void);
 #define SOUP_REQUEST_ERROR soup_request_error_quark ()
 
+void soup_session_connect_async (SoupSession *session,
+						SoupURI					*destination,
+						GAsyncReadyCallback		callback,
+						gpointer				user_data);
+
+GIOStream* soup_session_connect_finish (SoupSession *session,
+					    GAsyncResult		     *result,
+					    GError				    **error);
+
 typedef enum {
 	SOUP_REQUEST_ERROR_BAD_URI,
 	SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
