[glib] Add GSocketClient::event, for tracking socket client status



commit 3f3e141ec8ffe8f40a2faced69d35cb161153107
Author: Dan Winship <danw gnome org>
Date:   Fri Aug 20 13:04:19 2010 -0400

    Add GSocketClient::event, for tracking socket client status
    
    This can be used for debugging, or for progress UIs ("Connecting to
    example.com..."), or to do low-level tweaking on the connection at
    various points in the process.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=665805

 docs/reference/gio/gio-sections.txt |    3 +-
 gio/gio.symbols                     |    1 +
 gio/gioenums.h                      |   38 +++++++
 gio/gsocketclient.c                 |  194 +++++++++++++++++++++++++++++++++--
 gio/gsocketclient.h                 |    6 +-
 gio/tests/send-data.c               |   23 ++++
 6 files changed, 253 insertions(+), 12 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 31d3bb2..62b233a 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1838,7 +1838,7 @@ GSocketPrivate
 <FILE>gsocketclient</FILE>
 <TITLE>GSocketClient</TITLE>
 GSocketClient
-g_socket_client_add_application_proxy
+GSocketClientEvent
 g_socket_client_new
 g_socket_client_connect
 g_socket_client_connect_async
@@ -1868,6 +1868,7 @@ g_socket_client_get_timeout
 g_socket_client_get_enable_proxy
 g_socket_client_get_tls
 g_socket_client_get_tls_validation_flags
+g_socket_client_add_application_proxy
 <SUBSECTION Standard>
 GSocketClientClass
 G_IS_SOCKET_CLIENT
diff --git a/gio/gio.symbols b/gio/gio.symbols
index a23f8ad..e91be95 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -998,6 +998,7 @@ g_socket_client_connect_to_service_finish
 g_socket_client_connect_to_uri
 g_socket_client_connect_to_uri_async
 g_socket_client_connect_to_uri_finish
+g_socket_client_event_get_type
 g_socket_client_get_enable_proxy
 g_socket_client_get_family
 g_socket_client_get_local_address
diff --git a/gio/gioenums.h b/gio/gioenums.h
index 47d72bd..122eb7e 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1517,6 +1517,44 @@ typedef enum {
   G_IO_MODULE_SCOPE_BLOCK_DUPLICATES
 } GIOModuleScopeFlags;
 
+/**
+ * GSocketClientEvent:
+ * @G_SOCKET_CLIENT_RESOLVING: The client is doing a DNS lookup.
+ * @G_SOCKET_CLIENT_RESOLVED: The client has completed a DNS lookup.
+ * @G_SOCKET_CLIENT_CONNECTING: The client is connecting to a remote
+ *   host (either a proxy or the destination server).
+ * @G_SOCKET_CLIENT_CONNECTED: The client has connected to a remote
+ *   host.
+ * @G_SOCKET_CLIENT_PROXY_NEGOTIATING: The client is negotiating
+ *   with a proxy to connect to the destination server.
+ * @G_SOCKET_CLIENT_PROXY_NEGOTIATED: The client has negotiated
+ *   with the proxy server.
+ * @G_SOCKET_CLIENT_TLS_HANDSHAKING: The client is performing a
+ *   TLS handshake.
+ * @G_SOCKET_CLIENT_TLS_HANDSHAKED: The client has performed a
+ *   TLS handshake.
+ * @G_SOCKET_CLIENT_COMPLETE: The client is done with a particular
+ *   #GSocketConnectable.
+ *
+ * Describes an event occurring on a #GSocketClient. See the
+ * #GSocketClient::event signal for more details.
+ *
+ * Additional values may be added to this type in the future.
+ *
+ * Since: 2.32
+ */
+typedef enum {
+  G_SOCKET_CLIENT_RESOLVING,
+  G_SOCKET_CLIENT_RESOLVED,
+  G_SOCKET_CLIENT_CONNECTING,
+  G_SOCKET_CLIENT_CONNECTED,
+  G_SOCKET_CLIENT_PROXY_NEGOTIATING,
+  G_SOCKET_CLIENT_PROXY_NEGOTIATED,
+  G_SOCKET_CLIENT_TLS_HANDSHAKING,
+  G_SOCKET_CLIENT_TLS_HANDSHAKED,
+  G_SOCKET_CLIENT_COMPLETE
+} GSocketClientEvent;
+
 G_END_DECLS
 
 #endif /* __GIO_ENUMS_H__ */
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 88f2b36..1a86bf5 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -75,6 +75,14 @@ G_DEFINE_TYPE (GSocketClient, g_socket_client, G_TYPE_OBJECT);
 
 enum
 {
+  EVENT,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum
+{
   PROP_NONE,
   PROP_FAMILY,
   PROP_TYPE,
@@ -615,6 +623,14 @@ g_socket_client_get_tls (GSocketClient *client)
  * g_tcp_wrapper_connection_get_base_io_stream() on the return value
  * to extract the #GTlsClientConnection.
  *
+ * If you need to modify the behavior of the TLS handshake (eg, by
+ * setting a client-side certificate to use, or connecting to the
+ * #GTlsConnection::accept-certificate signal), you can connect to
+ * @client's #GSocketClient::event signal and wait for it to be
+ * emitted with %G_SOCKET_CLIENT_TLS_HANDSHAKING, which will give you
+ * a chance to see the #GTlsClientConnection before the handshake
+ * starts.
+ *
  * Since: 2.28
  */
 void
@@ -678,6 +694,116 @@ g_socket_client_class_init (GSocketClientClass *class)
   gobject_class->set_property = g_socket_client_set_property;
   gobject_class->get_property = g_socket_client_get_property;
 
+  /**
+   * GSocketClient::event:
+   * @client: the #GSocketClient
+   * @event: the event that is occurring
+   * @connectable: the #GSocketConnectable that @event is occurring on
+   * @connection: the current representation of the connection
+   *
+   * Emitted when @client's activity on @connectable changes state.
+   * Among other things, this can be used to provide progress
+   * information about a network connection in the UI. The meanings of
+   * the different @event values are as follows:
+   *
+   * <variablelist>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_RESOLVING:</term>
+   *     <listitem><para>
+   *       @client is about to look up @connectable in DNS.
+   *       @connection will be %NULL.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_RESOLVED:</term>
+   *     <listitem><para>
+   *       @client has successfully resolved @connectable in DNS.
+   *       @connection will be %NULL.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_CONNECTING:</term>
+   *     <listitem><para>
+   *       @client is about to make a connection to a remote host;
+   *       either a proxy server or the destination server itself.
+   *       @connection is the #GSocketConnection, which is not yet
+   *       connected.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_CONNECTED:</term>
+   *     <listitem><para>
+   *       @client has successfully connected to a remote host.
+   *       @connection is the connected #GSocketConnection.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_PROXY_NEGOTIATING:</term>
+   *     <listitem><para>
+   *       @client is about to negotiate with a proxy to get it to
+   *       connect to @connectable. @connection is the
+   *       #GSocketConnection to the proxy server.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_PROXY_NEGOTIATED:</term>
+   *     <listitem><para>
+   *       @client has negotiated a connection to @connectable through
+   *       a proxy server. @connection is the stream returned from
+   *       g_proxy_connect(), which may or may not be a
+   *       #GSocketConnection.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_TLS_HANDSHAKING:</term>
+   *     <listitem><para>
+   *       @client is about to begin a TLS handshake. @connection is a
+   *       #GTlsClientConnection.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_TLS_HANDSHAKED:</term>
+   *     <listitem><para>
+   *       @client has successfully completed the TLS handshake.
+   *       @connection is a #GTlsClientConnection.
+   *     </para></listitem>
+   *   </varlistentry>
+   *   <varlistentry>
+   *     <term>%G_SOCKET_CLIENT_COMPLETE:</term>
+   *     <listitem><para>
+   *       @client has either successfully connected to @connectable
+   *       (in which case @connection is the #GSocketConnection that
+   *       it will be returning to the caller) or has failed (in which
+   *       case @connection is %NULL and the client is about to return
+   *       an error).
+   *     </para></listitem>
+   *   </varlistentry>
+   * </variablelist>
+   *
+   * Each event except %G_SOCKET_CLIENT_COMPLETE may be emitted
+   * multiple times (or not at all) for a given connectable (in
+   * particular, if @client ends up attempting to connect to more than
+   * one address). However, if @client emits the #GSocketClient:event
+   * signal at all for a given connectable, that it will always emit
+   * it with %G_SOCKET_CLIENT_COMPLETE when it is done.
+   *
+   * Note that there may be additional #GSocketClientEvent values in
+   * the future; unrecognized @event values should be ignored.
+   *
+   * Since: 2.32
+   */
+  signals[EVENT] =
+    g_signal_new (I_("event"),
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GSocketClientClass, event),
+		  NULL, NULL,
+		  NULL,
+		  G_TYPE_NONE, 3,
+		  G_TYPE_SOCKET_CLIENT_EVENT,
+		  G_TYPE_SOCKET_CONNECTABLE,
+		  G_TYPE_IO_STREAM);
+
   g_object_class_install_property (gobject_class, PROP_FAMILY,
 				   g_param_spec_enum ("family",
 						      P_("Socket family"),
@@ -754,6 +880,16 @@ g_socket_client_class_init (GSocketClientClass *class)
 						       G_PARAM_STATIC_STRINGS));
 }
 
+static void
+g_socket_client_emit_event (GSocketClient       *client,
+			    GSocketClientEvent  event,
+			    GSocketConnectable  *connectable,
+			    GIOStream           *connection)
+{
+  g_signal_emit (client, signals[EVENT], 0,
+		 event, connectable, connection);
+}
+
 /**
  * g_socket_client_connect:
  * @client: a #GSocketClient.
@@ -806,6 +942,7 @@ g_socket_client_connect (GSocketClient       *client,
       GSocketAddress *address = NULL;
       gboolean application_proxy = FALSE;
       GSocket *socket;
+      gboolean using_proxy;
 
       if (g_cancellable_is_cancelled (cancellable))
 	{
@@ -815,6 +952,8 @@ g_socket_client_connect (GSocketClient       *client,
 	}
 
       tmp_error = NULL;
+      g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVING,
+				  connectable, NULL);
       address = g_socket_address_enumerator_next (enumerator, cancellable,
 	      					  &tmp_error);
 
@@ -834,6 +973,11 @@ g_socket_client_connect (GSocketClient       *client,
                                  _("Unknown error on connect"));
 	  break;
 	}
+      g_socket_client_emit_event (client, G_SOCKET_CLIENT_RESOLVED,
+				  connectable, NULL);
+
+      using_proxy = (G_IS_PROXY_ADDRESS (address) &&
+		     client->priv->enable_proxy);
 
       /* clear error from previous attempt */
       g_clear_error (&last_error);
@@ -846,17 +990,21 @@ g_socket_client_connect (GSocketClient       *client,
 	}
 
       connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
-      if (!g_socket_connection_connect (G_SOCKET_CONNECTION (connection),
-					address, cancellable, &last_error))
+      g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTING, connectable, connection);
+
+      if (g_socket_connection_connect (G_SOCKET_CONNECTION (connection),
+				       address, cancellable, &last_error))
+	{
+	  g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTED, connectable, connection);
+	}
+      else
 	{
 	  clarify_connect_error (last_error, connectable, address);
 	  g_object_unref (connection);
 	  connection = NULL;
 	}
 
-      if (connection &&
-	  G_IS_PROXY_ADDRESS (address) &&
-	  client->priv->enable_proxy)
+      if (connection && using_proxy)
 	{
 	  GProxyAddress *proxy_addr = G_PROXY_ADDRESS (address);
 	  const gchar *protocol;
@@ -882,8 +1030,9 @@ g_socket_client_connect (GSocketClient       *client,
             }
           else if (proxy)
 	    {
-              GIOStream *proxy_connection;
+	      GIOStream *proxy_connection;
 
+	      g_socket_client_emit_event (client, G_SOCKET_CLIENT_PROXY_NEGOTIATING, connectable, connection);
 	      proxy_connection = g_proxy_connect (proxy,
 						  connection,
 						  proxy_addr,
@@ -892,6 +1041,9 @@ g_socket_client_connect (GSocketClient       *client,
 	      g_object_unref (connection);
 	      connection = proxy_connection;
 	      g_object_unref (proxy);
+
+	      if (connection)
+		g_socket_client_emit_event (client, G_SOCKET_CLIENT_PROXY_NEGOTIATED, connectable, connection);
 	    }
 	  else if (!g_hash_table_lookup_extended (client->priv->app_proxies,
 						  protocol, NULL, NULL))
@@ -920,8 +1072,13 @@ g_socket_client_connect (GSocketClient       *client,
 	    {
 	      g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
                                                             client->priv->tls_validation_flags);
-	      if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn),
-					       cancellable, &last_error))
+	      g_socket_client_emit_event (client, G_SOCKET_CLIENT_TLS_HANDSHAKING, connectable, connection);
+	      if (g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn),
+					      cancellable, &last_error))
+		{
+		  g_socket_client_emit_event (client, G_SOCKET_CLIENT_TLS_HANDSHAKED, connectable, connection);
+		}
+	      else
 		{
 		  g_object_unref (tlsconn);
 		  connection = NULL;
@@ -943,6 +1100,7 @@ g_socket_client_connect (GSocketClient       *client,
     }
   g_object_unref (enumerator);
 
+  g_socket_client_emit_event (client, G_SOCKET_CLIENT_COMPLETE, connectable, connection);
   return G_SOCKET_CONNECTION (connection);
 }
 
@@ -1127,6 +1285,8 @@ typedef struct
 static void
 g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
 {
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, data->connection);
+
   if (data->last_error)
     {
       g_simple_async_result_take_error (data->result, data->last_error);
@@ -1188,6 +1348,7 @@ enumerator_next_async (GSocketClientAsyncConnectData *data)
   g_clear_object (&data->proxy_addr);
   g_clear_object (&data->connection);
 
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
   g_socket_address_enumerator_next_async (data->enumerator,
 					  data->cancellable,
 					  g_socket_client_enumerator_callback,
@@ -1208,6 +1369,7 @@ g_socket_client_tls_handshake_callback (GObject      *object,
       g_object_unref (data->connection);
       data->connection = G_IO_STREAM (object);
 
+      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKED, data->connectable, data->connection);
       g_socket_client_async_connect_complete (data);
     }
   else
@@ -1235,6 +1397,7 @@ g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data)
     {
       g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
                                                     data->client->priv->tls_validation_flags);
+      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKING, data->connectable, G_IO_STREAM (tlsconn));
       g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn),
 					G_PRIORITY_DEFAULT,
 					data->cancellable,
@@ -1258,7 +1421,11 @@ g_socket_client_proxy_connect_callback (GObject      *object,
   data->connection = g_proxy_connect_finish (G_PROXY (object),
 					     result,
 					     &data->last_error);
-  if (!data->connection)
+  if (data->connection)
+    {
+      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATED, data->connectable, data->connection);
+    }
+  else
     {
       enumerator_next_async (data);
       return;
@@ -1289,6 +1456,8 @@ g_socket_client_connected_callback (GObject      *source,
       return;
     }
 
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTED, data->connectable, data->connection);
+
   /* wrong, but backward compatible */
   g_socket_set_blocking (data->current_socket, TRUE);
 
@@ -1298,7 +1467,7 @@ g_socket_client_connected_callback (GObject      *source,
       return;
     }
 
-  protocol  = g_proxy_address_get_protocol (data->proxy_addr);
+  protocol = g_proxy_address_get_protocol (data->proxy_addr);
   proxy = g_proxy_get_default_for_protocol (protocol);
 
   /* The connection should not be anything other than TCP,
@@ -1317,6 +1486,7 @@ g_socket_client_connected_callback (GObject      *source,
     }
   else if (proxy)
     {
+      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_PROXY_NEGOTIATING, data->connectable, data->connection);
       g_proxy_connect_async (proxy,
                              data->connection,
                              data->proxy_addr,
@@ -1377,6 +1547,9 @@ g_socket_client_enumerator_callback (GObject      *object,
       return;
     }
 
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVED,
+			      data->connectable, NULL);
+
   if (G_IS_PROXY_ADDRESS (address) &&
       data->client->priv->enable_proxy)
     data->proxy_addr = g_object_ref (G_PROXY_ADDRESS (address));
@@ -1395,6 +1568,7 @@ g_socket_client_enumerator_callback (GObject      *object,
   data->current_addr = address;
   data->connection = (GIOStream *) g_socket_connection_factory_create_connection (socket);
 
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTING, data->connectable, data->connection);
   g_socket_connection_connect_async (G_SOCKET_CONNECTION (data->connection),
 				     address, data->cancellable,
 				     g_socket_client_connected_callback, data);
diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h
index 180bedd..216310b 100644
--- a/gio/gsocketclient.h
+++ b/gio/gsocketclient.h
@@ -52,12 +52,16 @@ struct _GSocketClientClass
 {
   GObjectClass parent_class;
 
+  void (* event) (GSocketClient       *client,
+		  GSocketClientEvent  event,
+		  GSocketConnectable  *connectable,
+		  GIOStream           *connection);
+
   /* Padding for future expansion */
   void (*_g_reserved1) (void);
   void (*_g_reserved2) (void);
   void (*_g_reserved3) (void);
   void (*_g_reserved4) (void);
-  void (*_g_reserved5) (void);
 };
 
 struct _GSocketClient
diff --git a/gio/tests/send-data.c b/gio/tests/send-data.c
index 01a42b4..201e710 100644
--- a/gio/tests/send-data.c
+++ b/gio/tests/send-data.c
@@ -8,6 +8,7 @@ int cancel_timeout = 0;
 int io_timeout = 0;
 gboolean async = FALSE;
 gboolean graceful = FALSE;
+gboolean verbose = FALSE;
 static GOptionEntry cmd_entries[] = {
   {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout,
    "Cancel any op after the specified amount of seconds", NULL},
@@ -17,6 +18,8 @@ static GOptionEntry cmd_entries[] = {
    "Use graceful disconnect", NULL},
   {"timeout", 't', 0, G_OPTION_ARG_INT, &io_timeout,
    "Time out socket I/O after the specified number of seconds", NULL},
+  {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+   "Verbose debugging output", NULL},
   {NULL}
 };
 
@@ -56,6 +59,24 @@ async_cb (GObject *source_object,
   g_main_loop_quit (loop);
 }
 
+static void
+socket_client_event (GSocketClient *client,
+		     GSocketClientEvent event,
+		     GSocketConnectable *connectable,
+		     GSocketConnection *connection)
+{
+  static GEnumClass *event_class;
+  GTimeVal tv;
+
+  if (!event_class)
+    event_class = g_type_class_ref (G_TYPE_SOCKET_CLIENT_EVENT);
+
+  g_get_current_time (&tv);
+  printf ("% 12ld.%06ld GSocketClient => %s [%s]\n",
+	  tv.tv_sec, tv.tv_usec,
+	  g_enum_get_value (event_class, event)->value_nick,
+	  connection ? G_OBJECT_TYPE_NAME (connection) : "");
+}
 
 int
 main (int argc, char *argv[])
@@ -103,6 +124,8 @@ main (int argc, char *argv[])
   client = g_socket_client_new ();
   if (io_timeout)
     g_socket_client_set_timeout (client, io_timeout);
+  if (verbose)
+    g_signal_connect (client, "event", G_CALLBACK (socket_client_event), NULL);
 
   if (async)
     {



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