[libsoup] SoupConnection: make methods gio-like



commit 665a6e44005b4a0d5e4911b81e89d9984695e40b
Author: Dan Winship <danw gnome org>
Date:   Wed Jul 3 17:28:23 2013 -0400

    SoupConnection: make methods gio-like
    
    Port the SoupConnection methods to be gio-like, using
    GAsyncReadyCallback for the async ones, and returning GErrors rather
    than libsoup status codes. Add internal-gio-like SoupSocket connect
    methods for the SoupConnection methods to use.

 libsoup/soup-connection.c   |  268 +++++++++++++++++++------------------------
 libsoup/soup-connection.h   |   36 +++---
 libsoup/soup-misc-private.h |   11 ++
 libsoup/soup-session.c      |  139 ++++++++++++++++++-----
 libsoup/soup-socket.c       |  171 +++++++++++++++++++---------
 5 files changed, 374 insertions(+), 251 deletions(-)
---
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index f8b6cef..2b5dc49 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -442,113 +442,89 @@ re_emit_socket_event (SoupSocket          *socket,
                soup_connection_event (conn, event, connection);
 }
 
-typedef struct {
-       SoupConnection *conn;
-       SoupConnectionCallback callback;
-       gpointer callback_data;
-       GCancellable *cancellable;
-       guint event_id;
-} SoupConnectionAsyncConnectData;
-
 static void
-socket_connect_finished (SoupConnectionAsyncConnectData *data, guint status)
+socket_connect_finished (GTask *task, SoupSocket *sock, GError *error)
 {
-       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+       SoupConnection *conn = g_task_get_source_object (task);
+       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
 
-       if (priv->socket)
-               g_signal_handler_disconnect (priv->socket, data->event_id);
+       if (priv->async_context && !priv->use_thread_context)
+               g_main_context_pop_thread_default (priv->async_context);
 
-       if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
+       g_signal_handlers_disconnect_by_func (sock, G_CALLBACK (re_emit_socket_event), conn);
+
+       if (!error) {
                if (priv->ssl && !priv->proxy_uri) {
-                       soup_connection_event (data->conn,
+                       soup_connection_event (conn,
                                               G_SOCKET_CLIENT_TLS_HANDSHAKED,
                                               NULL);
                }
                if (!priv->ssl || !priv->proxy_uri) {
-                       soup_connection_event (data->conn,
+                       soup_connection_event (conn,
                                               G_SOCKET_CLIENT_COMPLETE,
                                               NULL);
                }
 
-               soup_connection_set_state (data->conn, SOUP_CONNECTION_IN_USE);
+               soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
                priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
-               start_idle_timer (data->conn);
-       }
+               start_idle_timer (conn);
 
-       if (data->callback) {
-               if (priv->proxy_uri != NULL)
-                       status = soup_status_proxify (status);
-               data->callback (data->conn, status, data->callback_data);
-       }
-       g_object_unref (data->conn);
-       if (data->cancellable)
-               g_object_unref (data->cancellable);
-       g_slice_free (SoupConnectionAsyncConnectData, data);
+               g_task_return_boolean (task, TRUE);
+       } else
+               g_task_return_error (task, error);
+       g_object_unref (task);
 }
 
-
 static void
 socket_handshake_complete (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-       SoupConnectionAsyncConnectData *data = user_data;
-       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+       SoupSocket *sock = SOUP_SOCKET (object);
+       GTask *task = user_data;
        GError *error = NULL;
-       guint status;
-
-       if (priv->async_context && !priv->use_thread_context)
-               g_main_context_pop_thread_default (priv->async_context);
 
-       if (soup_socket_handshake_finish (priv->socket, result, &error))
-               status = SOUP_STATUS_OK;
-       else if (!priv->ssl_fallback &&
-                g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
-               priv->ssl_fallback = TRUE;
-               status = SOUP_STATUS_TRY_AGAIN;
-       } else
-               status = SOUP_STATUS_SSL_FAILED;
-       g_clear_error (&error);
-
-       socket_connect_finished (data, status);
+       soup_socket_handshake_finish (sock, result, &error);
+       socket_connect_finished (task, sock, error);
 }
 
 static void
-socket_connect_result (SoupSocket *sock, guint status, gpointer user_data)
+socket_connect_complete (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-       SoupConnectionAsyncConnectData *data = user_data;
-       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
+       SoupSocket *sock = SOUP_SOCKET (object);
+       GTask *task = user_data;
+       SoupConnection *conn = g_task_get_source_object (task);
+       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
+       GError *error = NULL;
 
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               socket_connect_finished (data, status);
+       if (!soup_socket_connect_finish_internal (sock, result, &error)) {
+               socket_connect_finished (task, sock, error);
                return;
        }
 
-       priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
+       priv->proxy_uri = soup_socket_get_http_proxy_uri (sock);
 
        if (priv->ssl && !priv->proxy_uri) {
-               soup_connection_event (data->conn,
+               soup_connection_event (conn,
                                       G_SOCKET_CLIENT_TLS_HANDSHAKING,
                                       NULL);
 
-               if (priv->async_context && !priv->use_thread_context)
-                       g_main_context_push_thread_default (priv->async_context);
                soup_socket_handshake_async (sock, priv->remote_uri->host,
-                                            data->cancellable,
-                                            socket_handshake_complete, data);
+                                            g_task_get_cancellable (task),
+                                            socket_handshake_complete, task);
                return;
        }
 
-       socket_connect_finished (data, status);
+       socket_connect_finished (task, sock, NULL);
 }
 
 void
-soup_connection_connect_async (SoupConnection *conn,
-                              GCancellable *cancellable,
-                              SoupConnectionCallback callback,
-                              gpointer user_data)
+soup_connection_connect_async (SoupConnection      *conn,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
 {
-       SoupConnectionAsyncConnectData *data;
        SoupConnectionPrivate *priv;
        SoupAddress *remote_addr;
+       GTask *task;
 
        g_return_if_fail (SOUP_IS_CONNECTION (conn));
        priv = SOUP_CONNECTION_GET_PRIVATE (conn);
@@ -556,12 +532,6 @@ soup_connection_connect_async (SoupConnection *conn,
 
        soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
 
-       data = g_slice_new (SoupConnectionAsyncConnectData);
-       data->conn = g_object_ref (conn);
-       data->callback = callback;
-       data->callback_data = user_data;
-       data->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
-
        remote_addr = soup_address_new (priv->remote_uri->host,
                                        priv->remote_uri->port);
        priv->socket =
@@ -578,24 +548,38 @@ soup_connection_connect_async (SoupConnection *conn,
                                 NULL);
        g_object_unref (remote_addr);
 
-       data->event_id = g_signal_connect (priv->socket, "event",
-                                          G_CALLBACK (re_emit_socket_event),
-                                          data->conn);
+       g_signal_connect (priv->socket, "event",
+                         G_CALLBACK (re_emit_socket_event), conn);
 
-       soup_socket_connect_async (priv->socket, data->cancellable,
-                                  socket_connect_result, data);
+       if (priv->async_context && !priv->use_thread_context)
+               g_main_context_push_thread_default (priv->async_context);
+       task = g_task_new (conn, cancellable, callback, user_data);
+
+       soup_socket_connect_async_internal (priv->socket, cancellable,
+                                           socket_connect_complete, task);
+}
+
+gboolean
+soup_connection_connect_finish (SoupConnection  *conn,
+                               GAsyncResult    *result,
+                               GError         **error)
+{
+       return g_task_propagate_boolean (G_TASK (result), error);
 }
 
-guint
-soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
+gboolean
+soup_connection_connect_sync (SoupConnection  *conn,
+                             GCancellable    *cancellable,
+                             GError         **error)
 {
        SoupConnectionPrivate *priv;
-       guint status, event_id = 0;
+       guint event_id = 0;
        SoupAddress *remote_addr;
+       gboolean success = TRUE;
 
-       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
        priv = SOUP_CONNECTION_GET_PRIVATE (conn);
-       g_return_val_if_fail (priv->socket == NULL, SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (priv->socket == NULL, FALSE);
 
        soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING);
 
@@ -615,53 +599,42 @@ soup_connection_connect_sync (SoupConnection *conn, GCancellable *cancellable)
 
        event_id = g_signal_connect (priv->socket, "event",
                                     G_CALLBACK (re_emit_socket_event), conn);
-       status = soup_socket_connect_sync (priv->socket, cancellable);
-
-       if (!SOUP_STATUS_IS_SUCCESSFUL (status))
-               goto fail;
+       if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error)) {
+               success = FALSE;
+               goto done;
+       }
 
        priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket);
 
        if (priv->ssl && !priv->proxy_uri) {
-               GError *error = NULL;
-
                soup_connection_event (conn,
                                       G_SOCKET_CLIENT_TLS_HANDSHAKING,
                                       NULL);
-               if (soup_socket_handshake_sync (priv->socket,
-                                               priv->remote_uri->host,
-                                               cancellable, &error)) {
-                       soup_connection_event (conn,
-                                              G_SOCKET_CLIENT_TLS_HANDSHAKED,
-                                              NULL);
-                       status = SOUP_STATUS_OK;
-               } else if (!priv->ssl_fallback &&
-                          g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
-                       priv->ssl_fallback = TRUE;
-                       status = SOUP_STATUS_TRY_AGAIN;
-               } else
-                       status = SOUP_STATUS_SSL_FAILED;
-               g_clear_error (&error);
+               if (!soup_socket_handshake_sync (priv->socket,
+                                                priv->remote_uri->host,
+                                                cancellable, error)) {
+                       success = FALSE;
+                       goto done;
+               }
+               soup_connection_event (conn,
+                                      G_SOCKET_CLIENT_TLS_HANDSHAKED,
+                                      NULL);
        }
 
-       if (SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               if (!priv->ssl || !priv->proxy_uri) {
-                       soup_connection_event (conn,
-                                              G_SOCKET_CLIENT_COMPLETE,
-                                              NULL);
-               }
-               soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
-               priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
-               start_idle_timer (conn);
+       if (!priv->ssl || !priv->proxy_uri) {
+               soup_connection_event (conn,
+                                      G_SOCKET_CLIENT_COMPLETE,
+                                      NULL);
        }
+       soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE);
+       priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT;
+       start_idle_timer (conn);
 
- fail:
+ done:
        if (priv->socket && event_id)
                g_signal_handler_disconnect (priv->socket, event_id);
 
-       if (priv->proxy_uri != NULL)
-               status = soup_status_proxify (status);
-       return status;
+       return success;
 }
 
 gboolean
@@ -675,83 +648,74 @@ soup_connection_is_tunnelled (SoupConnection *conn)
        return priv->ssl && priv->proxy_uri != NULL;
 }
 
-guint
-soup_connection_start_ssl_sync (SoupConnection *conn,
-                               GCancellable   *cancellable)
+gboolean
+soup_connection_start_ssl_sync (SoupConnection  *conn,
+                               GCancellable    *cancellable,
+                               GError         **error)
 {
        SoupConnectionPrivate *priv;
-       guint status;
-       GError *error = NULL;
 
        g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE);
        priv = SOUP_CONNECTION_GET_PRIVATE (conn);
 
        soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
        if (soup_socket_handshake_sync (priv->socket, priv->remote_uri->host,
-                                       cancellable, &error)) {
+                                       cancellable, error)) {
                soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
                soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
-               status = SOUP_STATUS_OK;
-       } else if (!priv->ssl_fallback &&
-                  g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
-               priv->ssl_fallback = TRUE;
-               status = SOUP_STATUS_TRY_AGAIN;
-       }
-
-       return status;
+               return TRUE;
+       } else
+               return FALSE;
 }
 
 static void
 start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data)
 {
-       SoupConnectionAsyncConnectData *data = user_data;
-       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn);
-       guint status;
+       GTask *task = user_data;
+       SoupConnection *conn = g_task_get_source_object (task);
+       SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
        GError *error = NULL;
 
        if (priv->async_context && !priv->use_thread_context)
                g_main_context_pop_thread_default (priv->async_context);
 
        if (soup_socket_handshake_finish (priv->socket, result, &error)) {
-               soup_connection_event (data->conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
-               soup_connection_event (data->conn, G_SOCKET_CLIENT_COMPLETE, NULL);
-               status = SOUP_STATUS_OK;
-       } else if (!priv->ssl_fallback &&
-                g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
-               priv->ssl_fallback = TRUE;
-               status = SOUP_STATUS_TRY_AGAIN;
+               soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
+               soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL);
+               g_task_return_boolean (task, TRUE);
        } else
-               status = SOUP_STATUS_SSL_FAILED;
-       g_clear_error (&error);
-
-       data->callback (data->conn, status, data->callback_data);
-       g_object_unref (data->conn);
-       g_slice_free (SoupConnectionAsyncConnectData, data);
+               g_task_return_error (task, error);
+       g_object_unref (task);
 }
 
 void
-soup_connection_start_ssl_async (SoupConnection   *conn,
-                                GCancellable     *cancellable,
-                                SoupConnectionCallback callback,
-                                gpointer          user_data)
+soup_connection_start_ssl_async (SoupConnection      *conn,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
 {
        SoupConnectionPrivate *priv;
-       SoupConnectionAsyncConnectData *data;
+       GTask *task;
 
        g_return_if_fail (SOUP_IS_CONNECTION (conn));
        priv = SOUP_CONNECTION_GET_PRIVATE (conn);
 
-       data = g_slice_new (SoupConnectionAsyncConnectData);
-       data->conn = g_object_ref (conn);
-       data->callback = callback;
-       data->callback_data = user_data;
-
        soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
 
        if (priv->async_context && !priv->use_thread_context)
                g_main_context_push_thread_default (priv->async_context);
+       task = g_task_new (conn, cancellable, callback, user_data);
+
        soup_socket_handshake_async (priv->socket, priv->remote_uri->host,
-                                    cancellable, start_ssl_completed, data);
+                                    cancellable, start_ssl_completed, task);
+}
+
+gboolean
+soup_connection_start_ssl_finish (SoupConnection  *conn,
+                                 GAsyncResult    *result,
+                                 GError         **error)
+{
+       return g_task_propagate_boolean (G_TASK (result), error);
 }
 
 /**
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 50fb701..b70a8a3 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -35,10 +35,6 @@ typedef struct {
 GType soup_connection_get_type (void);
 
 
-typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
-                                               guint             status,
-                                               gpointer          data);
-
 #define SOUP_CONNECTION_LOCAL_ADDRESS   "local-address"
 #define SOUP_CONNECTION_REMOTE_URI      "remote-uri"
 #define SOUP_CONNECTION_PROXY_RESOLVER  "proxy-resolver"
@@ -53,18 +49,26 @@ typedef void  (*SoupConnectionCallback)        (SoupConnection   *conn,
 #define SOUP_CONNECTION_STATE           "state"
 #define SOUP_CONNECTION_MESSAGE         "message"
 
-void            soup_connection_connect_async  (SoupConnection   *conn,
-                                               GCancellable     *cancellable,
-                                               SoupConnectionCallback callback,
-                                               gpointer          user_data);
-guint           soup_connection_connect_sync   (SoupConnection   *conn,
-                                               GCancellable     *cancellable);
-guint           soup_connection_start_ssl_sync   (SoupConnection   *conn,
-                                                 GCancellable     *cancellable);
-void            soup_connection_start_ssl_async  (SoupConnection   *conn,
-                                                 GCancellable     *cancellable,
-                                                 SoupConnectionCallback callback,
-                                                 gpointer          user_data);
+void            soup_connection_connect_async    (SoupConnection       *conn,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              user_data);
+gboolean        soup_connection_connect_finish   (SoupConnection       *conn,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
+gboolean        soup_connection_connect_sync     (SoupConnection       *conn,
+                                                 GCancellable         *cancellable,
+                                                 GError              **error);
+gboolean        soup_connection_start_ssl_sync   (SoupConnection       *conn,
+                                                 GCancellable         *cancellable,
+                                                 GError              **error);
+void            soup_connection_start_ssl_async  (SoupConnection       *conn,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              user_data);
+gboolean        soup_connection_start_ssl_finish (SoupConnection       *conn,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
 
 void            soup_connection_disconnect     (SoupConnection   *conn);
 
diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h
index 948470f..50a3b98 100644
--- a/libsoup/soup-misc-private.h
+++ b/libsoup/soup-misc-private.h
@@ -16,6 +16,17 @@ char *soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
 gboolean soup_uri_is_http (SoupURI *uri, char **aliases);
 gboolean soup_uri_is_https (SoupURI *uri, char **aliases);
 
+gboolean soup_socket_connect_sync_internal   (SoupSocket          *sock,
+                                             GCancellable        *cancellable,
+                                             GError             **error);
+void     soup_socket_connect_async_internal  (SoupSocket          *sock,
+                                             GCancellable        *cancellable,
+                                             GAsyncReadyCallback  callback,
+                                             gpointer             user_data);
+gboolean soup_socket_connect_finish_internal (SoupSocket          *sock,
+                                             GAsyncResult        *result,
+                                             GError             **error);
+
 gboolean soup_socket_handshake_sync   (SoupSocket           *sock,
                                       const char           *host,
                                       GCancellable         *cancellable,
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index bd586cd..0fa65cd 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1508,10 +1508,59 @@ message_completed (SoupMessage *msg, gpointer user_data)
        }
 }
 
+static guint
+status_from_connect_error (SoupMessageQueueItem *item, GError *error)
+{
+       guint status;
+
+       if (!error)
+               return SOUP_STATUS_OK;
+
+       if (g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
+               SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (item->session);
+               SoupSessionHost *host;
+
+               g_mutex_lock (&priv->conn_lock);
+               host = get_host_for_message (item->session, item->msg);
+               if (!host->ssl_fallback) {
+                       host->ssl_fallback = TRUE;
+                       status = SOUP_STATUS_TRY_AGAIN;
+               } else
+                       status = SOUP_STATUS_SSL_FAILED;
+               g_mutex_unlock (&priv->conn_lock);
+       } else if (error->domain == G_TLS_ERROR)
+               status = SOUP_STATUS_SSL_FAILED;
+       else if (error->domain == G_RESOLVER_ERROR)
+               status = SOUP_STATUS_CANT_RESOLVE;
+       else if (error->domain == G_IO_ERROR) {
+               if (error->code == G_IO_ERROR_CANCELLED)
+                       status = SOUP_STATUS_CANCELLED;
+               else if (error->code == G_IO_ERROR_HOST_UNREACHABLE ||
+                        error->code == G_IO_ERROR_NETWORK_UNREACHABLE ||
+                        error->code == G_IO_ERROR_CONNECTION_REFUSED)
+                       status = SOUP_STATUS_CANT_CONNECT;
+               else if (error->code == G_IO_ERROR_PROXY_FAILED ||
+                        error->code == G_IO_ERROR_PROXY_AUTH_FAILED ||
+                        error->code == G_IO_ERROR_PROXY_NEED_AUTH ||
+                        error->code == G_IO_ERROR_PROXY_NOT_ALLOWED)
+                       status = SOUP_STATUS_CANT_CONNECT_PROXY;
+               else
+                       status = SOUP_STATUS_IO_ERROR;
+       } else
+               status = SOUP_STATUS_IO_ERROR;
+
+       g_error_free (error);
+
+       if (item->conn && soup_connection_is_via_proxy (item->conn))
+               return soup_status_proxify (status);
+       else
+               return status;
+}
+
 static void
-tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
+tunnel_complete (SoupMessageQueueItem *tunnel_item,
+                guint status, GError *error)
 {
-       SoupMessageQueueItem *tunnel_item = user_data;
        SoupMessageQueueItem *item = tunnel_item->related;
        SoupSession *session = tunnel_item->session;
 
@@ -1522,8 +1571,10 @@ tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
                item->state = SOUP_MESSAGE_FINISHING;
        soup_message_set_https_status (item->msg, item->conn);
 
+       if (!status)
+               status = status_from_connect_error (item, error);
        if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               soup_connection_disconnect (conn);
+               soup_connection_disconnect (item->conn);
                soup_session_set_item_connection (session, item, NULL);
                soup_session_set_item_status (session, item, status);
        }
@@ -1535,6 +1586,19 @@ tunnel_complete (SoupConnection *conn, guint status, gpointer user_data)
 }
 
 static void
+tunnel_handshake_complete (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupMessageQueueItem *tunnel_item = user_data;
+       GError *error = NULL;
+
+       soup_connection_start_ssl_finish (conn, result, &error);
+       tunnel_complete (tunnel_item, 0, error);
+}
+
+static void
 tunnel_message_completed (SoupMessage *msg, gpointer user_data)
 {
        SoupMessageQueueItem *tunnel_item = user_data;
@@ -1559,16 +1623,19 @@ tunnel_message_completed (SoupMessage *msg, gpointer user_data)
 
        status = tunnel_item->msg->status_code;
        if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
-               tunnel_complete (item->conn, status, tunnel_item);
+               tunnel_complete (tunnel_item, status, NULL);
                return;
        }
 
        if (tunnel_item->async) {
                soup_connection_start_ssl_async (item->conn, item->cancellable,
-                                                tunnel_complete, tunnel_item);
+                                                tunnel_handshake_complete,
+                                                tunnel_item);
        } else {
-               status = soup_connection_start_ssl_sync (item->conn, item->cancellable);
-               tunnel_complete (item->conn, status, tunnel_item);
+               GError *error = NULL;
+
+               soup_connection_start_ssl_sync (item->conn, item->cancellable, &error);
+               tunnel_complete (tunnel_item, 0, error);
        }
 }
 
@@ -1602,34 +1669,48 @@ tunnel_connect (SoupMessageQueueItem *item)
 }
 
 static void
-got_connection (SoupConnection *conn, guint status, gpointer user_data)
+connect_complete (SoupMessageQueueItem *item, SoupConnection *conn, GError *error)
 {
-       SoupMessageQueueItem *item = user_data;
        SoupSession *session = item->session;
+       guint status;
 
        soup_message_set_https_status (item->msg, item->conn);
 
-       if (status != SOUP_STATUS_OK) {
-               soup_connection_disconnect (conn);
-               if (item->state == SOUP_MESSAGE_CONNECTING) {
-                       soup_session_set_item_status (session, item, status);
-                       soup_session_set_item_connection (session, item, NULL);
-                       item->state = SOUP_MESSAGE_READY;
-               }
-       } else
+       if (!error) {
                item->state = SOUP_MESSAGE_CONNECTED;
+               return;
+       }
 
-       if (item->async) {
-               if (item->state == SOUP_MESSAGE_CONNECTED ||
-                   item->state == SOUP_MESSAGE_READY)
-                       async_run_queue (item->session);
-               else
-                       soup_session_kick_queue (item->session);
-
-               soup_message_queue_item_unref (item);
+       status = status_from_connect_error (item, error);
+       soup_connection_disconnect (conn);
+       if (item->state == SOUP_MESSAGE_CONNECTING) {
+               soup_session_set_item_status (session, item, status);
+               soup_session_set_item_connection (session, item, NULL);
+               item->state = SOUP_MESSAGE_READY;
        }
 }
 
+static void
+connect_async_complete (GObject      *object,
+                       GAsyncResult *result,
+                       gpointer      user_data)
+{
+       SoupConnection *conn = SOUP_CONNECTION (object);
+       SoupMessageQueueItem *item = user_data;
+       GError *error = NULL;
+
+       soup_connection_connect_finish (conn, result, &error);
+       connect_complete (item, conn, error);
+
+       if (item->state == SOUP_MESSAGE_CONNECTED ||
+           item->state == SOUP_MESSAGE_READY)
+               async_run_queue (item->session);
+       else
+               soup_session_kick_queue (item->session);
+
+       soup_message_queue_item_unref (item);
+}
+
 /* requires conn_lock */
 static SoupConnection *
 get_connection_for_host (SoupSession *session,
@@ -1783,13 +1864,13 @@ get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup)
        if (item->async) {
                soup_message_queue_item_ref (item);
                soup_connection_connect_async (item->conn, item->cancellable,
-                                              got_connection, item);
+                                              connect_async_complete, item);
                return FALSE;
        } else {
-               guint status;
+               GError *error = NULL;
 
-               status = soup_connection_connect_sync (item->conn, item->cancellable);
-               got_connection (item->conn, status, item);
+               soup_connection_connect_sync (item->conn, item->cancellable, &error);
+               connect_complete (item, conn, error);
 
                return TRUE;
        }
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 9caf0d2..d4046ec 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -666,38 +666,36 @@ re_emit_socket_client_event (GSocketClient       *client,
                       event, connection);
 }
 
-static guint
-socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
+static gboolean
+socket_connect_finish (SoupSocket *sock, GSocketConnection *conn)
 {
        SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
 
-       if (priv->connect_cancel) {
-               GCancellable *cancellable = priv->connect_cancel;
-               gboolean cancelled = g_cancellable_is_cancelled (cancellable);
+       g_clear_object (&priv->connect_cancel);
 
-               g_object_unref (priv->connect_cancel);
-               priv->connect_cancel = NULL;
-               if (cancelled) {
-                       g_clear_error (&error);
-                       return SOUP_STATUS_CANCELLED;
-               }
-       }
+       if (conn) {
+               priv->conn = (GIOStream *)conn;
+               priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
+               finish_socket_setup (priv);
+               return TRUE;
+       } else
+               return FALSE;
+}
 
-       if (error) {
-               if (error->domain == G_RESOLVER_ERROR) {
-                       g_error_free (error);
-                       return SOUP_STATUS_CANT_RESOLVE;
-               } else {
-                       g_error_free (error);
-                       return SOUP_STATUS_CANT_CONNECT;
-               }
-       }
+static guint
+socket_legacy_error (SoupSocket *sock, GError *error)
+{
+       guint status;
 
-       priv->conn = (GIOStream *)conn;
-       priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
-       finish_socket_setup (priv);
+       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+               status = SOUP_STATUS_CANCELLED;
+       else if (error->domain == G_RESOLVER_ERROR)
+               status = SOUP_STATUS_CANT_RESOLVE;
+       else
+               status = SOUP_STATUS_CANT_CONNECT;
 
-       return SOUP_STATUS_OK;
+       g_error_free (error);
+       return status;
 }
 
 static GSocketClient *
@@ -722,6 +720,57 @@ new_socket_client (SoupSocket *sock)
        return client;
 }
 
+static void
+async_connected (GObject *client, GAsyncResult *result, gpointer data)
+{
+       GTask *task = data;
+       SoupSocket *sock = g_task_get_source_object (task);
+       GSocketConnection *conn;
+       GError *error = NULL;
+
+       conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
+                                              result, &error);
+       if (socket_connect_finish (sock, conn))
+               g_task_return_boolean (task, TRUE);
+       else
+               g_task_return_error (task, error);
+}
+
+gboolean
+soup_socket_connect_finish_internal (SoupSocket    *sock,
+                                    GAsyncResult  *result,
+                                    GError       **error)
+{
+       return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+soup_socket_connect_async_internal (SoupSocket          *sock,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
+{
+       SoupSocketPrivate *priv;
+       GSocketClient *client;
+       GTask *task;
+
+       g_return_if_fail (SOUP_IS_SOCKET (sock));
+       priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       g_return_if_fail (!priv->is_server);
+       g_return_if_fail (priv->gsock == NULL);
+       g_return_if_fail (priv->remote_addr != NULL);
+
+       priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+       task = g_task_new (sock, priv->connect_cancel, callback, user_data);
+
+       client = new_socket_client (sock);
+       g_socket_client_connect_async (client,
+                                      G_SOCKET_CONNECTABLE (priv->remote_addr),
+                                      priv->connect_cancel,
+                                      async_connected, task);
+       g_object_unref (client);
+}
+
 /**
  * SoupSocketCallback:
  * @sock: the #SoupSocket
@@ -738,22 +787,25 @@ typedef struct {
 } SoupSocketAsyncConnectData;
 
 static void
-async_connected (GObject *client, GAsyncResult *result, gpointer data)
+legacy_connect_async_cb (GObject       *object,
+                        GAsyncResult  *result,
+                        gpointer       user_data)
 {
-       SoupSocketAsyncConnectData *sacd = data;
-       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+       SoupSocket *sock = SOUP_SOCKET (object);
+       SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       SoupSocketAsyncConnectData *sacd = user_data;
        GError *error = NULL;
-       GSocketConnection *conn;
        guint status;
 
        if (priv->async_context && !priv->use_thread_context)
                g_main_context_pop_thread_default (priv->async_context);
 
-       conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
-                                              result, &error);
-       status = socket_connected (sacd->sock, conn, error);
+       if (soup_socket_connect_finish_internal (sock, result, &error))
+               status = SOUP_STATUS_OK;
+       else
+               status = socket_legacy_error (sock, error);
 
-       sacd->callback (sacd->sock, status, sacd->user_data);
+       sacd->callback (sock, status, sacd->user_data);
        g_object_unref (sacd->sock);
        g_slice_free (SoupSocketAsyncConnectData, sacd);
 }
@@ -779,10 +831,11 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
 {
        SoupSocketPrivate *priv;
        SoupSocketAsyncConnectData *sacd;
-       GSocketClient *client;
 
        g_return_if_fail (SOUP_IS_SOCKET (sock));
        priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       g_return_if_fail (!priv->is_server);
+       g_return_if_fail (priv->gsock == NULL);
        g_return_if_fail (priv->remote_addr != NULL);
 
        sacd = g_slice_new0 (SoupSocketAsyncConnectData);
@@ -790,17 +843,38 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
        sacd->callback = callback;
        sacd->user_data = user_data;
 
-       priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
-
        if (priv->async_context && !priv->use_thread_context)
                g_main_context_push_thread_default (priv->async_context);
 
+       soup_socket_connect_async_internal (sock, cancellable,
+                                           legacy_connect_async_cb,
+                                           sacd);
+}
+
+gboolean
+soup_socket_connect_sync_internal (SoupSocket    *sock,
+                                  GCancellable  *cancellable,
+                                  GError       **error)
+{
+       SoupSocketPrivate *priv;
+       GSocketClient *client;
+       GSocketConnection *conn;
+
+       g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
+       priv = SOUP_SOCKET_GET_PRIVATE (sock);
+       g_return_val_if_fail (!priv->is_server, SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
+       g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
+
+       priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+
        client = new_socket_client (sock);
-       g_socket_client_connect_async (client,
-                                      G_SOCKET_CONNECTABLE (priv->remote_addr),
-                                      priv->connect_cancel,
-                                      async_connected, sacd);
+       conn = g_socket_client_connect (client,
+                                       G_SOCKET_CONNECTABLE (priv->remote_addr),
+                                       priv->connect_cancel, error);
        g_object_unref (client);
+
+       return socket_connect_finish (sock, conn);
 }
 
 /**
@@ -820,8 +894,6 @@ guint
 soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
 {
        SoupSocketPrivate *priv;
-       GSocketClient *client;
-       GSocketConnection *conn;
        GError *error = NULL;
 
        g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
@@ -830,19 +902,10 @@ soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
        g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
        g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
 
-       if (cancellable)
-               g_object_ref (cancellable);
+       if (soup_socket_connect_sync_internal (sock, cancellable, &error))
+               return SOUP_STATUS_OK;
        else
-               cancellable = g_cancellable_new ();
-       priv->connect_cancel = cancellable;
-
-       client = new_socket_client (sock);
-       conn = g_socket_client_connect (client,
-                                       G_SOCKET_CONNECTABLE (priv->remote_addr),
-                                       priv->connect_cancel, &error);
-       g_object_unref (client);
-
-       return socket_connected (sock, conn, error);
+               return socket_legacy_error (sock, error);
 }
 
 /**


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