From 3f3e141ec8ffe8f40a2faced69d35cb161153107 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Fri, 20 Aug 2010 13:04:19 -0400 Subject: [PATCH] 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 gsocketclient GSocketClient 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 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: + * + * + * + * %G_SOCKET_CLIENT_RESOLVING: + * + * @client is about to look up @connectable in DNS. + * @connection will be %NULL. + * + * + * + * %G_SOCKET_CLIENT_RESOLVED: + * + * @client has successfully resolved @connectable in DNS. + * @connection will be %NULL. + * + * + * + * %G_SOCKET_CLIENT_CONNECTING: + * + * @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. + * + * + * + * %G_SOCKET_CLIENT_CONNECTED: + * + * @client has successfully connected to a remote host. + * @connection is the connected #GSocketConnection. + * + * + * + * %G_SOCKET_CLIENT_PROXY_NEGOTIATING: + * + * @client is about to negotiate with a proxy to get it to + * connect to @connectable. @connection is the + * #GSocketConnection to the proxy server. + * + * + * + * %G_SOCKET_CLIENT_PROXY_NEGOTIATED: + * + * @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. + * + * + * + * %G_SOCKET_CLIENT_TLS_HANDSHAKING: + * + * @client is about to begin a TLS handshake. @connection is a + * #GTlsClientConnection. + * + * + * + * %G_SOCKET_CLIENT_TLS_HANDSHAKED: + * + * @client has successfully completed the TLS handshake. + * @connection is a #GTlsClientConnection. + * + * + * + * %G_SOCKET_CLIENT_COMPLETE: + * + * @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). + * + * + * + * + * 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) { -- 2.7.4