diff options
Diffstat (limited to 'libsoup/soup-connection.c')
-rw-r--r-- | libsoup/soup-connection.c | 633 |
1 files changed, 262 insertions, 371 deletions
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index 48c3d00c..fce589fa 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -11,23 +11,22 @@ #include "soup-connection.h" #include "soup.h" -#include "soup-marshal.h" #include "soup-message-queue.h" #include "soup-misc-private.h" typedef struct { SoupSocket *socket; + SoupAddress *local_addr; SoupURI *remote_uri, *proxy_uri; - SoupProxyURIResolver *proxy_resolver; - gboolean use_gproxyresolver; + GProxyResolver *proxy_resolver; GTlsDatabase *tlsdb; gboolean ssl, ssl_strict, ssl_fallback; GMainContext *async_context; gboolean use_thread_context; - SoupMessageQueueItem *cur_item; + SoupMessage *current_msg; SoupConnectionState state; time_t unused_timeout; guint io_timeout, idle_timeout; @@ -49,6 +48,7 @@ static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_0, + PROP_LOCAL_ADDRESS, PROP_REMOTE_URI, PROP_PROXY_RESOLVER, PROP_SSL, @@ -60,13 +60,11 @@ enum { PROP_TIMEOUT, PROP_IDLE_TIMEOUT, PROP_STATE, - PROP_MESSAGE, LAST_PROP }; static void stop_idle_timer (SoupConnectionPrivate *priv); -static void clear_current_item (SoupConnection *conn); /* Number of seconds after which we close a connection that hasn't yet * been used. @@ -87,6 +85,7 @@ soup_connection_finalize (GObject *object) g_clear_pointer (&priv->proxy_uri, soup_uri_free); g_clear_object (&priv->tlsdb); g_clear_object (&priv->proxy_resolver); + g_clear_object (&priv->local_addr); g_clear_pointer (&priv->async_context, g_main_context_unref); G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object); @@ -99,13 +98,7 @@ soup_connection_dispose (GObject *object) SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); stop_idle_timer (priv); - /* Make sure clear_current_item doesn't re-establish the timeout */ - priv->idle_timeout = 0; - if (priv->cur_item) { - g_warning ("Disposing connection with cur_item set"); - clear_current_item (conn); - } if (priv->socket) { g_warning ("Disposing connection while connected"); soup_connection_disconnect (conn); @@ -119,18 +112,16 @@ soup_connection_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object); - SoupProxyURIResolver *proxy_resolver; switch (prop_id) { + case PROP_LOCAL_ADDRESS: + priv->local_addr = g_value_dup_object (value); + break; case PROP_REMOTE_URI: priv->remote_uri = g_value_dup_boxed (value); break; case PROP_PROXY_RESOLVER: - proxy_resolver = g_value_get_object (value); - if (proxy_resolver && SOUP_IS_PROXY_RESOLVER_DEFAULT (proxy_resolver)) - priv->use_gproxyresolver = TRUE; - else if (proxy_resolver) - priv->proxy_resolver = g_object_ref (proxy_resolver); + priv->proxy_resolver = g_value_dup_object (value); break; case PROP_SSL: priv->ssl = g_value_get_boolean (value); @@ -176,6 +167,9 @@ soup_connection_get_property (GObject *object, guint prop_id, SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object); switch (prop_id) { + case PROP_LOCAL_ADDRESS: + g_value_set_object (value, priv->local_addr); + break; case PROP_REMOTE_URI: g_value_set_boxed (value, priv->remote_uri); break; @@ -206,12 +200,6 @@ soup_connection_get_property (GObject *object, guint prop_id, case PROP_STATE: g_value_set_enum (value, priv->state); break; - case PROP_MESSAGE: - if (priv->cur_item) - g_value_set_object (value, priv->cur_item->msg); - else - g_value_set_object (value, NULL); - break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -248,11 +236,18 @@ soup_connection_class_init (SoupConnectionClass *connection_class) G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (SoupConnectionClass, disconnected), NULL, NULL, - _soup_marshal_NONE__NONE, + NULL, G_TYPE_NONE, 0); /* properties */ g_object_class_install_property ( + object_class, PROP_LOCAL_ADDRESS, + g_param_spec_object (SOUP_CONNECTION_LOCAL_ADDRESS, + "Local address", + "Address of local end of socket", + SOUP_TYPE_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_property ( object_class, PROP_REMOTE_URI, g_param_spec_boxed (SOUP_CONNECTION_REMOTE_URI, "Remote URI", @@ -263,8 +258,8 @@ soup_connection_class_init (SoupConnectionClass *connection_class) object_class, PROP_PROXY_RESOLVER, g_param_spec_object (SOUP_CONNECTION_PROXY_RESOLVER, "Proxy resolver", - "SoupProxyURIResolver to use", - SOUP_TYPE_PROXY_URI_RESOLVER, + "GProxyResolver to use", + G_TYPE_PROXY_RESOLVER, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property ( object_class, PROP_SSL, @@ -328,13 +323,20 @@ soup_connection_class_init (SoupConnectionClass *connection_class) "Current state of connection", SOUP_TYPE_CONNECTION_STATE, SOUP_CONNECTION_NEW, G_PARAM_READWRITE)); - g_object_class_install_property ( - object_class, PROP_MESSAGE, - g_param_spec_object (SOUP_CONNECTION_MESSAGE, - "Message", - "Message being processed", - SOUP_TYPE_MESSAGE, - G_PARAM_READABLE)); +} + +static void +soup_connection_event (SoupConnection *conn, + GSocketClientEvent event, + GIOStream *connection) +{ + SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); + + if (!connection && priv->socket) + connection = soup_socket_get_connection (priv->socket); + + g_signal_emit (conn, signals[EVENT], 0, + event, connection); } static gboolean @@ -367,99 +369,71 @@ stop_idle_timer (SoupConnectionPrivate *priv) } static void -current_item_restarted (SoupMessage *msg, gpointer user_data) +current_msg_got_body (SoupMessage *msg, gpointer user_data) { SoupConnection *conn = user_data; SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); priv->unused_timeout = 0; + + if (priv->proxy_uri && + msg->method == SOUP_METHOD_CONNECT && + SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { + soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATED, NULL); + + /* We're now effectively no longer proxying */ + g_clear_pointer (&priv->proxy_uri, soup_uri_free); + } + + priv->reusable = soup_message_is_keepalive (msg); } static void -set_current_item (SoupConnection *conn, SoupMessageQueueItem *item) +clear_current_msg (SoupConnection *conn) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); + SoupMessage *msg; - g_return_if_fail (priv->cur_item == NULL); - - g_object_freeze_notify (G_OBJECT (conn)); - - stop_idle_timer (priv); - - item->state = SOUP_MESSAGE_RUNNING; - priv->cur_item = item; - g_object_notify (G_OBJECT (conn), "message"); - priv->reusable = FALSE; - - g_signal_connect (item->msg, "restarted", - G_CALLBACK (current_item_restarted), conn); - - if (item->msg->method == SOUP_METHOD_CONNECT) { - g_signal_emit (conn, signals[EVENT], 0, - G_SOCKET_CLIENT_PROXY_NEGOTIATING, - soup_socket_get_connection (priv->socket)); - } else if (priv->state == SOUP_CONNECTION_IDLE) - soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); + msg = priv->current_msg; + priv->current_msg = NULL; - g_object_thaw_notify (G_OBJECT (conn)); + g_signal_handlers_disconnect_by_func (msg, G_CALLBACK (current_msg_got_body), conn); + g_object_unref (msg); } static void -clear_current_item (SoupConnection *conn) +set_current_msg (SoupConnection *conn, SoupMessage *msg) { SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); - g_object_freeze_notify (G_OBJECT (conn)); + g_return_if_fail (priv->state == SOUP_CONNECTION_IN_USE); - priv->unused_timeout = 0; - start_idle_timer (conn); + g_object_freeze_notify (G_OBJECT (conn)); - if (priv->cur_item) { - SoupMessageQueueItem *item; + if (priv->current_msg) { + g_return_if_fail (priv->current_msg->method == SOUP_METHOD_CONNECT); + clear_current_msg (conn); + } - item = priv->cur_item; - priv->cur_item = NULL; - g_object_notify (G_OBJECT (conn), "message"); + stop_idle_timer (priv); - g_signal_handlers_disconnect_by_func (item->msg, G_CALLBACK (current_item_restarted), conn); + priv->current_msg = g_object_ref (msg); + priv->reusable = FALSE; - if (item->msg->method == SOUP_METHOD_CONNECT && - SOUP_STATUS_IS_SUCCESSFUL (item->msg->status_code)) { - g_signal_emit (conn, signals[EVENT], 0, - G_SOCKET_CLIENT_PROXY_NEGOTIATED, - soup_socket_get_connection (priv->socket)); + g_signal_connect (msg, "got-body", + G_CALLBACK (current_msg_got_body), conn); - /* We're now effectively no longer proxying */ - soup_uri_free (priv->proxy_uri); - priv->proxy_uri = NULL; - } - - if (!soup_message_is_keepalive (item->msg) || !priv->reusable) - soup_connection_disconnect (conn); - } + if (priv->proxy_uri && msg->method == SOUP_METHOD_CONNECT) + soup_connection_event (conn, G_SOCKET_CLIENT_PROXY_NEGOTIATING, NULL); g_object_thaw_notify (G_OBJECT (conn)); } static void -soup_connection_event (SoupConnection *conn, - GSocketClientEvent event, - GIOStream *connection) -{ - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); - - if (!connection && priv->socket) - connection = soup_socket_get_connection (priv->socket); - - g_signal_emit (conn, signals[EVENT], 0, - event, connection); -} - -static void -proxy_socket_event (SoupSocket *socket, - GSocketClientEvent event, - GIOStream *connection, - gpointer user_data) +re_emit_socket_event (SoupSocket *socket, + GSocketClientEvent event, + GIOStream *connection, + gpointer user_data) { SoupConnection *conn = user_data; @@ -469,98 +443,103 @@ proxy_socket_event (SoupSocket *socket, } static void -socket_disconnected (SoupSocket *sock, gpointer conn) -{ - soup_connection_disconnect (conn); -} - -typedef struct { - SoupConnection *conn; - SoupConnectionCallback callback; - gpointer callback_data; - GCancellable *cancellable; - guint event_id; -} SoupConnectionAsyncConnectData; - -static void -socket_connect_finished (SoupSocket *socket, guint status, gpointer user_data) +socket_connect_finished (GTask *task, SoupSocket *sock, GError *error) { - SoupConnectionAsyncConnectData *data = user_data; - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); + SoupConnection *conn = g_task_get_source_object (task); + SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); - g_signal_handler_disconnect (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_connect (priv->socket, "disconnected", - G_CALLBACK (socket_disconnected), data->conn); + 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); - } else if (status == SOUP_STATUS_TLS_FAILED) { - priv->ssl_fallback = TRUE; - status = SOUP_STATUS_TRY_AGAIN; - } + 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) +{ + SoupSocket *sock = SOUP_SOCKET (object); + GTask *task = user_data; + GError *error = NULL; + + 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 (sock, status, data); + if (!soup_socket_connect_finish_internal (sock, result, &error)) { + socket_connect_finished (task, sock, error); return; } - if (priv->use_gproxyresolver) - 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) { - if (soup_socket_start_ssl (sock, data->cancellable)) { - soup_connection_event (data->conn, - G_SOCKET_CLIENT_TLS_HANDSHAKING, - NULL); - soup_socket_handshake_async (sock, data->cancellable, - socket_connect_finished, data); - return; - } + soup_connection_event (conn, + G_SOCKET_CLIENT_TLS_HANDSHAKING, + NULL); - status = SOUP_STATUS_SSL_FAILED; + soup_socket_handshake_async (sock, priv->remote_uri->host, + g_task_get_cancellable (task), + socket_handshake_complete, task); + return; } - socket_connect_finished (sock, status, data); + socket_connect_finished (task, sock, NULL); } -static void -connect_async_to_uri (SoupConnectionAsyncConnectData *data, SoupURI *uri) +void +soup_connection_connect_async (SoupConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); + SoupConnectionPrivate *priv; SoupAddress *remote_addr; + GTask *task; + + g_return_if_fail (SOUP_IS_CONNECTION (conn)); + priv = SOUP_CONNECTION_GET_PRIVATE (conn); + g_return_if_fail (priv->socket == NULL); + + soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); + + /* Set the protocol to ensure correct proxy resolution. */ + remote_addr = + g_object_new (SOUP_TYPE_ADDRESS, + SOUP_ADDRESS_NAME, priv->remote_uri->host, + SOUP_ADDRESS_PORT, priv->remote_uri->port, + SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme, + NULL); - remote_addr = soup_address_new (uri->host, uri->port); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr, SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb, @@ -568,178 +547,107 @@ connect_async_to_uri (SoupConnectionAsyncConnectData *data, SoupURI *uri) SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback, SOUP_SOCKET_ASYNC_CONTEXT, priv->async_context, SOUP_SOCKET_USE_THREAD_CONTEXT, priv->use_thread_context, - SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver, + SOUP_SOCKET_PROXY_RESOLVER, priv->proxy_resolver, SOUP_SOCKET_TIMEOUT, priv->io_timeout, SOUP_SOCKET_CLEAN_DISPOSE, TRUE, + SOUP_SOCKET_LOCAL_ADDRESS, priv->local_addr, NULL); g_object_unref (remote_addr); - data->event_id = g_signal_connect (priv->socket, "event", - G_CALLBACK (proxy_socket_event), - data->conn); - - soup_socket_connect_async (priv->socket, data->cancellable, - socket_connect_result, data); -} - -static void -proxy_resolver_result (SoupProxyURIResolver *resolver, - guint status, SoupURI *proxy_uri, - gpointer user_data) -{ - SoupConnectionAsyncConnectData *data = user_data; - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); + g_signal_connect (priv->socket, "event", + G_CALLBACK (re_emit_socket_event), conn); - if (status != SOUP_STATUS_OK) { - socket_connect_finished (NULL, status, data); - return; - } + 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); - if (proxy_uri) { - priv->proxy_uri = soup_uri_copy (proxy_uri); - connect_async_to_uri (data, proxy_uri); - } else - connect_async_to_uri (data, priv->remote_uri); + soup_socket_connect_async_internal (priv->socket, cancellable, + socket_connect_complete, task); } -void -soup_connection_connect_async (SoupConnection *conn, - GCancellable *cancellable, - SoupConnectionCallback callback, - gpointer user_data) +gboolean +soup_connection_connect_finish (SoupConnection *conn, + GAsyncResult *result, + GError **error) { - SoupConnectionAsyncConnectData *data; - SoupConnectionPrivate *priv; - GMainContext *async_context; - - g_return_if_fail (SOUP_IS_CONNECTION (conn)); - priv = SOUP_CONNECTION_GET_PRIVATE (conn); - g_return_if_fail (priv->socket == NULL); - - 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; - - if (!priv->proxy_resolver) { - connect_async_to_uri (data, priv->remote_uri); - return; - } - - if (priv->use_thread_context) - async_context = g_main_context_get_thread_default (); - else - async_context = priv->async_context; - - soup_proxy_uri_resolver_get_proxy_uri_async (priv->proxy_resolver, - priv->remote_uri, - async_context, - cancellable, - proxy_resolver_result, - data); + 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; - SoupURI *connect_uri; + 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); - if (priv->proxy_resolver) { - status = soup_proxy_uri_resolver_get_proxy_uri_sync (priv->proxy_resolver, - priv->remote_uri, - cancellable, - &priv->proxy_uri); - if (status != SOUP_STATUS_OK) - goto fail; - - if (priv->proxy_uri) - connect_uri = priv->proxy_uri; - else - connect_uri = priv->remote_uri; - } else - connect_uri = priv->remote_uri; + /* Set the protocol to ensure correct proxy resolution. */ + remote_addr = + g_object_new (SOUP_TYPE_ADDRESS, + SOUP_ADDRESS_NAME, priv->remote_uri->host, + SOUP_ADDRESS_PORT, priv->remote_uri->port, + SOUP_ADDRESS_PROTOCOL, priv->remote_uri->scheme, + NULL); - remote_addr = soup_address_new (connect_uri->host, connect_uri->port); priv->socket = soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr, - SOUP_SOCKET_USE_PROXY, priv->use_gproxyresolver, + SOUP_SOCKET_PROXY_RESOLVER, priv->proxy_resolver, SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb, SOUP_SOCKET_SSL_STRICT, priv->ssl_strict, SOUP_SOCKET_SSL_FALLBACK, priv->ssl_fallback, SOUP_SOCKET_FLAG_NONBLOCKING, FALSE, SOUP_SOCKET_TIMEOUT, priv->io_timeout, SOUP_SOCKET_CLEAN_DISPOSE, TRUE, + SOUP_SOCKET_LOCAL_ADDRESS, priv->local_addr, NULL); g_object_unref (remote_addr); event_id = g_signal_connect (priv->socket, "event", - G_CALLBACK (proxy_socket_event), conn); - status = soup_socket_connect_sync (priv->socket, cancellable); - - if (!SOUP_STATUS_IS_SUCCESSFUL (status)) - goto fail; + G_CALLBACK (re_emit_socket_event), conn); + if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error)) { + success = FALSE; + goto done; + } - if (priv->use_gproxyresolver) - priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket); + priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket); if (priv->ssl && !priv->proxy_uri) { - if (!soup_socket_start_ssl (priv->socket, cancellable)) - status = SOUP_STATUS_SSL_FAILED; - else { - soup_connection_event (conn, - G_SOCKET_CLIENT_TLS_HANDSHAKING, - NULL); - status = soup_socket_handshake_sync (priv->socket, cancellable); - if (status == SOUP_STATUS_OK) { - soup_connection_event (conn, - G_SOCKET_CLIENT_TLS_HANDSHAKED, - NULL); - } else if (status == SOUP_STATUS_TLS_FAILED) { - priv->ssl_fallback = TRUE; - status = SOUP_STATUS_TRY_AGAIN; - } + soup_connection_event (conn, + G_SOCKET_CLIENT_TLS_HANDSHAKING, + NULL); + 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)) { - g_signal_connect (priv->socket, "disconnected", - G_CALLBACK (socket_disconnected), 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); - } else { - fail: - if (priv->socket) { - soup_socket_disconnect (priv->socket); - g_object_unref (priv->socket); - priv->socket = NULL; - } + 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->socket) + 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 @@ -753,94 +661,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; g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); priv = SOUP_CONNECTION_GET_PRIVATE (conn); - if (!soup_socket_start_proxy_ssl (priv->socket, - priv->remote_uri->host, - cancellable)) - return SOUP_STATUS_SSL_FAILED; - soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); - status = soup_socket_handshake_sync (priv->socket, cancellable); - if (status == SOUP_STATUS_OK) + if (soup_socket_handshake_sync (priv->socket, priv->remote_uri->host, + cancellable, error)) { soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); - else if (status == SOUP_STATUS_TLS_FAILED) { - priv->ssl_fallback = TRUE; - status = SOUP_STATUS_TRY_AGAIN; - } - - return status; + soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL); + return TRUE; + } else + return FALSE; } static void -start_ssl_completed (SoupSocket *socket, guint status, gpointer user_data) +start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data) { - SoupConnectionAsyncConnectData *data = user_data; - SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (data->conn); - - if (status == SOUP_STATUS_OK) - soup_connection_event (data->conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); - else if (status == SOUP_STATUS_TLS_FAILED) { - priv->ssl_fallback = TRUE; - status = SOUP_STATUS_TRY_AGAIN; - } - - data->callback (data->conn, status, data->callback_data); - g_object_unref (data->conn); - g_slice_free (SoupConnectionAsyncConnectData, data); -} + GTask *task = user_data; + SoupConnection *conn = g_task_get_source_object (task); + SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn); + GError *error = NULL; -static gboolean -idle_start_ssl_completed (gpointer user_data) -{ - SoupConnectionAsyncConnectData *data = user_data; + if (priv->async_context && !priv->use_thread_context) + g_main_context_pop_thread_default (priv->async_context); - start_ssl_completed (NULL, SOUP_STATUS_SSL_FAILED, data); - return FALSE; + if (soup_socket_handshake_finish (priv->socket, result, &error)) { + 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 + 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; - GMainContext *async_context; + 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->use_thread_context) - async_context = g_main_context_get_thread_default (); - else - async_context = priv->async_context; + 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); - if (!soup_socket_start_proxy_ssl (priv->socket, - priv->remote_uri->host, - cancellable)) { - soup_add_completion (async_context, - idle_start_ssl_completed, data); - return; - } + soup_socket_handshake_async (priv->socket, priv->remote_uri->host, + cancellable, start_ssl_completed, task); +} - soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); - soup_socket_handshake_async (priv->socket, cancellable, - start_ssl_completed, data); +gboolean +soup_connection_start_ssl_finish (SoupConnection *conn, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); } /** @@ -864,14 +752,9 @@ soup_connection_disconnect (SoupConnection *conn) soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED); if (priv->socket) { - /* Set the socket to NULL at the beginning to avoid reentrancy - * issues. soup_socket_disconnect() could trigger a reentrant - * call unref'ing and disconnecting the socket twice. - */ SoupSocket *socket = priv->socket; + priv->socket = NULL; - g_signal_handlers_disconnect_by_func (socket, - socket_disconnected, conn); soup_socket_disconnect (socket); g_object_unref (socket); } @@ -936,7 +819,6 @@ void soup_connection_set_state (SoupConnection *conn, SoupConnectionState state) { SoupConnectionPrivate *priv; - SoupConnectionState old_state; g_return_if_fail (SOUP_IS_CONNECTION (conn)); g_return_if_fail (state >= SOUP_CONNECTION_NEW && @@ -945,21 +827,26 @@ soup_connection_set_state (SoupConnection *conn, SoupConnectionState state) g_object_freeze_notify (G_OBJECT (conn)); priv = SOUP_CONNECTION_GET_PRIVATE (conn); - old_state = priv->state; - priv->state = state; - if ((state == SOUP_CONNECTION_IDLE || - state == SOUP_CONNECTION_DISCONNECTED) && - old_state == SOUP_CONNECTION_IN_USE) - clear_current_item (conn); - g_object_notify (G_OBJECT (conn), "state"); - g_object_thaw_notify (G_OBJECT (conn)); -} + if (priv->current_msg) { + g_warn_if_fail (state == SOUP_CONNECTION_IDLE || + state == SOUP_CONNECTION_DISCONNECTED); + clear_current_msg (conn); + } -void -soup_connection_set_reusable (SoupConnection *conn) -{ - SOUP_CONNECTION_GET_PRIVATE (conn)->reusable = TRUE; + if (state == SOUP_CONNECTION_IDLE && !priv->reusable) { + /* This will recursively call set_state() */ + soup_connection_disconnect (conn); + } else { + priv->state = state; + + if (priv->state == SOUP_CONNECTION_IDLE) + start_idle_timer (conn); + + g_object_notify (G_OBJECT (conn), "state"); + } + + g_object_thaw_notify (G_OBJECT (conn)); } gboolean @@ -987,9 +874,13 @@ soup_connection_send_request (SoupConnection *conn, g_return_if_fail (SOUP_IS_CONNECTION (conn)); g_return_if_fail (item != NULL); priv = SOUP_CONNECTION_GET_PRIVATE (conn); - g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && priv->state != SOUP_CONNECTION_DISCONNECTED); + g_return_if_fail (priv->state != SOUP_CONNECTION_NEW && + priv->state != SOUP_CONNECTION_DISCONNECTED); + + if (item->msg != priv->current_msg) + set_current_msg (conn, item->msg); + else + priv->reusable = FALSE; - if (item != priv->cur_item) - set_current_item (conn, item); soup_message_send_request (item, completion_cb, user_data); } |