summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2020-10-28 09:41:36 +0100
committerCarlos Garcia Campos <cgarcia@igalia.com>2020-11-03 11:46:16 +0100
commit86e88118cf9f9779370e938fe23ea6b3e4f9d35d (patch)
treeaf4e6f10680106d90fab9f19e3bc7f4b14f0f81f
parentf5e71c142360c446fbfe91394000feab48efeab3 (diff)
downloadlibsoup-86e88118cf9f9779370e938fe23ea6b3e4f9d35d.tar.gz
connection: move client side impl from SoupSocket to SoupConnection
-rw-r--r--libsoup/soup-connection.c660
-rw-r--r--libsoup/soup-connection.h32
-rw-r--r--libsoup/soup-logger.c8
-rw-r--r--libsoup/soup-message-io-data.c1
-rw-r--r--libsoup/soup-message-io.c15
-rw-r--r--libsoup/soup-message-private.h2
-rw-r--r--libsoup/soup-message.c33
-rw-r--r--libsoup/soup-session.c44
-rw-r--r--libsoup/soup-socket.c2
-rw-r--r--tests/connection-test.c25
-rw-r--r--tests/timeout-test.c6
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",