[glib] gnetworkaddress: Add g_network_address_new_loopback() constructor
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] gnetworkaddress: Add g_network_address_new_loopback() constructor
- Date: Sat, 29 Nov 2014 19:22:49 +0000 (UTC)
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]