[libsoup/wip/server-api: 4/7] soup-socket: add properties to create a SoupSocket from an fd or GSocket
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/server-api: 4/7] soup-socket: add properties to create a SoupSocket from an fd or GSocket
- Date: Mon, 17 Mar 2014 16:24:16 +0000 (UTC)
commit bf540ec3c407cbfcd07225fa041f0b1dfd386350
Author: Dan Winship <danw gnome org>
Date: Tue Mar 11 09:01:57 2014 -0400
soup-socket: add properties to create a SoupSocket from an fd or GSocket
FIXME bug number
libsoup/soup-socket-private.h | 2 +
libsoup/soup-socket.c | 142 ++++++++++++++++++++++---
po/POTFILES.in | 1 +
tests/socket-test.c | 231 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 361 insertions(+), 15 deletions(-)
---
diff --git a/libsoup/soup-socket-private.h b/libsoup/soup-socket-private.h
index a059768..f661cc5 100644
--- a/libsoup/soup-socket-private.h
+++ b/libsoup/soup-socket-private.h
@@ -11,6 +11,8 @@
#define SOUP_SOCKET_CLEAN_DISPOSE "clean-dispose"
#define SOUP_SOCKET_PROXY_RESOLVER "proxy-resolver"
#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,
diff --git a/libsoup/soup-socket.c b/libsoup/soup-socket.c
index 754fa46..9ddef6d 100644
--- a/libsoup/soup-socket.c
+++ b/libsoup/soup-socket.c
@@ -11,6 +11,7 @@
#include <string.h>
+#include <glib/gi18n-lib.h>
#include <gio/gnetworking.h>
#include "soup-socket.h"
@@ -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,
@@ -92,12 +99,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)
@@ -105,10 +115,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)
{
@@ -167,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;
@@ -194,11 +258,17 @@ soup_socket_set_property (GObject *object, guint prop_id,
SoupSocketPrivate *priv = SOUP_SOCKET_GET_PRIVATE (object);
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);
@@ -247,6 +317,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;
@@ -408,6 +481,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:
*
@@ -474,8 +562,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,
@@ -646,6 +744,12 @@ soup_socket_class_init (SoupSocketClass *socket_class)
G_PARAM_READWRITE));
}
+static void
+soup_socket_initable_interface_init (GInitableIface *initable_interface)
+{
+ initable_interface->init = soup_socket_initable_init;
+}
+
/**
* soup_socket_new:
@@ -693,7 +797,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;
@@ -1017,11 +1121,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);
+ finish_socket_setup (new);
if (new_priv->ssl_creds) {
if (!soup_socket_start_proxy_ssl (new, NULL, NULL)) {
@@ -1036,6 +1141,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
@@ -1058,8 +1174,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
@@ -1075,7 +1189,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))
@@ -1087,10 +1201,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 21c70d4..23a19a5 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 5bcc3b0..821b1a2 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 ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]