summaryrefslogtreecommitdiff
path: root/libsoup/soup-socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-socket.c')
-rw-r--r--libsoup/soup-socket.c482
1 files changed, 307 insertions, 175 deletions
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 2ac359de..b9f1dfca 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -11,11 +11,12 @@
#include <string.h>
+#include <gio/gnetworking.h>
+
#include "soup-socket.h"
#include "soup.h"
#include "soup-filter-input-stream.h"
#include "soup-io-stream.h"
-#include "soup-marshal.h"
#include "soup-misc-private.h"
/**
@@ -58,7 +59,7 @@ enum {
PROP_CLEAN_DISPOSE,
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
- PROP_USE_PROXY,
+ PROP_PROXY_RESOLVER,
LAST_PROP
};
@@ -70,6 +71,7 @@ typedef struct {
GInputStream *istream;
GOutputStream *ostream;
GTlsCertificateFlags tls_errors;
+ GProxyResolver *proxy_resolver;
guint non_blocking:1;
guint is_server:1;
@@ -78,7 +80,6 @@ typedef struct {
guint ssl_fallback:1;
guint clean_dispose:1;
guint use_thread_context:1;
- guint use_proxy:1;
gpointer ssl_creds;
GMainContext *async_context;
@@ -149,6 +150,8 @@ soup_socket_finalize (GObject *object)
g_clear_object (&priv->local_addr);
g_clear_object (&priv->remote_addr);
+ g_clear_object (&priv->proxy_resolver);
+
if (priv->watch_src) {
if (priv->clean_dispose && !priv->is_server)
g_warning ("Disposing socket %p during async op", object);
@@ -179,6 +182,7 @@ finish_socket_setup (SoupSocketPrivate *priv)
priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream));
g_socket_set_timeout (priv->gsock, priv->timeout);
+ g_socket_set_option (priv->gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL);
}
static void
@@ -219,8 +223,8 @@ soup_socket_set_property (GObject *object, guint prop_id,
if (priv->conn)
g_socket_set_timeout (priv->gsock, priv->timeout);
break;
- case PROP_USE_PROXY:
- priv->use_proxy = g_value_get_boolean (value);
+ case PROP_PROXY_RESOLVER:
+ priv->proxy_resolver = g_value_dup_object (value);
break;
case PROP_CLEAN_DISPOSE:
priv->clean_dispose = g_value_get_boolean (value);
@@ -280,8 +284,8 @@ soup_socket_get_property (GObject *object, guint prop_id,
case PROP_TLS_ERRORS:
g_value_set_flags (value, priv->tls_errors);
break;
- case PROP_USE_PROXY:
- g_value_set_boolean (value, priv->use_proxy);
+ case PROP_PROXY_RESOLVER:
+ g_value_set_object (value, priv->proxy_resolver);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -317,7 +321,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, readable),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -333,7 +337,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, writable),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -349,7 +353,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (SoupSocketClass, disconnected),
NULL, NULL,
- _soup_marshal_NONE__NONE,
+ NULL,
G_TYPE_NONE, 0);
/**
@@ -369,7 +373,7 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupSocketClass, new_connection),
NULL, NULL,
- _soup_marshal_NONE__OBJECT,
+ NULL,
G_TYPE_NONE, 1,
SOUP_TYPE_SOCKET);
/**
@@ -544,14 +548,14 @@ soup_socket_class_init (SoupSocketClass *socket_class)
* Alias for the #SoupSocket:use-thread-context property. (Use
* g_main_context_get_thread_default())
*
- * Since: 2.36.1
+ * Since: 2.38
*/
/**
* SoupSocket:use-thread-context:
*
* Use g_main_context_get_thread_default().
*
- * Since: 2.36.1
+ * Since: 2.38
*/
g_object_class_install_property (
object_class, PROP_USE_THREAD_CONTEXT,
@@ -618,12 +622,12 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_PARAM_READABLE));
g_object_class_install_property (
- object_class, PROP_USE_PROXY,
- g_param_spec_boolean (SOUP_SOCKET_USE_PROXY,
- "Use proxy",
- "Use #GProxyResolver",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ object_class, PROP_PROXY_RESOLVER,
+ g_param_spec_object (SOUP_SOCKET_PROXY_RESOLVER,
+ "Proxy resolver",
+ "GProxyResolver to use",
+ G_TYPE_PROXY_RESOLVER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
@@ -651,11 +655,11 @@ soup_socket_new (const char *optname1, ...)
}
static void
-proxy_socket_client_event (GSocketClient *client,
- GSocketClientEvent event,
- GSocketConnectable *connectable,
- GIOStream *connection,
- gpointer user_data)
+re_emit_socket_client_event (GSocketClient *client,
+ GSocketClientEvent event,
+ GSocketConnectable *connectable,
+ GIOStream *connection,
+ gpointer user_data)
{
SoupSocket *sock = user_data;
@@ -663,35 +667,115 @@ proxy_socket_client_event (GSocketClient *client,
event, connection);
}
+static gboolean
+socket_connect_finish (SoupSocket *sock, GSocketConnection *conn)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ g_clear_object (&priv->connect_cancel);
+
+ if (conn) {
+ priv->conn = (GIOStream *)conn;
+ priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
+ finish_socket_setup (priv);
+ return TRUE;
+ } else
+ return FALSE;
+}
+
static guint
-socket_connected (SoupSocket *sock, GSocketConnection *conn, GError *error)
+socket_legacy_error (SoupSocket *sock, GError *error)
+{
+ guint status;
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ status = SOUP_STATUS_CANCELLED;
+ else if (error->domain == G_RESOLVER_ERROR)
+ status = SOUP_STATUS_CANT_RESOLVE;
+ else
+ status = SOUP_STATUS_CANT_CONNECT;
+
+ g_error_free (error);
+ return status;
+}
+
+static GSocketClient *
+new_socket_client (SoupSocket *sock)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ GSocketClient *client = g_socket_client_new ();
- if (priv->connect_cancel) {
- GCancellable *cancellable = priv->connect_cancel;
+ g_signal_connect (client, "event",
+ G_CALLBACK (re_emit_socket_client_event), sock);
+ if (priv->proxy_resolver) {
+ g_socket_client_set_proxy_resolver (client, priv->proxy_resolver);
+ g_socket_client_add_application_proxy (client, "http");
+ } else
+ g_socket_client_set_enable_proxy (client, FALSE);
+ if (priv->timeout)
+ g_socket_client_set_timeout (client, priv->timeout);
- g_object_unref (priv->connect_cancel);
- priv->connect_cancel = NULL;
- if (g_cancellable_is_cancelled (cancellable))
- return SOUP_STATUS_CANCELLED;
- }
+ if (priv->local_addr) {
+ GSocketAddress *addr;
- if (error) {
- if (error->domain == G_RESOLVER_ERROR) {
- g_error_free (error);
- return SOUP_STATUS_CANT_RESOLVE;
- } else {
- g_error_free (error);
- return SOUP_STATUS_CANT_CONNECT;
- }
+ addr = soup_address_get_gsockaddr (priv->local_addr);
+ g_socket_client_set_local_address (client, addr);
+ g_object_unref (addr);
}
- priv->conn = (GIOStream *)conn;
- priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
- finish_socket_setup (priv);
+ return client;
+}
- return SOUP_STATUS_OK;
+static void
+async_connected (GObject *client, GAsyncResult *result, gpointer data)
+{
+ GTask *task = data;
+ SoupSocket *sock = g_task_get_source_object (task);
+ GSocketConnection *conn;
+ GError *error = NULL;
+
+ conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
+ result, &error);
+ if (socket_connect_finish (sock, conn))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+gboolean
+soup_socket_connect_finish_internal (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+void
+soup_socket_connect_async_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SoupSocketPrivate *priv;
+ GSocketClient *client;
+ GTask *task;
+
+ g_return_if_fail (SOUP_IS_SOCKET (sock));
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_if_fail (!priv->is_server);
+ g_return_if_fail (priv->gsock == NULL);
+ g_return_if_fail (priv->remote_addr != NULL);
+
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+ task = g_task_new (sock, priv->connect_cancel, callback, user_data);
+
+ client = new_socket_client (sock);
+ g_socket_client_connect_async (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel,
+ async_connected, task);
+ g_object_unref (client);
}
/**
@@ -710,22 +794,25 @@ typedef struct {
} SoupSocketAsyncConnectData;
static void
-async_connected (GObject *client, GAsyncResult *result, gpointer data)
+legacy_connect_async_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- SoupSocketAsyncConnectData *sacd = data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sacd->sock);
+ SoupSocket *sock = SOUP_SOCKET (object);
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ SoupSocketAsyncConnectData *sacd = user_data;
GError *error = NULL;
- GSocketConnection *conn;
guint status;
if (priv->async_context && !priv->use_thread_context)
g_main_context_pop_thread_default (priv->async_context);
- conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (client),
- result, &error);
- status = socket_connected (sacd->sock, conn, error);
+ if (soup_socket_connect_finish_internal (sock, result, &error))
+ status = SOUP_STATUS_OK;
+ else
+ status = socket_legacy_error (sock, error);
- sacd->callback (sacd->sock, status, sacd->user_data);
+ sacd->callback (sock, status, sacd->user_data);
g_object_unref (sacd->sock);
g_slice_free (SoupSocketAsyncConnectData, sacd);
}
@@ -751,10 +838,11 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
{
SoupSocketPrivate *priv;
SoupSocketAsyncConnectData *sacd;
- GSocketClient *client;
g_return_if_fail (SOUP_IS_SOCKET (sock));
priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_if_fail (!priv->is_server);
+ g_return_if_fail (priv->gsock == NULL);
g_return_if_fail (priv->remote_addr != NULL);
sacd = g_slice_new0 (SoupSocketAsyncConnectData);
@@ -762,25 +850,38 @@ soup_socket_connect_async (SoupSocket *sock, GCancellable *cancellable,
sacd->callback = callback;
sacd->user_data = user_data;
- priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
-
if (priv->async_context && !priv->use_thread_context)
g_main_context_push_thread_default (priv->async_context);
- client = g_socket_client_new ();
- g_signal_connect (client, "event",
- G_CALLBACK (proxy_socket_client_event), sock);
- if (priv->use_proxy)
- g_socket_client_add_application_proxy (client, "http");
- else
- g_socket_client_set_enable_proxy (client, FALSE);
- if (priv->timeout)
- g_socket_client_set_timeout (client, priv->timeout);
- g_socket_client_connect_async (client,
- G_SOCKET_CONNECTABLE (priv->remote_addr),
- priv->connect_cancel,
- async_connected, sacd);
+ soup_socket_connect_async_internal (sock, cancellable,
+ legacy_connect_async_cb,
+ sacd);
+}
+
+gboolean
+soup_socket_connect_sync_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupSocketPrivate *priv;
+ GSocketClient *client;
+ GSocketConnection *conn;
+
+ g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
+ priv = SOUP_SOCKET_GET_PRIVATE (sock);
+ g_return_val_if_fail (!priv->is_server, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
+ g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
+
+ priv->connect_cancel = cancellable ? g_object_ref (cancellable) : g_cancellable_new ();
+
+ client = new_socket_client (sock);
+ conn = g_socket_client_connect (client,
+ G_SOCKET_CONNECTABLE (priv->remote_addr),
+ priv->connect_cancel, error);
g_object_unref (client);
+
+ return socket_connect_finish (sock, conn);
}
/**
@@ -800,8 +901,6 @@ guint
soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
{
SoupSocketPrivate *priv;
- GSocketClient *client;
- GSocketConnection *conn;
GError *error = NULL;
g_return_val_if_fail (SOUP_IS_SOCKET (sock), SOUP_STATUS_MALFORMED);
@@ -810,29 +909,23 @@ soup_socket_connect_sync (SoupSocket *sock, GCancellable *cancellable)
g_return_val_if_fail (priv->gsock == NULL, SOUP_STATUS_MALFORMED);
g_return_val_if_fail (priv->remote_addr != NULL, SOUP_STATUS_MALFORMED);
- if (cancellable)
- g_object_ref (cancellable);
- else
- cancellable = g_cancellable_new ();
- priv->connect_cancel = cancellable;
-
- client = g_socket_client_new ();
- g_signal_connect (client, "event",
- G_CALLBACK (proxy_socket_client_event), sock);
- if (priv->use_proxy)
- g_socket_client_add_application_proxy (client, "http");
+ if (soup_socket_connect_sync_internal (sock, cancellable, &error))
+ return SOUP_STATUS_OK;
else
- g_socket_client_set_enable_proxy (client, FALSE);
- if (priv->timeout)
- g_socket_client_set_timeout (client, priv->timeout);
- conn = g_socket_client_connect (client,
- G_SOCKET_CONNECTABLE (priv->remote_addr),
- priv->connect_cancel, &error);
- g_object_unref (client);
-
- return socket_connected (sock, conn, error);
+ return socket_legacy_error (sock, error);
}
+/**
+ * soup_socket_get_fd:
+ * @sock: a #SoupSocket
+ *
+ * Gets @sock's underlying file descriptor.
+ *
+ * Note that fiddling with the file descriptor may break the
+ * #SoupSocket.
+ *
+ * Return value: @sock's file descriptor.
+ */
int
soup_socket_get_fd (SoupSocket *sock)
{
@@ -1012,37 +1105,11 @@ soup_socket_accept_certificate (GTlsConnection *conn, GTlsCertificate *cert,
return TRUE;
}
-/**
- * soup_socket_start_ssl:
- * @sock: the socket
- * @cancellable: a #GCancellable
- *
- * Starts using SSL on @socket.
- *
- * Return value: success or failure
- **/
-gboolean
-soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
-{
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
-
- return soup_socket_start_proxy_ssl (sock, soup_address_get_name (priv->remote_addr), cancellable);
-}
-
-/**
- * soup_socket_start_proxy_ssl:
- * @sock: the socket
- * @ssl_host: hostname of the SSL server
- * @cancellable: a #GCancellable
- *
- * Starts using SSL on @socket, expecting to find a host named
- * @ssl_host.
- *
- * Return value: success or failure
- **/
-gboolean
-soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
- GCancellable *cancellable)
+static gboolean
+soup_socket_setup_ssl (SoupSocket *sock,
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GError **error)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
GTlsBackend *backend = g_tls_backend_get_default ();
@@ -1050,7 +1117,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
if (G_IS_TLS_CONNECTION (priv->conn))
return TRUE;
- if (g_cancellable_is_cancelled (cancellable))
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
return FALSE;
priv->ssl = TRUE;
@@ -1061,7 +1128,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
identity = g_network_address_new (ssl_host, 0);
conn = g_initable_new (g_tls_backend_get_client_connection_type (backend),
- NULL, NULL,
+ cancellable, error,
"base-io-stream", priv->conn,
"server-identity", identity,
"database", priv->ssl_creds,
@@ -1085,7 +1152,7 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
GTlsServerConnection *conn;
conn = g_initable_new (g_tls_backend_get_server_connection_type (backend),
- NULL, NULL,
+ cancellable, error,
"base-io-stream", priv->conn,
"certificate", priv->ssl_creds,
"use-system-certdb", FALSE,
@@ -1110,76 +1177,103 @@ soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
return TRUE;
}
+
+/**
+ * soup_socket_start_ssl:
+ * @sock: the socket
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_ssl (SoupSocket *sock, GCancellable *cancellable)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ return soup_socket_setup_ssl (sock, soup_address_get_name (priv->remote_addr),
+ cancellable, NULL);
+}
-guint
+/**
+ * soup_socket_start_proxy_ssl:
+ * @sock: the socket
+ * @ssl_host: hostname of the SSL server
+ * @cancellable: a #GCancellable
+ *
+ * Starts using SSL on @socket, expecting to find a host named
+ * @ssl_host.
+ *
+ * Return value: success or failure
+ **/
+gboolean
+soup_socket_start_proxy_ssl (SoupSocket *sock, const char *ssl_host,
+ GCancellable *cancellable)
+{
+ return soup_socket_setup_ssl (sock, ssl_host, cancellable, NULL);
+}
+
+gboolean
soup_socket_handshake_sync (SoupSocket *sock,
- GCancellable *cancellable)
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GError **error)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
- GError *error = NULL;
- priv->ssl = TRUE;
- if (g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
- cancellable, &error))
- return SOUP_STATUS_OK;
- else if (!priv->ssl_fallback &&
- g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS)) {
- g_error_free (error);
- return SOUP_STATUS_TLS_FAILED;
- } else {
- g_error_free (error);
- return SOUP_STATUS_SSL_FAILED;
- }
+ if (!soup_socket_setup_ssl (sock, ssl_host, cancellable, error))
+ return FALSE;
+
+ return g_tls_connection_handshake (G_TLS_CONNECTION (priv->conn),
+ cancellable, error);
}
static void
handshake_async_ready (GObject *source, GAsyncResult *result, gpointer user_data)
{
- SoupSocketAsyncConnectData *data = user_data;
- SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (data->sock);
+ GTask *task = user_data;
GError *error = NULL;
- guint status;
-
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_pop_thread_default (priv->async_context);
if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (source),
result, &error))
- status = SOUP_STATUS_OK;
- else if (!priv->ssl_fallback &&
- g_error_matches (error, G_TLS_ERROR, G_TLS_ERROR_NOT_TLS))
- status = SOUP_STATUS_TLS_FAILED;
+ g_task_return_boolean (task, TRUE);
else
- status = SOUP_STATUS_SSL_FAILED;
- g_clear_error (&error);
-
- data->callback (data->sock, status, data->user_data);
- g_object_unref (data->sock);
- g_slice_free (SoupSocketAsyncConnectData, data);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
void
-soup_socket_handshake_async (SoupSocket *sock,
- GCancellable *cancellable,
- SoupSocketCallback callback,
- gpointer user_data)
+soup_socket_handshake_async (SoupSocket *sock,
+ const char *ssl_host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
- SoupSocketAsyncConnectData *data;
+ GTask *task;
+ GError *error = NULL;
- priv->ssl = TRUE;
+ task = g_task_new (sock, cancellable, callback, user_data);
- data = g_slice_new (SoupSocketAsyncConnectData);
- data->sock = g_object_ref (sock);
- data->callback = callback;
- data->user_data = user_data;
+ if (!soup_socket_setup_ssl (sock, ssl_host, cancellable, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_push_thread_default (priv->async_context);
g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn),
G_PRIORITY_DEFAULT,
cancellable, handshake_async_ready,
- data);
+ task);
+}
+
+gboolean
+soup_socket_handshake_finish (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
}
/**
@@ -1242,8 +1336,10 @@ soup_socket_disconnect (SoupSocket *sock)
*/
g_object_ref (sock);
- /* Give all readers a chance to notice the connection close */
- g_signal_emit (sock, signals[READABLE], 0);
+ if (priv->non_blocking) {
+ /* Give all readers a chance to notice the connection close */
+ g_signal_emit (sock, signals[READABLE], 0);
+ }
/* FIXME: can't disconnect until all data is read */
@@ -1278,6 +1374,9 @@ soup_socket_is_connected (SoupSocket *sock)
*
* Returns the #SoupAddress corresponding to the local end of @sock.
*
+ * Calling this method on an unconnected socket is considered to be
+ * an error, and produces undefined results.
+ *
* Return value: (transfer none): the #SoupAddress
**/
SoupAddress *
@@ -1293,13 +1392,25 @@ soup_socket_get_local_address (SoupSocket *sock)
GSocketAddress *addr;
struct sockaddr_storage sa;
gssize sa_len;
+ GError *error = NULL;
- addr = g_socket_get_local_address (priv->gsock, NULL);
+ if (priv->gsock == NULL) {
+ g_warning ("%s: socket not connected", G_STRLOC);
+ goto unlock;
+ }
+
+ addr = g_socket_get_local_address (priv->gsock, &error);
+ if (addr == NULL) {
+ g_warning ("%s: %s", G_STRLOC, error->message);
+ g_error_free (error);
+ goto unlock;
+ }
sa_len = g_socket_address_get_native_size (addr);
g_socket_address_to_native (addr, &sa, sa_len, NULL);
priv->local_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
g_object_unref (addr);
}
+unlock:
g_mutex_unlock (&priv->addrlock);
return priv->local_addr;
@@ -1311,6 +1422,9 @@ soup_socket_get_local_address (SoupSocket *sock)
*
* Returns the #SoupAddress corresponding to the remote end of @sock.
*
+ * Calling this method on an unconnected socket is considered to be
+ * an error, and produces undefined results.
+ *
* Return value: (transfer none): the #SoupAddress
**/
SoupAddress *
@@ -1326,13 +1440,25 @@ soup_socket_get_remote_address (SoupSocket *sock)
GSocketAddress *addr;
struct sockaddr_storage sa;
gssize sa_len;
+ GError *error = NULL;
+
+ if (priv->gsock == NULL) {
+ g_warning ("%s: socket not connected", G_STRLOC);
+ goto unlock;
+ }
- addr = g_socket_get_remote_address (priv->gsock, NULL);
+ addr = g_socket_get_remote_address (priv->gsock, &error);
+ if (addr == NULL) {
+ g_warning ("%s: %s", G_STRLOC, error->message);
+ g_error_free (error);
+ goto unlock;
+ }
sa_len = g_socket_address_get_native_size (addr);
g_socket_address_to_native (addr, &sa, sa_len, NULL);
priv->remote_addr = soup_address_new_from_sockaddr ((struct sockaddr *)&sa, sa_len);
g_object_unref (addr);
}
+unlock:
g_mutex_unlock (&priv->addrlock);
return priv->remote_addr;
@@ -1344,18 +1470,24 @@ soup_socket_get_http_proxy_uri (SoupSocket *sock)
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
GSocketAddress *addr;
GProxyAddress *paddr;
+ SoupURI *uri;
if (!priv->gsock)
return NULL;
addr = g_socket_get_remote_address (priv->gsock, NULL);
- if (!addr || !G_IS_PROXY_ADDRESS (addr))
+ if (!addr || !G_IS_PROXY_ADDRESS (addr)) {
+ if (addr)
+ g_object_unref (addr);
return NULL;
+ }
paddr = G_PROXY_ADDRESS (addr);
if (strcmp (g_proxy_address_get_protocol (paddr), "http") != 0)
return NULL;
- return soup_uri_new (g_proxy_address_get_uri (paddr));
+ uri = soup_uri_new (g_proxy_address_get_uri (paddr));
+ g_object_unref (addr);
+ return uri;
}
static gboolean
@@ -1530,7 +1662,7 @@ soup_socket_read_until (SoupSocket *sock, gpointer buffer, gsize len,
SOUP_FILTER_INPUT_STREAM (priv->istream),
buffer, len, boundary, boundary_len,
!priv->non_blocking,
- got_boundary, cancellable, &my_err);
+ TRUE, got_boundary, cancellable, &my_err);
status = translate_read_status (sock, cancellable,
my_nread, nread, my_err, error);
}