gio: port networking classes from GSimpleAsyncResult to GTask
[platform/upstream/glib.git] / gio / gsocketclient.c
index 0519346..ddbe697 100644 (file)
@@ -34,7 +34,7 @@
 #include <gio/gsocketconnection.h>
 #include <gio/gproxyaddressenumerator.h>
 #include <gio/gproxyaddress.h>
-#include <gio/gsimpleasyncresult.h>
+#include <gio/gtask.h>
 #include <gio/gcancellable.h>
 #include <gio/gioerror.h>
 #include <gio/gsocket.h>
@@ -46,6 +46,7 @@
 #include <gio/gtcpwrapperconnection.h>
 #include <gio/gtlscertificate.h>
 #include <gio/gtlsclientconnection.h>
+#include <gio/ginetaddress.h>
 #include "glibintl.h"
 
 
@@ -55,8 +56,8 @@
  * @include: gio/gio.h
  * @see_also: #GSocketConnection, #GSocketListener
  *
- * #GSocketClient is a high-level utility class for connecting to a
- * network host using a connection oriented socket type.
+ * #GSocketClient is a lightweight high-level utility class for connecting to
+ * network host using a connection oriented socket type.
  *
  * You create a #GSocketClient object, set any options you want, and then
  * call a sync or async connect operation, which returns a #GSocketConnection
@@ -66,6 +67,9 @@
  * the underlying socket that is in use. For instance, for a TCP/IP connection
  * it will be a #GTcpConnection.
  *
+ * As #GSocketClient is a lightweight object, you don't need to cache it. You
+ * can just create a new one any time you need one.
+ *
  * Since: 2.22
  */
 
@@ -74,6 +78,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,
@@ -138,7 +150,7 @@ create_socket (GSocketClient  *client,
   return socket;
 }
 
-gboolean
+static gboolean
 can_use_proxy (GSocketClient *client)
 {
   GSocketClientPrivate *priv = client->priv;
@@ -148,6 +160,40 @@ can_use_proxy (GSocketClient *client)
 }
 
 static void
+clarify_connect_error (GError             *error,
+                      GSocketConnectable *connectable,
+                      GSocketAddress     *address)
+{
+  const char *name;
+  char *tmp_name = NULL;
+
+  if (G_IS_PROXY_ADDRESS (address))
+    {
+      name = tmp_name = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (address)));
+
+      g_prefix_error (&error, _("Could not connect to proxy server %s: "), name);
+    }
+  else
+    {
+      if (G_IS_NETWORK_ADDRESS (connectable))
+       name = g_network_address_get_hostname (G_NETWORK_ADDRESS (connectable));
+      else if (G_IS_NETWORK_SERVICE (connectable))
+       name = g_network_service_get_domain (G_NETWORK_SERVICE (connectable));
+      else if (G_IS_INET_SOCKET_ADDRESS (connectable))
+       name = tmp_name = g_inet_address_to_string (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (connectable)));
+      else
+       name = NULL;
+
+      if (name)
+       g_prefix_error (&error, _("Could not connect to %s: "), name);
+      else
+       g_prefix_error (&error, _("Could not connect: "));
+    }
+
+  g_free (tmp_name);
+}
+
+static void
 g_socket_client_init (GSocketClient *client)
 {
   client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
@@ -423,7 +469,7 @@ g_socket_client_set_protocol (GSocketClient   *client,
  *
  * See g_socket_client_set_local_address() for details.
  *
- * Returns: (transfer none): a #GSocketAddres or %NULL. don't free
+ * Returns: (transfer none): a #GSocketAddress or %NULL. Do not free.
  *
  * Since: 2.22
  */
@@ -436,7 +482,7 @@ g_socket_client_get_local_address (GSocketClient *client)
 /**
  * g_socket_client_set_local_address:
  * @client: a #GSocketClient.
- * @address: a #GSocketAddress, or %NULL
+ * @address: (allow-none): a #GSocketAddress, or %NULL
  *
  * Sets the local address of the socket client.
  * The sockets created by this object will bound to the
@@ -580,6 +626,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
@@ -643,6 +697,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"),
@@ -719,6 +883,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.
@@ -769,7 +943,9 @@ g_socket_client_connect (GSocketClient       *client,
   while (connection == NULL)
     {
       GSocketAddress *address = NULL;
+      gboolean application_proxy = FALSE;
       GSocket *socket;
+      gboolean using_proxy;
 
       if (g_cancellable_is_cancelled (cancellable))
        {
@@ -779,6 +955,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);
 
@@ -798,6 +976,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);
@@ -809,12 +992,22 @@ g_socket_client_connect (GSocketClient       *client,
          continue;
        }
 
-      if (g_socket_connect (socket, address, cancellable, &last_error))
-       connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
+      connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
+      g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTING, connectable, connection);
 
-      if (connection &&
-         G_IS_PROXY_ADDRESS (address) &&
-         client->priv->enable_proxy)
+      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 && using_proxy)
        {
          GProxyAddress *proxy_addr = G_PROXY_ADDRESS (address);
          const gchar *protocol;
@@ -833,15 +1026,16 @@ g_socket_client_connect (GSocketClient       *client,
 
               g_set_error_literal (&last_error,
                   G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                  _("Trying to proxy over non-TCP connection is not supported."));
+                  _("Proxying over a non-TCP connection is not supported."));
 
              g_object_unref (connection);
              connection = NULL;
             }
           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,
@@ -850,6 +1044,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))
@@ -860,9 +1057,13 @@ g_socket_client_connect (GSocketClient       *client,
              g_object_unref (connection);
              connection = NULL;
            }
+         else
+           {
+             application_proxy = TRUE;
+           }
        }
 
-      if (connection && client->priv->tls)
+      if (!application_proxy && connection && client->priv->tls)
        {
          GIOStream *tlsconn;
 
@@ -874,8 +1075,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;
@@ -897,6 +1103,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);
 }
 
@@ -971,7 +1178,6 @@ g_socket_client_connect_to_host (GSocketClient  *client,
  * @service: the name of the service to connect to
  * @cancellable: (allow-none): a #GCancellable, or %NULL
  * @error: a pointer to a #GError, or %NULL
- * @returns: (transfer full): a #GSocketConnection if successful, or %NULL on error
  *
  * Attempts to create a TCP connection to a service.
  *
@@ -987,6 +1193,8 @@ g_socket_client_connect_to_host (GSocketClient  *client,
  * In the event of any failure (DNS error, service not found, no hosts
  * connectable) %NULL is returned and @error (if non-%NULL) is set
  * accordingly.
+ *
+ * Returns: (transfer full): a #GSocketConnection if successful, or %NULL on error
  */
 GSocketConnection *
 g_socket_client_connect_to_service (GSocketClient  *client,
@@ -1063,13 +1271,13 @@ g_socket_client_connect_to_uri (GSocketClient  *client,
 
 typedef struct
 {
-  GSimpleAsyncResult *result;
-  GCancellable *cancellable;
+  GTask *task;
   GSocketClient *client;
 
   GSocketConnectable *connectable;
   GSocketAddressEnumerator *enumerator;
   GProxyAddress *proxy_addr;
+  GSocketAddress *current_addr;
   GSocket *current_socket;
   GIOStream *connection;
 
@@ -1077,42 +1285,39 @@ typedef struct
 } GSocketClientAsyncConnectData;
 
 static void
-g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
+g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
 {
-  if (data->last_error)
-    {
-      g_simple_async_result_take_error (data->result, data->last_error);
-    }
-  else
-    {
-      g_assert (data->connection);
+  g_clear_object (&data->connectable);
+  g_clear_object (&data->enumerator);
+  g_clear_object (&data->proxy_addr);
+  g_clear_object (&data->current_addr);
+  g_clear_object (&data->current_socket);
+  g_clear_object (&data->connection);
 
-      if (!G_IS_SOCKET_CONNECTION (data->connection))
-       {
-         GSocketConnection *wrapper_connection;
+  g_clear_error (&data->last_error);
 
-         wrapper_connection = g_tcp_wrapper_connection_new (data->connection,
-                                                            data->current_socket);
-         g_object_unref (data->connection);
-         data->connection = (GIOStream *)wrapper_connection;
-       }
+  g_slice_free (GSocketClientAsyncConnectData, data);
+}
+
+static void
+g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
+{
+  g_assert (data->connection);
+
+  if (!G_IS_SOCKET_CONNECTION (data->connection))
+    {
+      GSocketConnection *wrapper_connection;
 
-      g_simple_async_result_set_op_res_gpointer (data->result,
-                                                data->connection,
-                                                g_object_unref);
+      wrapper_connection = g_tcp_wrapper_connection_new (data->connection,
+                                                        data->current_socket);
+      g_object_unref (data->connection);
+      data->connection = (GIOStream *)wrapper_connection;
     }
 
-  g_simple_async_result_complete (data->result);
-  g_object_unref (data->result);
-  g_object_unref (data->connectable);
-  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);
+  g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, data->connection);
+  g_task_return_pointer (data->task, data->connection, g_object_unref);
+  data->connection = NULL;
+  g_object_unref (data->task);
 }
 
 
@@ -1132,8 +1337,15 @@ set_last_error (GSocketClientAsyncConnectData *data,
 static void
 enumerator_next_async (GSocketClientAsyncConnectData *data)
 {
+  /* We need to cleanup the state */
+  g_clear_object (&data->current_socket);
+  g_clear_object (&data->current_addr);
+  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_task_get_cancellable (data->task),
                                          g_socket_client_enumerator_callback,
                                          data);
 }
@@ -1151,19 +1363,15 @@ 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
     {
       g_object_unref (object);
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-      g_object_unref (data->connection);
-      data->connection = NULL;
-
       enumerator_next_async (data);
     }
-
-  g_socket_client_async_connect_complete (data);
 }
 
 static void
@@ -1184,19 +1392,15 @@ 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,
+                                       g_task_get_cancellable (data->task),
                                        g_socket_client_tls_handshake_callback,
                                        data);
     }
   else
     {
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-      g_object_unref (data->connection);
-      data->connection = NULL;
-
       enumerator_next_async (data);
     }
 }
@@ -1212,11 +1416,12 @@ 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
     {
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-
       enumerator_next_async (data);
       return;
     }
@@ -1225,21 +1430,42 @@ g_socket_client_proxy_connect_callback (GObject      *object,
 }
 
 static void
-g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
+g_socket_client_connected_callback (GObject      *source,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
 {
+  GSocketClientAsyncConnectData *data = user_data;
+  GError *error = NULL;
   GProxy *proxy;
   const gchar *protocol;
 
+  if (!g_socket_connection_connect_finish (G_SOCKET_CONNECTION (source),
+                                          result, &error))
+    {
+      clarify_connect_error (error, data->connectable,
+                            data->current_addr);
+      set_last_error (data, error);
+
+      /* try next one */
+      enumerator_next_async (data);
+      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);
+
   if (!data->proxy_addr)
     {
       g_socket_client_tls_handshake (data);
       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 else then TCP Connection,
+  /* The connection should not be anything other than TCP,
    * but let's put a safety guard in case
    */
   if (!G_IS_TCP_CONNECTION (data->connection))
@@ -1249,21 +1475,17 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
 
       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;
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
+          _("Proxying over a non-TCP connection is not supported."));
 
       enumerator_next_async (data);
     }
   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,
-                             data->cancellable,
+                             g_task_get_cancellable (data->task),
                              g_socket_client_proxy_connect_callback,
                              data);
       g_object_unref (proxy);
@@ -1277,65 +1499,14 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
           _("Proxy protocol '%s' is not supported."),
           protocol);
 
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-      g_object_unref (data->connection);
-      data->connection = NULL;
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-
       enumerator_next_async (data);
     }
-}
-
-static void
-g_socket_client_socket_connected (GSocketClientAsyncConnectData *data)
-{
-  g_socket_set_blocking (data->current_socket, TRUE);
-
-  data->connection = (GIOStream *)
-    g_socket_connection_factory_create_connection (data->current_socket);
-
-  g_socket_client_proxy_connect (data);
-}
-
-static gboolean
-g_socket_client_socket_callback (GSocket *socket,
-                                GIOCondition condition,
-                                GSocketClientAsyncConnectData *data)
-{
-  GError *error = NULL;
-
-  if (g_cancellable_is_cancelled (data->cancellable))
-    {
-      /* Cancelled, return done with last error being cancelled */
-      g_clear_error (&data->last_error);
-      g_object_unref (data->current_socket);
-      data->current_socket = NULL;
-      g_cancellable_set_error_if_cancelled (data->cancellable,
-                                           &data->last_error);
-
-      g_socket_client_async_connect_complete (data);
-      return FALSE;
-    }
   else
     {
-      /* socket is ready for writing means connect done, did it succeed? */
-      if (!g_socket_check_connect_result (data->current_socket, &error))
-       {
-         set_last_error (data, error);
-         g_object_unref (data->current_socket);
-         data->current_socket = NULL;
-
-         /* try next one */
-         enumerator_next_async (data);
-
-         return FALSE;
-       }
+      /* Simply complete the connection, we don't want to do TLS handshake
+       * as the application proxy handling may need proxy handshake first */
+      g_socket_client_async_connect_complete (data);
     }
-
-  g_socket_client_socket_connected (data);
-  return FALSE;
 }
 
 static void
@@ -1346,31 +1517,37 @@ g_socket_client_enumerator_callback (GObject      *object,
   GSocketClientAsyncConnectData *data = user_data;
   GSocketAddress *address = NULL;
   GSocket *socket;
-  GError *tmp_error = NULL;
+  GError *error = NULL;
 
-  if (g_cancellable_is_cancelled (data->cancellable))
-    {
-      g_clear_error (&data->last_error);
-      g_cancellable_set_error_if_cancelled (data->cancellable, &data->last_error);
-      g_socket_client_async_connect_complete (data);
-      return;
-    }
+  if (g_task_return_error_if_cancelled (data->task))
+    return;
 
   address = g_socket_address_enumerator_next_finish (data->enumerator,
-                                                    result, &tmp_error);
-
+                                                    result, &error);
   if (address == NULL)
     {
-      if (tmp_error)
-       set_last_error (data, tmp_error);
-      else if (data->last_error == NULL)
-        g_set_error_literal (&data->last_error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                             _("Unknown error on connect"));
-
-      g_socket_client_async_connect_complete (data);
+      g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
+      if (!error)
+       {
+         if (data->last_error)
+           {
+             error = data->last_error;
+             data->last_error = NULL;
+           }
+         else
+           {
+             g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                  _("Unknown error on connect"));
+           }
+       }
+      g_task_return_error (data->task, error);
+      g_object_unref (data->task);
       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));
@@ -1378,49 +1555,27 @@ g_socket_client_enumerator_callback (GObject      *object,
   g_clear_error (&data->last_error);
 
   socket = create_socket (data->client, address, &data->last_error);
-  if (socket != NULL)
+  if (socket == NULL)
     {
-      g_socket_set_blocking (socket, FALSE);
-      if (g_socket_connect (socket, address, data->cancellable, &tmp_error))
-       {
-         data->current_socket = socket;
-         g_socket_client_socket_connected (data);
-
-         g_object_unref (address);
-         return;
-       }
-      else if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_PENDING))
-       {
-         GSource *source;
-
-         data->current_socket = socket;
-         g_error_free (tmp_error);
-
-         source = g_socket_create_source (socket, G_IO_OUT,
-                                          data->cancellable);
-         g_source_set_callback (source,
-                                (GSourceFunc) g_socket_client_socket_callback,
-                                data, NULL);
-         g_source_attach (source, g_main_context_get_thread_default ());
-         g_source_unref (source);
-
-         g_object_unref (address);
-         return;
-       }
-      else
-       {
-         data->last_error = tmp_error;
-         g_object_unref (socket);
-       }
+      g_object_unref (address);
+      enumerator_next_async (data);
+      return;
     }
 
-  g_object_unref (address);
-  enumerator_next_async (data);
+  data->current_socket = socket;
+  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,
+                                    g_task_get_cancellable (data->task),
+                                    g_socket_client_connected_callback, data);
 }
 
 /**
  * g_socket_client_connect_async:
- * @client: a #GTcpClient
+ * @client: a #GSocketClient
  * @connectable: a #GSocketConnectable specifying the remote address.
  * @cancellable: (allow-none): a #GCancellable, or %NULL
  * @callback: (scope async): a #GAsyncReadyCallback
@@ -1446,16 +1601,7 @@ g_socket_client_connect_async (GSocketClient       *client,
   g_return_if_fail (G_IS_SOCKET_CLIENT (client));
 
   data = g_slice_new0 (GSocketClientAsyncConnectData);
-
-  data->result = g_simple_async_result_new (G_OBJECT (client),
-                                           callback, user_data,
-                                           g_socket_client_connect_async);
   data->client = client;
-  if (cancellable)
-    data->cancellable = g_object_ref (cancellable);
-  else
-    data->cancellable = NULL;
-  data->last_error = NULL;
   data->connectable = g_object_ref (connectable);
 
   if (can_use_proxy (client))
@@ -1463,12 +1609,15 @@ g_socket_client_connect_async (GSocketClient       *client,
   else
       data->enumerator = g_socket_connectable_enumerate (connectable);
 
+  data->task = g_task_new (client, cancellable, callback, user_data);
+  g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free);
+
   enumerator_next_async (data);
 }
 
 /**
  * g_socket_client_connect_to_host_async:
- * @client: a #GTcpClient
+ * @client: a #GSocketClient
  * @host_and_port: the name and optionally the port of the host to connect to
  * @default_port: the default port to connect to
  * @cancellable: (allow-none): a #GCancellable, or %NULL
@@ -1499,8 +1648,9 @@ g_socket_client_connect_to_host_async (GSocketClient        *client,
                                         &error);
   if (connectable == NULL)
     {
-      g_simple_async_report_take_gerror_in_idle (G_OBJECT (client),
-                                           callback, user_data, error);
+      g_task_report_error (client, callback, user_data,
+                           g_socket_client_connect_to_host_async,
+                           error);
     }
   else
     {
@@ -1574,8 +1724,9 @@ g_socket_client_connect_to_uri_async (GSocketClient        *client,
   connectable = g_network_address_parse_uri (uri, default_port, &error);
   if (connectable == NULL)
     {
-      g_simple_async_report_take_gerror_in_idle (G_OBJECT (client),
-                                           callback, user_data, error);
+      g_task_report_error (client, callback, user_data,
+                           g_socket_client_connect_to_uri_async,
+                           error);
     }
   else
     {
@@ -1605,12 +1756,9 @@ g_socket_client_connect_finish (GSocketClient  *client,
                                GAsyncResult   *result,
                                GError        **error)
 {
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_return_val_if_fail (g_task_is_valid (result, client), NULL);
 
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
 /**
@@ -1696,6 +1844,10 @@ g_socket_client_connect_to_uri_finish (GSocketClient  *client,
  * proxy protocols that are reused between protocols. A good example
  * is HTTP. It can be used to proxy HTTP, FTP and Gopher and can also
  * be use as generic socket proxy through the HTTP CONNECT method.
+ *
+ * When the proxy is detected as being an application proxy, TLS handshake
+ * will be skipped. This is required to let the application do the proxy
+ * specific handshake.
  */
 void
 g_socket_client_add_application_proxy (GSocketClient *client,