diff options
author | Dan Winship <danw@gnome.org> | 2013-07-03 17:28:23 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2013-10-19 20:39:08 -0400 |
commit | 665a6e44005b4a0d5e4911b81e89d9984695e40b (patch) | |
tree | 320b6916f4a41841d4d1336fedd44f0d48163628 | |
parent | 88103312ffac176b8ffeb159bd75fc3bbe2c452e (diff) | |
download | libsoup-665a6e44005b4a0d5e4911b81e89d9984695e40b.tar.gz |
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.
-rw-r--r-- | libsoup/soup-connection.c | 268 | ||||
-rw-r--r-- | libsoup/soup-connection.h | 36 | ||||
-rw-r--r-- | libsoup/soup-misc-private.h | 11 | ||||
-rw-r--r-- | libsoup/soup-session.c | 139 | ||||
-rw-r--r-- | 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 f8b6cef7..2b5dc49e 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 50fb7018..b70a8a36 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 948470ff..50a3b988 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 bd586cd3..0fa65cda 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 9caf0d2c..d4046ec2 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); } /** |