Commit 128c4132 authored by Philip Withnall's avatar Philip Withnall

gsocketconnectable: Add a to_string() virtual method

Add string serialisation functions for GNetworkAddress, GSocketAddress,
GUnixSocketAddress, GInetSocketAddress, GNetworkService and
GSocketConnectable. These are intended for use in debug output, not for
serialisation in network or disc protocols.

They are implemented as a new virtual method on GSocketConnectable:
g_socket_connectable_to_string().

GInetSocketAddress and GUnixSocketAddress now implement
GSocketConnectable directly to implement to_string(). Previously they
implemented it via their abstract parent class, GSocketAddress.

https://bugzilla.gnome.org/show_bug.cgi?id=737116
parent 4e631d2e
......@@ -1915,6 +1915,7 @@ GSocketConnectable
GSocketConnectableIface
g_socket_connectable_enumerate
g_socket_connectable_proxy_enumerate
g_socket_connectable_to_string
<SUBSECTION>
GSocketAddressEnumerator
g_socket_address_enumerator_next
......
......@@ -26,6 +26,7 @@
#include "ginetsocketaddress.h"
#include "ginetaddress.h"
#include "gnetworkingprivate.h"
#include "gsocketconnectable.h"
#include "gioerror.h"
#include "glibintl.h"
......@@ -54,7 +55,13 @@ struct _GInetSocketAddressPrivate
guint32 scope_id;
};
G_DEFINE_TYPE_WITH_PRIVATE (GInetSocketAddress, g_inet_socket_address, G_TYPE_SOCKET_ADDRESS)
static void g_inet_socket_address_connectable_iface_init (GSocketConnectableIface *iface);
static gchar *g_inet_socket_address_connectable_to_string (GSocketConnectable *connectable);
G_DEFINE_TYPE_WITH_CODE (GInetSocketAddress, g_inet_socket_address, G_TYPE_SOCKET_ADDRESS,
G_ADD_PRIVATE (GInetSocketAddress)
G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
g_inet_socket_address_connectable_iface_init))
enum {
PROP_0,
......@@ -301,6 +308,59 @@ g_inet_socket_address_class_init (GInetSocketAddressClass *klass)
G_PARAM_STATIC_STRINGS));
}
static void
g_inet_socket_address_connectable_iface_init (GSocketConnectableIface *iface)
{
GSocketConnectableIface *parent_iface = g_type_interface_peek_parent (iface);
iface->enumerate = parent_iface->enumerate;
iface->proxy_enumerate = parent_iface->proxy_enumerate;
iface->to_string = g_inet_socket_address_connectable_to_string;
}
static gchar *
g_inet_socket_address_connectable_to_string (GSocketConnectable *connectable)
{
GInetSocketAddress *sa;
GInetAddress *a;
gchar *a_string;
GString *out;
guint16 port;
sa = G_INET_SOCKET_ADDRESS (connectable);
a = g_inet_socket_address_get_address (sa);
out = g_string_new ("");
/* Address. */
a_string = g_inet_address_to_string (a);
g_string_append (out, a_string);
g_free (a_string);
/* Scope ID (IPv6 only). */
if (g_inet_address_get_family (a) == G_SOCKET_FAMILY_IPV6 &&
g_inet_socket_address_get_scope_id (sa) != 0)
{
g_string_append_printf (out, "%%%u",
g_inet_socket_address_get_scope_id (sa));
}
/* Port. */
port = g_inet_socket_address_get_port (sa);
if (port != 0)
{
/* Disambiguate ports from IPv6 addresses using square brackets. */
if (g_inet_address_get_family (a) == G_SOCKET_FAMILY_IPV6)
{
g_string_prepend (out, "[");
g_string_append (out, "]");
}
g_string_append_printf (out, ":%u", port);
}
return g_string_free (out, FALSE);
}
static void
g_inet_socket_address_init (GInetSocketAddress *address)
{
......
......@@ -86,6 +86,7 @@ static void g_network_address_get_property (GObject *object,
static void g_network_address_connectable_iface_init (GSocketConnectableIface *iface);
static GSocketAddressEnumerator *g_network_address_connectable_enumerate (GSocketConnectable *connectable);
static GSocketAddressEnumerator *g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable);
static gchar *g_network_address_connectable_to_string (GSocketConnectable *connectable);
G_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
G_ADD_PRIVATE (GNetworkAddress)
......@@ -145,6 +146,7 @@ g_network_address_connectable_iface_init (GSocketConnectableIface *connectable_i
{
connectable_iface->enumerate = g_network_address_connectable_enumerate;
connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate;
connectable_iface->to_string = g_network_address_connectable_to_string;
}
static void
......@@ -1111,3 +1113,27 @@ g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
return proxy_enum;
}
static gchar *
g_network_address_connectable_to_string (GSocketConnectable *connectable)
{
GNetworkAddress *addr;
const gchar *scheme;
guint16 port;
GString *out; /* owned */
addr = G_NETWORK_ADDRESS (connectable);
out = g_string_new ("");
scheme = g_network_address_get_scheme (addr);
if (scheme != NULL)
g_string_append_printf (out, "%s:", scheme);
g_string_append (out, g_network_address_get_hostname (addr));
port = g_network_address_get_port (addr);
if (port != 0)
g_string_append_printf (out, ":%u", port);
return g_string_free (out, FALSE);
}
......@@ -89,6 +89,7 @@ static void g_network_service_get_property (GObject *object,
static void g_network_service_connectable_iface_init (GSocketConnectableIface *iface);
static GSocketAddressEnumerator *g_network_service_connectable_enumerate (GSocketConnectable *connectable);
static GSocketAddressEnumerator *g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable);
static gchar *g_network_service_connectable_to_string (GSocketConnectable *connectable);
G_DEFINE_TYPE_WITH_CODE (GNetworkService, g_network_service, G_TYPE_OBJECT,
G_ADD_PRIVATE (GNetworkService)
......@@ -159,6 +160,7 @@ g_network_service_connectable_iface_init (GSocketConnectableIface *connectable_i
{
connectable_iface->enumerate = g_network_service_connectable_enumerate;
connectable_iface->proxy_enumerate = g_network_service_connectable_proxy_enumerate;
connectable_iface->to_string = g_network_service_connectable_to_string;
}
static void
......@@ -743,3 +745,15 @@ g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable)
return addr_enum;
}
static gchar *
g_network_service_connectable_to_string (GSocketConnectable *connectable)
{
GNetworkService *service;
service = G_NETWORK_SERVICE (connectable);
return g_strdup_printf ("(%s, %s, %s, %s)", service->priv->service,
service->priv->protocol, service->priv->domain,
service->priv->scheme);
}
......@@ -129,6 +129,7 @@ g_socket_address_connectable_iface_init (GSocketConnectableIface *connectable_if
{
connectable_iface->enumerate = g_socket_address_connectable_enumerate;
connectable_iface->proxy_enumerate = g_socket_address_connectable_proxy_enumerate;
/* to_string() is implemented by subclasses */
}
static void
......
......@@ -146,3 +146,34 @@ g_socket_connectable_proxy_enumerate (GSocketConnectable *connectable)
else
return (* iface->enumerate) (connectable);
}
/**
* g_socket_connectable_to_string:
* @connectable: a #GSocketConnectable
*
* Format a #GSocketConnectable as a string. This is a human-readable format for
* use in debugging output, and is not a stable serialization format. It is not
* suitable for use in user interfaces as it exposes too much information for a
* user.
*
* If the #GSocketConnectable implementation does not support string formatting,
* the implementation’s type name will be returned as a fallback.
*
* Returns: (transfer full): the formatted string
*
* Since: 2.48.0
*/
gchar *
g_socket_connectable_to_string (GSocketConnectable *connectable)
{
GSocketConnectableIface *iface;
g_return_val_if_fail (G_IS_SOCKET_CONNECTABLE (connectable), NULL);
iface = G_SOCKET_CONNECTABLE_GET_IFACE (connectable);
if (iface->to_string != NULL)
return iface->to_string (connectable);
else
return g_strdup (G_OBJECT_TYPE_NAME (connectable));
}
......@@ -44,6 +44,8 @@ typedef struct _GSocketConnectableIface GSocketConnectableIface;
* @g_iface: The parent interface.
* @enumerate: Creates a #GSocketAddressEnumerator
* @proxy_enumerate: Creates a #GProxyAddressEnumerator
* @to_string: Format the connectable’s address as a string for debugging.
* Implementing this is optional. (Since: 2.48.0.)
*
* Provides an interface for returning a #GSocketAddressEnumerator
* and #GProxyAddressEnumerator
......@@ -58,6 +60,7 @@ struct _GSocketConnectableIface
GSocketAddressEnumerator * (* proxy_enumerate) (GSocketConnectable *connectable);
gchar * (* to_string) (GSocketConnectable *connectable);
};
GLIB_AVAILABLE_IN_ALL
......@@ -69,6 +72,9 @@ GSocketAddressEnumerator *g_socket_connectable_enumerate (GSocketConnectable *co
GLIB_AVAILABLE_IN_ALL
GSocketAddressEnumerator *g_socket_connectable_proxy_enumerate (GSocketConnectable *connectable);
GLIB_AVAILABLE_IN_2_48
gchar *g_socket_connectable_to_string (GSocketConnectable *addr);
G_END_DECLS
......
......@@ -24,6 +24,7 @@
#include <string.h>
#include "gunixsocketaddress.h"
#include "gsocketconnectable.h"
#include "glibintl.h"
#include "gnetworking.h"
......@@ -76,7 +77,13 @@ struct _GUnixSocketAddressPrivate
GUnixSocketAddressType address_type;
};
G_DEFINE_TYPE_WITH_PRIVATE (GUnixSocketAddress, g_unix_socket_address, G_TYPE_SOCKET_ADDRESS)
static void g_unix_socket_address_connectable_iface_init (GSocketConnectableIface *iface);
static gchar *g_unix_socket_address_connectable_to_string (GSocketConnectable *connectable);
G_DEFINE_TYPE_WITH_CODE (GUnixSocketAddress, g_unix_socket_address, G_TYPE_SOCKET_ADDRESS,
G_ADD_PRIVATE (GUnixSocketAddress)
G_IMPLEMENT_INTERFACE (G_TYPE_SOCKET_CONNECTABLE,
g_unix_socket_address_connectable_iface_init))
static void
g_unix_socket_address_set_property (GObject *object,
......@@ -299,6 +306,49 @@ g_unix_socket_address_class_init (GUnixSocketAddressClass *klass)
G_PARAM_STATIC_STRINGS));
}
static void
g_unix_socket_address_connectable_iface_init (GSocketConnectableIface *iface)
{
GSocketConnectableIface *parent_iface = g_type_interface_peek_parent (iface);
iface->enumerate = parent_iface->enumerate;
iface->proxy_enumerate = parent_iface->proxy_enumerate;
iface->to_string = g_unix_socket_address_connectable_to_string;
}
static gchar *
g_unix_socket_address_connectable_to_string (GSocketConnectable *connectable)
{
GUnixSocketAddress *ua;
GString *out;
const gchar *path;
gsize path_len, i;
ua = G_UNIX_SOCKET_ADDRESS (connectable);
/* Anonymous sockets have no path. */
if (ua->priv->address_type == G_UNIX_SOCKET_ADDRESS_ANONYMOUS)
return g_strdup ("anonymous");
path = g_unix_socket_address_get_path (ua);
path_len = g_unix_socket_address_get_path_len (ua);
out = g_string_sized_new (path_len);
/* Return the #GUnixSocketAddress:path, but with all non-printable characters
* (including nul bytes) escaped to hex. */
for (i = 0; i < path_len; i++)
{
guint8 c = path[i];
if (g_ascii_isprint (path[i]))
g_string_append_c (out, c);
else
g_string_append_printf (out, "\\x%02x", (guint) c);
}
return g_string_free (out, FALSE);
}
static void
g_unix_socket_address_init (GUnixSocketAddress *address)
{
......
......@@ -235,6 +235,55 @@ test_socket_address (void)
g_object_unref (saddr);
}
static void
test_socket_address_to_string (void)
{
GSocketAddress *sa = NULL;
GInetAddress *ia = NULL;
gchar *str = NULL;
/* IPv4. */
ia = g_inet_address_new_from_string ("123.1.123.1");
sa = g_inet_socket_address_new (ia, 80);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (sa));
g_assert_cmpstr (str, ==, "123.1.123.1:80");
g_free (str);
g_object_unref (sa);
g_object_unref (ia);
/* IPv6. */
ia = g_inet_address_new_from_string ("::80");
sa = g_inet_socket_address_new (ia, 80);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (sa));
g_assert_cmpstr (str, ==, "[::80]:80");
g_free (str);
g_object_unref (sa);
g_object_unref (ia);
/* IPv6 without port. */
ia = g_inet_address_new_from_string ("::80");
sa = g_inet_socket_address_new (ia, 0);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (sa));
g_assert_cmpstr (str, ==, "::80");
g_free (str);
g_object_unref (sa);
g_object_unref (ia);
/* IPv6 with scope. */
ia = g_inet_address_new_from_string ("::1");
sa = G_SOCKET_ADDRESS (g_object_new (G_TYPE_INET_SOCKET_ADDRESS,
"address", ia,
"port", 123,
"flowinfo", 10,
"scope-id", 25,
NULL));
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (sa));
g_assert_cmpstr (str, ==, "[::1%25]:123");
g_free (str);
g_object_unref (sa);
g_object_unref (ia);
}
static void
test_mask_parse (void)
{
......@@ -368,6 +417,7 @@ main (int argc, char *argv[])
g_test_add_func ("/inet-address/bytes", test_bytes);
g_test_add_func ("/inet-address/property", test_property);
g_test_add_func ("/socket-address/basic", test_socket_address);
g_test_add_func ("/socket-address/to-string", test_socket_address_to_string);
g_test_add_func ("/address-mask/parse", test_mask_parse);
g_test_add_func ("/address-mask/property", test_mask_property);
g_test_add_func ("/address-mask/equal", test_mask_equal);
......
......@@ -477,6 +477,43 @@ test_loopback_async (void)
g_object_unref (addr);
}
static void
test_to_string (void)
{
GSocketConnectable *addr = NULL;
gchar *str = NULL;
GError *error = NULL;
/* Without port. */
addr = g_network_address_new ("some-hostname", 0);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "some-hostname");
g_free (str);
g_object_unref (addr);
/* With port. */
addr = g_network_address_new ("some-hostname", 123);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "some-hostname:123");
g_free (str);
g_object_unref (addr);
/* With scheme and port. */
addr = g_network_address_parse_uri ("http://some-hostname:123", 80, &error);
g_assert_no_error (error);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "http:some-hostname:123");
g_free (str);
g_object_unref (addr);
/* Loopback. */
addr = g_network_address_new ("localhost", 456);
str = g_socket_connectable_to_string (addr);
g_assert_cmpstr (str, ==, "localhost:456");
g_free (str);
g_object_unref (addr);
}
int
main (int argc, char *argv[])
{
......@@ -520,6 +557,7 @@ main (int argc, char *argv[])
g_test_add_func ("/network-address/loopback/basic", test_loopback_basic);
g_test_add_func ("/network-address/loopback/sync", test_loopback_sync);
g_test_add_func ("/network-address/loopback/async", test_loopback_async);
g_test_add_func ("/network-address/to-string", test_to_string);
return g_test_run ();
}
......@@ -67,6 +67,45 @@ test_unix_socket_address_construct (void)
g_object_unref (a);
}
static void
test_unix_socket_address_to_string (void)
{
GSocketAddress *addr = NULL;
gchar *str = NULL;
/* ADDRESS_PATH. */
addr = g_unix_socket_address_new_with_type ("/some/path", -1,
G_UNIX_SOCKET_ADDRESS_PATH);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
g_assert_cmpstr (str, ==, "/some/path");
g_free (str);
g_object_unref (addr);
/* ADDRESS_ANONYMOUS. */
addr = g_unix_socket_address_new_with_type ("", 0,
G_UNIX_SOCKET_ADDRESS_ANONYMOUS);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
g_assert_cmpstr (str, ==, "anonymous");
g_free (str);
g_object_unref (addr);
/* ADDRESS_ABSTRACT. */
addr = g_unix_socket_address_new_with_type ("abstract-path\0✋", 17,
G_UNIX_SOCKET_ADDRESS_ABSTRACT);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
g_assert_cmpstr (str, ==, "abstract-path\\x00\\xe2\\x9c\\x8b");
g_free (str);
g_object_unref (addr);
/* ADDRESS_ABSTRACT_PADDED. */
addr = g_unix_socket_address_new_with_type ("abstract-path\0✋", 17,
G_UNIX_SOCKET_ADDRESS_ABSTRACT_PADDED);
str = g_socket_connectable_to_string (G_SOCKET_CONNECTABLE (addr));
g_assert_cmpstr (str, ==, "abstract-path\\x00\\xe2\\x9c\\x8b");
g_free (str);
g_object_unref (addr);
}
int
main (int argc,
char **argv)
......@@ -74,6 +113,7 @@ main (int argc,
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/socket/address/unix/construct", test_unix_socket_address_construct);
g_test_add_func ("/socket/address/unix/to-string", test_unix_socket_address_to_string);
return g_test_run ();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment