diff options
author | Dan Winship <danw@gnome.org> | 2014-05-02 09:12:22 -0400 |
---|---|---|
committer | Dan Winship <danw@gnome.org> | 2014-05-02 09:12:22 -0400 |
commit | d17d1baeb000ce398f61d58a813a69a8bd7cef08 (patch) | |
tree | 3950f35597ef5774ee15b99c26dbe1809eacd912 | |
parent | 3ccb212449e7ac4f43888eee93366be934a87445 (diff) | |
parent | dc94ea443305ae2a326f462bf8ce9fc03a3ad4b9 (diff) | |
download | libsoup-d17d1baeb000ce398f61d58a813a69a8bd7cef08.tar.gz |
soup-socket: misc improvements
-rw-r--r-- | libsoup/Makefile.am | 2 | ||||
-rw-r--r-- | libsoup/soup-connection.c | 170 | ||||
-rw-r--r-- | libsoup/soup-connection.h | 16 | ||||
-rw-r--r-- | libsoup/soup-message-client-io.c | 2 | ||||
-rw-r--r-- | libsoup/soup-message-server-io.c | 1 | ||||
-rw-r--r-- | libsoup/soup-misc-private.h | 33 | ||||
-rw-r--r-- | libsoup/soup-session.c | 124 | ||||
-rw-r--r-- | libsoup/soup-socket-private.h | 82 | ||||
-rw-r--r-- | libsoup/soup-socket-properties.c | 74 | ||||
-rw-r--r-- | libsoup/soup-socket.c | 221 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | tests/socket-test.c | 231 |
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 (); |