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

    Add soup_session_proxy_connect() function

diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index be4cd7f..2b9bdcb 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -379,6 +379,7 @@ soup_session_new_with_options
 soup_session_pause_message
 soup_session_prefetch_dns
 soup_session_prepare_for_uri
+soup_session_proxy_connect
 soup_session_queue_message
 soup_session_redirect_message
 soup_session_remove_feature
diff --git a/libsoup/soup-message-queue.h b/libsoup/soup-message-queue.h
index f86a129..039c65d 100644
--- a/libsoup/soup-message-queue.h
+++ b/libsoup/soup-message-queue.h
@@ -56,6 +56,7 @@ struct _SoupMessageQueueItem {
 	guint removed              : 1;
 	guint priority             : 3;
 	guint ref_count            : 28;
+	guint force_tunnel         : 1;
 	SoupMessageQueueItem *prev, *next;
 	SoupMessageQueueItem *related;
 };
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index b3081f1..25debe9 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"
@@ -1565,13 +1566,17 @@ tunnel_complete (SoupMessageQueueItem *tunnel_item,
 {
 	SoupMessageQueueItem *item = tunnel_item->related;
 	SoupSession *session = tunnel_item->session;
+	gboolean is_ssl;
 
 	soup_message_finished (tunnel_item->msg);
 	soup_message_queue_item_unref (tunnel_item);
 
 	if (item->msg->status_code)
 		item->state = SOUP_MESSAGE_FINISHING;
-	soup_message_set_https_status (item->msg, item->conn);
+
+	g_object_get (G_OBJECT (item->conn), "ssl", &is_ssl, NULL);
+	if (is_ssl)
+		soup_message_set_https_status (item->msg, item->conn);
 
 	item->error = error;
 	if (!status)
@@ -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);
@@ -1947,14 +1967,16 @@ soup_session_process_queue_item (SoupSession          *session,
 
 		case SOUP_MESSAGE_FINISHING:
 			item->state = SOUP_MESSAGE_FINISHED;
-			soup_message_finished (item->msg);
+			if (item->force_tunnel)
+				soup_message_finished (item->msg);
 			if (item->state != SOUP_MESSAGE_FINISHED) {
 				g_return_if_fail (!item->new_api);
 				break;
 			}
 
 			soup_message_queue_item_ref (item);
-			soup_session_unqueue_item (session, item);
+			if (!item->force_tunnel)
+				soup_session_unqueue_item (session, item);
 			if (item->async && item->callback)
 				item->callback (session, item->msg, item->callback_data);
 			soup_message_queue_item_unref (item);
@@ -4556,3 +4578,92 @@ soup_request_error_quark (void)
 		error = g_quark_from_static_string ("soup_request_error_quark");
 	return error;
 }
+
+typedef struct {
+	SoupProxiedCallback callback;
+	gpointer orig_data;
+	SoupMessageQueueItem *item;
+} InterData;
+
+void connection_callback(SoupSession *session, SoupMessage *msg, gpointer user_data);
+
+void connection_callback(SoupSession *session,
+					SoupMessage *msg,
+					gpointer inter_data) {
+	InterData *cb_data;
+	SoupSocket *sock;
+	SoupURI *uri;
+	SoupSessionHost *host;
+	GIOStream *iostream;
+	GError *error;
+	SoupConnection *soup_conn;
+	SoupSessionPrivate *priv;
+	SoupMessageQueueItem *item;
+	SoupProxiedCallback user_cb;
+	gpointer user_data;
+
+	cb_data = (InterData*) inter_data;
+	user_cb = cb_data->callback;
+	user_data = cb_data->orig_data;
+	item = cb_data->item;
+	free(cb_data);
+
+	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);
+	soup_session_unqueue_item (session, item);
+
+	if (!soup_conn) {
+		user_cb(NULL, error, user_data);
+		return;
+	}
+
+	priv = SOUP_SESSION_GET_PRIVATE (session);
+	uri = soup_message_get_uri (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_set (G_OBJECT (soup_conn), "stay-connected", TRUE, NULL);
+	g_object_unref (soup_conn);
+
+	iostream = soup_socket_get_connection(sock);
+	g_object_set (G_OBJECT (sock), "keep-connection", TRUE, NULL);
+	g_object_unref (sock);
+
+	user_cb(iostream, NULL, user_data);
+}
+
+void soup_session_proxy_connect(SoupSession *session,
+					SoupURI *destination,
+					SoupProxiedCallback callback,
+					gpointer user_data) {
+	SoupMessage *msg;
+	InterData *inter_data;
+	SoupMessageQueueItem *item;
+
+	msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, destination);
+	item = soup_session_append_queue_item (session, msg, TRUE, TRUE,
+					       NULL, NULL);
+
+	inter_data = malloc(sizeof(InterData));
+	inter_data->callback = callback;
+	inter_data->orig_data = user_data;
+	inter_data->item = item;
+	soup_message_queue_item_ref (item);
+
+	item->new_api = TRUE;
+	item->force_tunnel = TRUE;
+	item->callback = connection_callback;
+	item->callback_data = inter_data;
+
+	item->task = g_task_new (session, item->cancellable, NULL, NULL);
+	g_task_set_task_data (item->task, item, (GDestroyNotify) soup_message_queue_item_unref);
+	g_task_set_check_cancellable (item->task, FALSE);
+	soup_session_kick_queue (session);
+}
diff --git a/libsoup/soup-session.h b/libsoup/soup-session.h
index 67a59ea..678b6f3 100644
--- a/libsoup/soup-session.h
+++ b/libsoup/soup-session.h
@@ -198,6 +198,13 @@ SOUP_AVAILABLE_IN_2_42
 GQuark soup_request_error_quark (void);
 #define SOUP_REQUEST_ERROR soup_request_error_quark ()
 
+typedef void (*SoupProxiedCallback) (GIOStream *iostream, GError *error, gpointer user_data);
+
+void soup_session_proxy_connect               (SoupSession *session,
+						SoupURI					*destination,
+						SoupProxiedCallback		callback,
+						gpointer				user_data);
+
 typedef enum {
 	SOUP_REQUEST_ERROR_BAD_URI,
 	SOUP_REQUEST_ERROR_UNSUPPORTED_URI_SCHEME,
