diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2020-10-28 09:41:36 +0100 |
---|---|---|
committer | Carlos Garcia Campos <cgarcia@igalia.com> | 2020-11-03 11:46:16 +0100 |
commit | 86e88118cf9f9779370e938fe23ea6b3e4f9d35d (patch) | |
tree | af4e6f10680106d90fab9f19e3bc7f4b14f0f81f | |
parent | f5e71c142360c446fbfe91394000feab48efeab3 (diff) | |
download | libsoup-86e88118cf9f9779370e938fe23ea6b3e4f9d35d.tar.gz |
connection: move client side impl from SoupSocket to SoupConnection
-rw-r--r-- | libsoup/soup-connection.c | 660 | ||||
-rw-r--r-- | libsoup/soup-connection.h | 32 | ||||
-rw-r--r-- | libsoup/soup-logger.c | 8 | ||||
-rw-r--r-- | libsoup/soup-message-io-data.c | 1 | ||||
-rw-r--r-- | libsoup/soup-message-io.c | 15 | ||||
-rw-r--r-- | libsoup/soup-message-private.h | 2 | ||||
-rw-r--r-- | libsoup/soup-message.c | 33 | ||||
-rw-r--r-- | libsoup/soup-session.c | 44 | ||||
-rw-r--r-- | libsoup/soup-socket.c | 2 | ||||
-rw-r--r-- | tests/connection-test.c | 25 | ||||
-rw-r--r-- | tests/timeout-test.c | 6 |
11 files changed, 536 insertions, 292 deletions
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c index d9f3af52..d585efd9 100644 --- a/libsoup/soup-connection.c +++ b/libsoup/soup-connection.c @@ -11,16 +11,20 @@ #include "soup-connection.h" #include "soup.h" +#include "soup-io-stream.h" #include "soup-message-queue.h" #include "soup-socket-private.h" #include "soup-private-enum-types.h" +#include <gio/gnetworking.h> struct _SoupConnection { GObject parent_instance; }; typedef struct { - SoupSocket *socket; + GIOStream *connection; + GSocketConnectable *remote_connectable; + GIOStream *iostream; SoupSocketProperties *socket_props; SoupURI *remote_uri, *proxy_uri; @@ -31,6 +35,8 @@ typedef struct { time_t unused_timeout; GSource *idle_timeout_src; gboolean reusable; + + GCancellable *cancellable; } SoupConnectionPrivate; G_DEFINE_TYPE_WITH_PRIVATE (SoupConnection, soup_connection, G_TYPE_OBJECT) @@ -74,13 +80,22 @@ soup_connection_finalize (GObject *object) g_clear_pointer (&priv->remote_uri, soup_uri_free); g_clear_pointer (&priv->proxy_uri, soup_uri_free); g_clear_pointer (&priv->socket_props, soup_socket_properties_unref); + g_clear_object (&priv->remote_connectable); g_clear_object (&priv->current_msg); - if (priv->socket) { - g_signal_handlers_disconnect_by_data (priv->socket, object); - g_object_unref (priv->socket); + if (priv->cancellable) { + g_warning ("Disposing connection %p during connect", object); + g_object_unref (priv->cancellable); + } + + if (priv->connection) { + g_warning ("Disposing connection %p while still connected", object); + g_io_stream_close (priv->connection, NULL, NULL); + g_object_unref (priv->connection); } + g_clear_object (&priv->iostream); + G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object); } @@ -217,11 +232,8 @@ soup_connection_event (SoupConnection *conn, { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - if (!connection && priv->socket) - connection = soup_socket_get_connection (priv->socket); - g_signal_emit (conn, signals[EVENT], 0, - event, connection); + event, connection ? connection : priv->connection); } static gboolean @@ -315,177 +327,313 @@ set_current_msg (SoupConnection *conn, SoupMessage *msg) } static void -re_emit_socket_event (SoupSocket *socket, +soup_connection_set_connection (SoupConnection *conn, + GIOStream *connection) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + + g_clear_object (&priv->connection); + priv->connection = connection; + g_clear_object (&priv->iostream); + priv->iostream = soup_io_stream_new (G_IO_STREAM (priv->connection), FALSE); +} + +static void +re_emit_socket_event (GSocketClient *client, GSocketClientEvent event, + GSocketConnectable *connectable, GIOStream *connection, - gpointer user_data) + SoupConnection *conn) { - SoupConnection *conn = user_data; - /* We handle COMPLETE ourselves */ - if (event != G_SOCKET_CLIENT_COMPLETE) - soup_connection_event (conn, event, connection); + if (event == G_SOCKET_CLIENT_COMPLETE) + return; + + soup_connection_event (conn, event, connection); } -static void -socket_connect_finished (GTask *task, SoupSocket *sock, GError *error) +static GSocketClient * +new_socket_client (SoupConnection *conn) { - SoupConnection *conn = g_task_get_source_object (task); - SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GSocketClient *client; + SoupSocketProperties *props = priv->socket_props; - if (!error) { - if (!priv->ssl || !priv->proxy_uri) { - soup_connection_event (conn, - G_SOCKET_CLIENT_COMPLETE, - NULL); - } + client = g_socket_client_new (); + g_signal_connect_object (client, "event", + G_CALLBACK (re_emit_socket_event), + conn, 0); - soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); - priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; - start_idle_timer (conn); + if (props->proxy_resolver) { + g_socket_client_set_proxy_resolver (client, props->proxy_resolver); + g_socket_client_add_application_proxy (client, "http"); + } else + g_socket_client_set_enable_proxy (client, FALSE); + if (props->io_timeout) + g_socket_client_set_timeout (client, props->io_timeout); + if (props->local_addr) + g_socket_client_set_local_address (client, G_SOCKET_ADDRESS (props->local_addr)); - g_task_return_boolean (task, TRUE); - } else - g_task_return_error (task, error); - g_object_unref (task); + return client; } -static void -socket_handshake_complete (GObject *object, GAsyncResult *result, gpointer user_data) +static gboolean +tls_connection_accept_certificate (GTlsConnection *conn, + GTlsCertificate *cert, + GTlsCertificateFlags errors, + gpointer user_data) { - SoupSocket *sock = SOUP_SOCKET (object); - GTask *task = user_data; - GError *error = NULL; + return TRUE; +} + +static GTlsClientConnection * +new_tls_connection (SoupConnection *conn, + GSocketConnection *connection, + GError **error) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GTlsClientConnection *tls_connection; + + tls_connection = g_initable_new (g_tls_backend_get_client_connection_type (g_tls_backend_get_default ()), + priv->cancellable, error, + "base-io-stream", connection, + "server-identity", priv->remote_connectable, + "database", priv->socket_props->tlsdb, + "require-close-notify", FALSE, + "interaction", priv->socket_props->tls_interaction, + NULL); + if (!tls_connection) + return NULL; + + if (!priv->socket_props->ssl_strict) { + g_signal_connect_object (tls_connection, "accept-certificate", + G_CALLBACK (tls_connection_accept_certificate), + conn, 0); + } + + return tls_connection; +} + +static gboolean +soup_connection_connected (SoupConnection *conn, + GSocketConnection *connection, + GError **error) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GSocket *socket; + GSocketAddress *addr; + + socket = g_socket_connection_get_socket (connection); + g_socket_set_timeout (socket, priv->socket_props->io_timeout); + g_socket_set_option (socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); - soup_socket_handshake_finish (sock, result, &error); - socket_connect_finished (task, sock, error); + addr = g_socket_get_remote_address (socket, NULL); + if (addr && G_IS_PROXY_ADDRESS (addr)) { + GProxyAddress *paddr = G_PROXY_ADDRESS (addr); + + if (strcmp (g_proxy_address_get_protocol (paddr), "http") == 0) + priv->proxy_uri = soup_uri_new (g_proxy_address_get_uri (paddr)); + } + g_clear_object (&addr); + + if (priv->ssl && !priv->proxy_uri) { + GTlsClientConnection *tls_connection; + + tls_connection = new_tls_connection (conn, connection, error); + if (!tls_connection) + return FALSE; + + g_object_unref (connection); + soup_connection_set_connection (conn, G_IO_STREAM (tls_connection)); + } else { + soup_connection_set_connection (conn, G_IO_STREAM (connection)); + } + + return TRUE; } static void -socket_connect_complete (GObject *object, GAsyncResult *result, gpointer user_data) +soup_connection_complete (SoupConnection *conn) { - SoupSocket *sock = SOUP_SOCKET (object); - GTask *task = user_data; - SoupConnection *conn = g_task_get_source_object (task); - SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - GError *error = NULL; + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - if (!soup_socket_connect_finish_internal (sock, result, &error)) { - socket_connect_finished (task, sock, error); - return; - } + g_clear_object (&priv->cancellable); - priv->proxy_uri = soup_socket_get_http_proxy_uri (sock); + if (!priv->ssl || !priv->proxy_uri) { + soup_connection_event (conn, + G_SOCKET_CLIENT_COMPLETE, + NULL); + } - if (priv->ssl && !priv->proxy_uri) { - soup_socket_handshake_async (sock, - g_task_get_cancellable (task), - socket_handshake_complete, task); - return; - } + soup_connection_set_state (conn, SOUP_CONNECTION_IN_USE); + priv->unused_timeout = time (NULL) + SOUP_CONNECTION_UNUSED_TIMEOUT; + start_idle_timer (conn); +} + +static void +handshake_ready_cb (GTlsConnection *tls_connection, + GAsyncResult *result, + GTask *task) +{ + SoupConnection *conn = g_task_get_source_object (task); + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GError *error = NULL; + + if (g_tls_connection_handshake_finish (tls_connection, result, &error)) { + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); + soup_connection_complete (conn); + g_task_return_boolean (task, TRUE); + } else { + g_clear_object (&priv->cancellable); + g_task_return_error (task, error); + } + g_object_unref (task); +} - socket_connect_finished (task, sock, NULL); +static void +connect_async_ready_cb (GSocketClient *client, + GAsyncResult *result, + GTask *task) +{ + SoupConnection *conn = g_task_get_source_object (task); + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GSocketConnection *connection; + GError *error = NULL; + + connection = g_socket_client_connect_finish (client, result, &error); + if (!connection) { + g_task_return_error (task, error); + g_object_unref (task); + g_clear_object (&priv->cancellable); + return; + } + + if (!soup_connection_connected (conn, connection, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + g_object_unref (connection); + g_clear_object (&priv->cancellable); + return; + } + + if (G_IS_TLS_CONNECTION (priv->connection)) { + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); + + g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->connection), + g_task_get_priority (task), + priv->cancellable, + (GAsyncReadyCallback)handshake_ready_cb, + task); + return; + } + + soup_connection_complete (conn); + g_task_return_boolean (task, TRUE); + g_object_unref (task); } void soup_connection_connect_async (SoupConnection *conn, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - SoupConnectionPrivate *priv; - GInetSocketAddress *remote_addr; - GTask *task; - - g_return_if_fail (SOUP_IS_CONNECTION (conn)); - priv = soup_connection_get_instance_private (conn); - g_return_if_fail (priv->socket == NULL); + SoupConnectionPrivate *priv; + GTask *task; + GSocketClient *client; - soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); + g_return_if_fail (SOUP_IS_CONNECTION (conn)); - /* Set the protocol to ensure correct proxy resolution. */ - remote_addr = - g_object_new (G_TYPE_NETWORK_ADDRESS, - "hostname", priv->remote_uri->host, - "port", priv->remote_uri->port, - "scheme", priv->remote_uri->scheme, - NULL); + priv = soup_connection_get_instance_private (conn); - priv->socket = - soup_socket_new ("remote-connectable", remote_addr, - "socket-properties", priv->socket_props, - NULL); - g_object_unref (remote_addr); + soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); - g_signal_connect (priv->socket, "event", - G_CALLBACK (re_emit_socket_event), conn); + /* Set the protocol to ensure correct proxy resolution. */ + priv->remote_connectable = + g_object_new (G_TYPE_NETWORK_ADDRESS, + "hostname", priv->remote_uri->host, + "port", priv->remote_uri->port, + "scheme", priv->remote_uri->scheme, + NULL); - task = g_task_new (conn, cancellable, callback, user_data); + priv->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); + task = g_task_new (conn, priv->cancellable, callback, user_data); + g_task_set_priority (task, io_priority); - soup_socket_connect_async_internal (priv->socket, cancellable, - socket_connect_complete, task); + client = new_socket_client (conn); + g_socket_client_connect_async (client, + priv->remote_connectable, + priv->cancellable, + (GAsyncReadyCallback)connect_async_ready_cb, + task); + g_object_unref (client); } gboolean soup_connection_connect_finish (SoupConnection *conn, - GAsyncResult *result, - GError **error) + GAsyncResult *result, + GError **error) { - return g_task_propagate_boolean (G_TASK (result), error); + return g_task_propagate_boolean (G_TASK (result), error); } gboolean -soup_connection_connect_sync (SoupConnection *conn, - GCancellable *cancellable, - GError **error) +soup_connection_connect (SoupConnection *conn, + GCancellable *cancellable, + GError **error) { - SoupConnectionPrivate *priv; - GNetworkAddress *remote_addr; + SoupConnectionPrivate *priv; + GSocketClient *client; + GSocketConnection *connection; - g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); - priv = soup_connection_get_instance_private (conn); - g_return_val_if_fail (priv->socket == NULL, FALSE); - - soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); - - /* Set the protocol to ensure correct proxy resolution. */ - remote_addr = - g_object_new (G_TYPE_NETWORK_ADDRESS, - "hostname", priv->remote_uri->host, - "port", priv->remote_uri->port, - "scheme", priv->remote_uri->scheme, - NULL); - - priv->socket = - soup_socket_new ("remote-connectable", remote_addr, - "socket-properties", priv->socket_props, - "non-blocking", FALSE, - NULL); - g_object_unref (remote_addr); - - g_signal_connect (priv->socket, "event", - G_CALLBACK (re_emit_socket_event), conn); - if (!soup_socket_connect_sync_internal (priv->socket, cancellable, error)) - return FALSE; + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); - priv->proxy_uri = soup_socket_get_http_proxy_uri (priv->socket); + priv = soup_connection_get_instance_private (conn); - if (priv->ssl && !priv->proxy_uri) { - if (!soup_socket_handshake_sync (priv->socket, - cancellable, error)) - return FALSE; - } + soup_connection_set_state (conn, SOUP_CONNECTION_CONNECTING); - 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); + /* Set the protocol to ensure correct proxy resolution. */ + priv->remote_connectable = + g_object_new (G_TYPE_NETWORK_ADDRESS, + "hostname", priv->remote_uri->host, + "port", priv->remote_uri->port, + "scheme", priv->remote_uri->scheme, + NULL); - return TRUE; + priv->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); + + client = new_socket_client (conn); + connection = g_socket_client_connect (client, + priv->remote_connectable, + priv->cancellable, + error); + g_object_unref (client); + + if (!connection) { + g_clear_object (&priv->cancellable); + return FALSE; + } + + if (!soup_connection_connected (conn, connection, error)) { + g_object_unref (connection); + g_clear_object (&priv->cancellable); + return FALSE; + } + + if (G_IS_TLS_CONNECTION (priv->connection)) { + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); + if (!g_tls_connection_handshake (G_TLS_CONNECTION (priv->connection), + priv->cancellable, error)) { + g_clear_object (&priv->cancellable); + return FALSE; + } + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); + } + + soup_connection_complete (conn); + + return TRUE; } gboolean @@ -499,64 +647,109 @@ soup_connection_is_tunnelled (SoupConnection *conn) return priv->ssl && priv->proxy_uri != NULL; } -gboolean -soup_connection_start_ssl_sync (SoupConnection *conn, - GCancellable *cancellable, - GError **error) +static void +tunnel_handshake_ready_cb (GTlsConnection *tls_connection, + GAsyncResult *result, + GTask *task) { - SoupConnectionPrivate *priv; + SoupConnection *conn = g_task_get_source_object (task); + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GError *error = NULL; - g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); - priv = soup_connection_get_instance_private (conn); + g_clear_object (&priv->cancellable); - if (soup_socket_handshake_sync (priv->socket, - cancellable, error)) { - soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL); - return TRUE; - } else - return FALSE; + if (g_tls_connection_handshake_finish (tls_connection, 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); } -static void -start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data) +void +soup_connection_tunnel_handshake_async (SoupConnection *conn, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SoupConnectionPrivate *priv; + GTask *task; + GTlsClientConnection *tls_connection; + GError *error = NULL; + + g_return_if_fail (SOUP_IS_CONNECTION (conn)); + + priv = soup_connection_get_instance_private (conn); + g_return_if_fail (G_IS_SOCKET_CONNECTION (priv->connection)); + g_return_if_fail (priv->cancellable == NULL); + + priv->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); + task = g_task_new (conn, priv->cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + + tls_connection = new_tls_connection (conn, G_SOCKET_CONNECTION (priv->connection), &error); + if (!tls_connection) { + g_task_return_error (task, error); + g_object_unref (task); + g_clear_object (&priv->cancellable); + return; + } + + soup_connection_set_connection (conn, G_IO_STREAM (tls_connection)); + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); + g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->connection), + g_task_get_priority (task), + priv->cancellable, + (GAsyncReadyCallback)tunnel_handshake_ready_cb, + task); +} + +gboolean +soup_connection_tunnel_handshake_finish (SoupConnection *conn, + GAsyncResult *result, + GError **error) { - GTask *task = user_data; - SoupConnection *conn = g_task_get_source_object (task); - SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - GError *error = NULL; + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); - if (soup_socket_handshake_finish (priv->socket, result, &error)) { - 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); + return g_task_propagate_boolean (G_TASK (result), error); } -void -soup_connection_start_ssl_async (SoupConnection *conn, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +gboolean +soup_connection_tunnel_handshake (SoupConnection *conn, + GCancellable *cancellable, + GError **error) { - SoupConnectionPrivate *priv; - GTask *task; + SoupConnectionPrivate *priv; + GTlsClientConnection *tls_connection; - g_return_if_fail (SOUP_IS_CONNECTION (conn)); - priv = soup_connection_get_instance_private (conn); + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); - task = g_task_new (conn, cancellable, callback, user_data); + priv = soup_connection_get_instance_private (conn); + g_return_val_if_fail (G_IS_SOCKET_CONNECTION (priv->connection), FALSE); + g_return_val_if_fail (priv->cancellable == NULL, FALSE); - soup_socket_handshake_async (priv->socket, - cancellable, start_ssl_completed, task); -} + tls_connection = new_tls_connection (conn, G_SOCKET_CONNECTION (priv->connection), error); + if (!tls_connection) + return FALSE; -gboolean -soup_connection_start_ssl_finish (SoupConnection *conn, - GAsyncResult *result, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (result), error); + soup_connection_set_connection (conn, G_IO_STREAM (tls_connection)); + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL); + + priv->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); + if (!g_tls_connection_handshake (G_TLS_CONNECTION (priv->connection), + priv->cancellable, error)) { + g_clear_object (&priv->cancellable); + return FALSE; + } + g_clear_object (&priv->cancellable); + + soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL); + soup_connection_event (conn, G_SOCKET_CLIENT_COMPLETE, NULL); + + return TRUE; } /** @@ -569,38 +762,84 @@ soup_connection_start_ssl_finish (SoupConnection *conn, void soup_connection_disconnect (SoupConnection *conn) { - SoupConnectionPrivate *priv; - SoupConnectionState old_state; + SoupConnectionPrivate *priv; + SoupConnectionState old_state; - g_return_if_fail (SOUP_IS_CONNECTION (conn)); - priv = soup_connection_get_instance_private (conn); + g_return_if_fail (SOUP_IS_CONNECTION (conn)); + priv = soup_connection_get_instance_private (conn); - old_state = priv->state; - if (old_state != SOUP_CONNECTION_DISCONNECTED) - soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED); + old_state = priv->state; + if (old_state != SOUP_CONNECTION_DISCONNECTED) + soup_connection_set_state (conn, SOUP_CONNECTION_DISCONNECTED); - if (priv->socket) { - SoupSocket *socket = priv->socket; + if (priv->cancellable) { + g_cancellable_cancel (priv->cancellable); + priv->cancellable = NULL; + } - g_signal_handlers_disconnect_by_func (socket, G_CALLBACK (re_emit_socket_event), conn); + if (priv->connection) { + GIOStream *connection; - priv->socket = NULL; - soup_socket_disconnect (socket); - g_object_unref (socket); - } + connection = priv->connection; + priv->connection = NULL; - if (old_state != SOUP_CONNECTION_DISCONNECTED) - g_signal_emit (conn, signals[DISCONNECTED], 0); + g_io_stream_close (connection, NULL, NULL); + g_signal_handlers_disconnect_by_data (connection, conn); + g_object_unref (connection); + } + + if (old_state != SOUP_CONNECTION_DISCONNECTED) + g_signal_emit (conn, signals[DISCONNECTED], 0); } -SoupSocket * +GSocket * soup_connection_get_socket (SoupConnection *conn) { - SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); + GSocketConnection *connection = NULL; - g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL); + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL); + + if (G_IS_TLS_CONNECTION (priv->connection)) { + g_object_get (priv->connection, "base-io-stream", &connection, NULL); + g_object_unref (connection); + } else if (G_IS_SOCKET_CONNECTION (priv->connection)) + connection = G_SOCKET_CONNECTION (priv->connection); + + return connection ? g_socket_connection_get_socket (connection) : NULL; +} + +GIOStream * +soup_connection_get_iostream (SoupConnection *conn) +{ + SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - return priv->socket; + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL); + + return priv->iostream; +} + +GIOStream * +soup_connection_steal_iostream (SoupConnection *conn) +{ + SoupConnectionPrivate *priv; + GSocket *socket; + GIOStream *iostream; + + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), NULL); + + socket = soup_connection_get_socket (conn); + g_socket_set_timeout (socket, 0); + + priv = soup_connection_get_instance_private (conn); + iostream = priv->iostream; + priv->iostream = NULL; + + g_object_set_data_full (G_OBJECT (iostream), "GSocket", + g_object_ref (socket), g_object_unref); + g_clear_object (&priv->connection); + + return iostream; } SoupURI * @@ -633,23 +872,44 @@ soup_connection_is_via_proxy (SoupConnection *conn) return priv->proxy_uri != NULL; } +gboolean +soup_connection_get_tls_info (SoupConnection *conn, + GTlsCertificate **certificate, + GTlsCertificateFlags *errors) +{ + SoupConnectionPrivate *priv; + GTlsConnection *tls_connection; + + g_return_val_if_fail (SOUP_IS_CONNECTION (conn), FALSE); + + priv = soup_connection_get_instance_private (conn); + if (!G_IS_TLS_CONNECTION (priv->connection)) + return FALSE; + + tls_connection = G_TLS_CONNECTION (priv->connection); + if (certificate) + *certificate = g_tls_connection_get_peer_certificate (tls_connection); + if (errors) + *errors = g_tls_connection_get_peer_certificate_errors (tls_connection); + + return TRUE; +} + static gboolean is_idle_connection_disconnected (SoupConnection *conn) { SoupConnectionPrivate *priv = soup_connection_get_instance_private (conn); - GIOStream *iostream; GInputStream *istream; char buffer[1]; GError *error = NULL; - if (!soup_socket_is_connected (priv->socket)) + if (!g_socket_is_connected (soup_connection_get_socket (conn))) return TRUE; if (priv->unused_timeout && priv->unused_timeout < time (NULL)) return TRUE; - iostream = soup_socket_get_iostream (priv->socket); - istream = g_io_stream_get_input_stream (iostream); + istream = g_io_stream_get_input_stream (priv->iostream); /* This is tricky. The goal is to check if the socket is readable. If * so, that means either the server has disconnected or it's broken (it diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h index 9f613a15..53b67337 100644 --- a/libsoup/soup-connection.h +++ b/libsoup/soup-connection.h @@ -25,33 +25,39 @@ typedef enum { } SoupConnectionState; void soup_connection_connect_async (SoupConnection *conn, + int io_priority, 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, +gboolean soup_connection_connect (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_tunnel_handshake_async (SoupConnection *conn, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean soup_connection_tunnel_handshake_finish (SoupConnection *conn, + GAsyncResult *result, + GError **error); +gboolean soup_connection_tunnel_handshake (SoupConnection *conn, + GCancellable *cancellable, + GError **error); void soup_connection_disconnect (SoupConnection *conn); -SoupSocket *soup_connection_get_socket (SoupConnection *conn); +GSocket *soup_connection_get_socket (SoupConnection *conn); +GIOStream *soup_connection_get_iostream (SoupConnection *conn); +GIOStream *soup_connection_steal_iostream (SoupConnection *conn); SoupURI *soup_connection_get_remote_uri (SoupConnection *conn); SoupURI *soup_connection_get_proxy_uri (SoupConnection *conn); gboolean soup_connection_is_via_proxy (SoupConnection *conn); gboolean soup_connection_is_tunnelled (SoupConnection *conn); +gboolean soup_connection_get_tls_info (SoupConnection *conn, + GTlsCertificate **certificate, + GTlsCertificateFlags *errors); SoupConnectionState soup_connection_get_state (SoupConnection *conn); void soup_connection_set_state (SoupConnection *conn, diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c index 399253a9..02683835 100644 --- a/libsoup/soup-logger.c +++ b/libsoup/soup-logger.c @@ -41,7 +41,7 @@ * <informalexample><screen> * > POST /unauth HTTP/1.1 * > Soup-Debug-Timestamp: 1200171744 - * > Soup-Debug: SoupSessionAsync 1 (0x612190), SoupMessage 1 (0x617000), SoupSocket 1 (0x612220) + * > Soup-Debug: SoupSessionAsync 1 (0x612190), SoupMessage 1 (0x617000), GSocket 1 (0x612220) * > Host: localhost * > Content-Type: text/plain * > Connection: close @@ -60,7 +60,7 @@ * received. * * The <literal>Soup-Debug</literal> line gives further debugging - * information about the #SoupSession, #SoupMessage, and #SoupSocket + * information about the #SoupSession, #SoupMessage, and #GSocket * involved; the hex numbers are the addresses of the objects in * question (which may be useful if you are running in a debugger). * The decimal IDs are simply counters that uniquely identify objects @@ -498,7 +498,7 @@ soup_logger_print_basic_auth (SoupLogger *logger, const char *value) static void print_request (SoupLogger *logger, SoupMessage *msg, - SoupSocket *socket, gboolean restarted) + GSocket *socket, gboolean restarted) { SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger); SoupLoggerLogLevel log_level; @@ -682,7 +682,7 @@ starting (SoupMessage *msg, gpointer user_data) gboolean restarted; guint msg_id; SoupConnection *conn; - SoupSocket *socket; + GSocket *socket; msg_id = soup_logger_get_id (logger, msg); if (msg_id) diff --git a/libsoup/soup-message-io-data.c b/libsoup/soup-message-io-data.c index 8af99467..0dd63e28 100644 --- a/libsoup/soup-message-io-data.c +++ b/libsoup/soup-message-io-data.c @@ -71,6 +71,7 @@ soup_message_io_data_read_headers (SoupMessageIOData *io, if (io->read_header_buf->len > 0) break; + g_print ("DBG: soup_message_io_data_read_headers\n"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT, _("Connection terminated unexpectedly")); diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c index 34d40d00..91aff529 100644 --- a/libsoup/soup-message-io.c +++ b/libsoup/soup-message-io.c @@ -91,19 +91,17 @@ soup_message_io_finished (SoupMessage *msg) g_object_unref (msg); } -GIOStream * -soup_message_io_steal (SoupMessage *msg) +void +soup_message_io_stolen (SoupMessage *msg) { SoupClientMessageIOData *io; SoupMessageIOCompletionFn completion_cb; gpointer completion_data; - GIOStream *iostream; io = soup_message_get_io_data (msg); - if (!io || !io->base.iostream) - return NULL; + if (!io) + return; - iostream = g_object_ref (io->base.iostream); completion_cb = io->base.completion_cb; completion_data = io->base.completion_data; @@ -112,8 +110,6 @@ soup_message_io_steal (SoupMessage *msg) if (completion_cb) completion_cb (G_OBJECT (msg), SOUP_MESSAGE_IO_STOLEN, completion_data); g_object_unref (msg); - - return iostream; } static gint @@ -807,6 +803,7 @@ soup_message_io_update_status (SoupMessage *msg, error->message); } else if (!SOUP_STATUS_IS_TRANSPORT_ERROR (soup_message_get_status (msg)) && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + g_print ("DBG: soup_message_io_update_status: %s\n", error->message); soup_message_set_status (msg, SOUP_STATUS_IO_ERROR); } @@ -1026,7 +1023,7 @@ soup_message_send_request (SoupMessageQueueItem *item, io->item = item; soup_message_queue_item_ref (item); io->cancellable = io->item->cancellable; - io->base.iostream = g_object_ref (soup_socket_get_iostream (soup_connection_get_socket (io->item->conn))); + io->base.iostream = g_object_ref (soup_connection_get_iostream (io->item->conn)); io->base.istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (io->base.iostream)); io->base.ostream = g_io_stream_get_output_stream (io->base.iostream); io->base.async_context = g_main_context_ref_thread_default (); diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h index 643140ae..a7436f8c 100644 --- a/libsoup/soup-message-private.h +++ b/libsoup/soup-message-private.h @@ -52,7 +52,7 @@ void soup_message_io_pause (SoupMessage *msg); void soup_message_io_unpause (SoupMessage *msg); gboolean soup_message_is_io_paused (SoupMessage *msg); gboolean soup_message_io_in_progress (SoupMessage *msg); -GIOStream *soup_message_io_steal (SoupMessage *msg); +void soup_message_io_stolen (SoupMessage *msg); gboolean soup_message_io_read_headers (SoupMessage *msg, SoupFilterInputStream *stream, diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c index 8cda7ea5..48b87ce5 100644 --- a/libsoup/soup-message.c +++ b/libsoup/soup-message.c @@ -1700,29 +1700,16 @@ soup_message_get_is_top_level_navigation (SoupMessage *msg) void soup_message_set_https_status (SoupMessage *msg, SoupConnection *conn) { - SoupSocket *sock; - - sock = conn ? soup_connection_get_socket (conn) : NULL; - if (sock && soup_socket_is_ssl (sock)) { - GTlsCertificate *certificate; - GTlsCertificateFlags errors; - - g_object_get (sock, - "tls-certificate", &certificate, - "tls-errors", &errors, - NULL); - g_object_set (msg, - "tls-certificate", certificate, - "tls-errors", errors, - NULL); - if (certificate) - g_object_unref (certificate); - } else { - g_object_set (msg, - "tls-certificate", NULL, - "tls-errors", 0, - NULL); - } + GTlsCertificate *certificate = NULL; + GTlsCertificateFlags errors = 0; + + if (conn) + soup_connection_get_tls_info (conn, &certificate, &errors); + + g_object_set (msg, + "tls-certificate", certificate, + "tls-errors", errors, + NULL); } /** diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c index 9311a095..689f13b5 100644 --- a/libsoup/soup-session.c +++ b/libsoup/soup-session.c @@ -1354,15 +1354,13 @@ tunnel_complete (SoupMessageQueueItem *tunnel_item, } static void -tunnel_handshake_complete (GObject *object, - GAsyncResult *result, - gpointer user_data) +tunnel_handshake_complete (SoupConnection *conn, + GAsyncResult *result, + SoupMessageQueueItem *tunnel_item) { - SoupConnection *conn = SOUP_CONNECTION (object); - SoupMessageQueueItem *tunnel_item = user_data; GError *error = NULL; - soup_connection_start_ssl_finish (conn, result, &error); + soup_connection_tunnel_handshake_finish (conn, result, &error); tunnel_complete (tunnel_item, 0, error); } @@ -1398,13 +1396,15 @@ tunnel_message_completed (SoupMessage *msg, SoupMessageIOCompletion completion, } if (tunnel_item->async) { - soup_connection_start_ssl_async (item->conn, item->cancellable, - tunnel_handshake_complete, - tunnel_item); + soup_connection_tunnel_handshake_async (item->conn, + item->task ? g_task_get_priority (item->task) : 0, + item->cancellable, + (GAsyncReadyCallback)tunnel_handshake_complete, + tunnel_item); } else { GError *error = NULL; - soup_connection_start_ssl_sync (item->conn, item->cancellable, &error); + soup_connection_tunnel_handshake (item->conn, item->cancellable, &error); tunnel_complete (tunnel_item, 0, error); } } @@ -1637,13 +1637,15 @@ get_connection (SoupMessageQueueItem *item, gboolean *should_cleanup) if (item->async) { soup_message_queue_item_ref (item); - soup_connection_connect_async (item->conn, item->cancellable, + soup_connection_connect_async (item->conn, + item->task ? g_task_get_priority (item->task) : 0, + item->cancellable, connect_async_complete, item); return FALSE; } else { GError *error = NULL; - soup_connection_connect_sync (item->conn, item->cancellable, &error); + soup_connection_connect (item->conn, item->cancellable, &error); connect_complete (item, conn, error); return TRUE; @@ -3928,7 +3930,6 @@ steal_connection (SoupSession *session, { SoupSessionPrivate *priv = soup_session_get_instance_private (session); SoupConnection *conn; - SoupSocket *sock; SoupSessionHost *host; GIOStream *stream; @@ -3941,19 +3942,10 @@ steal_connection (SoupSession *session, drop_connection (session, host, conn); g_mutex_unlock (&priv->conn_lock); - sock = soup_connection_get_socket (conn); - g_object_set (sock, - "timeout", 0, - NULL); - - if (item->connect_only) - stream = g_object_ref (soup_socket_get_connection (sock)); - else - stream = soup_message_io_steal (item->msg); - g_object_set_data_full (G_OBJECT (stream), "GSocket", - soup_socket_steal_gsocket (sock), - g_object_unref); - g_object_unref (conn); + stream = soup_connection_steal_iostream (conn); + if (!item->connect_only) + soup_message_io_stolen (item->msg); + g_object_unref (conn); return stream; } diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c index 9b471d39..5af34a90 100644 --- a/libsoup/soup-socket.c +++ b/libsoup/soup-socket.c @@ -1251,6 +1251,8 @@ soup_socket_setup_ssl (SoupSocket *sock, g_object_unref (priv->conn); priv->conn = G_IO_STREAM (conn); + g_print ("DBG: soup_socket_setup_ssl: strict: %s\n", priv->ssl_strict ? "yes" : "no"); + if (!priv->ssl_strict) { g_signal_connect (conn, "accept-certificate", G_CALLBACK (soup_socket_accept_certificate), diff --git a/tests/connection-test.c b/tests/connection-test.c index 2021cad2..46fdbcb4 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -228,10 +228,10 @@ do_content_length_framing_test (void) static void message_started_socket_collector (SoupMessage *msg, - SoupSocket **sockets) + GSocket **sockets) { SoupConnection *conn = soup_message_get_connection (msg); - SoupSocket *socket = soup_connection_get_socket (conn); + GSocket *socket = soup_connection_get_socket (conn); int i; debug_printf (2, " msg %p => socket %p\n", msg, socket); @@ -265,7 +265,7 @@ static void do_timeout_test_for_session (SoupSession *session) { SoupMessage *msg; - SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL }; + GSocket *sockets[4] = { NULL, NULL, NULL, NULL }; SoupURI *timeout_uri; int i; GBytes *body; @@ -332,7 +332,7 @@ do_persistent_connection_timeout_test_with_cancellation (void) { SoupSession *session; SoupMessage *msg; - SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL }; + GSocket *sockets[4] = { NULL, NULL, NULL, NULL }; SoupURI *timeout_uri; GCancellable *cancellable; GInputStream *response; @@ -549,10 +549,10 @@ do_max_conns_test (void) static void np_message_started (SoupMessage *msg, - SoupSocket **save_socket) + GSocket **save_socket) { SoupConnection *conn = soup_message_get_connection (msg); - SoupSocket *socket = soup_connection_get_socket (conn); + GSocket *socket = soup_connection_get_socket (conn); *save_socket = g_object_ref (socket); } @@ -568,12 +568,11 @@ np_request_queued (SoupSession *session, } static void -np_request_unqueued (SoupSession *session, SoupMessage *msg, - gpointer user_data) +np_request_unqueued (SoupSession *session, + SoupMessage *msg, + GSocket **socket) { - SoupSocket *socket = *(SoupSocket **)user_data; - - g_assert_false (soup_socket_is_connected (socket)); + g_assert_false (g_socket_is_connected (*socket)); } static void @@ -589,7 +588,7 @@ static void do_non_persistent_test_for_session (SoupSession *session) { SoupMessage *msg; - SoupSocket *socket = NULL; + GSocket *socket = NULL; GMainLoop *loop; loop = g_main_loop_new (NULL, FALSE); @@ -631,7 +630,7 @@ static void do_non_idempotent_test_for_session (SoupSession *session) { SoupMessage *msg; - SoupSocket *sockets[4] = { NULL, NULL, NULL, NULL }; + GSocket *sockets[4] = { NULL, NULL, NULL, NULL }; int i; GBytes *body; diff --git a/tests/timeout-test.c b/tests/timeout-test.c index 217a0784..38cd7072 100644 --- a/tests/timeout-test.c +++ b/tests/timeout-test.c @@ -17,7 +17,7 @@ message_finished (SoupMessage *msg, gpointer user_data) static void request_started_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) { - SoupSocket **ret = user_data; + GSocket **ret = user_data; SoupConnection *conn = soup_message_get_connection (msg); *ret = soup_connection_get_socket (conn); @@ -58,8 +58,8 @@ do_msg_tests_for_session (SoupSession *timeout_session, SoupURI *fast_uri, SoupURI *slow_uri) { - SoupSocket *ret, *idle_first = NULL, *idle_second; - SoupSocket *plain_first = NULL, *plain_second; + GSocket *ret, *idle_first = NULL, *idle_second; + GSocket *plain_first = NULL, *plain_second; if (idle_session) { g_signal_connect (idle_session, "request-started", |