cleanup
[platform/upstream/glib.git] / gio / gsocketclient.c
index d14234d..2cd9893 100644 (file)
@@ -14,9 +14,7 @@
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General
- * Public License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
- * Boston, MA 02111-1307, USA.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
  *
  * Authors: Ryan Lortie <desrt@desrt.ca>
  *          Alexander Larsson <alexl@redhat.com>
 #include <gio/gsocketaddressenumerator.h>
 #include <gio/gsocketconnectable.h>
 #include <gio/gsocketconnection.h>
+#include <gio/gioprivate.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>
 #include <gio/gnetworkaddress.h>
 #include <gio/gnetworkservice.h>
 #include <gio/gproxy.h>
+#include <gio/gproxyresolver.h>
 #include <gio/gsocketaddress.h>
 #include <gio/gtcpconnection.h>
 #include <gio/gtcpwrapperconnection.h>
@@ -56,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
  * 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
  */
 
 
-G_DEFINE_TYPE (GSocketClient, g_socket_client, G_TYPE_OBJECT);
+enum
+{
+  EVENT,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
 
 enum
 {
@@ -83,7 +92,8 @@ enum
   PROP_TIMEOUT,
   PROP_ENABLE_PROXY,
   PROP_TLS,
-  PROP_TLS_VALIDATION_FLAGS
+  PROP_TLS_VALIDATION_FLAGS,
+  PROP_PROXY_RESOLVER
 };
 
 struct _GSocketClientPrivate
@@ -97,8 +107,11 @@ struct _GSocketClientPrivate
   GHashTable *app_proxies;
   gboolean tls;
   GTlsCertificateFlags tls_validation_flags;
+  GProxyResolver *proxy_resolver;
 };
 
+G_DEFINE_TYPE_WITH_PRIVATE (GSocketClient, g_socket_client, G_TYPE_OBJECT)
+
 static GSocket *
 create_socket (GSocketClient  *client,
               GSocketAddress *dest_address,
@@ -185,9 +198,7 @@ clarify_connect_error (GError             *error,
 static void
 g_socket_client_init (GSocketClient *client)
 {
-  client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
-                                             G_TYPE_SOCKET_CLIENT,
-                                             GSocketClientPrivate);
+  client->priv = g_socket_client_get_instance_private (client);
   client->priv->type = G_SOCKET_TYPE_STREAM;
   client->priv->app_proxies = g_hash_table_new_full (g_str_hash,
                                                     g_str_equal,
@@ -216,11 +227,10 @@ g_socket_client_finalize (GObject *object)
 {
   GSocketClient *client = G_SOCKET_CLIENT (object);
 
-  if (client->priv->local_address)
-    g_object_unref (client->priv->local_address);
+  g_clear_object (&client->priv->local_address);
+  g_clear_object (&client->priv->proxy_resolver);
 
-  if (G_OBJECT_CLASS (g_socket_client_parent_class)->finalize)
-    (*G_OBJECT_CLASS (g_socket_client_parent_class)->finalize) (object);
+  G_OBJECT_CLASS (g_socket_client_parent_class)->finalize (object);
 
   g_hash_table_unref (client->priv->app_proxies);
 }
@@ -267,6 +277,10 @@ g_socket_client_get_property (GObject    *object,
        g_value_set_flags (value, g_socket_client_get_tls_validation_flags (client));
        break;
 
+      case PROP_PROXY_RESOLVER:
+       g_value_set_object (value, g_socket_client_get_proxy_resolver (client));
+       break;
+
       default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -314,6 +328,10 @@ g_socket_client_set_property (GObject      *object,
       g_socket_client_set_tls_validation_flags (client, g_value_get_flags (value));
       break;
 
+    case PROP_PROXY_RESOLVER:
+      g_socket_client_set_proxy_resolver (client, g_value_get_object (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -471,7 +489,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
@@ -568,6 +586,8 @@ g_socket_client_get_enable_proxy (GSocketClient *client)
  * #GProxyResolver to determine if a proxy protocol such as SOCKS is
  * needed, and automatically do the necessary proxy negotiation.
  *
+ * See also g_socket_client_set_proxy_resolver().
+ *
  * Since: 2.26
  */
 void
@@ -615,6 +635,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
@@ -667,17 +695,144 @@ g_socket_client_set_tls_validation_flags (GSocketClient        *client,
     }
 }
 
+/**
+ * g_socket_client_get_proxy_resolver:
+ * @client: a #GSocketClient.
+ *
+ * Gets the #GProxyResolver being used by @client. Normally, this will
+ * be the resolver returned by g_proxy_resolver_get_default(), but you
+ * can override it with g_socket_client_set_proxy_resolver().
+ *
+ * Returns: (transfer none): The #GProxyResolver being used by
+ *   @client.
+ *
+ * Since: 2.36
+ */
+GProxyResolver *
+g_socket_client_get_proxy_resolver (GSocketClient *client)
+{
+  if (client->priv->proxy_resolver)
+    return client->priv->proxy_resolver;
+  else
+    return g_proxy_resolver_get_default ();
+}
+
+/**
+ * g_socket_client_set_proxy_resolver:
+ * @client: a #GSocketClient.
+ * @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL for the
+ *   default.
+ *
+ * Overrides the #GProxyResolver used by @client. You can call this if
+ * you want to use specific proxies, rather than using the system
+ * default proxy settings.
+ *
+ * Note that whether or not the proxy resolver is actually used
+ * depends on the setting of #GSocketClient:enable-proxy, which is not
+ * changed by this function (but which is %TRUE by default)
+ *
+ * Since: 2.36
+ */
+void
+g_socket_client_set_proxy_resolver (GSocketClient  *client,
+                                    GProxyResolver *proxy_resolver)
+{
+  /* We have to be careful to avoid calling
+   * g_proxy_resolver_get_default() until we're sure we need it,
+   * because trying to load the default proxy resolver module will
+   * break some test programs that aren't expecting it (eg,
+   * tests/gsettings).
+   */
+
+  if (client->priv->proxy_resolver)
+    g_object_unref (client->priv->proxy_resolver);
+
+  client->priv->proxy_resolver = proxy_resolver;
+
+  if (client->priv->proxy_resolver)
+    g_object_ref (client->priv->proxy_resolver);
+}
+
 static void
 g_socket_client_class_init (GSocketClientClass *class)
 {
   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
 
-  g_type_class_add_private (class, sizeof (GSocketClientPrivate));
-
   gobject_class->finalize = g_socket_client_finalize;
   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.  Since GLib 2.40, you can access the remote
+   *   address via g_socket_connection_get_remote_address().
+   *
+   * - %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"),
@@ -752,6 +907,32 @@ g_socket_client_class_init (GSocketClientClass *class)
                                                       G_PARAM_CONSTRUCT |
                                                       G_PARAM_READWRITE |
                                                       G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GSocketClient:proxy-resolver:
+   *
+   * The proxy resolver to use
+   *
+   * Since: 2.36
+   */
+  g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
+                                   g_param_spec_object ("proxy-resolver",
+                                                        P_("Proxy resolver"),
+                                                        P_("The proxy resolver to use"),
+                                                        G_TYPE_PROXY_RESOLVER,
+                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_READWRITE |
+                                                        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);
 }
 
 /**
@@ -797,7 +978,16 @@ g_socket_client_connect (GSocketClient       *client,
   last_error = NULL;
 
   if (can_use_proxy (client))
-    enumerator = g_socket_connectable_proxy_enumerate (connectable);
+    {
+      enumerator = g_socket_connectable_proxy_enumerate (connectable);
+      if (client->priv->proxy_resolver &&
+          G_IS_PROXY_ADDRESS_ENUMERATOR (enumerator))
+        {
+          g_object_set (G_OBJECT (enumerator),
+                        "proxy-resolver", client->priv->proxy_resolver,
+                        NULL);
+        }
+    }
   else
     enumerator = g_socket_connectable_enumerate (connectable);
 
@@ -806,6 +996,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 +1006,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 +1027,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);
@@ -845,14 +1043,24 @@ 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_connection_set_cached_remote_address ((GSocketConnection*)connection, address);
+      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_connection_set_cached_remote_address ((GSocketConnection*)connection, NULL);
+         g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTED, connectable, connection);
+       }
       else
-       clarify_connect_error (last_error, connectable, address);
+       {
+         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;
@@ -871,15 +1079,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,
@@ -888,6 +1097,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))
@@ -916,8 +1128,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;
@@ -939,6 +1156,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);
 }
 
@@ -1013,7 +1231,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.
  *
@@ -1029,6 +1246,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,
@@ -1105,8 +1324,7 @@ g_socket_client_connect_to_uri (GSocketClient  *client,
 
 typedef struct
 {
-  GSimpleAsyncResult *result;
-  GCancellable *cancellable;
+  GTask *task;
   GSocketClient *client;
 
   GSocketConnectable *connectable;
@@ -1120,44 +1338,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);
 
-      g_simple_async_result_set_op_res_gpointer (data->result,
-                                                data->connection,
-                                                g_object_unref);
+  if (!G_IS_SOCKET_CONNECTION (data->connection))
+    {
+      GSocketConnection *wrapper_connection;
+
+      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_addr)
-    g_object_unref (data->current_addr);
-  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);
 }
 
 
@@ -1183,8 +1396,9 @@ 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_task_get_cancellable (data->task),
                                          g_socket_client_enumerator_callback,
                                          data);
 }
@@ -1203,6 +1417,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
@@ -1230,9 +1445,10 @@ 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);
     }
@@ -1253,7 +1469,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;
@@ -1263,21 +1483,49 @@ 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_task_return_error_if_cancelled (data->task))
+    {
+      g_object_unref (data->task);
+      return;
+    }
+
+  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_connection_set_cached_remote_address ((GSocketConnection*)data->connection, NULL);
+  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))
@@ -1287,16 +1535,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."));
+          _("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);
@@ -1321,54 +1570,6 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *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_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))
-       {
-         clarify_connect_error (error, data->connectable,
-                                data->current_addr);
-         set_last_error (data, error);
-
-         /* try next one */
-         enumerator_next_async (data);
-
-         return FALSE;
-       }
-    }
-
-  g_socket_client_socket_connected (data);
-  return FALSE;
-}
-
-static void
 g_socket_client_enumerator_callback (GObject      *object,
                                     GAsyncResult *result,
                                     gpointer      user_data)
@@ -1376,31 +1577,40 @@ 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))
+  if (g_task_return_error_if_cancelled (data->task))
     {
-      g_clear_error (&data->last_error);
-      g_cancellable_set_error_if_cancelled (data->cancellable, &data->last_error);
-      g_socket_client_async_connect_complete (data);
+      g_object_unref (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));
@@ -1408,44 +1618,23 @@ 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;
-         data->current_addr = address;
-         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);
-         return;
-       }
-      else
-       {
-         clarify_connect_error (tmp_error, data->connectable, address);
-         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_connection_set_cached_remote_address ((GSocketConnection*)data->connection, address);
+  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);
 }
 
 /**
@@ -1476,22 +1665,25 @@ 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))
+    {
       data->enumerator = g_socket_connectable_proxy_enumerate (connectable);
+      if (client->priv->proxy_resolver &&
+          G_IS_PROXY_ADDRESS_ENUMERATOR (data->enumerator))
+        {
+          g_object_set (G_OBJECT (data->enumerator),
+                        "proxy-resolver", client->priv->proxy_resolver,
+                        NULL);
+        }
+    }
   else
-      data->enumerator = g_socket_connectable_enumerate (connectable);
+    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);
 }
@@ -1529,8 +1721,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
     {
@@ -1604,8 +1797,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
     {
@@ -1635,12 +1829,9 @@ g_socket_client_connect_finish (GSocketClient  *client,
                                GAsyncResult   *result,
                                GError        **error)
 {
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
+  g_return_val_if_fail (g_task_is_valid (result, client), NULL);
 
-  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
 /**