summaryrefslogtreecommitdiff
path: root/libsoup/soup-connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-connection.c')
-rw-r--r--libsoup/soup-connection.c633
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);
}