diff options
author | Carlos Garcia Campos <cgarcia@igalia.com> | 2022-05-06 09:23:43 +0200 |
---|---|---|
committer | Carlos Garcia Campos <cgarcia@igalia.com> | 2022-08-12 09:22:54 +0200 |
commit | 25e96cad913cde650ca56ff76c3924fd97eb7125 (patch) | |
tree | a152bc18f58c196c79ecc8d8263b8df1f2c17cc4 | |
parent | 36770e8b8265add935b8d530f3207bab12fc9188 (diff) | |
download | libsoup-25e96cad913cde650ca56ff76c3924fd97eb7125.tar.gz |
server: split SoupSocket into SoupListener and SoupServerConnection
-rw-r--r-- | libsoup/meson.build | 3 | ||||
-rw-r--r-- | libsoup/server/soup-listener.c | 378 | ||||
-rw-r--r-- | libsoup/server/soup-listener.h | 26 | ||||
-rw-r--r-- | libsoup/server/soup-server-connection.c | 656 | ||||
-rw-r--r-- | libsoup/server/soup-server-connection.h | 42 | ||||
-rw-r--r-- | libsoup/server/soup-server-io.c | 21 | ||||
-rw-r--r-- | libsoup/server/soup-server-message-private.h | 6 | ||||
-rw-r--r-- | libsoup/server/soup-server-message.c | 61 | ||||
-rw-r--r-- | libsoup/server/soup-server.c | 206 | ||||
-rw-r--r-- | libsoup/server/soup-socket.c | 865 | ||||
-rw-r--r-- | libsoup/server/soup-socket.h | 36 | ||||
-rw-r--r-- | tests/connection-test.c | 43 | ||||
-rw-r--r-- | tests/meson.build | 1 | ||||
-rw-r--r-- | tests/socket-test.c | 274 |
14 files changed, 1274 insertions, 1344 deletions
diff --git a/libsoup/meson.build b/libsoup/meson.build index 332aceef..ed811397 100644 --- a/libsoup/meson.build +++ b/libsoup/meson.build @@ -45,12 +45,13 @@ soup_sources = [ 'server/soup-auth-domain.c', 'server/soup-auth-domain-basic.c', 'server/soup-auth-domain-digest.c', + 'server/soup-listener.c', 'server/soup-message-body.c', 'server/soup-path-map.c', 'server/soup-server.c', + 'server/soup-server-connection.c', 'server/soup-server-io.c', 'server/soup-server-message.c', - 'server/soup-socket.c', 'websocket/soup-websocket.c', 'websocket/soup-websocket-connection.c', diff --git a/libsoup/server/soup-listener.c b/libsoup/server/soup-listener.c new file mode 100644 index 00000000..841887ce --- /dev/null +++ b/libsoup/server/soup-listener.c @@ -0,0 +1,378 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-listener.c: Socket listening networking code. + * + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <gio/gnetworking.h> + +#include "soup-listener.h" +#include "soup.h" +#include "soup-io-stream.h" +#include "soup-server-connection.h" + +enum { + NEW_CONNECTION, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + + PROP_SOCKET, + PROP_TLS_CERTIFICATE, + PROP_TLS_DATABASE, + PROP_TLS_AUTH_MODE, + + LAST_PROPERTY +}; + +static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; + +struct _SoupListener { + GObject parent_instance; +}; + +typedef struct { + GSocket *socket; + GIOStream *conn; + GIOStream *iostream; + GInetSocketAddress *local_addr; + + GTlsCertificate *tls_certificate; + GTlsDatabase *tls_database; + GTlsAuthenticationMode tls_auth_mode; + + GSource *source; +} SoupListenerPrivate; + +G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupListener, soup_listener, G_TYPE_OBJECT) + +static void +soup_listener_init (SoupListener *listener) +{ +} + +static gboolean +listen_watch (GObject *pollable, + SoupListener *listener) +{ + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + GSocket *socket; + SoupServerConnection *conn; + + socket = g_socket_accept (priv->socket, NULL, NULL); + if (!socket) + return G_SOURCE_REMOVE; + + conn = soup_server_connection_new (socket, priv->tls_certificate, priv->tls_database, priv->tls_auth_mode); + g_signal_emit (listener, signals[NEW_CONNECTION], 0, conn); + g_object_unref (conn); + + return G_SOURCE_CONTINUE; +} + +static void +soup_listener_constructed (GObject *object) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + g_socket_set_option (priv->socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); + + priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->socket); + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + priv->source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (priv->iostream)), NULL); + g_source_set_callback (priv->source, (GSourceFunc)listen_watch, listener, NULL); + g_source_attach (priv->source, g_main_context_get_thread_default ()); + + G_OBJECT_CLASS (soup_listener_parent_class)->constructed (object); +} + +static void +soup_listener_finalize (GObject *object) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + if (priv->conn) { + g_io_stream_close (priv->conn, NULL, NULL); + g_clear_object (&priv->conn); + } + + g_clear_object (&priv->socket); + g_clear_object (&priv->iostream); + + g_clear_object (&priv->tls_certificate); + g_clear_object (&priv->tls_database); + + if (priv->source) { + g_source_destroy (priv->source); + g_source_unref (priv->source); + } + + G_OBJECT_CLASS (soup_listener_parent_class)->finalize (object); +} + +static void +soup_listener_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + switch (prop_id) { + case PROP_SOCKET: + priv->socket = g_value_dup_object (value); + break; + case PROP_TLS_CERTIFICATE: + priv->tls_certificate = g_value_dup_object (value); + break; + case PROP_TLS_DATABASE: + priv->tls_database = g_value_dup_object (value); + break; + case PROP_TLS_AUTH_MODE: + priv->tls_auth_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_listener_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SoupListener *listener = SOUP_LISTENER (object); + SoupListenerPrivate *priv = soup_listener_get_instance_private (listener); + + switch (prop_id) { + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + case PROP_TLS_CERTIFICATE: + g_value_set_object (value, priv->tls_certificate); + break; + case PROP_TLS_DATABASE: + g_value_set_object (value, priv->tls_database); + break; + case PROP_TLS_AUTH_MODE: + g_value_set_enum (value, priv->tls_auth_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_listener_class_init (SoupListenerClass *listener_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (listener_class); + + object_class->constructed = soup_listener_constructed; + object_class->finalize = soup_listener_finalize; + object_class->set_property = soup_listener_set_property; + object_class->get_property = soup_listener_get_property; + + /** + * SoupListener::new-connection: + * @listener: the listener + * @conn: the new connection + * + * Emitted when a listening socket receives a new connection. + * + * You must ref the @new if you want to keep it; otherwise it + * will be destroyed after the signal is emitted. + **/ + signals[NEW_CONNECTION] = + g_signal_new ("new-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 1, + SOUP_TYPE_SERVER_CONNECTION); + + /* properties */ + properties[PROP_SOCKET] = + g_param_spec_object ("socket", + "Socket", + "The underlying GSocket", + G_TYPE_SOCKET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_CERTIFICATE] = + g_param_spec_object ("tls-certificate", + "TLS Certificate", + "The server TLS certificate", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_DATABASE] = + g_param_spec_object ("tls-database", + "TLS Database", + "The server TLS database", + G_TYPE_TLS_DATABASE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_AUTH_MODE] = + g_param_spec_enum ("tls-auth-mode", + "TLS Authentication Mode", + "The server TLS authentication mode", + G_TYPE_TLS_AUTHENTICATION_MODE, + G_TLS_AUTHENTICATION_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROPERTY, properties); +} + +SoupListener * +soup_listener_new (GSocket *socket, + GError **error) +{ + int listening; + + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (!g_socket_get_option (socket, SOL_SOCKET, SO_ACCEPTCONN, &listening, error)) { + g_prefix_error (error, _("Could not import existing socket: ")); + return NULL; + } + + if (!listening && !g_socket_is_connected (socket)) { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Can’t import unconnected socket")); + return NULL; + } + + return g_object_new (SOUP_TYPE_LISTENER, "socket", socket, NULL); +} + +SoupListener * +soup_listener_new_for_address (GSocketAddress *address, + GError **error) +{ + GSocket *socket; + GSocketFamily family; + SoupListener *listener; + + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + family = g_socket_address_get_family (address); + socket = g_socket_new (family, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, error); + if (!socket) + return NULL; + + if (family == G_SOCKET_FAMILY_IPV6) { + GError *option_error = NULL; + + g_socket_set_option (socket, IPPROTO_IPV6, IPV6_V6ONLY, TRUE, &option_error); + if (option_error) { + g_warning ("Failed to set IPv6 only on socket: %s", option_error->message); + g_error_free (option_error); + } + } + + if (!g_socket_bind (socket, address, TRUE, error)) { + g_object_unref (socket); + + return NULL; + } + + if (!g_socket_listen (socket, error)) { + g_object_unref (socket); + + return NULL; + } + + listener = g_object_new (SOUP_TYPE_LISTENER, "socket", socket, NULL); + g_object_unref (socket); + + return listener; +} + +GSocket * +soup_listener_get_socket (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), NULL); + + priv = soup_listener_get_instance_private (listener); + + return priv->socket; +} + +void +soup_listener_disconnect (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_if_fail (SOUP_IS_LISTENER (listener)); + + priv = soup_listener_get_instance_private (listener); + g_clear_object (&priv->socket); + if (priv->conn) { + g_io_stream_close (priv->conn, NULL, NULL); + g_clear_object (&priv->conn); + } +} + +gboolean +soup_listener_is_ssl (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), FALSE); + + priv = soup_listener_get_instance_private (listener); + + return priv->tls_certificate != NULL; +} + +GInetSocketAddress * +soup_listener_get_address (SoupListener *listener) +{ + SoupListenerPrivate *priv; + + g_return_val_if_fail (SOUP_IS_LISTENER (listener), NULL); + + priv = soup_listener_get_instance_private (listener); + + if (!priv->local_addr) { + GError *error = NULL; + + priv->local_addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (priv->socket, &error)); + if (priv->local_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->local_addr; +} diff --git a/libsoup/server/soup-listener.h b/libsoup/server/soup-listener.h new file mode 100644 index 00000000..cf2d73ac --- /dev/null +++ b/libsoup/server/soup-listener.h @@ -0,0 +1,26 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#pragma once + +#include "soup-types.h" + +G_BEGIN_DECLS + +#define SOUP_TYPE_LISTENER (soup_listener_get_type ()) +G_DECLARE_FINAL_TYPE (SoupListener, soup_listener, SOUP, LISTENER, GObject) + +SoupListener *soup_listener_new (GSocket *socket, + GError **error); +SoupListener *soup_listener_new_for_address (GSocketAddress *address, + GError **error); + +void soup_listener_disconnect (SoupListener *listener); +gboolean soup_listener_is_ssl (SoupListener *listener); +GSocket *soup_listener_get_socket (SoupListener *listener); +GInetSocketAddress *soup_listener_get_address (SoupListener *listener); + +G_END_DECLS diff --git a/libsoup/server/soup-server-connection.c b/libsoup/server/soup-server-connection.c new file mode 100644 index 00000000..8dbaad96 --- /dev/null +++ b/libsoup/server/soup-server-connection.c @@ -0,0 +1,656 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * soup-server-connection.c: Connection networking code. + * + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n-lib.h> +#include <gio/gnetworking.h> + +#include "soup-server-connection.h" +#include "soup.h" +#include "soup-io-stream.h" + +enum { + DISCONNECTED, + ACCEPT_CERTIFICATE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +enum { + PROP_0, + + PROP_SOCKET, + PROP_CONNECTION, + PROP_LOCAL_ADDRESS, + PROP_REMOTE_ADDRESS, + PROP_TLS_CERTIFICATE, + PROP_TLS_DATABASE, + PROP_TLS_AUTH_MODE, + PROP_TLS_PEER_CERTIFICATE, + PROP_TLS_PEER_CERTIFICATE_ERRORS, + + LAST_PROPERTY +}; + +static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; + +struct _SoupServerConnection { + GObject parent_instance; +}; + +typedef struct { + GSocket *socket; + GIOStream *conn; + GIOStream *iostream; + + GSocketAddress *local_addr; + GSocketAddress *remote_addr; + + GTlsCertificate *tls_certificate; + GTlsDatabase *tls_database; + GTlsAuthenticationMode tls_auth_mode; +} SoupServerConnectionPrivate; + +G_DEFINE_FINAL_TYPE_WITH_PRIVATE (SoupServerConnection, soup_server_connection, G_TYPE_OBJECT) + +static void +soup_server_connection_init (SoupServerConnection *conn) +{ +} + +static void +disconnect_internal (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + g_clear_object (&priv->socket); + + g_io_stream_close (priv->conn, NULL, NULL); + g_signal_handlers_disconnect_by_data (priv->conn, conn); + g_clear_object (&priv->conn); +} + +static void +soup_server_connection_finalize (GObject *object) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + if (priv->conn) + disconnect_internal (conn); + + g_clear_object (&priv->iostream); + + g_clear_object (&priv->local_addr); + g_clear_object (&priv->remote_addr); + + g_clear_object (&priv->tls_certificate); + g_clear_object (&priv->tls_database); + + G_OBJECT_CLASS (soup_server_connection_parent_class)->finalize (object); +} + +static void +soup_server_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + switch (prop_id) { + case PROP_SOCKET: + priv->socket = g_value_dup_object (value); + break; + case PROP_CONNECTION: + priv->conn = g_value_dup_object (value); + if (priv->conn) + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + break; + case PROP_LOCAL_ADDRESS: + priv->local_addr = g_value_dup_object (value); + break; + case PROP_REMOTE_ADDRESS: + priv->remote_addr = g_value_dup_object (value); + break; + case PROP_TLS_CERTIFICATE: + priv->tls_certificate = g_value_dup_object (value); + break; + case PROP_TLS_DATABASE: + priv->tls_database = g_value_dup_object (value); + break; + case PROP_TLS_AUTH_MODE: + priv->tls_auth_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_server_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SoupServerConnection *conn = SOUP_SERVER_CONNECTION (object); + SoupServerConnectionPrivate *priv = soup_server_connection_get_instance_private (conn); + + switch (prop_id) { + case PROP_SOCKET: + g_value_set_object (value, priv->socket); + break; + case PROP_CONNECTION: + g_value_set_object (value, priv->conn); + break; + case PROP_LOCAL_ADDRESS: + g_value_set_object (value, soup_server_connection_get_local_address (conn)); + break; + case PROP_REMOTE_ADDRESS: + g_value_set_object (value, soup_server_connection_get_remote_address (conn)); + break; + case PROP_TLS_CERTIFICATE: + g_value_set_object (value, priv->tls_certificate); + break; + case PROP_TLS_DATABASE: + g_value_set_object (value, priv->tls_database); + break; + case PROP_TLS_AUTH_MODE: + g_value_set_enum (value, priv->tls_auth_mode); + break; + case PROP_TLS_PEER_CERTIFICATE: + g_value_set_object (value, soup_server_connection_get_tls_peer_certificate (conn)); + break; + case PROP_TLS_PEER_CERTIFICATE_ERRORS: + g_value_set_flags (value, soup_server_connection_get_tls_peer_certificate_errors (conn)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +soup_server_connection_class_init (SoupServerConnectionClass *conn_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (conn_class); + + object_class->finalize = soup_server_connection_finalize; + object_class->set_property = soup_server_connection_set_property; + object_class->get_property = soup_server_connection_get_property; + + /** + * SoupServerConnection::disconnected: + * @conn: the connection + * + * Emitted when the connection is disconnected, for whatever reason. + **/ + signals[DISCONNECTED] = + g_signal_new ("disconnected", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + NULL, + G_TYPE_NONE, 0); + + signals[ACCEPT_CERTIFICATE] = + g_signal_new ("accept-certificate", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, + g_signal_accumulator_true_handled, NULL, + NULL, + G_TYPE_BOOLEAN, 2, + G_TYPE_TLS_CERTIFICATE, + G_TYPE_TLS_CERTIFICATE_FLAGS); + + /* properties */ + properties[PROP_SOCKET] = + g_param_spec_object ("socket", + "Socket", + "The connection underlying GSocket", + G_TYPE_SOCKET, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + properties[PROP_CONNECTION] = + g_param_spec_object ("connection", + "GIOStream", + "The socket's underlying GIOStream", + G_TYPE_IO_STREAM, + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_LOCAL_ADDRESS] = + g_param_spec_object ("local-address", + "Local address", + "Address of local end of socket", + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_REMOTE_ADDRESS] = + g_param_spec_object ("remote-address", + "Remote address", + "Address of remote end of socket", + G_TYPE_SOCKET_ADDRESS, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_CERTIFICATE] = + g_param_spec_object ("tls-certificate", + "TLS Certificate", + "The server TLS certificate", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_DATABASE] = + g_param_spec_object ("tls-database", + "TLS Database", + "The server TLS database", + G_TYPE_TLS_DATABASE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_AUTH_MODE] = + g_param_spec_enum ("tls-auth-mode", + "TLS Authentication Mode", + "The server TLS authentication mode", + G_TYPE_TLS_AUTHENTICATION_MODE, + G_TLS_AUTHENTICATION_NONE, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_PEER_CERTIFICATE] = + g_param_spec_object ("tls-peer-certificate", + "TLS Peer Certificate", + "The TLS peer certificate associated with the message", + G_TYPE_TLS_CERTIFICATE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + properties[PROP_TLS_PEER_CERTIFICATE_ERRORS] = + g_param_spec_flags ("tls-peer-certificate-errors", + "TLS Peer Certificate Errors", + "The verification errors on the message's TLS peer certificate", + G_TYPE_TLS_CERTIFICATE_FLAGS, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, LAST_PROPERTY, properties); +} + +SoupServerConnection * +soup_server_connection_new (GSocket *socket, + GTlsCertificate *tls_certificate, + GTlsDatabase *tls_database, + GTlsAuthenticationMode tls_auth_mode) +{ + g_return_val_if_fail (G_IS_SOCKET (socket), NULL); + g_return_val_if_fail (!tls_certificate || G_IS_TLS_CERTIFICATE (tls_certificate), NULL); + g_return_val_if_fail (!tls_database || G_IS_TLS_DATABASE (tls_database), NULL); + + return g_object_new (SOUP_TYPE_SERVER_CONNECTION, + "socket", socket, + "tls-certificate", tls_certificate, + "tls-database", tls_database, + "tls-auth-mode", tls_auth_mode, + NULL); +} + +SoupServerConnection * +soup_server_connection_new_for_connection (GIOStream *connection, + GSocketAddress *local_addr, + GSocketAddress *remote_addr) +{ + g_return_val_if_fail (G_IS_IO_STREAM (connection), NULL); + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (local_addr), NULL); + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (remote_addr), NULL); + + return g_object_new (SOUP_TYPE_SERVER_CONNECTION, + "connection", connection, + "local-address", local_addr, + "remote-address", remote_addr, + NULL); +} + +static gboolean +tls_connection_accept_certificate (SoupServerConnection *conn, + GTlsCertificate *tls_certificate, + GTlsCertificateFlags tls_errors) +{ + gboolean accept = FALSE; + + g_signal_emit (conn, signals[ACCEPT_CERTIFICATE], 0, + tls_certificate, tls_errors, &accept); + return accept; +} + +static void +tls_connection_peer_certificate_changed (SoupServerConnection *conn) +{ + g_object_notify_by_pspec (G_OBJECT (conn), properties[PROP_TLS_CERTIFICATE]); +} + +static void +tls_connection_handshake_ready_cb (GTlsConnection *conn, + GAsyncResult *result, + GTask *task) +{ + GError *error = NULL; + + if (g_tls_connection_handshake_finish (conn, result, &error)) + g_task_return_boolean (task, TRUE); + else + g_task_return_error (task, error); + g_object_unref (task); +} + +void +soup_server_connection_setup_async (SoupServerConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + SoupServerConnectionPrivate *priv; + GIOStream *connection; + + g_return_if_fail (SOUP_IS_SERVER_CONNECTION (conn)); + g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); + + priv = soup_server_connection_get_instance_private (conn); + + task = g_task_new (conn, cancellable, callback, user_data); + if (priv->conn || !priv->socket) { + g_task_return_boolean (task, TRUE); + g_object_unref (task); + + return; + } + + connection = (GIOStream *)g_socket_connection_factory_create_connection (priv->socket); + g_socket_set_option (priv->socket, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); + + if (priv->tls_certificate) { + GPtrArray *advertised_protocols; + GError *error = NULL; + + advertised_protocols = g_ptr_array_sized_new (3); + g_ptr_array_add (advertised_protocols, "http/1.1"); + g_ptr_array_add (advertised_protocols, "http/1.0"); + g_ptr_array_add (advertised_protocols, NULL); + + priv->conn = g_initable_new (g_tls_backend_get_server_connection_type (g_tls_backend_get_default ()), + cancellable, &error, + "base-io-stream", connection, + "certificate", priv->tls_certificate, + "database", priv->tls_database, + "authentication-mode", priv->tls_auth_mode, + "require-close-notify", FALSE, + "advertised-protocols", advertised_protocols->pdata, + NULL); + g_ptr_array_unref (advertised_protocols); + g_object_unref (connection); + if (!priv->conn) { + g_task_return_error (task, error); + g_object_unref (task); + + return; + } + + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + + g_signal_connect_object (priv->conn, "accept-certificate", + G_CALLBACK (tls_connection_accept_certificate), + conn, G_CONNECT_SWAPPED); + g_signal_connect_object (priv->conn, "notify::peer-certificate", + G_CALLBACK (tls_connection_peer_certificate_changed), + conn, G_CONNECT_SWAPPED); + + g_tls_connection_handshake_async (G_TLS_CONNECTION (priv->conn), + G_PRIORITY_DEFAULT, NULL, + (GAsyncReadyCallback)tls_connection_handshake_ready_cb, + task); + return; + } + + priv->conn = connection; + priv->iostream = soup_io_stream_new (priv->conn, FALSE); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +gboolean +soup_server_connection_setup_finish (SoupServerConnection *conn, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +GSocket * +soup_server_connection_get_socket (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->socket; +} + +GSocket * +soup_server_connection_steal_socket (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + GSocket *socket; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + socket = g_steal_pointer (&priv->socket); + g_clear_object (&priv->conn); + g_clear_object (&priv->iostream); + + return socket; +} + +GIOStream * +soup_server_connection_get_iostream (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->iostream; +} + +/** + * soup_server_connection_is_ssl: + * @conn: a #SoupServerConnection + * + * Tests if @sock is doing (or has attempted to do) SSL. + * + * Returns: %TRUE if @conn has SSL credentials set + **/ +gboolean +soup_server_connection_is_ssl (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), FALSE); + + priv = soup_server_connection_get_instance_private (conn); + + return G_IS_TLS_CONNECTION (priv->conn) || priv->tls_certificate; +} + +/** + * soup_server_connection_disconnect: + * @sock: a #SoupServerConnection + * + * Disconnects @conn. Any further read or write attempts on it will fail. + **/ +void +soup_server_connection_disconnect (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_if_fail (SOUP_IS_SERVER_CONNECTION (conn)); + + priv = soup_server_connection_get_instance_private (conn); + + if (!priv->conn) + return; + + disconnect_internal (conn); + + /* Keep ref around signals in case the object is unreferenced + * in a handler + */ + g_object_ref (conn); + + /* FIXME: can't disconnect until all data is read */ + + /* Then let everyone know we're disconnected */ + g_signal_emit (conn, signals[DISCONNECTED], 0); + + g_object_unref (conn); +} + +/** + * soup_server_connection_is_connected: + * @conn: a #SoupServerConnection + * + * Tests if @conn is connected to another host + * + * Returns: %TRUE or %FALSE. + **/ +gboolean +soup_server_connection_is_connected (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), FALSE); + + priv = soup_server_connection_get_instance_private (conn); + + return priv->conn && !g_io_stream_is_closed (priv->conn); +} + +/** + * soup_server_connection_get_local_address: + * @conn: a #SoupServerConnection + * + * Returns the #GInetSocketAddress corresponding to the local end of @conn. + * + * Calling this method on an unconnected socket is considered to be + * an error, and produces undefined results. + * + * Returns: (transfer none): the #GSocketAddress + **/ +GSocketAddress * +soup_server_connection_get_local_address (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + if (!priv->local_addr) { + GError *error = NULL; + + priv->local_addr = g_socket_get_local_address (priv->socket, &error); + if (priv->local_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->local_addr; +} + +/** + * soup_server_connection_get_remote_address: + * @conn: a #SoupServerConnection + * + * Returns the #GInetSocketAddress corresponding to the remote end of @conn. + * + * Calling this method on an unconnected socket is considered to be + * an error, and produces undefined results. + * + * Returns: (transfer none): the #GSocketAddress + **/ +GSocketAddress * +soup_server_connection_get_remote_address (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + if (!priv->remote_addr) { + GError *error = NULL; + + priv->remote_addr = g_socket_get_remote_address (priv->socket, &error); + if (priv->remote_addr == NULL) { + g_warning ("%s: %s", G_STRLOC, error->message); + g_error_free (error); + return NULL; + } + } + + return priv->remote_addr; +} + +GTlsCertificate * +soup_server_connection_get_tls_peer_certificate (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), NULL); + + priv = soup_server_connection_get_instance_private (conn); + + if (!G_IS_TLS_CONNECTION (priv->conn)) + return NULL; + + return g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (priv->conn)); +} + +GTlsCertificateFlags +soup_server_connection_get_tls_peer_certificate_errors (SoupServerConnection *conn) +{ + SoupServerConnectionPrivate *priv; + + g_return_val_if_fail (SOUP_IS_SERVER_CONNECTION (conn), 0); + + priv = soup_server_connection_get_instance_private (conn); + + if (!G_IS_TLS_CONNECTION (priv->conn)) + return 0; + + return g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (priv->conn)); +} diff --git a/libsoup/server/soup-server-connection.h b/libsoup/server/soup-server-connection.h new file mode 100644 index 00000000..bebd3edd --- /dev/null +++ b/libsoup/server/soup-server-connection.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2022 Igalia S.L. + * Copyright (C) 2000-2003, Ximian, Inc. + */ + +#pragma once + +#include "soup-types.h" +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define SOUP_TYPE_SERVER_CONNECTION (soup_server_connection_get_type ()) +G_DECLARE_FINAL_TYPE (SoupServerConnection, soup_server_connection, SOUP, SERVER_CONNECTION, GObject) + +SoupServerConnection *soup_server_connection_new (GSocket *socket, + GTlsCertificate *tls_certificate, + GTlsDatabase *tls_database, + GTlsAuthenticationMode tls_auth_mode); +SoupServerConnection *soup_server_connection_new_for_connection (GIOStream *connection, + GSocketAddress *local_addr, + GSocketAddress *remote_addr); +void soup_server_connection_setup_async (SoupServerConnection *conn, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean soup_server_connection_setup_finish (SoupServerConnection *conn, + GAsyncResult *result, + GError **error); +gboolean soup_server_connection_is_ssl (SoupServerConnection *conn); +void soup_server_connection_disconnect (SoupServerConnection *conn); +gboolean soup_server_connection_is_connected (SoupServerConnection *conn); +GSocket *soup_server_connection_get_socket (SoupServerConnection *conn); +GSocket *soup_server_connection_steal_socket (SoupServerConnection *conn); +GIOStream *soup_server_connection_get_iostream (SoupServerConnection *conn); +GSocketAddress *soup_server_connection_get_local_address (SoupServerConnection *conn); +GSocketAddress *soup_server_connection_get_remote_address (SoupServerConnection *conn); +GTlsCertificate *soup_server_connection_get_tls_peer_certificate (SoupServerConnection *conn); +GTlsCertificateFlags soup_server_connection_get_tls_peer_certificate_errors (SoupServerConnection *conn); + +G_END_DECLS diff --git a/libsoup/server/soup-server-io.c b/libsoup/server/soup-server-io.c index d1af115b..03438da7 100644 --- a/libsoup/server/soup-server-io.c +++ b/libsoup/server/soup-server-io.c @@ -18,7 +18,6 @@ #include "soup-server-message-private.h" #include "soup-message-headers-private.h" #include "soup-misc.h" -#include "soup-socket.h" struct _SoupServerMessageIOData { SoupMessageIOData base; @@ -570,7 +569,7 @@ parse_headers (SoupServerMessage *msg, { char *req_method, *req_path, *url; SoupHTTPVersion version; - SoupSocket *sock; + SoupServerConnection *conn; const char *req_host; guint status; GUri *uri; @@ -606,12 +605,12 @@ parse_headers (SoupServerMessage *msg, return SOUP_STATUS_BAD_REQUEST; } - sock = soup_server_message_get_soup_socket (msg); + conn = soup_server_message_get_connection (msg); if (!strcmp (req_path, "*") && req_host) { /* Eg, "OPTIONS * HTTP/1.1" */ url = g_strdup_printf ("%s://%s/", - soup_socket_is_ssl (sock) ? "https" : "http", + soup_server_connection_is_ssl (conn) ? "https" : "http", req_host); uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); soup_server_message_set_options_ping (msg, TRUE); @@ -624,21 +623,21 @@ parse_headers (SoupServerMessage *msg, uri = g_uri_parse (req_path, SOUP_HTTP_URI_FLAGS, NULL); } else if (req_host) { url = g_strdup_printf ("%s://%s%s", - soup_socket_is_ssl (sock) ? "https" : "http", + soup_server_connection_is_ssl (conn) ? "https" : "http", req_host, req_path); uri = g_uri_parse (url, SOUP_HTTP_URI_FLAGS, NULL); g_free (url); } else if (soup_server_message_get_http_version (msg) == SOUP_HTTP_1_0) { /* No Host header, no AbsoluteUri */ - GInetSocketAddress *addr = soup_socket_get_local_address (sock); + GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_server_connection_get_local_address (conn)); GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); char *local_ip = g_inet_address_to_string (inet_addr); int port = g_inet_socket_address_get_port (addr); if (port == 0) port = -1; - uri = g_uri_build (SOUP_HTTP_URI_FLAGS, - soup_socket_is_ssl (sock) ? "https" : "http", + uri = g_uri_build (SOUP_HTTP_URI_FLAGS, + soup_server_connection_is_ssl (conn) ? "https" : "http", NULL, local_ip, port, req_path, NULL, NULL); g_free (local_ip); } else @@ -886,14 +885,14 @@ soup_server_message_read_request (SoupServerMessage *msg, gpointer user_data) { SoupServerMessageIOData *io; - SoupSocket *sock; + SoupServerConnection *conn; io = g_slice_new0 (SoupServerMessageIOData); io->base.completion_cb = completion_cb; io->base.completion_data = user_data; - sock = soup_server_message_get_soup_socket (msg); - io->iostream = g_object_ref (soup_socket_get_iostream (sock)); + conn = soup_server_message_get_connection (msg); + io->iostream = g_object_ref (soup_server_connection_get_iostream (conn)); io->istream = g_io_stream_get_input_stream (io->iostream); io->ostream = g_io_stream_get_output_stream (io->iostream); diff --git a/libsoup/server/soup-server-message-private.h b/libsoup/server/soup-server-message-private.h index 55ef608c..d1efd645 100644 --- a/libsoup/server/soup-server-message-private.h +++ b/libsoup/server/soup-server-message-private.h @@ -8,14 +8,14 @@ #include "soup-server-message.h" #include "soup-auth-domain.h" #include "soup-message-io-data.h" -#include "soup-socket.h" +#include "soup-server-connection.h" -SoupServerMessage *soup_server_message_new (SoupSocket *sock); +SoupServerMessage *soup_server_message_new (SoupServerConnection *conn); void soup_server_message_set_uri (SoupServerMessage *msg, GUri *uri); void soup_server_message_set_method (SoupServerMessage *msg, const char *method); -SoupSocket *soup_server_message_get_soup_socket (SoupServerMessage *msg); +SoupServerConnection *soup_server_message_get_connection (SoupServerMessage *msg); void soup_server_message_set_auth (SoupServerMessage *msg, SoupAuthDomain *domain, char *user); diff --git a/libsoup/server/soup-server-message.c b/libsoup/server/soup-server-message.c index 9a3388c7..c7943b78 100644 --- a/libsoup/server/soup-server-message.c +++ b/libsoup/server/soup-server-message.c @@ -15,7 +15,6 @@ #include "soup-connection.h" #include "soup-server-message-private.h" #include "soup-message-headers-private.h" -#include "soup-socket.h" #include "soup-uri-utils-private.h" /** @@ -38,7 +37,7 @@ struct _SoupServerMessage { GObject parent; - SoupSocket *sock; + SoupServerConnection *conn; GSocket *gsock; SoupAuthDomain *auth_domain; char *auth_user; @@ -130,9 +129,9 @@ soup_server_message_finalize (GObject *object) g_clear_object (&msg->remote_addr); g_clear_object (&msg->local_addr); - if (msg->sock) { - g_signal_handlers_disconnect_by_data (msg->sock, msg); - g_object_unref (msg->sock); + if (msg->conn) { + g_signal_handlers_disconnect_by_data (msg->conn, msg); + g_object_unref (msg->conn); } g_clear_object (&msg->gsock); g_clear_pointer (&msg->remote_ip, g_free); @@ -403,15 +402,15 @@ soup_server_message_class_init (SoupServerMessageClass *klass) } static void -socket_disconnected (SoupServerMessage *msg) +connection_disconnected (SoupServerMessage *msg) { g_signal_emit (msg, signals[DISCONNECTED], 0); } static gboolean -socket_accept_certificate (SoupServerMessage *msg, - GTlsCertificate *tls_certificate, - GTlsCertificateFlags *tls_errors) +connection_accept_certificate (SoupServerMessage *msg, + GTlsCertificate *tls_certificate, + GTlsCertificateFlags *tls_errors) { gboolean accept = FALSE; @@ -436,33 +435,33 @@ soup_server_message_set_tls_peer_certificate (SoupServerMessage *msg, } static void -re_emit_tls_certificate_changed (SoupServerMessage *msg, - GParamSpec *pspec, - SoupSocket *sock) +re_emit_tls_certificate_changed (SoupServerMessage *msg, + GParamSpec *pspec, + SoupServerConnection *conn) { soup_server_message_set_tls_peer_certificate (msg, - soup_socket_get_tls_certificate (sock), - soup_socket_get_tls_certificate_errors (sock)); + soup_server_connection_get_tls_peer_certificate (conn), + soup_server_connection_get_tls_peer_certificate_errors (conn)); } SoupServerMessage * -soup_server_message_new (SoupSocket *sock) +soup_server_message_new (SoupServerConnection *conn) { SoupServerMessage *msg; msg = g_object_new (SOUP_TYPE_SERVER_MESSAGE, NULL); - msg->sock = g_object_ref (sock); - msg->gsock = soup_socket_get_gsocket (sock); + msg->conn = g_object_ref (conn); + msg->gsock = soup_server_connection_get_socket (conn); if (msg->gsock) g_object_ref (msg->gsock); - g_signal_connect_object (sock, "disconnected", - G_CALLBACK (socket_disconnected), + g_signal_connect_object (conn, "disconnected", + G_CALLBACK (connection_disconnected), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (sock, "accept-certificate", - G_CALLBACK (socket_accept_certificate), + g_signal_connect_object (conn, "accept-certificate", + G_CALLBACK (connection_accept_certificate), msg, G_CONNECT_SWAPPED); - g_signal_connect_object (sock, "notify::tls-certificate", + g_signal_connect_object (conn, "notify::tls-certificate", G_CALLBACK (re_emit_tls_certificate_changed), msg, G_CONNECT_SWAPPED); @@ -478,10 +477,10 @@ soup_server_message_set_uri (SoupServerMessage *msg, msg->uri = soup_uri_copy_with_normalized_flags (uri); } -SoupSocket * -soup_server_message_get_soup_socket (SoupServerMessage *msg) +SoupServerConnection * +soup_server_message_get_connection (SoupServerMessage *msg) { - return msg->sock; + return msg->conn; } void @@ -954,7 +953,7 @@ soup_server_message_get_remote_address (SoupServerMessage *msg) msg->remote_addr = msg->gsock ? g_socket_get_remote_address (msg->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_remote_address (msg->sock))); + G_SOCKET_ADDRESS (g_object_ref (soup_server_connection_get_remote_address (msg->conn))); return msg->remote_addr; } @@ -980,7 +979,7 @@ soup_server_message_get_local_address (SoupServerMessage *msg) msg->local_addr = msg->gsock ? g_socket_get_local_address (msg->gsock, NULL) : - G_SOCKET_ADDRESS (g_object_ref (soup_socket_get_local_address (msg->sock))); + G_SOCKET_ADDRESS (g_object_ref (soup_server_connection_get_local_address (msg->conn))); return msg->local_addr; } @@ -1013,7 +1012,7 @@ soup_server_message_get_remote_host (SoupServerMessage *msg) iaddr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (addr)); msg->remote_ip = g_inet_address_to_string (iaddr); } else { - GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_socket_get_remote_address (msg->sock)); + GInetSocketAddress *addr = G_INET_SOCKET_ADDRESS (soup_server_connection_get_remote_address (msg->conn)); GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); msg->remote_ip = g_inet_address_to_string (inet_addr); } @@ -1050,13 +1049,13 @@ soup_server_message_steal_connection (SoupServerMessage *msg) stream = soup_server_message_io_steal (msg); if (stream) { g_object_set_data_full (G_OBJECT (stream), "GSocket", - soup_socket_steal_gsocket (msg->sock), + soup_server_connection_steal_socket (msg->conn), g_object_unref); } - g_signal_handlers_disconnect_by_data (msg, msg->sock); + g_signal_handlers_disconnect_by_data (msg, msg->conn); - socket_disconnected (msg); + connection_disconnected (msg); g_object_unref (msg); return stream; diff --git a/libsoup/server/soup-server.c b/libsoup/server/soup-server.c index ebb1841b..b0f948eb 100644 --- a/libsoup/server/soup-server.c +++ b/libsoup/server/soup-server.c @@ -19,7 +19,7 @@ #include "soup.h" #include "soup-misc.h" #include "soup-path-map.h" -#include "soup-socket.h" +#include "soup-listener.h" #include "soup-uri-utils-private.h" #include "websocket/soup-websocket.h" #include "websocket/soup-websocket-connection.h" @@ -185,8 +185,10 @@ static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; G_DEFINE_TYPE_WITH_PRIVATE (SoupServer, soup_server, G_TYPE_OBJECT) -static void start_request (SoupServer *server, - SoupServerMessage *msg); +static void request_finished (SoupServerMessage *msg, + SoupMessageIOCompletion completion, + SoupServer *server); + static void free_handler (SoupServerHandler *handler) { @@ -746,7 +748,7 @@ soup_server_get_listeners (SoupServer *server) listeners = NULL; for (iter = priv->listeners; iter; iter = iter->next) - listeners = g_slist_prepend (listeners, soup_socket_get_gsocket (iter->data)); + listeners = g_slist_prepend (listeners, soup_listener_get_socket (iter->data)); /* priv->listeners has the sockets in reverse order from how * they were added, so listeners now has them back in the @@ -831,7 +833,7 @@ got_headers (SoupServer *server, gboolean rejected = FALSE; char *auth_user; SoupMessageHeaders *headers; - SoupSocket *sock; + SoupServerConnection *conn; /* Add required response headers */ headers = soup_server_message_get_response_headers (msg); @@ -845,10 +847,10 @@ got_headers (SoupServer *server, if (soup_server_message_get_status (msg) != 0) return; - sock = soup_server_message_get_soup_socket (msg); + conn = soup_server_message_get_connection (msg); uri = soup_server_message_get_uri (msg); - if ((soup_socket_is_ssl (sock) && !soup_uri_is_https (uri)) || - (!soup_socket_is_ssl (sock) && !soup_uri_is_http (uri))) { + if ((soup_server_connection_is_ssl (conn) && !soup_uri_is_https (uri)) || + (!soup_server_connection_is_ssl (conn) && !soup_uri_is_http (uri))) { soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL); return; } @@ -996,19 +998,74 @@ client_disconnected (SoupServer *server, soup_server_message_io_finished (msg); } +typedef struct { + SoupServer *server; + SoupServerMessage *msg; +} SetupConnectionData; + +static void +connection_setup_ready (SoupServerConnection *conn, + GAsyncResult *result, + SetupConnectionData *data) +{ + SoupServerPrivate *priv = soup_server_get_instance_private (data->server); + + if (soup_server_connection_setup_finish (conn, result, NULL)) { + if (g_slist_find (priv->clients, data->msg)) { + soup_server_message_read_request (data->msg, + (SoupMessageIOCompletionFn)request_finished, + data->server); + } + } else { + soup_server_connection_disconnect (conn); + } + + g_object_unref (data->msg); + g_object_unref (data->server); + g_free (data); +} + static void -soup_server_accept_socket (SoupServer *server, - SoupSocket *sock) +soup_server_accept_connection (SoupServer *server, + SoupServerConnection *conn) { SoupServerPrivate *priv = soup_server_get_instance_private (server); SoupServerMessage *msg; + SetupConnectionData *data; - msg = soup_server_message_new (sock); + msg = soup_server_message_new (conn); g_signal_connect_object (msg, "disconnected", G_CALLBACK (client_disconnected), server, G_CONNECT_SWAPPED); + g_signal_connect_object (msg, "got-headers", + G_CALLBACK (got_headers), + server, G_CONNECT_SWAPPED); + g_signal_connect_object (msg, "got-body", + G_CALLBACK (got_body), + server, G_CONNECT_SWAPPED); + if (priv->server_header) { + SoupMessageHeaders *headers; + + headers = soup_server_message_get_response_headers (msg); + soup_message_headers_append_common (headers, SOUP_HEADER_SERVER, + priv->server_header); + } + priv->clients = g_slist_prepend (priv->clients, msg); - start_request (server, msg); + + g_signal_emit (server, signals[REQUEST_STARTED], 0, msg); + + if (soup_server_connection_is_connected (conn)) { + soup_server_message_read_request (msg, + (SoupMessageIOCompletionFn)request_finished, + server); + return; + } + + data = g_new (SetupConnectionData, 1); + data->server = g_object_ref (server); + data->msg = g_object_ref (msg); + soup_server_connection_setup_async (conn, NULL, (GAsyncReadyCallback)connection_setup_ready, data); } static void @@ -1017,7 +1074,7 @@ request_finished (SoupServerMessage *msg, SoupServer *server) { SoupServerPrivate *priv = soup_server_get_instance_private (server); - SoupSocket *sock = soup_server_message_get_soup_socket (msg); + SoupServerConnection *conn = soup_server_message_get_connection (msg); gboolean failed; if (completion == SOUP_MESSAGE_IO_STOLEN) { @@ -1037,50 +1094,22 @@ request_finished (SoupServerMessage *msg, } if (completion == SOUP_MESSAGE_IO_COMPLETE && - soup_socket_is_connected (sock) && + soup_server_connection_is_connected (conn) && soup_server_message_is_keepalive (msg) && priv->listeners) { - g_object_ref (sock); + g_object_ref (conn); priv->clients = g_slist_remove (priv->clients, msg); g_object_unref (msg); - soup_server_accept_socket (server, sock); - g_object_unref (sock); + soup_server_accept_connection (server, conn); + g_object_unref (conn); return; } - soup_socket_disconnect (sock); + soup_server_connection_disconnect (conn); g_object_unref (msg); } -static void -start_request (SoupServer *server, - SoupServerMessage *msg) -{ - SoupServerPrivate *priv = soup_server_get_instance_private (server); - - if (priv->server_header) { - SoupMessageHeaders *headers; - - headers = soup_server_message_get_response_headers (msg); - soup_message_headers_append_common (headers, SOUP_HEADER_SERVER, - priv->server_header); - } - - g_signal_connect_object (msg, "got-headers", - G_CALLBACK (got_headers), - server, G_CONNECT_SWAPPED); - g_signal_connect_object (msg, "got-body", - G_CALLBACK (got_body), - server, G_CONNECT_SWAPPED); - - g_signal_emit (server, signals[REQUEST_STARTED], 0, msg); - - soup_server_message_read_request (msg, - (SoupMessageIOCompletionFn)request_finished, - server); -} - /** * soup_server_accept_iostream: * @server: a #SoupServer @@ -1104,29 +1133,21 @@ soup_server_accept_iostream (SoupServer *server, GSocketAddress *remote_addr, GError **error) { - SoupSocket *sock; - - sock = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, - "iostream", stream, - "local-address", local_addr, - "remote-connectable", remote_addr, - NULL); - - if (!sock) - return FALSE; + SoupServerConnection *conn; - soup_server_accept_socket (server, sock); - g_object_unref (sock); + conn = soup_server_connection_new_for_connection (stream, local_addr, remote_addr); + soup_server_accept_connection (server, conn); + g_object_unref (conn); return TRUE; } static void -new_connection (SoupSocket *listener, SoupSocket *sock, gpointer user_data) +new_connection (SoupListener *listener, + SoupServerConnection *conn, + SoupServer *server) { - SoupServer *server = user_data; - - soup_server_accept_socket (server, sock); + soup_server_accept_connection (server, conn); } /** @@ -1147,7 +1168,7 @@ soup_server_disconnect (SoupServer *server) { SoupServerPrivate *priv; GSList *listeners, *clients, *iter; - SoupSocket *listener; + SoupListener *listener; g_return_if_fail (SOUP_IS_SERVER (server)); priv = soup_server_get_instance_private (server); @@ -1160,13 +1181,13 @@ soup_server_disconnect (SoupServer *server) for (iter = clients; iter; iter = iter->next) { SoupServerMessage *msg = iter->data; - soup_socket_disconnect (soup_server_message_get_soup_socket (msg)); + soup_server_connection_disconnect (soup_server_message_get_connection (msg)); } g_slist_free (clients); for (iter = listeners; iter; iter = iter->next) { listener = iter->data; - soup_socket_disconnect (listener); + soup_listener_disconnect (listener); g_object_unref (listener); } g_slist_free (listeners); @@ -1189,9 +1210,10 @@ soup_server_disconnect (SoupServer *server) */ static gboolean -soup_server_listen_internal (SoupServer *server, SoupSocket *listener, +soup_server_listen_internal (SoupServer *server, + SoupListener *listener, SoupServerListenOptions options, - GError **error) + GError **error) { SoupServerPrivate *priv = soup_server_get_instance_private (server); @@ -1215,23 +1237,9 @@ soup_server_listen_internal (SoupServer *server, SoupSocket *listener, G_BINDING_SYNC_CREATE); } - if (soup_socket_get_gsocket (listener) == NULL) { - if (!soup_socket_listen (listener, error)) { - GInetSocketAddress *addr = soup_socket_get_local_address (listener); - GInetAddress *inet_addr = g_inet_socket_address_get_address (addr); - char *local_ip = g_inet_address_to_string (inet_addr); - - g_prefix_error (error, - _("Could not listen on address %s, port %d: "), - local_ip, - g_inet_socket_address_get_port (addr)); - g_free (local_ip); - return FALSE; - } - } - - g_signal_connect (listener, "new_connection", - G_CALLBACK (new_connection), server); + g_signal_connect (listener, "new-connection", + G_CALLBACK (new_connection), + server); /* Note: soup_server_listen_ipv4_ipv6() below relies on the * fact that this does g_slist_prepend(). @@ -1274,8 +1282,8 @@ soup_server_listen (SoupServer *server, GSocketAddress *address, GError **error) { SoupServerPrivate *priv; - SoupSocket *listener; - gboolean success, ipv6_only; + SoupListener *listener; + gboolean success; g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); g_return_val_if_fail (!(options & SOUP_SERVER_LISTEN_IPV4_ONLY) && @@ -1284,10 +1292,9 @@ soup_server_listen (SoupServer *server, GSocketAddress *address, priv = soup_server_get_instance_private (server); g_return_val_if_fail (priv->disposed == FALSE, FALSE); - ipv6_only = g_socket_address_get_family (address) == G_SOCKET_FAMILY_IPV6; - listener = soup_socket_new ("local-address", address, - "ipv6-only", ipv6_only, - NULL); + listener = soup_listener_new_for_address (address, error); + if (!listener) + return FALSE; success = soup_server_listen_internal (server, listener, options, error); g_object_unref (listener); @@ -1306,7 +1313,7 @@ soup_server_listen_ipv4_ipv6 (SoupServer *server, SoupServerPrivate *priv = soup_server_get_instance_private (server); GSocketAddress *addr4, *addr6; GError *my_error = NULL; - SoupSocket *v4sock; + SoupListener *v4sock; guint v4port; g_return_val_if_fail (iaddr4 != NULL || iaddr6 != NULL, FALSE); @@ -1323,7 +1330,7 @@ soup_server_listen_ipv4_ipv6 (SoupServer *server, g_object_unref (addr4); v4sock = priv->listeners->data; - v4port = g_inet_socket_address_get_port (soup_socket_get_local_address (v4sock)); + v4port = g_inet_socket_address_get_port (soup_listener_get_address (v4sock)); } else { v4sock = NULL; v4port = port; @@ -1355,7 +1362,7 @@ soup_server_listen_ipv4_ipv6 (SoupServer *server, if (v4sock) { priv->listeners = g_slist_remove (priv->listeners, v4sock); - soup_socket_disconnect (v4sock); + soup_listener_disconnect (v4sock); g_object_unref (v4sock); } @@ -1493,7 +1500,7 @@ soup_server_listen_socket (SoupServer *server, GSocket *socket, GError **error) { SoupServerPrivate *priv; - SoupSocket *listener; + SoupListener *listener; gboolean success; g_return_val_if_fail (SOUP_IS_SERVER (server), FALSE); @@ -1504,10 +1511,7 @@ soup_server_listen_socket (SoupServer *server, GSocket *socket, priv = soup_server_get_instance_private (server); g_return_val_if_fail (priv->disposed == FALSE, FALSE); - listener = g_initable_new (SOUP_TYPE_SOCKET, NULL, error, - "gsocket", socket, - "ipv6-only", TRUE, - NULL); + listener = soup_listener_new (socket, error); if (!listener) return FALSE; @@ -1539,7 +1543,7 @@ soup_server_get_uris (SoupServer *server) { SoupServerPrivate *priv; GSList *uris, *l; - SoupSocket *listener; + SoupListener *listener; GInetSocketAddress *addr; GInetAddress *inet_addr; char *ip; @@ -1551,7 +1555,7 @@ soup_server_get_uris (SoupServer *server) for (l = priv->listeners, uris = NULL; l; l = l->next) { listener = l->data; - addr = soup_socket_get_local_address (listener); + addr = soup_listener_get_address (listener); inet_addr = g_inet_socket_address_get_address (addr); ip = g_inet_address_to_string (inet_addr); port = g_inet_socket_address_get_port (addr); @@ -1560,7 +1564,7 @@ soup_server_get_uris (SoupServer *server) port = -1; uri = g_uri_build (SOUP_HTTP_URI_FLAGS, - soup_socket_is_ssl (listener) ? "https" : "http", + soup_listener_is_ssl (listener) ? "https" : "http", NULL, ip, port, "/", NULL, NULL); uris = g_slist_prepend (uris, uri); diff --git a/libsoup/server/soup-socket.c b/libsoup/server/soup-socket.c deleted file mode 100644 index 74ccacac..00000000 --- a/libsoup/server/soup-socket.c +++ /dev/null @@ -1,865 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * soup-socket.c: Socket networking code. - * - * Copyright (C) 2000-2003, Ximian, Inc. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <string.h> - -#include <glib/gi18n-lib.h> -#include <gio/gnetworking.h> - -#include "soup-socket.h" -#include "soup.h" -#include "soup-io-stream.h" - -/*<private> - * SECTION:soup-socket - * @short_description: A network socket - * - * #SoupSocket is libsoup's TCP socket type. While it is primarily - * intended for internal use, #SoupSocket<!-- -->s are exposed in the - * API in various places, and some of their methods (eg, - * soup_socket_get_remote_address()) may be useful to applications. - **/ - -enum { - DISCONNECTED, - NEW_CONNECTION, - ACCEPT_CERTIFICATE, - LAST_SIGNAL -}; - -static guint signals[LAST_SIGNAL] = { 0 }; - -enum { - PROP_0, - - PROP_GSOCKET, - PROP_IOSTREAM, - PROP_LOCAL_ADDRESS, - PROP_REMOTE_ADDRESS, - PROP_REMOTE_CONNECTABLE, - PROP_IPV6_ONLY, - PROP_TLS_CERTIFICATE, - PROP_TLS_DATABASE, - PROP_TLS_AUTH_MODE, - PROP_TLS_PEER_CERTIFICATE, - PROP_TLS_PEER_CERTIFICATE_ERRORS, - - LAST_PROPERTY -}; - -static GParamSpec *properties[LAST_PROPERTY] = { NULL, }; - -struct _SoupSocket { - GObject parent_instance; -}; - -typedef struct { - GInetSocketAddress *local_addr, *remote_addr; - GSocketConnectable *remote_connectable; - GIOStream *conn, *iostream; - GSocket *gsock; - GInputStream *istream; - GOutputStream *ostream; - - guint ipv6_only:1; - guint ssl:1; - GTlsCertificate *tls_certificate; - GTlsDatabase *tls_database; - GTlsAuthenticationMode tls_auth_mode; - - GMainContext *async_context; - GSource *watch_src; -} SoupSocketPrivate; - -static void soup_socket_initable_interface_init (GInitableIface *initable_interface); - -G_DEFINE_FINAL_TYPE_WITH_CODE (SoupSocket, soup_socket, G_TYPE_OBJECT, - G_ADD_PRIVATE (SoupSocket) - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - soup_socket_initable_interface_init)) - -static void finish_socket_setup (SoupSocket *sock); -static void finish_listener_setup (SoupSocket *sock); - -static void -soup_socket_init (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - priv->async_context = g_main_context_ref_thread_default (); -} - -static gboolean -soup_socket_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - SoupSocket *sock = SOUP_SOCKET (initable); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->conn) { - g_warn_if_fail (priv->gsock == NULL); - - finish_socket_setup (sock); - } - - 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) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - g_clear_object (&priv->gsock); - if (priv->conn) { - g_io_stream_close (priv->conn, NULL, NULL); - g_signal_handlers_disconnect_by_data (priv->conn, sock); - g_clear_object (&priv->conn); - } -} - -static void -soup_socket_finalize (GObject *object) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->conn) - disconnect_internal (SOUP_SOCKET (object)); - - g_clear_object (&priv->conn); - g_clear_object (&priv->iostream); - g_clear_object (&priv->istream); - g_clear_object (&priv->ostream); - - g_clear_object (&priv->local_addr); - g_clear_object (&priv->remote_addr); - g_clear_object (&priv->remote_connectable); - - g_clear_object (&priv->tls_certificate); - g_clear_object (&priv->tls_database); - - if (priv->watch_src) { - g_source_destroy (priv->watch_src); - g_source_unref (priv->watch_src); - } - g_clear_pointer (&priv->async_context, g_main_context_unref); - - G_OBJECT_CLASS (soup_socket_parent_class)->finalize (object); -} - -static void -finish_socket_setup (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - if (priv->gsock) { - if (!priv->conn) - priv->conn = (GIOStream *)g_socket_connection_factory_create_connection (priv->gsock); - - g_socket_set_option (priv->gsock, IPPROTO_TCP, TCP_NODELAY, TRUE, NULL); - } - - if (!priv->conn) - return; - - if (!priv->iostream) - priv->iostream = soup_io_stream_new (priv->conn, FALSE); - if (!priv->istream) - priv->istream = g_object_ref (g_io_stream_get_input_stream (priv->iostream)); - if (!priv->ostream) - priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream)); -} - -static void -soup_socket_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - switch (prop_id) { - case PROP_GSOCKET: - priv->gsock = g_value_dup_object (value); - break; - case PROP_IOSTREAM: - priv->conn = g_value_dup_object (value); - break; - case PROP_LOCAL_ADDRESS: - priv->local_addr = g_value_dup_object (value); - break; - case PROP_REMOTE_ADDRESS: - priv->remote_addr = g_value_dup_object (value); - break; - case PROP_REMOTE_CONNECTABLE: - priv->remote_connectable = g_value_dup_object (value); - break; - case PROP_IPV6_ONLY: - priv->ipv6_only = g_value_get_boolean (value); - break; - case PROP_TLS_CERTIFICATE: - priv->tls_certificate = g_value_dup_object (value); - break; - case PROP_TLS_DATABASE: - priv->tls_database = g_value_dup_object (value); - break; - case PROP_TLS_AUTH_MODE: - priv->tls_auth_mode = g_value_get_enum (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -soup_socket_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - SoupSocket *sock = SOUP_SOCKET (object); - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - switch (prop_id) { - case PROP_LOCAL_ADDRESS: - g_value_set_object (value, soup_socket_get_local_address (sock)); - break; - case PROP_REMOTE_ADDRESS: - g_value_set_object (value, soup_socket_get_remote_address (sock)); - break; - case PROP_REMOTE_CONNECTABLE: - g_value_set_object (value, priv->remote_connectable); - break; - case PROP_IPV6_ONLY: - g_value_set_boolean (value, priv->ipv6_only); - break; - case PROP_TLS_CERTIFICATE: - g_value_set_object (value, priv->tls_certificate); - break; - case PROP_TLS_DATABASE: - g_value_set_object (value, priv->tls_database); - break; - case PROP_TLS_AUTH_MODE: - g_value_set_enum (value, priv->tls_auth_mode); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -soup_socket_class_init (SoupSocketClass *socket_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (socket_class); - - /* virtual method override */ - object_class->finalize = soup_socket_finalize; - object_class->set_property = soup_socket_set_property; - object_class->get_property = soup_socket_get_property; - - /* signals */ - - /** - * SoupSocket::disconnected: - * @sock: the socket - * - * Emitted when the socket is disconnected, for whatever - * reason. - **/ - signals[DISCONNECTED] = - g_signal_new ("disconnected", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 0); - - /** - * SoupSocket::new-connection: - * @sock: the socket - * @new: the new socket - * - * Emitted when a listening socket receives a new connection. - * - * Has to be set up with [func@soup_socket_listen]. - * - * You must ref the @new if you want to keep it; otherwise it - * will be destroyed after the signal is emitted. - **/ - signals[NEW_CONNECTION] = - g_signal_new ("new_connection", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST, - 0, - NULL, NULL, - NULL, - G_TYPE_NONE, 1, - SOUP_TYPE_SOCKET); - - signals[ACCEPT_CERTIFICATE] = - g_signal_new ("accept-certificate", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - 0, - g_signal_accumulator_true_handled, NULL, - NULL, - G_TYPE_BOOLEAN, 2, - G_TYPE_TLS_CERTIFICATE, - G_TYPE_TLS_CERTIFICATE_FLAGS); - - /* properties */ - properties[PROP_GSOCKET] = - g_param_spec_object ("gsocket", - "GSocket", - "The socket's underlying GSocket", - G_TYPE_SOCKET, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - properties[PROP_IOSTREAM] = - g_param_spec_object ("iostream", - "GIOStream", - "The socket's underlying GIOStream", - G_TYPE_IO_STREAM, - G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - properties[PROP_LOCAL_ADDRESS] = - g_param_spec_object ("local-address", - "Local address", - "Address of local end of socket", - G_TYPE_INET_SOCKET_ADDRESS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - properties[PROP_REMOTE_ADDRESS] = - g_param_spec_object ("remote-address", - "Remote address", - "Address of remote end of socket", - G_TYPE_SOCKET_ADDRESS, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_REMOTE_CONNECTABLE] = - g_param_spec_object ("remote-connectable", - "Remote address", - "Address to connect to", - G_TYPE_SOCKET_CONNECTABLE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS); - - properties[PROP_IPV6_ONLY] = - g_param_spec_boolean ("ipv6-only", - "IPv6 only", - "IPv6 only", - FALSE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_TLS_CERTIFICATE] = - g_param_spec_object ("tls-certificate", - "TLS Certificate", - "The server TLS certificate", - G_TYPE_TLS_CERTIFICATE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_TLS_DATABASE] = - g_param_spec_object ("tls-database", - "TLS Database", - "The server TLS database", - G_TYPE_TLS_DATABASE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_TLS_AUTH_MODE] = - g_param_spec_enum ("tls-auth-mode", - "TLS Authentication Mode", - "The server TLS authentication mode", - G_TYPE_TLS_AUTHENTICATION_MODE, - G_TLS_AUTHENTICATION_NONE, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_TLS_PEER_CERTIFICATE] = - g_param_spec_object ("tls-peer-certificate", - "TLS Peer Certificate", - "The TLS peer certificate associated with the message", - G_TYPE_TLS_CERTIFICATE, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - properties[PROP_TLS_PEER_CERTIFICATE_ERRORS] = - g_param_spec_flags ("tls-peer-certificate-errors", - "TLS Peer Certificate Errors", - "The verification errors on the message's TLS peer certificate", - G_TYPE_TLS_CERTIFICATE_FLAGS, 0, - G_PARAM_READABLE | - G_PARAM_STATIC_STRINGS); - - g_object_class_install_properties (object_class, LAST_PROPERTY, properties); -} - -static void -soup_socket_initable_interface_init (GInitableIface *initable_interface) -{ - initable_interface->init = soup_socket_initable_init; -} - - -/** - * soup_socket_new: - * @optname1: name of first property to set (or %NULL) - * @...: value of @optname1, followed by additional property/value pairs - * - * Creates a new (disconnected) socket - * - * Returns: the new socket - **/ -SoupSocket * -soup_socket_new (const char *optname1, ...) -{ - SoupSocket *sock; - va_list ap; - - va_start (ap, optname1); - sock = (SoupSocket *)g_object_new_valist (SOUP_TYPE_SOCKET, - optname1, ap); - va_end (ap); - - return sock; -} - -GSocket * -soup_socket_get_gsocket (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - - priv = soup_socket_get_instance_private (sock); - - return priv->gsock; -} - -GSocket * -soup_socket_steal_gsocket (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - GSocket *gsock; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - gsock = g_steal_pointer (&priv->gsock); - g_clear_object (&priv->conn); - g_clear_object (&priv->iostream); - - return gsock; -} - -GIOStream * -soup_socket_get_iostream (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - - priv = soup_socket_get_instance_private (sock); - - return priv->iostream; -} - -static gboolean -tls_connection_accept_certificate (SoupSocket *sock, - GTlsCertificate *tls_certificate, - GTlsCertificateFlags tls_errors) -{ - gboolean accept = FALSE; - - g_signal_emit (sock, signals[ACCEPT_CERTIFICATE], 0, - tls_certificate, tls_errors, &accept); - return accept; -} - -static void -tls_connection_peer_certificate_changed (SoupSocket *sock) -{ - g_object_notify_by_pspec (G_OBJECT (sock), properties[PROP_TLS_CERTIFICATE]); -} - -static gboolean -soup_socket_setup_ssl (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - GTlsBackend *backend = g_tls_backend_get_default (); - GTlsServerConnection *conn; - - if (G_IS_TLS_CONNECTION (priv->conn)) - return TRUE; - - priv->ssl = TRUE; - - conn = g_initable_new (g_tls_backend_get_server_connection_type (backend), - NULL, NULL, - "base-io-stream", priv->conn, - "certificate", priv->tls_certificate, - "database", priv->tls_database, - "authentication-mode", priv->tls_auth_mode, - "require-close-notify", FALSE, - NULL); - if (!conn) - return FALSE; - - g_object_unref (priv->conn); - priv->conn = G_IO_STREAM (conn); - - g_signal_connect_object (priv->conn, "accept-certificate", - G_CALLBACK (tls_connection_accept_certificate), - sock, G_CONNECT_SWAPPED); - g_signal_connect_object (priv->conn, "notify::peer-certificate", - G_CALLBACK (tls_connection_peer_certificate_changed), - sock, G_CONNECT_SWAPPED); - - g_clear_object (&priv->istream); - g_clear_object (&priv->ostream); - g_clear_object (&priv->iostream); - priv->iostream = soup_io_stream_new (priv->conn, FALSE); - priv->istream = g_object_ref (g_io_stream_get_input_stream (priv->iostream)); - priv->ostream = g_object_ref (g_io_stream_get_output_stream (priv->iostream)); - - return TRUE; -} - -static gboolean -listen_watch (GObject *pollable, gpointer data) -{ - SoupSocket *sock = data, *new; - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock), *new_priv; - GSocket *new_gsock; - - new_gsock = g_socket_accept (priv->gsock, NULL, NULL); - if (!new_gsock) - return FALSE; - - new = g_object_new (SOUP_TYPE_SOCKET, NULL); - new_priv = soup_socket_get_instance_private (new); - new_priv->gsock = new_gsock; - new_priv->async_context = g_main_context_ref (priv->async_context); - new_priv->ssl = priv->ssl; - if (priv->tls_certificate) - new_priv->tls_certificate = g_object_ref (priv->tls_certificate); - if (priv->tls_database) - new_priv->tls_database = g_object_ref (priv->tls_database); - new_priv->tls_auth_mode = priv->tls_auth_mode; - finish_socket_setup (new); - - if (new_priv->tls_certificate) { - if (!soup_socket_setup_ssl (new)) { - g_object_unref (new); - return TRUE; - } - } - - g_signal_emit (sock, signals[NEW_CONNECTION], 0, new); - g_object_unref (new); - - return TRUE; -} - -static void -finish_listener_setup (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - priv->watch_src = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (priv->istream), NULL); - g_source_set_callback (priv->watch_src, (GSourceFunc)listen_watch, sock, NULL); - g_source_attach (priv->watch_src, priv->async_context); -} - -/** - * soup_socket_listen: - * @sock: a server #SoupSocket (which must not already be connected or listening) - * @error: error pointer - * - * Makes @sock start listening on its local address. - * - * When connections come in, @sock will emit #SoupSocket::new_connection. - * - * Returns: whether or not @sock is now listening. - **/ -gboolean -soup_socket_listen (SoupSocket *sock, - GError **error) - -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE); - priv = soup_socket_get_instance_private (sock); - g_return_val_if_fail (priv->gsock == NULL, FALSE); - g_return_val_if_fail (priv->local_addr != NULL, FALSE); - - /* @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 - * have to make a new addr by calling getsockname(), which - * will have the right port number. - */ - g_return_val_if_fail (priv->local_addr != NULL, FALSE); - - priv->gsock = g_socket_new (g_socket_address_get_family (G_SOCKET_ADDRESS (priv->local_addr)), - G_SOCKET_TYPE_STREAM, - G_SOCKET_PROTOCOL_DEFAULT, - error); - if (!priv->gsock) - goto cant_listen; - finish_socket_setup (sock); - - if (priv->ipv6_only) { - GError *error = NULL; - g_socket_set_option (priv->gsock, IPPROTO_IPV6, IPV6_V6ONLY, TRUE, &error); - if (error) { - g_warning ("Failed to set IPv6 only on socket: %s", error->message); - g_error_free (error); - } - } - - /* Bind */ - if (!g_socket_bind (priv->gsock, G_SOCKET_ADDRESS (priv->local_addr), TRUE, error)) - goto cant_listen; - /* Force local_addr to be re-resolved now */ - g_clear_object (&priv->local_addr); - - /* Listen */ - if (!g_socket_listen (priv->gsock, error)) - goto cant_listen; - finish_listener_setup (sock); - return TRUE; - - cant_listen: - if (priv->conn) - disconnect_internal (sock); - - return FALSE; -} - -/** - * soup_socket_is_ssl: - * @sock: a #SoupSocket - * - * Tests if @sock is doing (or has attempted to do) SSL. - * - * Returns: %TRUE if @sock has SSL credentials set - **/ -gboolean -soup_socket_is_ssl (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - return priv->ssl || priv->tls_certificate; -} - -/** - * soup_socket_disconnect: - * @sock: a #SoupSocket - * - * Disconnects @sock. Any further read or write attempts on it will - * fail. - **/ -void -soup_socket_disconnect (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_if_fail (SOUP_IS_SOCKET (sock)); - priv = soup_socket_get_instance_private (sock); - - if (!priv->conn) - return; - - disconnect_internal (sock); - - /* Keep ref around signals in case the object is unreferenced - * in a handler - */ - g_object_ref (sock); - - /* FIXME: can't disconnect until all data is read */ - - /* Then let everyone know we're disconnected */ - g_signal_emit (sock, signals[DISCONNECTED], 0); - - g_object_unref (sock); -} - -/** - * soup_socket_is_connected: - * @sock: a #SoupSocket - * - * Tests if @sock is connected to another host - * - * Returns: %TRUE or %FALSE. - **/ -gboolean -soup_socket_is_connected (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), FALSE); - priv = soup_socket_get_instance_private (sock); - - return priv->conn && !g_io_stream_is_closed (priv->conn); -} - -/** - * soup_socket_get_local_address: - * @sock: a #SoupSocket - * - * Returns the #GInetSocketAddress corresponding to the local end of @sock. - * - * Calling this method on an unconnected socket is considered to be - * an error, and produces undefined results. - * - * Returns: (transfer none): the #GInetSocketAddress - **/ -GInetSocketAddress * -soup_socket_get_local_address (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - if (!priv->local_addr) { - GError *error = NULL; - - if (priv->gsock == NULL) { - g_warning ("%s: socket not connected", G_STRLOC); - return NULL; - } - - priv->local_addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (priv->gsock, &error)); - if (priv->local_addr == NULL) { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - return NULL; - } - } - - return priv->local_addr; -} - -/** - * soup_socket_get_remote_address: - * @sock: a #SoupSocket - * - * Returns the #GInetSocketAddress corresponding to the remote end of @sock. - * - * Calling this method on an unconnected socket is considered to be - * an error, and produces undefined results. - * - * Returns: (transfer none): the #GInetSocketAddress - **/ -GInetSocketAddress * -soup_socket_get_remote_address (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - priv = soup_socket_get_instance_private (sock); - - if (!priv->remote_addr) { - GError *error = NULL; - - // We may be conencting to a socket address rather than a network address - if (G_IS_INET_SOCKET_ADDRESS (priv->remote_connectable)) { - priv->remote_addr = g_object_ref (G_INET_SOCKET_ADDRESS (priv->remote_connectable)); - return priv->remote_addr; - } - - if (priv->gsock == NULL) { - g_warning ("%s: socket not connected", G_STRLOC); - return NULL; - } - - priv->remote_addr = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (priv->gsock, &error)); - if (priv->remote_addr == NULL) { - g_warning ("%s: %s", G_STRLOC, error->message); - g_error_free (error); - return NULL; - } - } - - return priv->remote_addr; -} - -GIOStream * -soup_socket_get_connection (SoupSocket *sock) -{ - SoupSocketPrivate *priv = soup_socket_get_instance_private (sock); - - return priv->conn; -} - -GTlsCertificate * -soup_socket_get_tls_certificate (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), NULL); - - priv = soup_socket_get_instance_private (sock); - - if (!G_IS_TLS_CONNECTION (priv->conn)) - return NULL; - - return g_tls_connection_get_peer_certificate (G_TLS_CONNECTION (priv->conn)); -} - -GTlsCertificateFlags -soup_socket_get_tls_certificate_errors (SoupSocket *sock) -{ - SoupSocketPrivate *priv; - - g_return_val_if_fail (SOUP_IS_SOCKET (sock), 0); - - priv = soup_socket_get_instance_private (sock); - - if (!G_IS_TLS_CONNECTION (priv->conn)) - return 0; - - return g_tls_connection_get_peer_certificate_errors (G_TLS_CONNECTION (priv->conn)); -} diff --git a/libsoup/server/soup-socket.h b/libsoup/server/soup-socket.h deleted file mode 100644 index 051b3dcb..00000000 --- a/libsoup/server/soup-socket.h +++ /dev/null @@ -1,36 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2000-2003, Ximian, Inc. - */ - -#pragma once - -#include "soup-types.h" - -G_BEGIN_DECLS - -#define SOUP_TYPE_SOCKET (soup_socket_get_type ()) -G_DECLARE_FINAL_TYPE (SoupSocket, soup_socket, SOUP, SOCKET, GObject) - -SoupSocket *soup_socket_new (const char *optname1, - ...) G_GNUC_NULL_TERMINATED; - -gboolean soup_socket_listen (SoupSocket *sock, - GError **error); - -gboolean soup_socket_is_ssl (SoupSocket *sock); - -void soup_socket_disconnect (SoupSocket *sock); -gboolean soup_socket_is_connected (SoupSocket *sock); -GIOStream *soup_socket_get_connection (SoupSocket *sock); -GSocket *soup_socket_get_gsocket (SoupSocket *sock); -GSocket *soup_socket_steal_gsocket (SoupSocket *sock); -GIOStream *soup_socket_get_iostream (SoupSocket *sock); - -GInetSocketAddress *soup_socket_get_local_address (SoupSocket *sock); -GInetSocketAddress *soup_socket_get_remote_address (SoupSocket *sock); - -GTlsCertificate *soup_socket_get_tls_certificate (SoupSocket *sock); -GTlsCertificateFlags soup_socket_get_tls_certificate_errors (SoupSocket *sock); - -G_END_DECLS diff --git a/tests/connection-test.c b/tests/connection-test.c index 4f454f39..6b07d6bd 100644 --- a/tests/connection-test.c +++ b/tests/connection-test.c @@ -6,7 +6,7 @@ #include "test-utils.h" #include "soup-connection.h" -#include "soup-socket.h" +#include "soup-server-connection.h" #include "soup-server-message-private.h" #include <gio/gnetworking.h> @@ -24,17 +24,17 @@ forget_close (SoupServerMessage *msg, } static void -close_socket (SoupServerMessage *msg, - SoupSocket *sock) +close_socket (SoupServerMessage *msg, + SoupServerConnection *conn) { - GSocket *gsocket; + GSocket *socket; int sockfd; /* Actually calling soup_socket_disconnect() here would cause * us to leak memory, so just shutdown the socket instead. */ - gsocket = soup_socket_get_gsocket (sock); - sockfd = g_socket_get_fd (gsocket); + socket = soup_server_connection_get_socket (conn); + sockfd = g_socket_get_fd (socket); #ifdef G_OS_WIN32 shutdown (sockfd, SD_SEND); #else @@ -50,10 +50,10 @@ close_socket (SoupServerMessage *msg, } static gboolean -timeout_socket (GObject *pollable, - SoupSocket *sock) +timeout_socket (GObject *pollable, + SoupServerConnection *conn) { - soup_socket_disconnect (sock); + soup_server_connection_disconnect (conn); return FALSE; } @@ -62,7 +62,7 @@ timeout_request_started (SoupServer *server, SoupServerMessage *msg, gpointer user_data) { - SoupSocket *sock; + SoupServerConnection *conn; GMainContext *context = g_main_context_get_thread_default (); GIOStream *iostream; GInputStream *istream; @@ -70,21 +70,22 @@ timeout_request_started (SoupServer *server, g_signal_handlers_disconnect_by_func (server, timeout_request_started, NULL); - sock = soup_server_message_get_soup_socket (msg); - iostream = soup_socket_get_iostream (sock); + conn = soup_server_message_get_connection (msg); + iostream = soup_server_connection_get_iostream (conn); istream = g_io_stream_get_input_stream (iostream); source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (istream), NULL); - g_source_set_callback (source, (GSourceFunc)timeout_socket, sock, NULL); + g_source_set_callback (source, (GSourceFunc)timeout_socket, conn, NULL); g_source_attach (source, g_main_context_get_thread_default ()); g_source_unref (source); g_mutex_unlock (&server_mutex); - while (soup_socket_is_connected (sock)) + while (soup_server_connection_is_connected (conn)) g_main_context_iteration (context, TRUE); } static void -setup_timeout_persistent (SoupServer *server, SoupSocket *sock) +setup_timeout_persistent (SoupServer *server, + SoupServerConnection *conn) { /* In order for the test to work correctly, we have to * close the connection *after* the client side writes @@ -143,16 +144,16 @@ server_callback (SoupServer *server, "Connection", "close"); if (too_long) { - SoupSocket *sock; + SoupServerConnection *conn; /* soup-message-io will wait for us to add * another chunk after the first, to fill out * the declared Content-Length. Instead, we * forcibly close the socket at that point. */ - sock = soup_server_message_get_soup_socket (msg); + conn = soup_server_message_get_connection (msg); g_signal_connect (msg, "wrote-chunk", - G_CALLBACK (close_socket), sock); + G_CALLBACK (close_socket), conn); } else if (no_close) { /* Remove the 'Connection: close' after writing * the headers, so that when we check it after @@ -166,10 +167,10 @@ server_callback (SoupServer *server, } if (!strcmp (path, "/timeout-persistent")) { - SoupSocket *sock; + SoupServerConnection *conn; - sock = soup_server_message_get_soup_socket (msg); - setup_timeout_persistent (server, sock); + conn = soup_server_message_get_connection (msg); + setup_timeout_persistent (server, conn); } soup_server_message_set_status (msg, SOUP_STATUS_OK, NULL); diff --git a/tests/meson.build b/tests/meson.build index bd76bab9..9f1d7aeb 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -94,7 +94,6 @@ tests = [ {'name': 'server-auth'}, {'name': 'server'}, {'name': 'sniffing'}, - {'name': 'socket'}, {'name': 'ssl', 'dependencies': [gnutls_dep], 'depends': mock_pkcs11_module, diff --git a/tests/socket-test.c b/tests/socket-test.c deleted file mode 100644 index 059f0005..00000000 --- a/tests/socket-test.c +++ /dev/null @@ -1,274 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ -/* - * Copyright 2007-2012 Red Hat, Inc. - * Copyright 2012 Nokia Corporation - */ - -#include "test-utils.h" -#include "libsoup/server/soup-socket.h" - -#include <fcntl.h> -#include <gio/gnetworking.h> - -#ifdef G_OS_WIN32 -#include <io.h> -#endif - -static void -assert_host_equals (GInetSocketAddress *addr, const char *host) -{ - char *addr_host = g_inet_address_to_string (g_inet_socket_address_get_address (addr)); - g_assert_cmpstr (addr_host, ==, host); - g_free (addr_host); -} - -static void -do_unconnected_socket_test (void) -{ - GInetSocketAddress *addr; - GSocketAddress *localhost; - SoupSocket *sock; - GSocketClient *client; - GSocketConnectable *remote_connectable; - GSocketConnection *conn; - GSocket *client_socket; - guint res; - - g_test_bug ("673083"); - - localhost = g_inet_socket_address_new_from_string ("127.0.0.1", 0); - - sock = soup_socket_new ("local-address", localhost, - NULL); - g_assert_true (sock != NULL); - - addr = soup_socket_get_local_address (sock); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), ==, 0); - - /* fails with ENOTCONN */ - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - "*socket not connected*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - res = soup_socket_listen (sock, NULL); - g_assert_true (res); - - addr = soup_socket_get_local_address (sock); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), >, 0); - - client = g_socket_client_new (); - remote_connectable = G_SOCKET_CONNECTABLE (soup_socket_get_local_address (sock)); - conn = g_socket_client_connect (client, remote_connectable, NULL, NULL); - g_assert_true (conn != NULL); - client_socket = g_socket_connection_get_socket (conn); - g_assert_true (client_socket != NULL); - addr = G_INET_SOCKET_ADDRESS (g_socket_get_local_address (client_socket, NULL)); - g_assert_true (addr != NULL); - g_object_unref (addr); - addr = G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (client_socket, NULL)); - g_assert_true (addr != NULL); - assert_host_equals (addr, "127.0.0.1"); - g_assert_cmpuint (g_inet_socket_address_get_port (addr), >, 0); - g_object_unref (addr); - g_object_unref (conn); - g_object_unref (client); - - client = g_socket_client_new (); - remote_connectable = G_SOCKET_CONNECTABLE (soup_socket_get_local_address (sock)); - /* save it for later */ - - /* listening socket fails with ENOTCONN */ - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - /* We can't check the error message since it comes from - * libc and is locale-dependent. - */ - "*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - soup_socket_disconnect (sock); - - g_test_expect_message ("libsoup", G_LOG_LEVEL_WARNING, - /* This error message comes from soup-socket.c though */ - "*socket not connected*"); - addr = soup_socket_get_remote_address (sock); - g_test_assert_expected_messages (); - g_assert_null (addr); - - conn = g_socket_client_connect (client, remote_connectable, NULL, NULL); - g_assert_false (conn != NULL); - - g_object_unref (localhost); - g_object_unref (client); - g_object_unref (sock); -} - -static int -socket_get_fd (SoupSocket *socket) -{ - return g_socket_get_fd (soup_socket_get_gsocket (socket)); -} - -static void -do_socket_from_fd_server_test (void) -{ - GSocket *gsock; - SoupSocket *sock; - GInetSocketAddress *local; - GSocketAddress *gaddr; - 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, - "gsocket", gsock, - NULL); - g_assert_no_error (error); - g_assert_nonnull (sock); - - g_object_get (G_OBJECT (sock), - "local-address", &local, - NULL); - g_assert_cmpint (socket_get_fd (sock), ==, g_socket_get_fd (gsock)); - g_assert_true (soup_socket_is_connected (sock)); - - assert_host_equals (local, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_address_get_port (local), ==, g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (gaddr))); - g_object_unref (local); - g_object_unref (gaddr); - - 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; - GInetSocketAddress *local, *remote; - GSocketAddress *gaddr; - GError *error = NULL; - - /* 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, - "gsocket", 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); - - 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, - "gsocket", gsock2, - NULL); - g_assert_no_error (error); - g_assert_nonnull (sock2); - - g_object_get (G_OBJECT (sock2), - "local-address", &local, - "remote-address", &remote, - NULL); - g_assert_cmpint (socket_get_fd (sock2), ==, g_socket_get_fd (gsock2)); - g_assert_true (soup_socket_is_connected (sock2)); - - assert_host_equals (local, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_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); - assert_host_equals (remote, "127.0.0.1"); - g_assert_cmpint (g_inet_socket_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) -{ - int ret; - - test_init (argc, argv, NULL); - - g_test_add_func ("/sockets/unconnected", do_unconnected_socket_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 (); - - test_cleanup (); - return ret; -} |