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
This commit is contained in:
Philip Withnall 2015-10-04 15:24:24 +01:00
parent 4e631d2e5f
commit 128c413261
11 changed files with 319 additions and 2 deletions

View File

@ -1915,6 +1915,7 @@ GSocketConnectable
GSocketConnectableIface GSocketConnectableIface
g_socket_connectable_enumerate g_socket_connectable_enumerate
g_socket_connectable_proxy_enumerate g_socket_connectable_proxy_enumerate
g_socket_connectable_to_string
<SUBSECTION> <SUBSECTION>
GSocketAddressEnumerator GSocketAddressEnumerator
g_socket_address_enumerator_next g_socket_address_enumerator_next

View File

@ -26,6 +26,7 @@
#include "ginetsocketaddress.h" #include "ginetsocketaddress.h"
#include "ginetaddress.h" #include "ginetaddress.h"
#include "gnetworkingprivate.h" #include "gnetworkingprivate.h"
#include "gsocketconnectable.h"
#include "gioerror.h" #include "gioerror.h"
#include "glibintl.h" #include "glibintl.h"
@ -54,7 +55,13 @@ struct _GInetSocketAddressPrivate
guint32 scope_id; 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 { enum {
PROP_0, PROP_0,
@ -301,6 +308,59 @@ g_inet_socket_address_class_init (GInetSocketAddressClass *klass)
G_PARAM_STATIC_STRINGS)); 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 static void
g_inet_socket_address_init (GInetSocketAddress *address) g_inet_socket_address_init (GInetSocketAddress *address)
{ {

View File

@ -86,6 +86,7 @@ static void g_network_address_get_property (GObject *object,
static void g_network_address_connectable_iface_init (GSocketConnectableIface *iface); 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_enumerate (GSocketConnectable *connectable);
static GSocketAddressEnumerator *g_network_address_connectable_proxy_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_DEFINE_TYPE_WITH_CODE (GNetworkAddress, g_network_address, G_TYPE_OBJECT,
G_ADD_PRIVATE (GNetworkAddress) 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->enumerate = g_network_address_connectable_enumerate;
connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate; connectable_iface->proxy_enumerate = g_network_address_connectable_proxy_enumerate;
connectable_iface->to_string = g_network_address_connectable_to_string;
} }
static void static void
@ -1111,3 +1113,27 @@ g_network_address_connectable_proxy_enumerate (GSocketConnectable *connectable)
return proxy_enum; 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);
}

View File

@ -89,6 +89,7 @@ static void g_network_service_get_property (GObject *object,
static void g_network_service_connectable_iface_init (GSocketConnectableIface *iface); 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_enumerate (GSocketConnectable *connectable);
static GSocketAddressEnumerator *g_network_service_connectable_proxy_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_DEFINE_TYPE_WITH_CODE (GNetworkService, g_network_service, G_TYPE_OBJECT,
G_ADD_PRIVATE (GNetworkService) 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->enumerate = g_network_service_connectable_enumerate;
connectable_iface->proxy_enumerate = g_network_service_connectable_proxy_enumerate; connectable_iface->proxy_enumerate = g_network_service_connectable_proxy_enumerate;
connectable_iface->to_string = g_network_service_connectable_to_string;
} }
static void static void
@ -743,3 +745,15 @@ g_network_service_connectable_proxy_enumerate (GSocketConnectable *connectable)
return addr_enum; 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);
}

View File

@ -129,6 +129,7 @@ g_socket_address_connectable_iface_init (GSocketConnectableIface *connectable_if
{ {
connectable_iface->enumerate = g_socket_address_connectable_enumerate; connectable_iface->enumerate = g_socket_address_connectable_enumerate;
connectable_iface->proxy_enumerate = g_socket_address_connectable_proxy_enumerate; connectable_iface->proxy_enumerate = g_socket_address_connectable_proxy_enumerate;
/* to_string() is implemented by subclasses */
} }
static void static void

View File

@ -146,3 +146,34 @@ g_socket_connectable_proxy_enumerate (GSocketConnectable *connectable)
else else
return (* iface->enumerate) (connectable); 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 implementations 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));
}

View File

@ -44,6 +44,8 @@ typedef struct _GSocketConnectableIface GSocketConnectableIface;
* @g_iface: The parent interface. * @g_iface: The parent interface.
* @enumerate: Creates a #GSocketAddressEnumerator * @enumerate: Creates a #GSocketAddressEnumerator
* @proxy_enumerate: Creates a #GProxyAddressEnumerator * @proxy_enumerate: Creates a #GProxyAddressEnumerator
* @to_string: Format the connectables address as a string for debugging.
* Implementing this is optional. (Since: 2.48.0.)
* *
* Provides an interface for returning a #GSocketAddressEnumerator * Provides an interface for returning a #GSocketAddressEnumerator
* and #GProxyAddressEnumerator * and #GProxyAddressEnumerator
@ -58,6 +60,7 @@ struct _GSocketConnectableIface
GSocketAddressEnumerator * (* proxy_enumerate) (GSocketConnectable *connectable); GSocketAddressEnumerator * (* proxy_enumerate) (GSocketConnectable *connectable);
gchar * (* to_string) (GSocketConnectable *connectable);
}; };
GLIB_AVAILABLE_IN_ALL GLIB_AVAILABLE_IN_ALL
@ -69,6 +72,9 @@ GSocketAddressEnumerator *g_socket_connectable_enumerate (GSocketConnectable *co
GLIB_AVAILABLE_IN_ALL GLIB_AVAILABLE_IN_ALL
GSocketAddressEnumerator *g_socket_connectable_proxy_enumerate (GSocketConnectable *connectable); GSocketAddressEnumerator *g_socket_connectable_proxy_enumerate (GSocketConnectable *connectable);
GLIB_AVAILABLE_IN_2_48
gchar *g_socket_connectable_to_string (GSocketConnectable *addr);
G_END_DECLS G_END_DECLS

View File

@ -24,6 +24,7 @@
#include <string.h> #include <string.h>
#include "gunixsocketaddress.h" #include "gunixsocketaddress.h"
#include "gsocketconnectable.h"
#include "glibintl.h" #include "glibintl.h"
#include "gnetworking.h" #include "gnetworking.h"
@ -76,7 +77,13 @@ struct _GUnixSocketAddressPrivate
GUnixSocketAddressType address_type; 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 static void
g_unix_socket_address_set_property (GObject *object, g_unix_socket_address_set_property (GObject *object,
@ -299,6 +306,49 @@ g_unix_socket_address_class_init (GUnixSocketAddressClass *klass)
G_PARAM_STATIC_STRINGS)); 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 static void
g_unix_socket_address_init (GUnixSocketAddress *address) g_unix_socket_address_init (GUnixSocketAddress *address)
{ {

View File

@ -235,6 +235,55 @@ test_socket_address (void)
g_object_unref (saddr); 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 static void
test_mask_parse (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/bytes", test_bytes);
g_test_add_func ("/inet-address/property", test_property); 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/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/parse", test_mask_parse);
g_test_add_func ("/address-mask/property", test_mask_property); g_test_add_func ("/address-mask/property", test_mask_property);
g_test_add_func ("/address-mask/equal", test_mask_equal); g_test_add_func ("/address-mask/equal", test_mask_equal);

View File

@ -477,6 +477,43 @@ test_loopback_async (void)
g_object_unref (addr); 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 int
main (int argc, char *argv[]) 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/basic", test_loopback_basic);
g_test_add_func ("/network-address/loopback/sync", test_loopback_sync); 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/loopback/async", test_loopback_async);
g_test_add_func ("/network-address/to-string", test_to_string);
return g_test_run (); return g_test_run ();
} }

View File

@ -67,6 +67,45 @@ test_unix_socket_address_construct (void)
g_object_unref (a); 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 int
main (int argc, main (int argc,
char **argv) char **argv)
@ -74,6 +113,7 @@ main (int argc,
g_test_init (&argc, &argv, NULL); 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/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 (); return g_test_run ();
} }