[glib] gsocketconnectable: Add a to_string() virtual method



commit 128c413261f60c044aca14895ca2c5d2574d791e
Author: Philip Withnall <philip withnall collabora co uk>
Date:   Sun Oct 4 15:24:24 2015 +0100

    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

 docs/reference/gio/gio-sections.txt |    1 +
 gio/ginetsocketaddress.c            |   62 ++++++++++++++++++++++++++++++++++-
 gio/gnetworkaddress.c               |   26 ++++++++++++++
 gio/gnetworkservice.c               |   14 ++++++++
 gio/gsocketaddress.c                |    1 +
 gio/gsocketconnectable.c            |   31 +++++++++++++++++
 gio/gsocketconnectable.h            |    6 +++
 gio/gunixsocketaddress.c            |   52 ++++++++++++++++++++++++++++-
 gio/tests/inet-address.c            |   50 ++++++++++++++++++++++++++++
 gio/tests/network-address.c         |   38 +++++++++++++++++++++
 gio/tests/socket-address.c          |   40 ++++++++++++++++++++++
 11 files changed, 319 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index f0a4c49..9eb68b2 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -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
diff --git a/gio/ginetsocketaddress.c b/gio/ginetsocketaddress.c
index 5cf26f7..c38fa60 100644
--- a/gio/ginetsocketaddress.c
+++ b/gio/ginetsocketaddress.c
@@ -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,
@@ -302,6 +309,59 @@ g_inet_socket_address_class_init (GInetSocketAddressClass *klass)
 }
 
 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)
 {
   address->priv = g_inet_socket_address_get_instance_private (address);
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
index 144db44..99c495a 100644
--- a/gio/gnetworkaddress.c
+++ b/gio/gnetworkaddress.c
@@ -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);
+}
diff --git a/gio/gnetworkservice.c b/gio/gnetworkservice.c
index 497d6fd..d759819 100644
--- a/gio/gnetworkservice.c
+++ b/gio/gnetworkservice.c
@@ -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);
+}
diff --git a/gio/gsocketaddress.c b/gio/gsocketaddress.c
index d0caf33..cefd9e0 100644
--- a/gio/gsocketaddress.c
+++ b/gio/gsocketaddress.c
@@ -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
diff --git a/gio/gsocketconnectable.c b/gio/gsocketconnectable.c
index 79901aa..ef9f92d 100644
--- a/gio/gsocketconnectable.c
+++ b/gio/gsocketconnectable.c
@@ -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));
+}
diff --git a/gio/gsocketconnectable.h b/gio/gsocketconnectable.h
index f7db680..b562ff1 100644
--- a/gio/gsocketconnectable.h
+++ b/gio/gsocketconnectable.h
@@ -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
 
 
diff --git a/gio/gunixsocketaddress.c b/gio/gunixsocketaddress.c
index b1702cb..f4fc077 100644
--- a/gio/gunixsocketaddress.c
+++ b/gio/gunixsocketaddress.c
@@ -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,
@@ -300,6 +307,49 @@ g_unix_socket_address_class_init (GUnixSocketAddressClass *klass)
 }
 
 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)
 {
   address->priv = g_unix_socket_address_get_instance_private (address);
diff --git a/gio/tests/inet-address.c b/gio/tests/inet-address.c
index addcbb4..6e4c424 100644
--- a/gio/tests/inet-address.c
+++ b/gio/tests/inet-address.c
@@ -236,6 +236,55 @@ test_socket_address (void)
 }
 
 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)
 {
   GInetAddressMask *mask;
@@ -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);
diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c
index 73e323c..622af17 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -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 ();
 }
diff --git a/gio/tests/socket-address.c b/gio/tests/socket-address.c
index c3cd809..b3323c2 100644
--- a/gio/tests/socket-address.c
+++ b/gio/tests/socket-address.c
@@ -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 ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]