[glib] gnetworkaddress: Add g_network_address_new_loopback() constructor



commit 64f9bf96fdc47ab7fedd91d291f805c3e10322b4
Author: Dan Winship <danw gnome org>
Date:   Sun Nov 23 12:53:03 2014 -0500

    gnetworkaddress: Add g_network_address_new_loopback() constructor
    
    This is a convenience method for creating a GNetworkAddress which is
    guaranteed to return IPv4 and IPv6 loopback addresses. The program
    cannot guarantee that 'localhost' will resolve to both types of
    address, so programs which wish to connect to a local service over
    either IPv4 or IPv6 must currently manually create an IPv4 and another
    IPv6 socket, and detect which of the two are working. This new API
    allows the existing GSocketConnectable machinery to be used to
    automate that.
    
    Based on a patch from Philip Withnall.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=732317

 docs/reference/gio/gio-sections.txt |    5 +-
 gio/gnetworkaddress.c               |   45 ++++++++++++
 gio/gnetworkaddress.h               |    2 +
 gio/tests/network-address.c         |  132 +++++++++++++++++++++++++++++++++++
 4 files changed, 182 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 80980fd..fbaac6b 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1867,11 +1867,12 @@ g_socket_connectable_get_type
 <TITLE>GNetworkAddress</TITLE>
 GNetworkAddress
 g_network_address_new
+g_network_address_new_loopback
+g_network_address_parse
+g_network_address_parse_uri
 g_network_address_get_hostname
 g_network_address_get_port
 g_network_address_get_scheme
-g_network_address_parse
-g_network_address_parse_uri
 <SUBSECTION Standard>
 GNetworkAddressClass
 GNetworkAddressPrivate
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
index 5cb4c74..8e073fd 100644
--- a/gio/gnetworkaddress.c
+++ b/gio/gnetworkaddress.c
@@ -267,6 +267,12 @@ g_network_address_parse_sockaddr (GNetworkAddress *addr)
  * Creates a new #GSocketConnectable for connecting to the given
  * @hostname and @port.
  *
+ * Note that depending on the configuration of the machine, a
+ * @hostname of `localhost` may refer to the IPv4 loopback address
+ * only, or to both IPv4 and IPv6; use
+ * g_network_address_new_loopback() to create a #GNetworkAddress that
+ * is guaranteed to resolve to both addresses.
+ *
  * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
  *
  * Since: 2.22
@@ -282,6 +288,45 @@ g_network_address_new (const gchar *hostname,
 }
 
 /**
+ * g_network_address_new_loopback:
+ * @port: the port
+ *
+ * Creates a new #GSocketConnectable for connecting to the local host
+ * over a loopback connection to the given @port. This is intended for
+ * use in connecting to local services which may be running on IPv4 or
+ * IPv6.
+ *
+ * The connectable will return IPv4 and IPv6 loopback addresses,
+ * regardless of how the host resolves `localhost`. By contrast,
+ * g_network_address_new() will often only return an IPv4 address when
+ * resolving `localhost`, and an IPv6 address for `localhost6`.
+ *
+ * g_network_address_get_hostname() will always return `localhost` for
+ * #GNetworkAddresses created with this constructor.
+ *
+ * Returns: (transfer full) (type GNetworkAddress): the new #GNetworkAddress
+ *
+ * Since: 2.44
+ */
+GSocketConnectable *
+g_network_address_new_loopback (guint16 port)
+{
+  GNetworkAddress *addr;
+  GList *addrs = NULL;
+
+  addr = g_object_new (G_TYPE_NETWORK_ADDRESS,
+                       "hostname", "localhost",
+                       "port", port,
+                       NULL);
+
+  addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET6));
+  addrs = g_list_append (addrs, g_inet_address_new_loopback (AF_INET));
+  g_network_address_set_addresses (addr, addrs, 0);
+
+  return G_SOCKET_CONNECTABLE (addr);
+}
+
+/**
  * g_network_address_parse:
  * @host_and_port: the hostname and optionally a port
  * @default_port: the default port if not in @host_and_port
diff --git a/gio/gnetworkaddress.h b/gio/gnetworkaddress.h
index a9800f4..d1e7470 100644
--- a/gio/gnetworkaddress.h
+++ b/gio/gnetworkaddress.h
@@ -57,6 +57,8 @@ GType               g_network_address_get_type     (void) G_GNUC_CONST;
 GLIB_AVAILABLE_IN_ALL
 GSocketConnectable *g_network_address_new          (const gchar      *hostname,
                                                    guint16           port);
+GLIB_AVAILABLE_IN_2_44
+GSocketConnectable *g_network_address_new_loopback (guint16           port);
 GLIB_AVAILABLE_IN_ALL
 GSocketConnectable *g_network_address_parse        (const gchar      *host_and_port,
                                                    guint16           default_port,
diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c
index 44b6990..73e323c 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -348,6 +348,135 @@ test_uri_scope_id (void)
   g_object_unref (addr);
 }
 
+static void
+test_loopback_basic (void)
+{
+  GNetworkAddress *addr;  /* owned */
+
+  addr = G_NETWORK_ADDRESS (g_network_address_new_loopback (666));
+
+  /* Test basic properties. */
+  g_assert_cmpstr (g_network_address_get_hostname (addr), ==, "localhost");
+  g_assert_cmpuint (g_network_address_get_port (addr), ==, 666);
+  g_assert_null (g_network_address_get_scheme (addr));
+
+  g_object_unref (addr);
+}
+
+static void
+assert_socket_address_matches (GSocketAddress *a,
+                               const gchar    *expected_address,
+                               guint16         expected_port)
+{
+  GInetSocketAddress *sa;
+  gchar *str;  /* owned */
+
+  g_assert (G_IS_INET_SOCKET_ADDRESS (a));
+
+  sa = G_INET_SOCKET_ADDRESS (a);
+  g_assert_cmpint (g_inet_socket_address_get_port (sa), ==, expected_port);
+
+  str = g_inet_address_to_string (g_inet_socket_address_get_address (sa));
+  g_assert_cmpstr (str, ==, expected_address);
+  g_free (str);
+}
+
+static void
+test_loopback_sync (void)
+{
+  GSocketConnectable *addr;  /* owned */
+  GSocketAddressEnumerator *enumerator;  /* owned */
+  GSocketAddress *a;  /* owned */
+  GError *error = NULL;
+
+  addr = g_network_address_new_loopback (616);
+  enumerator = g_socket_connectable_enumerate (addr);
+
+  /* IPv6 address. */
+  a = g_socket_address_enumerator_next (enumerator, NULL, &error);
+  g_assert_no_error (error);
+  assert_socket_address_matches (a, "::1", 616);
+  g_object_unref (a);
+
+  /* IPv4 address. */
+  a = g_socket_address_enumerator_next (enumerator, NULL, &error);
+  g_assert_no_error (error);
+  assert_socket_address_matches (a, "127.0.0.1", 616);
+  g_object_unref (a);
+
+  /* End of results. */
+  g_assert_null (g_socket_address_enumerator_next (enumerator, NULL, &error));
+  g_assert_no_error (error);
+
+  g_object_unref (enumerator);
+  g_object_unref (addr);
+}
+
+typedef struct {
+  GList/*<owned GSocketAddress> */ *addrs;  /* owned */
+  GMainLoop *loop;  /* owned */
+} AsyncData;
+
+static void
+got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
+{
+  GSocketAddressEnumerator *enumerator;
+  AsyncData *data;
+  GSocketAddress *a;  /* owned */
+  GError *error = NULL;
+
+  enumerator = G_SOCKET_ADDRESS_ENUMERATOR (source_object);
+  data = user_data;
+
+  a = g_socket_address_enumerator_next_finish (enumerator, result, &error);
+  g_assert_no_error (error);
+
+  if (a == NULL)
+    {
+      /* End of results. */
+      data->addrs = g_list_reverse (data->addrs);
+      g_main_loop_quit (data->loop);
+    }
+  else
+    {
+      g_assert (G_IS_INET_SOCKET_ADDRESS (a));
+      data->addrs = g_list_prepend (data->addrs, a);
+
+      g_socket_address_enumerator_next_async (enumerator, NULL,
+                                              got_addr, user_data);
+    }
+}
+
+static void
+test_loopback_async (void)
+{
+  GSocketConnectable *addr;  /* owned */
+  GSocketAddressEnumerator *enumerator;  /* owned */
+  AsyncData data = { 0, };
+
+  addr = g_network_address_new_loopback (610);
+  enumerator = g_socket_connectable_enumerate (addr);
+
+  /* Get all the addresses. */
+  data.addrs = NULL;
+  data.loop = g_main_loop_new (NULL, FALSE);
+
+  g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+
+  g_main_loop_run (data.loop);
+  g_main_loop_unref (data.loop);
+
+  /* Check results. */
+  g_assert_cmpuint (g_list_length (data.addrs), ==, 2);
+  assert_socket_address_matches (data.addrs->data, "::1", 610);
+  assert_socket_address_matches (data.addrs->next->data, "127.0.0.1", 610);
+
+  g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref);
+
+  g_object_unref (enumerator);
+  g_object_unref (addr);
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -388,6 +517,9 @@ main (int argc, char *argv[])
 
   g_test_add_func ("/network-address/scope-id", test_host_scope_id);
   g_test_add_func ("/network-address/uri-scope-id", test_uri_scope_id);
+  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);
 
   return g_test_run ();
 }


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