summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2014-05-02 09:12:22 -0400
committerDan Winship <danw@gnome.org>2014-05-02 09:12:22 -0400
commitd17d1baeb000ce398f61d58a813a69a8bd7cef08 (patch)
tree3950f35597ef5774ee15b99c26dbe1809eacd912
parent3ccb212449e7ac4f43888eee93366be934a87445 (diff)
parentdc94ea443305ae2a326f462bf8ce9fc03a3ad4b9 (diff)
downloadlibsoup-d17d1baeb000ce398f61d58a813a69a8bd7cef08.tar.gz
soup-socket: misc improvements
-rw-r--r--libsoup/Makefile.am2
-rw-r--r--libsoup/soup-connection.c170
-rw-r--r--libsoup/soup-connection.h16
-rw-r--r--libsoup/soup-message-client-io.c2
-rw-r--r--libsoup/soup-message-server-io.c1
-rw-r--r--libsoup/soup-misc-private.h33
-rw-r--r--libsoup/soup-session.c124
-rw-r--r--libsoup/soup-socket-private.h82
-rw-r--r--libsoup/soup-socket-properties.c74
-rw-r--r--libsoup/soup-socket.c221
-rw-r--r--po/POTFILES.in1
-rw-r--r--tests/socket-test.c231
12 files changed, 675 insertions, 282 deletions
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 60920bdc..25a8dc1a 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -176,6 +176,8 @@ libsoup_2_4_la_SOURCES = \
soup-session-private.h \
soup-session-sync.c \
soup-socket.c \
+ soup-socket-private.h \
+ soup-socket-properties.c \
soup-status.c \
soup-tld.c \
soup-tld-private.h \
diff --git a/libsoup/soup-connection.c b/libsoup/soup-connection.c
index e0dbd59e..92571faa 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -12,24 +12,18 @@
#include "soup-connection.h"
#include "soup.h"
#include "soup-message-queue.h"
-#include "soup-misc-private.h"
+#include "soup-socket-private.h"
typedef struct {
SoupSocket *socket;
+ SoupSocketProperties *socket_props;
- SoupAddress *local_addr;
SoupURI *remote_uri, *proxy_uri;
- GProxyResolver *proxy_resolver;
- GTlsDatabase *tlsdb;
- gboolean ssl, ssl_strict, ssl_fallback;
-
- GMainContext *async_context;
- gboolean use_thread_context;
+ gboolean ssl, ssl_fallback;
SoupMessage *current_msg;
SoupConnectionState state;
time_t unused_timeout;
- guint io_timeout, idle_timeout;
GSource *idle_timeout_src;
gboolean reusable;
} SoupConnectionPrivate;
@@ -48,16 +42,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
- PROP_LOCAL_ADDRESS,
PROP_REMOTE_URI,
- PROP_PROXY_RESOLVER,
- PROP_SSL_CREDS,
- PROP_SSL_STRICT,
PROP_SSL_FALLBACK,
- PROP_ASYNC_CONTEXT,
- PROP_USE_THREAD_CONTEXT,
- PROP_TIMEOUT,
- PROP_IDLE_TIMEOUT,
+ PROP_SOCKET_PROPERTIES,
PROP_STATE,
LAST_PROP
@@ -82,10 +69,7 @@ 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_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_clear_pointer (&priv->socket_props, soup_socket_properties_unref);
G_OBJECT_CLASS (soup_connection_parent_class)->finalize (object);
}
@@ -108,9 +92,6 @@ soup_connection_set_property (GObject *object, guint prop_id,
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (object);
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);
if (priv->remote_uri)
@@ -118,33 +99,12 @@ soup_connection_set_property (GObject *object, guint prop_id,
else
priv->ssl = FALSE;
break;
- case PROP_PROXY_RESOLVER:
- priv->proxy_resolver = g_value_dup_object (value);
- break;
- case PROP_SSL_CREDS:
- if (priv->tlsdb)
- g_object_unref (priv->tlsdb);
- priv->tlsdb = g_value_dup_object (value);
- break;
- case PROP_SSL_STRICT:
- priv->ssl_strict = g_value_get_boolean (value);
break;
case PROP_SSL_FALLBACK:
priv->ssl_fallback = g_value_get_boolean (value);
break;
- case PROP_ASYNC_CONTEXT:
- priv->async_context = g_value_get_pointer (value);
- if (priv->async_context)
- g_main_context_ref (priv->async_context);
- break;
- case PROP_USE_THREAD_CONTEXT:
- priv->use_thread_context = g_value_get_boolean (value);
- break;
- case PROP_TIMEOUT:
- priv->io_timeout = g_value_get_uint (value);
- break;
- case PROP_IDLE_TIMEOUT:
- priv->idle_timeout = g_value_get_uint (value);
+ case PROP_SOCKET_PROPERTIES:
+ priv->socket_props = g_value_dup_boxed (value);
break;
case PROP_STATE:
soup_connection_set_state (SOUP_CONNECTION (object), g_value_get_uint (value));
@@ -162,32 +122,14 @@ 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;
- case PROP_SSL_CREDS:
- g_value_set_object (value, priv->tlsdb);
- break;
- case PROP_SSL_STRICT:
- g_value_set_boolean (value, priv->ssl_strict);
- break;
case PROP_SSL_FALLBACK:
g_value_set_boolean (value, priv->ssl_fallback);
break;
- case PROP_ASYNC_CONTEXT:
- g_value_set_pointer (value, priv->async_context ? g_main_context_ref (priv->async_context) : NULL);
- break;
- case PROP_USE_THREAD_CONTEXT:
- g_value_set_boolean (value, priv->use_thread_context);
- break;
- case PROP_TIMEOUT:
- g_value_set_uint (value, priv->io_timeout);
- break;
- case PROP_IDLE_TIMEOUT:
- g_value_set_uint (value, priv->idle_timeout);
+ case PROP_SOCKET_PROPERTIES:
+ g_value_set_boxed (value, priv->socket_props);
break;
case PROP_STATE:
g_value_set_enum (value, priv->state);
@@ -233,13 +175,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
/* 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",
@@ -247,27 +182,6 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
SOUP_TYPE_URI,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
- object_class, PROP_PROXY_RESOLVER,
- g_param_spec_object (SOUP_CONNECTION_PROXY_RESOLVER,
- "Proxy resolver",
- "GProxyResolver to use",
- G_TYPE_PROXY_RESOLVER,
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (
- object_class, PROP_SSL_CREDS,
- g_param_spec_object (SOUP_CONNECTION_SSL_CREDENTIALS,
- "SSL credentials",
- "SSL credentials for this connection",
- G_TYPE_TLS_DATABASE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (
- object_class, PROP_SSL_STRICT,
- g_param_spec_boolean (SOUP_CONNECTION_SSL_STRICT,
- "Strictly validate SSL certificates",
- "Whether certificate errors should be considered a connection error",
- TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (
object_class, PROP_SSL_FALLBACK,
g_param_spec_boolean (SOUP_CONNECTION_SSL_FALLBACK,
"SSLv3 fallback",
@@ -275,32 +189,12 @@ soup_connection_class_init (SoupConnectionClass *connection_class)
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
- object_class, PROP_ASYNC_CONTEXT,
- g_param_spec_pointer (SOUP_CONNECTION_ASYNC_CONTEXT,
- "Async GMainContext",
- "GMainContext to dispatch this connection's async I/O in",
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (
- object_class, PROP_USE_THREAD_CONTEXT,
- g_param_spec_boolean (SOUP_CONNECTION_USE_THREAD_CONTEXT,
- "Use thread context",
- "Use g_main_context_get_thread_default",
- FALSE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
- g_object_class_install_property (
- object_class, PROP_TIMEOUT,
- g_param_spec_uint (SOUP_CONNECTION_TIMEOUT,
- "Timeout value",
- "Value in seconds to timeout a blocking I/O",
- 0, G_MAXUINT, 0,
- G_PARAM_READWRITE));
- g_object_class_install_property (
- object_class, PROP_IDLE_TIMEOUT,
- g_param_spec_uint (SOUP_CONNECTION_IDLE_TIMEOUT,
- "Idle Timeout",
- "Connection lifetime when idle",
- 0, G_MAXUINT, 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ object_class, PROP_SOCKET_PROPERTIES,
+ g_param_spec_boxed (SOUP_CONNECTION_SOCKET_PROPERTIES,
+ "Socket properties",
+ "Socket properties",
+ SOUP_TYPE_SOCKET_PROPERTIES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property (
object_class, PROP_STATE,
g_param_spec_enum (SOUP_CONNECTION_STATE,
@@ -336,10 +230,10 @@ start_idle_timer (SoupConnection *conn)
{
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- if (priv->idle_timeout > 0 && !priv->idle_timeout_src) {
+ if (priv->socket_props->idle_timeout > 0 && !priv->idle_timeout_src) {
priv->idle_timeout_src =
- soup_add_timeout (priv->async_context,
- priv->idle_timeout * 1000,
+ soup_add_timeout (priv->socket_props->async_context,
+ priv->socket_props->idle_timeout * 1000,
idle_timeout, conn);
}
}
@@ -433,8 +327,7 @@ socket_connect_finished (GTask *task, SoupSocket *sock, GError *error)
SoupConnection *conn = g_task_get_source_object (task);
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_pop_thread_default (priv->async_context);
+ soup_socket_properties_pop_async_context (priv->socket_props);
g_signal_handlers_disconnect_by_func (sock, G_CALLBACK (re_emit_socket_event), conn);
@@ -527,23 +420,15 @@ soup_connection_connect_async (SoupConnection *conn,
priv->socket =
soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
- SOUP_SOCKET_SSL_CREDENTIALS, priv->tlsdb,
- SOUP_SOCKET_SSL_STRICT, priv->ssl_strict,
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_PROXY_RESOLVER, priv->proxy_resolver,
- SOUP_SOCKET_TIMEOUT, priv->io_timeout,
- SOUP_SOCKET_CLEAN_DISPOSE, TRUE,
- SOUP_SOCKET_LOCAL_ADDRESS, priv->local_addr,
+ SOUP_SOCKET_SOCKET_PROPERTIES, priv->socket_props,
NULL);
g_object_unref (remote_addr);
g_signal_connect (priv->socket, "event",
G_CALLBACK (re_emit_socket_event), conn);
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_push_thread_default (priv->async_context);
+ soup_socket_properties_push_async_context (priv->socket_props);
task = g_task_new (conn, cancellable, callback, user_data);
soup_socket_connect_async_internal (priv->socket, cancellable,
@@ -584,14 +469,9 @@ soup_connection_connect_sync (SoupConnection *conn,
priv->socket =
soup_socket_new (SOUP_SOCKET_REMOTE_ADDRESS, remote_addr,
- 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_SOCKET_PROPERTIES, priv->socket_props,
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);
@@ -674,8 +554,7 @@ start_ssl_completed (GObject *object, GAsyncResult *result, gpointer user_data)
SoupConnectionPrivate *priv = SOUP_CONNECTION_GET_PRIVATE (conn);
GError *error = NULL;
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_pop_thread_default (priv->async_context);
+ soup_socket_properties_pop_async_context (priv->socket_props);
if (soup_socket_handshake_finish (priv->socket, result, &error)) {
soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKED, NULL);
@@ -700,8 +579,7 @@ soup_connection_start_ssl_async (SoupConnection *conn,
soup_connection_event (conn, G_SOCKET_CLIENT_TLS_HANDSHAKING, NULL);
- if (priv->async_context && !priv->use_thread_context)
- g_main_context_push_thread_default (priv->async_context);
+ soup_socket_properties_push_async_context (priv->socket_props);
task = g_task_new (conn, cancellable, callback, user_data);
soup_socket_handshake_async (priv->socket, priv->remote_uri->host,
diff --git a/libsoup/soup-connection.h b/libsoup/soup-connection.h
index 454432dd..8df61129 100644
--- a/libsoup/soup-connection.h
+++ b/libsoup/soup-connection.h
@@ -35,18 +35,10 @@ typedef struct {
GType soup_connection_get_type (void);
-#define SOUP_CONNECTION_LOCAL_ADDRESS "local-address"
-#define SOUP_CONNECTION_REMOTE_URI "remote-uri"
-#define SOUP_CONNECTION_PROXY_RESOLVER "proxy-resolver"
-#define SOUP_CONNECTION_SSL_CREDENTIALS "ssl-creds"
-#define SOUP_CONNECTION_SSL_STRICT "ssl-strict"
-#define SOUP_CONNECTION_SSL_FALLBACK "ssl-fallback"
-#define SOUP_CONNECTION_ASYNC_CONTEXT "async-context"
-#define SOUP_CONNECTION_USE_THREAD_CONTEXT "use-thread-context"
-#define SOUP_CONNECTION_TIMEOUT "timeout"
-#define SOUP_CONNECTION_IDLE_TIMEOUT "idle-timeout"
-#define SOUP_CONNECTION_STATE "state"
-#define SOUP_CONNECTION_MESSAGE "message"
+#define SOUP_CONNECTION_REMOTE_URI "remote-uri"
+#define SOUP_CONNECTION_SSL_FALLBACK "ssl-fallback"
+#define SOUP_CONNECTION_SOCKET_PROPERTIES "socket-properties"
+#define SOUP_CONNECTION_STATE "state"
void soup_connection_connect_async (SoupConnection *conn,
GCancellable *cancellable,
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
index b145bbaf..f571ef73 100644
--- a/libsoup/soup-message-client-io.c
+++ b/libsoup/soup-message-client-io.c
@@ -17,7 +17,7 @@
#include "soup-connection.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
-#include "soup-misc-private.h"
+#include "soup-socket-private.h"
static guint
parse_response_headers (SoupMessage *msg,
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 2647b811..f70e9a9d 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -16,6 +16,7 @@
#include "soup.h"
#include "soup-message-private.h"
#include "soup-misc-private.h"
+#include "soup-socket-private.h"
static guint
parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h
index 50a3b988..94aa71d1 100644
--- a/libsoup/soup-misc-private.h
+++ b/libsoup/soup-misc-private.h
@@ -7,7 +7,6 @@
#ifndef SOUP_MISC_PRIVATE_H
#define SOUP_MISC_PRIVATE_H 1
-#include "soup-socket.h"
#include "soup-message-headers.h"
char *soup_uri_decoded_copy (const char *str, int length, int *decoded_length);
@@ -16,38 +15,6 @@ char *soup_uri_to_string_internal (SoupURI *uri, gboolean just_path_and_query,
gboolean soup_uri_is_http (SoupURI *uri, char **aliases);
gboolean soup_uri_is_https (SoupURI *uri, char **aliases);
-gboolean soup_socket_connect_sync_internal (SoupSocket *sock,
- GCancellable *cancellable,
- GError **error);
-void soup_socket_connect_async_internal (SoupSocket *sock,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean soup_socket_connect_finish_internal (SoupSocket *sock,
- GAsyncResult *result,
- GError **error);
-
-gboolean soup_socket_handshake_sync (SoupSocket *sock,
- const char *host,
- GCancellable *cancellable,
- GError **error);
-void soup_socket_handshake_async (SoupSocket *sock,
- const char *host,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean soup_socket_handshake_finish (SoupSocket *sock,
- GAsyncResult *result,
- GError **error);
-
-GSocket *soup_socket_get_gsocket (SoupSocket *sock);
-GIOStream *soup_socket_get_connection (SoupSocket *sock);
-GIOStream *soup_socket_get_iostream (SoupSocket *sock);
-
-#define SOUP_SOCKET_CLEAN_DISPOSE "clean-dispose"
-#define SOUP_SOCKET_PROXY_RESOLVER "proxy-resolver"
-SoupURI *soup_socket_get_http_proxy_uri (SoupSocket *sock);
-
/* At some point it might be possible to mark additional methods
* safe or idempotent...
*/
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index ca190d1e..6deff7ed 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -21,6 +21,7 @@
#include "soup-message-queue.h"
#include "soup-proxy-resolver-wrapper.h"
#include "soup-session-private.h"
+#include "soup-socket-private.h"
#define HOST_KEEP_ALIVE 5 * 60 * 1000 /* 5 min in msecs */
@@ -90,6 +91,16 @@ typedef struct {
gboolean ssl_strict;
gboolean tlsdb_use_default;
+ guint io_timeout, idle_timeout;
+ SoupAddress *local_addr;
+
+ GResolver *resolver;
+ GProxyResolver *proxy_resolver;
+ gboolean proxy_use_default;
+ SoupURI *proxy_uri;
+
+ SoupSocketProperties *socket_props;
+
SoupMessageQueue *queue;
char *user_agent;
@@ -103,9 +114,6 @@ typedef struct {
GHashTable *conns; /* SoupConnection -> SoupSessionHost */
guint num_conns;
guint max_conns, max_conns_per_host;
- guint io_timeout, idle_timeout;
-
- SoupAddress *local_addr;
/* Must hold the conn_lock before potentially creating a new
* SoupSessionHost, adding/removing a connection,
@@ -121,11 +129,6 @@ typedef struct {
gboolean use_thread_context;
GSList *run_queue_sources;
- GResolver *resolver;
- GProxyResolver *proxy_resolver;
- gboolean proxy_use_default;
- SoupURI *proxy_uri;
-
char **http_aliases, **https_aliases;
GHashTable *request_types;
@@ -350,9 +353,42 @@ soup_session_finalize (GObject *object)
g_hash_table_destroy (priv->request_types);
+ g_clear_pointer (&priv->socket_props, soup_socket_properties_unref);
+
G_OBJECT_CLASS (soup_session_parent_class)->finalize (object);
}
+static void
+ensure_socket_props (SoupSession *session)
+{
+ SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+ gboolean ssl_strict;
+
+ if (priv->socket_props)
+ return;
+
+ if (priv->proxy_use_default) {
+ priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
+ priv->proxy_use_default = FALSE;
+ }
+ if (priv->tlsdb_use_default) {
+ priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
+ priv->tlsdb_use_default = FALSE;
+ }
+
+ ssl_strict = priv->ssl_strict && (priv->tlsdb != NULL ||
+ SOUP_IS_PLAIN_SESSION (priv->session));
+
+ priv->socket_props = soup_socket_properties_new (priv->async_context,
+ priv->use_thread_context,
+ priv->proxy_resolver,
+ priv->local_addr,
+ priv->tlsdb,
+ ssl_strict,
+ priv->io_timeout,
+ priv->idle_timeout);
+}
+
/* Converts a language in POSIX format and to be RFC2616 compliant */
/* Based on code from epiphany-webkit (ephy_langs_append_languages()) */
static gchar *
@@ -600,19 +636,23 @@ soup_session_set_property (GObject *object, guint prop_id,
const char *user_agent;
SoupSessionFeature *feature;
GMainContext *async_context;
+ gboolean socket_props_changed = FALSE;
switch (prop_id) {
case PROP_LOCAL_ADDRESS:
priv->local_addr = g_value_dup_object (value);
+ socket_props_changed = TRUE;
break;
case PROP_PROXY_URI:
set_proxy_resolver (session, g_value_get_boxed (value),
NULL, NULL);
soup_session_abort (session);
+ socket_props_changed = TRUE;
break;
case PROP_PROXY_RESOLVER:
set_proxy_resolver (session, NULL, NULL,
g_value_get_object (value));
+ socket_props_changed = TRUE;
break;
case PROP_MAX_CONNS:
priv->max_conns = g_value_get_int (value);
@@ -633,15 +673,19 @@ soup_session_set_property (GObject *object, guint prop_id,
break;
case PROP_SSL_CA_FILE:
set_ssl_ca_file (session, g_value_get_string (value));
+ socket_props_changed = TRUE;
break;
case PROP_SSL_USE_SYSTEM_CA_FILE:
set_use_system_ca_file (session, g_value_get_boolean (value));
+ socket_props_changed = TRUE;
break;
case PROP_TLS_DATABASE:
set_tlsdb (session, g_value_get_object (value));
+ socket_props_changed = TRUE;
break;
case PROP_SSL_STRICT:
priv->ssl_strict = g_value_get_boolean (value);
+ socket_props_changed = TRUE;
break;
case PROP_ASYNC_CONTEXT:
async_context = g_value_get_pointer (value);
@@ -650,6 +694,7 @@ soup_session_set_property (GObject *object, guint prop_id,
priv->async_context = async_context;
if (priv->async_context)
g_main_context_ref (priv->async_context);
+ socket_props_changed = TRUE;
break;
case PROP_USE_THREAD_CONTEXT:
if (!g_value_get_boolean (value))
@@ -662,9 +707,11 @@ soup_session_set_property (GObject *object, guint prop_id,
if (priv->async_context)
g_main_context_ref (priv->async_context);
}
+ socket_props_changed = TRUE;
break;
case PROP_TIMEOUT:
priv->io_timeout = g_value_get_uint (value);
+ socket_props_changed = TRUE;
break;
case PROP_USER_AGENT:
g_free (priv->user_agent);
@@ -699,6 +746,7 @@ soup_session_set_property (GObject *object, guint prop_id,
break;
case PROP_IDLE_TIMEOUT:
priv->idle_timeout = g_value_get_uint (value);
+ socket_props_changed = TRUE;
break;
case PROP_ADD_FEATURE:
soup_session_add_feature (session, g_value_get_object (value));
@@ -719,30 +767,12 @@ soup_session_set_property (GObject *object, guint prop_id,
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
-}
-static GProxyResolver *
-get_proxy_resolver (SoupSession *session)
-{
- SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-
- if (priv->proxy_use_default) {
- priv->proxy_resolver = g_object_ref (g_proxy_resolver_get_default ());
- priv->proxy_use_default = FALSE;
+ if (priv->socket_props && socket_props_changed) {
+ soup_socket_properties_unref (priv->socket_props);
+ priv->socket_props = NULL;
+ ensure_socket_props (session);
}
- return priv->proxy_resolver;
-}
-
-static GTlsDatabase *
-get_tls_database (SoupSession *session)
-{
- SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-
- if (priv->tlsdb_use_default) {
- priv->tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
- priv->tlsdb_use_default = FALSE;
- }
- return priv->tlsdb;
}
static void
@@ -762,7 +792,8 @@ soup_session_get_property (GObject *object, guint prop_id,
g_value_set_boxed (value, priv->proxy_uri);
break;
case PROP_PROXY_RESOLVER:
- g_value_set_object (value, get_proxy_resolver (session));
+ ensure_socket_props (session);
+ g_value_set_object (value, priv->proxy_resolver);
break;
case PROP_MAX_CONNS:
g_value_set_int (value, priv->max_conns);
@@ -782,11 +813,13 @@ soup_session_get_property (GObject *object, guint prop_id,
break;
case PROP_SSL_USE_SYSTEM_CA_FILE:
tlsdb = g_tls_backend_get_default_database (g_tls_backend_get_default ());
- g_value_set_boolean (value, get_tls_database (session) == tlsdb);
+ ensure_socket_props (session);
+ g_value_set_boolean (value, priv->tlsdb == tlsdb);
g_clear_object (&tlsdb);
break;
case PROP_TLS_DATABASE:
- g_value_set_object (value, get_tls_database (session));
+ ensure_socket_props (session);
+ g_value_set_object (value, priv->tlsdb);
break;
case PROP_SSL_STRICT:
g_value_set_boolean (value, priv->ssl_strict);
@@ -1739,8 +1772,6 @@ get_connection_for_host (SoupSession *session,
SoupConnection *conn;
GSList *conns;
int num_pending = 0;
- GProxyResolver *proxy_resolver;
- GTlsDatabase *tlsdb;
if (priv->disposed)
return FALSE;
@@ -1777,22 +1808,13 @@ get_connection_for_host (SoupSession *session,
return NULL;
}
- proxy_resolver = get_proxy_resolver (session);
- tlsdb = get_tls_database (session);
-
- conn = g_object_new (
- SOUP_TYPE_CONNECTION,
- SOUP_CONNECTION_REMOTE_URI, host->uri,
- SOUP_CONNECTION_PROXY_RESOLVER, proxy_resolver,
- SOUP_CONNECTION_SSL_CREDENTIALS, tlsdb,
- SOUP_CONNECTION_SSL_STRICT, priv->ssl_strict && (tlsdb != NULL || SOUP_IS_PLAIN_SESSION (session)),
- SOUP_CONNECTION_ASYNC_CONTEXT, priv->async_context,
- SOUP_CONNECTION_USE_THREAD_CONTEXT, priv->use_thread_context,
- SOUP_CONNECTION_TIMEOUT, priv->io_timeout,
- SOUP_CONNECTION_IDLE_TIMEOUT, priv->idle_timeout,
- SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
- SOUP_CONNECTION_LOCAL_ADDRESS, priv->local_addr,
- NULL);
+ ensure_socket_props (session);
+ conn = g_object_new (SOUP_TYPE_CONNECTION,
+ SOUP_CONNECTION_REMOTE_URI, host->uri,
+ SOUP_CONNECTION_SSL_FALLBACK, host->ssl_fallback,
+ SOUP_CONNECTION_SOCKET_PROPERTIES, priv->socket_props,
+ NULL);
+
g_signal_connect (conn, "disconnected",
G_CALLBACK (connection_disconnected),
session);
diff --git a/libsoup/soup-socket-private.h b/libsoup/soup-socket-private.h
new file mode 100644
index 00000000..159dd241
--- /dev/null
+++ b/libsoup/soup-socket-private.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2011-2014 Red Hat, Inc.
+ */
+
+#ifndef SOUP_SOCKET_PRIVATE_H
+#define SOUP_SOCKET_PRIVATE_H 1
+
+#include "soup-socket.h"
+
+#define SOUP_SOCKET_SOCKET_PROPERTIES "socket-properties"
+#define SOUP_SOCKET_CLOSE_ON_DISPOSE "close-on-dispose"
+#define SOUP_SOCKET_FD "fd"
+#define SOUP_SOCKET_GSOCKET "gsocket"
+
+gboolean soup_socket_connect_sync_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GError **error);
+void soup_socket_connect_async_internal (SoupSocket *sock,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_socket_connect_finish_internal (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error);
+
+gboolean soup_socket_handshake_sync (SoupSocket *sock,
+ const char *host,
+ GCancellable *cancellable,
+ GError **error);
+void soup_socket_handshake_async (SoupSocket *sock,
+ const char *host,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean soup_socket_handshake_finish (SoupSocket *sock,
+ GAsyncResult *result,
+ GError **error);
+
+GSocket *soup_socket_get_gsocket (SoupSocket *sock);
+GIOStream *soup_socket_get_connection (SoupSocket *sock);
+GIOStream *soup_socket_get_iostream (SoupSocket *sock);
+
+SoupURI *soup_socket_get_http_proxy_uri (SoupSocket *sock);
+
+
+typedef struct {
+ GMainContext *async_context;
+ gboolean use_thread_context;
+
+ GProxyResolver *proxy_resolver;
+ SoupAddress *local_addr;
+
+ GTlsDatabase *tlsdb;
+ gboolean ssl_strict;
+
+ guint io_timeout;
+ guint idle_timeout;
+
+ /*< private >*/
+ guint ref_count;
+} SoupSocketProperties;
+
+GType soup_socket_properties_get_type (void);
+#define SOUP_TYPE_SOCKET_PROPERTIES (soup_socket_properties_get_type ())
+
+SoupSocketProperties *soup_socket_properties_new (GMainContext *async_context,
+ gboolean use_thread_context,
+ GProxyResolver *proxy_resolver,
+ SoupAddress *local_addr,
+ GTlsDatabase *tlsdb,
+ gboolean ssl_strict,
+ guint io_timeout,
+ guint idle_timeout);
+
+SoupSocketProperties *soup_socket_properties_ref (SoupSocketProperties *props);
+void soup_socket_properties_unref (SoupSocketProperties *props);
+
+void soup_socket_properties_push_async_context (SoupSocketProperties *props);
+void soup_socket_properties_pop_async_context (SoupSocketProperties *props);
+
+#endif /* SOUP_SOCKET_PRIVATE_H */
diff --git a/libsoup/soup-socket-properties.c b/libsoup/soup-socket-properties.c
new file mode 100644
index 00000000..a7ed5128
--- /dev/null
+++ b/libsoup/soup-socket-properties.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#include "soup-socket-private.h"
+#include "soup.h"
+
+SoupSocketProperties *
+soup_socket_properties_new (GMainContext *async_context,
+ gboolean use_thread_context,
+ GProxyResolver *proxy_resolver,
+ SoupAddress *local_addr,
+ GTlsDatabase *tlsdb,
+ gboolean ssl_strict,
+ guint io_timeout,
+ guint idle_timeout)
+{
+ SoupSocketProperties *props;
+
+ props = g_slice_new (SoupSocketProperties);
+ props->ref_count = 1;
+
+ props->async_context = async_context ? g_main_context_ref (async_context) : NULL;
+ props->use_thread_context = use_thread_context;
+
+ props->proxy_resolver = proxy_resolver ? g_object_ref (proxy_resolver) : NULL;
+ props->local_addr = local_addr ? g_object_ref (local_addr) : NULL;
+
+ props->tlsdb = tlsdb ? g_object_ref (tlsdb) : NULL;
+ props->ssl_strict = ssl_strict;
+
+ props->io_timeout = io_timeout;
+ props->idle_timeout = idle_timeout;
+
+ return props;
+}
+
+SoupSocketProperties *
+soup_socket_properties_ref (SoupSocketProperties *props)
+{
+ props->ref_count++;
+ return props;
+}
+
+void
+soup_socket_properties_unref (SoupSocketProperties *props)
+{
+ if (--props->ref_count)
+ return;
+
+ g_clear_pointer (&props->async_context, g_main_context_unref);
+ g_clear_object (&props->proxy_resolver);
+ g_clear_object (&props->local_addr);
+ g_clear_object (&props->tlsdb);
+
+ g_slice_free (SoupSocketProperties, props);
+}
+
+void
+soup_socket_properties_push_async_context (SoupSocketProperties *props)
+{
+ if (props->async_context && !props->use_thread_context)
+ g_main_context_push_thread_default (props->async_context);
+}
+
+void
+soup_socket_properties_pop_async_context (SoupSocketProperties *props)
+{
+ if (props->async_context && !props->use_thread_context)
+ g_main_context_pop_thread_default (props->async_context);
+}
+
+G_DEFINE_BOXED_TYPE (SoupSocketProperties, soup_socket_properties, soup_socket_properties_ref, soup_socket_properties_unref)
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index b9f1dfca..b309169f 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -11,13 +11,14 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
#include <gio/gnetworking.h>
#include "soup-socket.h"
+#include "soup-socket-private.h"
#include "soup.h"
#include "soup-filter-input-stream.h"
#include "soup-io-stream.h"
-#include "soup-misc-private.h"
/**
* SECTION:soup-socket
@@ -29,7 +30,11 @@
* soup_socket_get_remote_address()) may be useful to applications.
**/
-G_DEFINE_TYPE (SoupSocket, soup_socket, G_TYPE_OBJECT)
+static void soup_socket_initable_interface_init (GInitableIface *initable_interface);
+
+G_DEFINE_TYPE_WITH_CODE (SoupSocket, soup_socket, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ soup_socket_initable_interface_init))
enum {
READABLE,
@@ -45,6 +50,8 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
+ PROP_FD,
+ PROP_GSOCKET,
PROP_LOCAL_ADDRESS,
PROP_REMOTE_ADDRESS,
PROP_NON_BLOCKING,
@@ -56,10 +63,10 @@ enum {
PROP_USE_THREAD_CONTEXT,
PROP_TIMEOUT,
PROP_TRUSTED_CERTIFICATE,
- PROP_CLEAN_DISPOSE,
PROP_TLS_CERTIFICATE,
PROP_TLS_ERRORS,
- PROP_PROXY_RESOLVER,
+ PROP_CLOSE_ON_DISPOSE,
+ PROP_SOCKET_PROPERTIES,
LAST_PROP
};
@@ -79,6 +86,7 @@ typedef struct {
guint ssl_strict:1;
guint ssl_fallback:1;
guint clean_dispose:1;
+ guint close_on_dispose:1;
guint use_thread_context:1;
gpointer ssl_creds;
@@ -90,12 +98,15 @@ typedef struct {
guint timeout;
GCancellable *connect_cancel;
+ int fd;
} SoupSocketPrivate;
#define SOUP_SOCKET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_SOCKET, SoupSocketPrivate))
static void soup_socket_peer_certificate_changed (GObject *conn,
GParamSpec *pspec,
gpointer user_data);
+static void finish_socket_setup (SoupSocket *sock);
+static void finish_listener_setup (SoupSocket *sock);
static void
soup_socket_init (SoupSocket *sock)
@@ -103,10 +114,63 @@ soup_socket_init (SoupSocket *sock)
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
priv->non_blocking = TRUE;
+ priv->fd = -1;
g_mutex_init (&priv->addrlock);
g_mutex_init (&priv->iolock);
}
+static gboolean
+soup_socket_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupSocket *sock = SOUP_SOCKET (initable);
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ if (priv->fd != -1) {
+ guint type, len = sizeof (type);
+
+ g_warn_if_fail (priv->gsock == NULL);
+
+ /* GSocket will g_error() this, so we have to check ourselves. */
+ if (getsockopt (priv->fd, SOL_SOCKET, SO_TYPE,
+ (gpointer)&type, (gpointer)&len) == -1) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Can't import non-socket as SoupSocket"));
+ return FALSE;
+ }
+
+ priv->gsock = g_socket_new_from_fd (priv->fd, error);
+ if (!priv->gsock)
+ return FALSE;
+ }
+
+ if (priv->gsock != NULL) {
+ int listening;
+
+ g_warn_if_fail (priv->local_addr == NULL);
+ g_warn_if_fail (priv->remote_addr == NULL);
+
+ if (!g_socket_get_option (priv->gsock,
+ SOL_SOCKET, SO_ACCEPTCONN,
+ &listening, error)) {
+ g_prefix_error (error, _("Could not import existing socket: "));
+ return FALSE;
+ }
+
+ finish_socket_setup (sock);
+ if (listening)
+ finish_listener_setup (sock);
+ else if (!g_socket_is_connected (priv->gsock)) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Can't import unconnected socket"));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
static void
disconnect_internal (SoupSocket *sock, gboolean close)
{
@@ -136,7 +200,7 @@ soup_socket_finalize (GObject *object)
g_warning ("Disposing socket %p during connect", object);
g_object_unref (priv->connect_cancel);
}
- if (priv->gsock) {
+ if (priv->gsock && priv->close_on_dispose) {
if (priv->clean_dispose)
g_warning ("Disposing socket %p while still connected", object);
disconnect_internal (SOUP_SOCKET (object), TRUE);
@@ -151,6 +215,7 @@ soup_socket_finalize (GObject *object)
g_clear_object (&priv->remote_addr);
g_clear_object (&priv->proxy_resolver);
+ g_clear_object (&priv->ssl_creds);
if (priv->watch_src) {
if (priv->clean_dispose && !priv->is_server)
@@ -165,10 +230,11 @@ soup_socket_finalize (GObject *object)
G_OBJECT_CLASS (soup_socket_parent_class)->finalize (object);
}
-
static void
-finish_socket_setup (SoupSocketPrivate *priv)
+finish_socket_setup (SoupSocket *sock)
{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
if (!priv->gsock)
return;
@@ -190,19 +256,28 @@ soup_socket_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
+ SoupSocketProperties *props;
switch (prop_id) {
+ case PROP_FD:
+ priv->fd = g_value_get_int (value);
+ break;
+ case PROP_GSOCKET:
+ priv->gsock = g_value_dup_object (value);
+ break;
case PROP_LOCAL_ADDRESS:
- priv->local_addr = (SoupAddress *)g_value_dup_object (value);
+ priv->local_addr = g_value_dup_object (value);
break;
case PROP_REMOTE_ADDRESS:
- priv->remote_addr = (SoupAddress *)g_value_dup_object (value);
+ priv->remote_addr = g_value_dup_object (value);
break;
case PROP_NON_BLOCKING:
priv->non_blocking = g_value_get_boolean (value);
break;
case PROP_SSL_CREDENTIALS:
priv->ssl_creds = g_value_get_pointer (value);
+ if (priv->ssl_creds)
+ g_object_ref (priv->ssl_creds);
break;
case PROP_SSL_STRICT:
priv->ssl_strict = g_value_get_boolean (value);
@@ -223,11 +298,35 @@ soup_socket_set_property (GObject *object, guint prop_id,
if (priv->conn)
g_socket_set_timeout (priv->gsock, priv->timeout);
break;
- case PROP_PROXY_RESOLVER:
- priv->proxy_resolver = g_value_dup_object (value);
+ case PROP_SOCKET_PROPERTIES:
+ props = g_value_get_boxed (value);
+ if (props) {
+ g_clear_pointer (&priv->async_context, g_main_context_unref);
+ if (props->async_context)
+ priv->async_context = g_main_context_ref (props->async_context);
+ priv->use_thread_context = props->use_thread_context;
+
+ g_clear_object (&priv->proxy_resolver);
+ if (props->proxy_resolver)
+ priv->proxy_resolver = g_object_ref (props->proxy_resolver);
+ g_clear_object (&priv->local_addr);
+ if (props->local_addr)
+ priv->local_addr = g_object_ref (props->local_addr);
+
+ g_clear_object (&priv->ssl_creds);
+ if (props->tlsdb)
+ priv->ssl_creds = g_object_ref (props->tlsdb);
+ priv->ssl_strict = props->ssl_strict;
+
+ priv->timeout = props->io_timeout;
+ if (priv->conn)
+ g_socket_set_timeout (priv->gsock, priv->timeout);
+
+ priv->clean_dispose = TRUE;
+ }
break;
- case PROP_CLEAN_DISPOSE:
- priv->clean_dispose = g_value_get_boolean (value);
+ case PROP_CLOSE_ON_DISPOSE:
+ priv->close_on_dispose = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -242,6 +341,9 @@ soup_socket_get_property (GObject *object, guint prop_id,
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
switch (prop_id) {
+ case PROP_FD:
+ g_value_set_int (value, priv->fd);
+ break;
case PROP_LOCAL_ADDRESS:
g_value_set_object (value, soup_socket_get_local_address (SOUP_SOCKET (object)));
break;
@@ -284,8 +386,9 @@ soup_socket_get_property (GObject *object, guint prop_id,
case PROP_TLS_ERRORS:
g_value_set_flags (value, priv->tls_errors);
break;
- case PROP_PROXY_RESOLVER:
- g_value_set_object (value, priv->proxy_resolver);
+ break;
+ case PROP_CLOSE_ON_DISPOSE:
+ g_value_set_boolean (value, priv->close_on_dispose);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -400,6 +503,21 @@ soup_socket_class_init (SoupSocketClass *socket_class)
/* properties */
+ g_object_class_install_property (
+ object_class, PROP_FD,
+ g_param_spec_int (SOUP_SOCKET_FD,
+ "FD",
+ "The socket's file descriptor",
+ -1, G_MAXINT, -1,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_GSOCKET,
+ g_param_spec_object (SOUP_SOCKET_GSOCKET,
+ "GSocket",
+ "The socket's underlying GSocket",
+ G_TYPE_SOCKET,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
/**
* SOUP_SOCKET_LOCAL_ADDRESS:
*
@@ -466,8 +584,18 @@ soup_socket_class_init (SoupSocketClass *socket_class)
/**
* SOUP_SOCKET_IS_SERVER:
*
- * Alias for the #SoupSocket:is-server property. (Whether or
- * not the socket is a server socket.)
+ * Alias for the #SoupSocket:is-server property, qv.
+ **/
+ /**
+ * SoupSocket:is-server:
+ *
+ * Whether or not the socket is a server socket.
+ *
+ * Note that for "ordinary" #SoupSockets this will be set for
+ * both listening sockets and the sockets emitted by
+ * #SoupSocket::new-connection, but for sockets created by
+ * setting #SoupSocket:fd, it will only be set for listening
+ * sockets.
**/
g_object_class_install_property (
object_class, PROP_IS_SERVER,
@@ -579,13 +707,6 @@ soup_socket_class_init (SoupSocketClass *socket_class)
0, G_MAXUINT, 0,
G_PARAM_READWRITE));
- g_object_class_install_property (
- object_class, PROP_CLEAN_DISPOSE,
- g_param_spec_boolean ("clean-dispose",
- "Clean dispose",
- "Warn on unclean dispose",
- FALSE,
- G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
/**
* SOUP_SOCKET_TLS_CERTIFICATE:
*
@@ -622,12 +743,26 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_PARAM_READABLE));
g_object_class_install_property (
- 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));
+ object_class, PROP_SOCKET_PROPERTIES,
+ g_param_spec_boxed (SOUP_SOCKET_SOCKET_PROPERTIES,
+ "Socket properties",
+ "Socket properties",
+ SOUP_TYPE_SOCKET_PROPERTIES,
+ G_PARAM_WRITABLE));
+
+ g_object_class_install_property (
+ object_class, PROP_CLOSE_ON_DISPOSE,
+ g_param_spec_boolean (SOUP_SOCKET_CLOSE_ON_DISPOSE,
+ "Close socket on disposal",
+ "Whether the socket is closed on disposal",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+}
+
+static void
+soup_socket_initable_interface_init (GInitableIface *initable_interface)
+{
+ initable_interface->init = soup_socket_initable_init;
}
@@ -677,7 +812,7 @@ socket_connect_finish (SoupSocket *sock, GSocketConnection *conn)
if (conn) {
priv->conn = (GIOStream *)conn;
priv->gsock = g_object_ref (g_socket_connection_get_socket (conn));
- finish_socket_setup (priv);
+ finish_socket_setup (sock);
return TRUE;
} else
return FALSE;
@@ -1001,11 +1136,12 @@ listen_watch (GObject *pollable, gpointer data)
new_priv->async_context = g_main_context_ref (priv->async_context);
new_priv->use_thread_context = priv->use_thread_context;
new_priv->non_blocking = priv->non_blocking;
+ new_priv->clean_dispose = priv->clean_dispose;
new_priv->is_server = TRUE;
new_priv->ssl = priv->ssl;
if (priv->ssl_creds)
- new_priv->ssl_creds = priv->ssl_creds;
- finish_socket_setup (new_priv);
+ new_priv->ssl_creds = g_object_ref (priv->ssl_creds);
+ finish_socket_setup (new);
if (new_priv->ssl_creds) {
if (!soup_socket_start_proxy_ssl (new, NULL, NULL)) {
@@ -1020,6 +1156,17 @@ listen_watch (GObject *pollable, gpointer data)
return TRUE;
}
+static void
+finish_listener_setup (SoupSocket *sock)
+{
+ SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (sock);
+
+ priv->is_server = TRUE;
+ priv->watch_src = soup_socket_create_watch (priv, G_IO_IN,
+ listen_watch, sock,
+ NULL);
+}
+
/**
* soup_socket_listen:
* @sock: a server #SoupSocket (which must not already be connected or
@@ -1042,8 +1189,6 @@ soup_socket_listen (SoupSocket *sock)
g_return_val_if_fail (priv->gsock == NULL, FALSE);
g_return_val_if_fail (priv->local_addr != NULL, FALSE);
- priv->is_server = TRUE;
-
/* @local_addr may have its port set to 0. So we intentionally
* don't store it in priv->local_addr, so that if the
* caller calls soup_socket_get_local_address() later, we'll
@@ -1059,7 +1204,7 @@ soup_socket_listen (SoupSocket *sock)
NULL);
if (!priv->gsock)
goto cant_listen;
- finish_socket_setup (priv);
+ finish_socket_setup (sock);
/* Bind */
if (!g_socket_bind (priv->gsock, addr, TRUE, NULL))
@@ -1071,10 +1216,8 @@ soup_socket_listen (SoupSocket *sock)
/* Listen */
if (!g_socket_listen (priv->gsock, NULL))
goto cant_listen;
+ finish_listener_setup (sock);
- priv->watch_src = soup_socket_create_watch (priv, G_IO_IN,
- listen_watch, sock,
- NULL);
g_object_unref (addr);
return TRUE;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 21c70d42..23a19a5e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -6,4 +6,5 @@ libsoup/soup-message-io.c
libsoup/soup-message-server-io.c
libsoup/soup-request.c
libsoup/soup-session.c
+libsoup/soup-socket.c
libsoup/soup-tld.c
diff --git a/tests/socket-test.c b/tests/socket-test.c
index 5bcc3b0c..821b1a22 100644
--- a/tests/socket-test.c
+++ b/tests/socket-test.c
@@ -5,7 +5,9 @@
*/
#include "test-utils.h"
+#include "libsoup/soup-socket-private.h"
+#include <fcntl.h>
#include <gio/gnetworking.h>
static void
@@ -108,6 +110,232 @@ do_unconnected_socket_test (void)
g_object_unref (sock);
}
+static void
+do_socket_from_fd_client_test (void)
+{
+ SoupServer *server;
+ GSocket *gsock;
+ SoupSocket *sock;
+ SoupAddress *local, *remote;
+ GSocketAddress *gaddr;
+ gboolean is_server;
+ int type;
+ GError *error = NULL;
+
+ server = soup_test_server_new (FALSE);
+
+ gsock = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", soup_server_get_port (server));
+ g_socket_connect (gsock, gaddr, NULL, &error);
+ g_object_unref (gaddr);
+ g_assert_no_error (error);
+ g_assert_true (g_socket_is_connected (gsock));
+
+ gaddr = g_socket_get_local_address (gsock, &error);
+ g_assert_no_error (error);
+
+ sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+ SOUP_SOCKET_FD, g_socket_get_fd (gsock),
+ SOUP_SOCKET_CLOSE_ON_DISPOSE, FALSE,
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (sock);
+
+ g_object_get (G_OBJECT (sock),
+ SOUP_SOCKET_LOCAL_ADDRESS, &local,
+ SOUP_SOCKET_REMOTE_ADDRESS, &remote,
+ SOUP_SOCKET_IS_SERVER, &is_server,
+ NULL);
+ g_assert_cmpint (soup_socket_get_fd (sock), ==, g_socket_get_fd (gsock));
+ g_assert_false (is_server);
+ g_assert_true (soup_socket_is_connected (sock));
+
+ g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1");
+ g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr)));
+ g_assert_cmpstr (soup_address_get_physical (remote), ==, "127.0.0.1");
+ g_assert_cmpint (soup_address_get_port (remote), ==, soup_server_get_port (server));
+
+ g_object_unref (local);
+ g_object_unref (remote);
+ g_object_unref (gaddr);
+
+ g_object_unref (sock);
+ /* We specified close-on-dispose=FALSE */
+ g_socket_get_option (gsock, SOL_SOCKET, SO_TYPE, &type, &error);
+ g_assert_no_error (error);
+
+ g_object_unref (gsock);
+
+ g_object_unref (server);
+}
+
+static void
+do_socket_from_fd_server_test (void)
+{
+ GSocket *gsock;
+ SoupSocket *sock;
+ SoupAddress *local;
+ GSocketAddress *gaddr;
+ gboolean is_server;
+ GError *error = NULL;
+
+ gsock = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
+ g_socket_bind (gsock, gaddr, TRUE, &error);
+ g_object_unref (gaddr);
+ g_assert_no_error (error);
+ g_socket_listen (gsock, &error);
+ g_assert_no_error (error);
+ g_assert_false (g_socket_is_connected (gsock));
+
+ gaddr = g_socket_get_local_address (gsock, &error);
+ g_assert_no_error (error);
+
+ sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+ SOUP_SOCKET_GSOCKET, gsock,
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (sock);
+
+ g_object_get (G_OBJECT (sock),
+ SOUP_SOCKET_LOCAL_ADDRESS, &local,
+ SOUP_SOCKET_IS_SERVER, &is_server,
+ NULL);
+ g_assert_cmpint (soup_socket_get_fd (sock), ==, g_socket_get_fd (gsock));
+ g_assert_true (is_server);
+ g_assert_true (soup_socket_is_connected (sock));
+
+ g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1");
+ g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr)));
+ g_object_unref (local);
+
+ g_object_unref (sock);
+
+ /* Closing the SoupSocket should have closed the GSocket */
+ g_assert_true (g_socket_is_closed (gsock));
+
+ g_object_unref (gsock);
+}
+
+static void
+do_socket_from_fd_bad_test (void)
+{
+ GSocket *gsock, *gsock2, *gsockcli;
+ SoupSocket *sock, *sock2;
+ SoupAddress *local, *remote;
+ GSocketAddress *gaddr;
+ gboolean is_server;
+ int fd;
+ GError *error = NULL;
+
+ /* Importing a non-socket fd gives an error */
+ fd = open (g_test_get_filename (G_TEST_DIST, "test-cert.pem", NULL), O_RDONLY);
+ g_assert_cmpint (fd, !=, -1);
+
+ sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+ SOUP_SOCKET_FD, fd,
+ NULL);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_clear_error (&error);
+ g_assert_null (sock);
+ close (fd);
+
+ /* Importing an unconnected socket gives an error */
+ gsock = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+ g_assert_false (g_socket_is_connected (gsock));
+
+ sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+ SOUP_SOCKET_FD, g_socket_get_fd (gsock),
+ NULL);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
+ g_clear_error (&error);
+ g_assert_null (sock);
+ g_object_unref (gsock);
+
+ /* Importing a non-listening server-side socket works, but
+ * gives the wrong answer for soup_socket_is_server().
+ */
+ gsock = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ gaddr = g_inet_socket_address_new_from_string ("127.0.0.1", 0);
+ g_socket_bind (gsock, gaddr, TRUE, &error);
+ g_object_unref (gaddr);
+ g_assert_no_error (error);
+ g_socket_listen (gsock, &error);
+ g_assert_no_error (error);
+ g_assert_false (g_socket_is_connected (gsock));
+
+ gaddr = g_socket_get_local_address (gsock, &error);
+ g_assert_no_error (error);
+
+ gsockcli = g_socket_new (G_SOCKET_FAMILY_IPV4,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ &error);
+ g_assert_no_error (error);
+
+ g_socket_connect (gsockcli, gaddr, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_true (g_socket_is_connected (gsockcli));
+
+ gsock2 = g_socket_accept (gsock, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (gsock2);
+
+ sock2 = g_initable_new (SOUP_TYPE_SOCKET, NULL, &error,
+ SOUP_SOCKET_GSOCKET, gsock2,
+ NULL);
+ g_assert_no_error (error);
+ g_assert_nonnull (sock2);
+
+ g_object_get (G_OBJECT (sock2),
+ SOUP_SOCKET_LOCAL_ADDRESS, &local,
+ SOUP_SOCKET_REMOTE_ADDRESS, &remote,
+ SOUP_SOCKET_IS_SERVER, &is_server,
+ NULL);
+ g_assert_cmpint (soup_socket_get_fd (sock2), ==, g_socket_get_fd (gsock2));
+ g_assert_true (soup_socket_is_connected (sock2));
+ /* This is wrong, but can't be helped. */
+ g_assert_false (is_server);
+
+ g_assert_cmpstr (soup_address_get_physical (local), ==, "127.0.0.1");
+ g_assert_cmpint (soup_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr)));
+ g_object_unref (gaddr);
+
+ gaddr = g_socket_get_local_address (gsockcli, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (soup_address_get_physical (remote), ==, "127.0.0.1");
+ g_assert_cmpint (soup_address_get_port (remote), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr)));
+ g_object_unref (gaddr);
+
+ g_object_unref (local);
+ g_object_unref (remote);
+
+ g_object_unref (sock2);
+
+ g_object_unref (gsock);
+ g_object_unref (gsock2);
+ g_object_unref (gsockcli);
+}
+
int
main (int argc, char **argv)
{
@@ -116,6 +344,9 @@ main (int argc, char **argv)
test_init (argc, argv, NULL);
g_test_add_func ("/sockets/unconnected", do_unconnected_socket_test);
+ g_test_add_func ("/sockets/from-fd/client", do_socket_from_fd_client_test);
+ g_test_add_func ("/sockets/from-fd/server", do_socket_from_fd_server_test);
+ g_test_add_func ("/sockets/from-fd/bad", do_socket_from_fd_bad_test);
ret = g_test_run ();