[glib] Hooked proxy enumeration into GSocketClient



commit a6c3820f46b9caabc45ab19aaf2669b4cb04c5d5
Author: Nicolas Dufresne <nicolas dufresne collabora co uk>
Date:   Tue Aug 10 16:48:45 2010 -0400

    Hooked proxy enumeration into GSocketClient
    
    This functionnallity can be disabled using property enable-proxy. It
    enumerates addresses using GSocketConnectable::proxy_enumerate() instead of
    enumerate(). When the returned address is of type GProxyAddress (a type
    based on GInetSocketAddress), it gets the proxy protocol handler using
    g_proxy_get_default_for_protocol() and call connect() on it.
    
    Reviewed-by: Dan Winship <danw gnome org>

 docs/reference/gio/gio-sections.txt |    2 +
 gio/gio.symbols                     |    2 +
 gio/gsocketclient.c                 |  307 +++++++++++++++++++++++++++++++----
 gio/gsocketclient.h                 |    3 +
 4 files changed, 282 insertions(+), 32 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index d0b1982..06861c3 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1798,6 +1798,8 @@ g_socket_client_get_local_address
 g_socket_client_get_protocol
 g_socket_client_get_socket_type
 g_socket_client_get_timeout
+g_socket_client_get_enable_proxy
+g_socket_client_set_enable_proxy
 <SUBSECTION Standard>
 GSocketClientClass
 G_IS_SOCKET_CLIENT
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 835debd..465dedc 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1359,12 +1359,14 @@ g_socket_client_get_local_address
 g_socket_client_get_protocol
 g_socket_client_get_socket_type
 g_socket_client_get_timeout
+g_socket_client_get_enable_proxy
 g_socket_client_new
 g_socket_client_set_family
 g_socket_client_set_local_address
 g_socket_client_set_protocol
 g_socket_client_set_socket_type
 g_socket_client_set_timeout
+g_socket_client_set_enable_proxy
 #endif
 #endif
 
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index a4d4ea9..9284366 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -26,18 +26,24 @@
 #include "gsocketclient.h"
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <gio/gioenumtypes.h>
 #include <gio/gsocketaddressenumerator.h>
 #include <gio/gsocketconnectable.h>
 #include <gio/gsocketconnection.h>
+#include <gio/gproxyaddressenumerator.h>
+#include <gio/gproxyaddress.h>
+#include <gio/gproxyconnection.h>
 #include <gio/gsimpleasyncresult.h>
 #include <gio/gcancellable.h>
 #include <gio/gioerror.h>
 #include <gio/gsocket.h>
 #include <gio/gnetworkaddress.h>
 #include <gio/gnetworkservice.h>
+#include <gio/gproxy.h>
 #include <gio/gsocketaddress.h>
+#include <gio/gtcpconnection.h>
 #include "glibintl.h"
 
 
@@ -71,7 +77,8 @@ enum
   PROP_TYPE,
   PROP_PROTOCOL,
   PROP_LOCAL_ADDRESS,
-  PROP_TIMEOUT
+  PROP_TIMEOUT,
+  PROP_ENABLE_PROXY,
 };
 
 struct _GSocketClientPrivate
@@ -81,6 +88,7 @@ struct _GSocketClientPrivate
   GSocketProtocol protocol;
   GSocketAddress *local_address;
   guint timeout;
+  gboolean enable_proxy;
 };
 
 static GSocket *
@@ -123,6 +131,15 @@ create_socket (GSocketClient  *client,
   return socket;
 }
 
+gboolean
+can_use_proxy (GSocketClient *client)
+{
+  GSocketClientPrivate *priv = client->priv;
+
+  return priv->enable_proxy
+          && priv->type == G_SOCKET_TYPE_STREAM;
+}
+
 static void
 g_socket_client_init (GSocketClient *client)
 {
@@ -190,6 +207,10 @@ g_socket_client_get_property (GObject    *object,
 	g_value_set_uint (value, client->priv->timeout);
 	break;
 
+      case PROP_ENABLE_PROXY:
+	g_value_set_boolean (value, client->priv->enable_proxy);
+	break;
+
       default:
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -225,6 +246,10 @@ g_socket_client_set_property (GObject      *object,
       g_socket_client_set_timeout (client, g_value_get_uint (value));
       break;
 
+    case PROP_ENABLE_PROXY:
+      g_socket_client_set_enable_proxy (client, g_value_get_boolean (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -399,7 +424,7 @@ g_socket_client_set_local_address (GSocketClient  *client,
 				   GSocketAddress *address)
 {
   if (address)
-  g_object_ref (address);
+    g_object_ref (address);
 
   if (client->priv->local_address)
     {
@@ -453,6 +478,46 @@ g_socket_client_set_timeout (GSocketClient *client,
   g_object_notify (G_OBJECT (client), "timeout");
 }
 
+/**
+ * g_socket_client_get_enable_proxy:
+ * @client: a #GSocketClient.
+ *
+ * Gets the proxy enable state; see g_socket_client_set_enable_proxy()
+ *
+ * Returns: whether proxying is enabled
+ *
+ * Since: 2.26
+ */
+gboolean
+g_socket_client_get_enable_proxy (GSocketClient *client)
+{
+  return client->priv->enable_proxy;
+}
+
+/**
+ * g_socket_client_set_enable_proxy:
+ * @client: a #GSocketClient.
+ * @enable: whether to enable proxies
+ *
+ * Sets whether or not @client attempts to make connections via a
+ * proxy server. When enabled (the default), #GSocketClient will use a
+ * #GProxyResolver to determine if a proxy protocol such as SOCKS is
+ * needed, and automatically do the necessary proxy negotiation.
+ *
+ * Since: 2.26
+ */
+void
+g_socket_client_set_enable_proxy (GSocketClient *client,
+				  gboolean       enable)
+{
+  enable = !!enable;
+  if (client->priv->enable_proxy == enable)
+    return;
+
+  client->priv->enable_proxy = enable;
+  g_object_notify (G_OBJECT (client), "enable-proxy");
+}
+
 static void
 g_socket_client_class_init (GSocketClientClass *class)
 {
@@ -512,6 +577,15 @@ g_socket_client_class_init (GSocketClientClass *class)
                                                       G_PARAM_READWRITE |
                                                       G_PARAM_STATIC_STRINGS));
 
+   g_object_class_install_property (gobject_class, PROP_ENABLE_PROXY,
+				    g_param_spec_boolean ("enable-proxy",
+							  P_("Enable proxy"),
+							  P_("Enable proxy support"),
+							  TRUE,
+							  G_PARAM_CONSTRUCT |
+							  G_PARAM_READWRITE |
+							  G_PARAM_STATIC_STRINGS));
+
 }
 
 /**
@@ -551,14 +625,19 @@ g_socket_client_connect (GSocketClient       *client,
 			 GError             **error)
 {
   GSocketConnection *connection = NULL;
-  GSocketAddressEnumerator *enumerator;
+  GSocketAddressEnumerator *enumerator = NULL;
   GError *last_error, *tmp_error;
 
   last_error = NULL;
-  enumerator = g_socket_connectable_enumerate (connectable);
+
+  if (can_use_proxy (client))
+    enumerator = g_socket_connectable_proxy_enumerate (connectable);
+  else
+    enumerator = g_socket_connectable_enumerate (connectable);
+
   while (connection == NULL)
     {
-      GSocketAddress *address;
+      GSocketAddress *address = NULL;
       GSocket *socket;
 
       if (g_cancellable_is_cancelled (cancellable))
@@ -570,7 +649,8 @@ g_socket_client_connect (GSocketClient       *client,
 
       tmp_error = NULL;
       address = g_socket_address_enumerator_next (enumerator, cancellable,
-						  &tmp_error);
+	      					  &tmp_error);
+
       if (address == NULL)
 	{
 	  if (tmp_error)
@@ -600,6 +680,71 @@ g_socket_client_connect (GSocketClient       *client,
 	  g_object_unref (socket);
 	}
 
+      if (connection &&
+	  G_IS_PROXY_ADDRESS (address) &&
+	  client->priv->enable_proxy)
+	{
+	  GProxyAddress *proxy_addr = G_PROXY_ADDRESS (address);
+	  const gchar *protocol;
+	  GProxy *proxy;
+
+	  protocol = g_proxy_address_get_protocol (proxy_addr);
+	  proxy = g_proxy_get_default_for_protocol (protocol);
+
+          /* The connection should not be anything else then TCP Connection,
+           * but let's put a safety guard in case
+	   */
+          if (!G_IS_TCP_CONNECTION (connection))
+            {
+              g_critical ("Trying to proxy over non-TCP connection, this is "
+                          "most likely a bug in GLib IO library.");
+
+              g_set_error_literal (&last_error,
+                  G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Trying to proxy over non-TCP connection is not supported."));
+
+	      g_object_unref (connection);
+	      connection = NULL;
+            }
+          else if (proxy)
+	    {
+              GIOStream *io_stream;
+              GTcpConnection *old_connection = G_TCP_CONNECTION (connection);
+
+	      io_stream = g_proxy_connect (proxy,
+					   G_IO_STREAM (old_connection),
+					   proxy_addr,
+					   cancellable,
+					   &last_error);
+
+              if (io_stream)
+                {
+                  if (G_IS_SOCKET_CONNECTION (io_stream))
+                    connection = G_SOCKET_CONNECTION (g_object_ref (io_stream));
+                  else
+                    connection = _g_proxy_connection_new (old_connection,
+                                                          io_stream);
+
+                  g_object_unref (io_stream);
+                }
+              else
+                {
+                  connection = NULL;
+                }
+
+              g_object_unref (old_connection);
+	      g_object_unref (proxy);
+	    }
+	  else
+	    {
+	      g_set_error (&last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			   _("Proxy protocol '%s' is not supported."),
+			   protocol);
+	      g_object_unref (connection);
+	      connection = NULL;
+	    }
+	}
+
       g_object_unref (address);
     }
   g_object_unref (enumerator);
@@ -720,7 +865,9 @@ typedef struct
   GSocketClient *client;
 
   GSocketAddressEnumerator *enumerator;
+  GProxyAddress *proxy_addr;
   GSocket *current_socket;
+  GSocketConnection *connection;
 
   GError *last_error;
 } GSocketClientAsyncConnectData;
@@ -728,8 +875,6 @@ typedef struct
 static void
 g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
 {
-  GSocketConnection *connection;
-
   if (data->last_error)
     {
       g_simple_async_result_set_from_error (data->result, data->last_error);
@@ -737,14 +882,10 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
     }
   else
     {
-      g_assert (data->current_socket);
+      g_assert (data->connection);
 
-      g_socket_set_blocking (data->current_socket, TRUE);
-
-      connection = g_socket_connection_factory_create_connection (data->current_socket);
-      g_object_unref (data->current_socket);
       g_simple_async_result_set_op_res_gpointer (data->result,
-						 connection,
+						 data->connection,
 						 g_object_unref);
     }
 
@@ -753,6 +894,10 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
   g_object_unref (data->enumerator);
   if (data->cancellable)
     g_object_unref (data->cancellable);
+  if (data->current_socket)
+    g_object_unref (data->current_socket);
+  if (data->proxy_addr)
+    g_object_unref (data->proxy_addr);
   g_slice_free (GSocketClientAsyncConnectData, data);
 }
 
@@ -770,6 +915,97 @@ set_last_error (GSocketClientAsyncConnectData *data,
   data->last_error = error;
 }
 
+static void
+enumerator_next_async (GSocketClientAsyncConnectData *data)
+{
+  g_socket_address_enumerator_next_async (data->enumerator,
+					  data->cancellable,
+					  g_socket_client_enumerator_callback,
+					  data);
+}
+
+static void
+g_socket_client_proxy_connect_callback (GObject      *object,
+					GAsyncResult *result,
+					gpointer      user_data)
+{
+  GSocketClientAsyncConnectData *data = user_data;
+  GIOStream *io_stream;
+  GTcpConnection *old_connection = G_TCP_CONNECTION (data->connection);
+
+  io_stream = g_proxy_connect_finish (G_PROXY (object),
+				      result,
+				      &data->last_error);
+
+  if (io_stream)
+    {
+      if (G_IS_SOCKET_CONNECTION (io_stream))
+        data->connection = G_SOCKET_CONNECTION (g_object_ref (io_stream));
+      else
+        data->connection = _g_proxy_connection_new (old_connection,
+                                                    io_stream);
+      g_object_unref (io_stream);
+    }
+  else
+    {
+      data->connection = NULL;
+    }
+
+  g_object_unref (old_connection);
+
+  g_socket_client_async_connect_complete (data);
+}
+
+static void
+g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
+{
+  GProxy *proxy;
+  const gchar *protocol = g_proxy_address_get_protocol (data->proxy_addr);
+
+  proxy = g_proxy_get_default_for_protocol (protocol);
+
+  /* The connection should not be anything else then TCP Connection,
+   * but let's put a safety guard in case
+   */
+  if (!G_IS_TCP_CONNECTION (data->connection))
+    {
+      g_critical ("Trying to proxy over non-TCP connection, this is "
+          "most likely a bug in GLib IO library.");
+
+      g_set_error_literal (&data->last_error,
+          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+          _("Trying to proxy over non-TCP connection is not supported."));
+
+      g_object_unref (data->connection);
+      data->connection = NULL;
+
+      enumerator_next_async (data);
+    }
+  else if (proxy)
+    {
+      g_proxy_connect_async (proxy,
+                             G_IO_STREAM (data->connection),
+                             data->proxy_addr,
+                             data->cancellable,
+                             g_socket_client_proxy_connect_callback,
+                             data);
+      g_object_unref (proxy);
+    }
+  else
+    {
+      g_clear_error (&data->last_error);
+
+      g_set_error (&data->last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+          _("Proxy protocol '%s' is not supported."),
+          protocol);
+
+      g_object_unref (data->connection);
+      data->connection = NULL;
+
+      enumerator_next_async (data);
+    }
+}
+
 static gboolean
 g_socket_client_socket_callback (GSocket *socket,
 				 GIOCondition condition,
@@ -796,16 +1032,23 @@ g_socket_client_socket_callback (GSocket *socket,
 	  data->current_socket = NULL;
 
 	  /* try next one */
-	  g_socket_address_enumerator_next_async (data->enumerator,
-						  data->cancellable,
-						  g_socket_client_enumerator_callback,
-						  data);
+	  enumerator_next_async (data);
 
 	  return FALSE;
 	}
     }
 
-  g_socket_client_async_connect_complete (data);
+  g_socket_set_blocking (data->current_socket, TRUE);
+
+  data->connection =
+    g_socket_connection_factory_create_connection (data->current_socket);
+  g_object_unref (data->current_socket);
+  data->current_socket = NULL;
+
+  if (data->proxy_addr)
+    g_socket_client_proxy_connect (data);
+  else
+    g_socket_client_async_connect_complete (data);
 
   return FALSE;
 }
@@ -816,7 +1059,7 @@ g_socket_client_enumerator_callback (GObject      *object,
 				     gpointer      user_data)
 {
   GSocketClientAsyncConnectData *data = user_data;
-  GSocketAddress *address;
+  GSocketAddress *address = NULL;
   GSocket *socket;
   GError *tmp_error = NULL;
 
@@ -843,6 +1086,10 @@ g_socket_client_enumerator_callback (GObject      *object,
       return;
     }
 
+  if (G_IS_PROXY_ADDRESS (address) &&
+      data->client->priv->enable_proxy)
+    data->proxy_addr = g_object_ref (G_PROXY_ADDRESS (address));
+
   g_clear_error (&data->last_error);
 
   socket = create_socket (data->client, address, &data->last_error);
@@ -880,13 +1127,10 @@ g_socket_client_enumerator_callback (GObject      *object,
 	  data->last_error = tmp_error;
 	  g_object_unref (socket);
 	}
-      g_object_unref (address);
     }
 
-  g_socket_address_enumerator_next_async (data->enumerator,
-					  data->cancellable,
-					  g_socket_client_enumerator_callback,
-					  data);
+  g_object_unref (address);
+  enumerator_next_async (data);
 }
 
 /**
@@ -916,7 +1160,7 @@ g_socket_client_connect_async (GSocketClient       *client,
 
   g_return_if_fail (G_IS_SOCKET_CLIENT (client));
 
-  data = g_slice_new (GSocketClientAsyncConnectData);
+  data = g_slice_new0 (GSocketClientAsyncConnectData);
 
   data->result = g_simple_async_result_new (G_OBJECT (client),
 					    callback, user_data,
@@ -924,14 +1168,13 @@ g_socket_client_connect_async (GSocketClient       *client,
   data->client = client;
   if (cancellable)
     data->cancellable = g_object_ref (cancellable);
+
+  if (can_use_proxy (client))
+      data->enumerator = g_socket_connectable_proxy_enumerate (connectable);
   else
-    data->cancellable = NULL;
-  data->last_error = NULL;
-  data->enumerator = g_socket_connectable_enumerate (connectable);
+      data->enumerator = g_socket_connectable_enumerate (connectable);
 
-  g_socket_address_enumerator_next_async (data->enumerator, cancellable,
-					  g_socket_client_enumerator_callback,
-					  data);
+  enumerator_next_async (data);
 }
 
 /**
diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h
index df9f5a2..ace29af 100644
--- a/gio/gsocketclient.h
+++ b/gio/gsocketclient.h
@@ -85,6 +85,9 @@ void                    g_socket_client_set_local_address               (GSocket
 guint                   g_socket_client_get_timeout                     (GSocketClient        *client);
 void                    g_socket_client_set_timeout                     (GSocketClient        *client,
 									 guint                 timeout);
+gboolean                g_socket_client_get_enable_proxy                (GSocketClient        *client);
+void                    g_socket_client_set_enable_proxy                (GSocketClient        *client,
+    									 gboolean	      enable);
 
 GSocketConnection *     g_socket_client_connect                         (GSocketClient        *client,
                                                                          GSocketConnectable   *connectable,



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]