[glib/wip/tingping/happy-eyeballs: 2/4] gnetworkaddress: Implement parallel ipv4 and ipv6 dns lookups
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/wip/tingping/happy-eyeballs: 2/4] gnetworkaddress: Implement parallel ipv4 and ipv6 dns lookups
- Date: Mon, 29 Oct 2018 19:30:37 +0000 (UTC)
commit 995f5df00b329e7620d1fba9c718b9c59adc4c37
Author: Patrick Griffis <pgriffis igalia com>
Date: Mon Oct 22 13:36:27 2018 -0400
gnetworkaddress: Implement parallel ipv4 and ipv6 dns lookups
As RFC 8305 recommends we can start multiple DNS queries in parallel
to more quickly make an initial response, especially when one is
particularly slow/broken.
gio/gnetworkaddress.c | 179 ++++++++++++++++++++++++++--------
gio/tests/Makefile.am | 7 +-
gio/tests/meson.build | 2 +-
gio/tests/mock-resolver.c | 151 +++++++++++++++++++++++++++++
gio/tests/mock-resolver.h | 17 ++++
gio/tests/network-address.c | 228 +++++++++++++++++++++++++++++++++++++++++++-
gio/tests/proxy-test.c | 23 ++++-
7 files changed, 560 insertions(+), 47 deletions(-)
---
diff --git a/gio/gnetworkaddress.c b/gio/gnetworkaddress.c
index 912ea5144..c5982f530 100644
--- a/gio/gnetworkaddress.c
+++ b/gio/gnetworkaddress.c
@@ -216,23 +216,20 @@ g_network_address_get_property (GObject *object,
}
static void
-g_network_address_set_addresses (GNetworkAddress *addr,
+g_network_address_add_addresses (GNetworkAddress *addr,
GList *addresses,
guint64 resolver_serial)
{
GList *a;
GSocketAddress *sockaddr;
- g_return_if_fail (addresses != NULL && addr->priv->sockaddrs == NULL);
-
for (a = addresses; a; a = a->next)
{
sockaddr = g_inet_socket_address_new (a->data, addr->priv->port);
- addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
+ addr->priv->sockaddrs = g_list_append (addr->priv->sockaddrs, sockaddr);
g_object_unref (a->data);
}
g_list_free (addresses);
- addr->priv->sockaddrs = g_list_reverse (addr->priv->sockaddrs);
addr->priv->resolver_serial = resolver_serial;
}
@@ -246,7 +243,7 @@ g_network_address_parse_sockaddr (GNetworkAddress *addr)
addr->priv->port);
if (sockaddr)
{
- addr->priv->sockaddrs = g_list_prepend (addr->priv->sockaddrs, sockaddr);
+ addr->priv->sockaddrs = g_list_append (addr->priv->sockaddrs, sockaddr);
return TRUE;
}
else
@@ -315,7 +312,7 @@ g_network_address_new_loopback (guint16 port)
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);
+ g_network_address_add_addresses (addr, addrs, 0);
return G_SOCKET_CONNECTABLE (addr);
}
@@ -878,7 +875,10 @@ typedef struct {
GNetworkAddress *addr;
GList *addresses;
- GList *next;
+ GList *current_item;
+ GTask *queued_task;
+ GError *last_error;
+ GSource *wait_source;
} GNetworkAddressAddressEnumerator;
typedef struct {
@@ -895,6 +895,9 @@ g_network_address_address_enumerator_finalize (GObject *object)
GNetworkAddressAddressEnumerator *addr_enum =
G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (object);
+ g_clear_pointer (&addr_enum->wait_source, g_source_destroy);
+ g_clear_object (&addr_enum->queued_task);
+ g_clear_error (&addr_enum->last_error);
g_object_unref (addr_enum->addr);
G_OBJECT_CLASS (_g_network_address_address_enumerator_parent_class)->finalize (object);
@@ -938,19 +941,19 @@ g_network_address_address_enumerator_next (GSocketAddressEnumerator *enumerator
return NULL;
}
- g_network_address_set_addresses (addr, addresses, serial);
+ g_network_address_add_addresses (addr, addresses, serial);
}
-
+
addr_enum->addresses = addr->priv->sockaddrs;
- addr_enum->next = addr_enum->addresses;
+ addr_enum->current_item = addr_enum->addresses;
g_object_unref (resolver);
}
- if (addr_enum->next == NULL)
+ if (addr_enum->current_item == NULL)
return NULL;
- sockaddr = addr_enum->next->data;
- addr_enum->next = addr_enum->next->next;
+ sockaddr = addr_enum->current_item->data;
+ addr_enum->current_item = g_list_next (addr_enum->current_item);
return g_object_ref (sockaddr);
}
@@ -961,12 +964,11 @@ have_addresses (GNetworkAddressAddressEnumerator *addr_enum,
GSocketAddress *sockaddr;
addr_enum->addresses = addr_enum->addr->priv->sockaddrs;
- addr_enum->next = addr_enum->addresses;
+ addr_enum->current_item = addr_enum->addresses;
- if (addr_enum->next)
+ if (addr_enum->current_item)
{
- sockaddr = g_object_ref (addr_enum->next->data);
- addr_enum->next = addr_enum->next->next;
+ sockaddr = g_object_ref (addr_enum->current_item->data);
}
else
sockaddr = NULL;
@@ -978,28 +980,109 @@ have_addresses (GNetworkAddressAddressEnumerator *addr_enum,
g_object_unref (task);
}
+static int
+on_address_timeout (gpointer user_data)
+{
+ GNetworkAddressAddressEnumerator *addr_enum = user_data;
+
+ /* If ipv6 didn't come in yet, just complete the task */
+ if (addr_enum->queued_task)
+ have_addresses (addr_enum, g_steal_pointer (&addr_enum->queued_task),
+ g_steal_pointer (&addr_enum->last_error));
+
+ addr_enum->wait_source = 0;
+ return G_SOURCE_REMOVE;
+}
+
static void
-got_addresses (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
+got_ipv6_addresses (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GTask *task = user_data;
- GNetworkAddressAddressEnumerator *addr_enum = g_task_get_source_object (task);
+ GNetworkAddressAddressEnumerator *addr_enum = user_data;
GResolver *resolver = G_RESOLVER (source_object);
GList *addresses;
GError *error = NULL;
- if (!addr_enum->addr->priv->sockaddrs)
+ addresses = g_resolver_lookup_by_name_with_flags_finish (resolver, result, &error);
+ if (!error)
{
- addresses = g_resolver_lookup_by_name_finish (resolver, result, &error);
+ g_network_address_add_addresses (addr_enum->addr, addresses,
+ g_resolver_get_serial (resolver));
+ }
+ else
+ g_debug ("IPv6 DNS error: %s", error->message);
- if (!error)
- {
- g_network_address_set_addresses (addr_enum->addr, addresses,
- g_resolver_get_serial (resolver));
- }
+ /* If ipv4 was first and waiting on us it can stop waiting */
+ g_clear_pointer (&addr_enum->wait_source, g_source_destroy);
+
+ /* If we got an error before ipv4 then let it handle it */
+ if (error && !addr_enum->last_error)
+ {
+ addr_enum->last_error = g_steal_pointer (&error);
+
+ /* This shouldn't happen but just in case ipv4 never responds */
+ addr_enum->wait_source = g_timeout_source_new (100);
+ g_source_set_callback (addr_enum->wait_source,
+ on_address_timeout,
+ g_object_ref (addr_enum),
+ g_object_unref);
+ g_source_attach (addr_enum->wait_source, g_main_context_get_thread_default ());
+ }
+ /* If we get ipv6 response first or error second then use it */
+ else if (addr_enum->queued_task)
+ {
+ g_clear_error (&addr_enum->last_error);
+ have_addresses (addr_enum, g_steal_pointer (&addr_enum->queued_task),
+ g_steal_pointer (&error));
}
- have_addresses (addr_enum, task, error);
+
+ g_object_unref (addr_enum);
+}
+
+static void
+got_ipv4_addresses (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GNetworkAddressAddressEnumerator *addr_enum = user_data;
+ GResolver *resolver = G_RESOLVER (source_object);
+ GList *addresses;
+ GError *error = NULL;
+
+ addresses = g_resolver_lookup_by_name_with_flags_finish (resolver, result, &error);
+ if (!error)
+ {
+ g_network_address_add_addresses (addr_enum->addr, addresses,
+ g_resolver_get_serial (resolver));
+ }
+ else
+ g_debug ("IPv4 DNS error: %s", error->message);
+
+ /* If ipv6 already came in and errored then we return.
+ * If ipv6 returned successfully then we don't need to do anything.
+ * Otherwise we should wait a short while for ipv6 as RFC 8305 suggests.
+ */
+ if (addr_enum->last_error)
+ {
+ g_assert (addr_enum->queued_task);
+ g_clear_error (&addr_enum->last_error);
+ have_addresses (addr_enum, g_steal_pointer (&addr_enum->queued_task),
+ g_steal_pointer (&error));
+ }
+ else if (addr_enum->queued_task)
+ {
+ addr_enum->last_error = g_steal_pointer (&error);
+ g_clear_pointer (&addr_enum->wait_source, g_source_destroy);
+ addr_enum->wait_source = g_timeout_source_new (50);
+ g_source_set_callback (addr_enum->wait_source,
+ on_address_timeout,
+ g_object_ref (addr_enum),
+ g_object_unref);
+ g_source_attach (addr_enum->wait_source, g_main_context_get_thread_default ());
+ }
+
+ g_object_unref (addr_enum);
}
static void
@@ -1012,11 +1095,12 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator *enum
G_NETWORK_ADDRESS_ADDRESS_ENUMERATOR (enumerator);
GSocketAddress *sockaddr;
GTask *task;
+ GNetworkAddress *addr = addr_enum->addr;
task = g_task_new (addr_enum, cancellable, callback, user_data);
g_task_set_source_tag (task, g_network_address_address_enumerator_next_async);
- if (addr_enum->addresses == NULL)
+ if (!addr_enum->addresses)
{
GNetworkAddress *addr = addr_enum->addr;
GResolver *resolver = g_resolver_get_default ();
@@ -1036,24 +1120,37 @@ g_network_address_address_enumerator_next_async (GSocketAddressEnumerator *enum
have_addresses (addr_enum, task, NULL);
else
{
- g_resolver_lookup_by_name_async (resolver,
- addr->priv->hostname,
- cancellable,
- got_addresses, task);
+ addr_enum->queued_task = g_steal_pointer (&task);
+ /* Lookup in parallel as per RFC 8305 */
+ g_resolver_lookup_by_name_with_flags_async (resolver,
+ addr->priv->hostname,
+ G_RESOLVER_NAME_LOOKUP_FLAGS_IPV6_ONLY,
+ cancellable,
+ got_ipv6_addresses, g_object_ref (addr_enum));
+ g_resolver_lookup_by_name_with_flags_async (resolver,
+ addr->priv->hostname,
+ G_RESOLVER_NAME_LOOKUP_FLAGS_IPV4_ONLY,
+ cancellable,
+ got_ipv4_addresses, g_object_ref (addr_enum));
}
g_object_unref (resolver);
return;
}
- addr_enum->addresses = addr->priv->sockaddrs;
- addr_enum->next = addr_enum->addresses;
g_object_unref (resolver);
}
- if (addr_enum->next)
+ if (!addr_enum->addresses)
+ {
+ g_assert (addr->priv->sockaddrs);
+ addr_enum->addresses = addr->priv->sockaddrs;
+ addr_enum->current_item = addr_enum->addresses;
+ sockaddr = g_object_ref (addr_enum->current_item->data);
+ }
+ else if (addr_enum->current_item->next)
{
- sockaddr = g_object_ref (addr_enum->next->data);
- addr_enum->next = addr_enum->next->next;
+ addr_enum->current_item = g_list_next (addr_enum->current_item);
+ sockaddr = g_object_ref (addr_enum->current_item->data);
}
else
sockaddr = NULL;
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 2df156e6b..69d31882d 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -50,7 +50,6 @@ test_programs = \
memory-output-stream \
monitor \
mount-operation \
- network-address \
network-monitor \
network-monitor-race \
permission \
@@ -195,6 +194,12 @@ schema_tests = \
wrong-category.gschema.xml \
$(NULL)
+test_programs += network-address
+network_address_SOURCES = \
+ network-address.c \
+ mock-resolver.c \
+ mock-resolver.h
+
test_programs += thumbnail-verification
dist_test_data += $(thumbnail_data_files)
thumbnail_data_files = $(addprefix thumbnails/,$(thumbnail_tests))
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 65ddcdad8..4e390605d 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -48,7 +48,7 @@ gio_tests = {
'memory-output-stream' : {},
'monitor' : {},
'mount-operation' : {},
- 'network-address' : {},
+ 'network-address' : {'extra_sources': ['mock-resolver.c']},
'network-monitor' : {},
'network-monitor-race' : {},
'permission' : {},
diff --git a/gio/tests/mock-resolver.c b/gio/tests/mock-resolver.c
new file mode 100644
index 000000000..99bd79096
--- /dev/null
+++ b/gio/tests/mock-resolver.c
@@ -0,0 +1,151 @@
+#include "mock-resolver.h"
+
+struct _MockResolver
+{
+ GResolver parent_instance;
+ guint ipv4_delay;
+ guint ipv6_delay;
+ GList *ipv4_results;
+ GList *ipv6_results;
+ GError *ipv4_error;
+ GError *ipv6_error;
+};
+
+G_DEFINE_TYPE (MockResolver, mock_resolver, G_TYPE_RESOLVER)
+
+MockResolver *
+mock_resolver_new (void)
+{
+ return g_object_new (MOCK_TYPE_RESOLVER, NULL);
+}
+
+void
+mock_resolver_set_ipv4_delay (MockResolver *self, guint delay)
+{
+ self->ipv4_delay = delay;
+}
+
+static gpointer
+copy_object (gconstpointer obj, gpointer user_data)
+{
+ return g_object_ref (G_OBJECT (obj));
+}
+
+void
+mock_resolver_set_ipv4_results (MockResolver *self, GList *results)
+{
+ if (self->ipv4_results)
+ g_list_free_full (self->ipv4_results, g_object_unref);
+ self->ipv4_results = g_list_copy_deep (results, copy_object, NULL);
+}
+
+void
+mock_resolver_set_ipv4_error (MockResolver *self, GError *error)
+{
+ g_clear_error (&self->ipv4_error);
+ if (error)
+ self->ipv4_error = g_error_copy (error);
+}
+
+void
+mock_resolver_set_ipv6_delay (MockResolver *self, guint delay)
+{
+ self->ipv6_delay = delay;
+}
+
+void
+mock_resolver_set_ipv6_results (MockResolver *self, GList *results)
+{
+ if (self->ipv6_results)
+ g_list_free_full (self->ipv6_results, g_object_unref);
+ self->ipv6_results = g_list_copy_deep (results, copy_object, NULL);
+}
+
+void
+mock_resolver_set_ipv6_error (MockResolver *self, GError *error)
+{
+ g_clear_error (&self->ipv6_error);
+ if (error)
+ self->ipv6_error = g_error_copy (error);
+}
+
+static void
+do_lookup_by_name (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ MockResolver *self = source_object;
+ GResolverNameLookupFlags flags = GPOINTER_TO_UINT(task_data);
+
+ if (flags == G_RESOLVER_LOOKUP_NAME_FLAGS_IPV4)
+ {
+ g_usleep (self->ipv4_delay * 1000);
+ if (self->ipv4_error)
+ g_task_return_error (task, g_error_copy (self->ipv4_error));
+ else
+ g_task_return_pointer (task, g_list_copy_deep (self->ipv4_results, copy_object, NULL), NULL);
+ }
+ else if (flags == G_RESOLVER_LOOKUP_NAME_FLAGS_IPV6)
+ {
+ g_usleep (self->ipv6_delay * 1000);
+ if (self->ipv6_error)
+ g_task_return_error (task, g_error_copy (self->ipv6_error));
+ else
+ g_task_return_pointer (task, g_list_copy_deep (self->ipv6_results, copy_object, NULL), NULL);
+ }
+ else
+ g_assert_not_reached ();
+}
+
+static void
+lookup_by_name_with_flags_async (GResolver *resolver,
+ const gchar *hostname,
+ GResolverNameLookupFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = g_task_new (resolver, cancellable, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER(flags), NULL);
+ g_task_run_in_thread (task, do_lookup_by_name);
+ g_object_unref (task);
+}
+
+static GList *
+lookup_by_name_with_flags_finish (GResolver *resolver,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+mock_resolver_finalize (GObject *object)
+{
+ MockResolver *self = (MockResolver*)object;
+
+ g_clear_error (&self->ipv4_error);
+ g_clear_error (&self->ipv6_error);
+ if (self->ipv6_results)
+ g_list_free_full (self->ipv6_results, g_object_unref);
+ if (self->ipv4_results)
+ g_list_free_full (self->ipv4_results, g_object_unref);
+
+ G_OBJECT_CLASS (mock_resolver_parent_class)->finalize (object);
+}
+
+static void
+mock_resolver_class_init (MockResolverClass *klass)
+{
+ GResolverClass *resolver_class = G_RESOLVER_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ resolver_class->lookup_by_name_with_flags_async = lookup_by_name_with_flags_async;
+ resolver_class->lookup_by_name_with_flags_finish = lookup_by_name_with_flags_finish;
+ object_class->finalize = mock_resolver_finalize;
+}
+
+static void
+mock_resolver_init (MockResolver *self)
+{
+}
diff --git a/gio/tests/mock-resolver.h b/gio/tests/mock-resolver.h
new file mode 100644
index 000000000..22b9cd23a
--- /dev/null
+++ b/gio/tests/mock-resolver.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MOCK_TYPE_RESOLVER (mock_resolver_get_type())
+G_DECLARE_FINAL_TYPE (MockResolver, mock_resolver, MOCK, RESOLVER, GResolver)
+
+MockResolver *mock_resolver_new (void);
+void mock_resolver_set_ipv4_delay (MockResolver *self, guint delay);
+void mock_resolver_set_ipv4_results (MockResolver *self, GList *results);
+void mock_resolver_set_ipv4_error (MockResolver *self, GError *error);
+void mock_resolver_set_ipv6_delay (MockResolver *self, guint delay);
+void mock_resolver_set_ipv6_results (MockResolver *self, GList *results);
+void mock_resolver_set_ipv6_error (MockResolver *self, GError *error);
+G_END_DECLS
diff --git a/gio/tests/network-address.c b/gio/tests/network-address.c
index 1a7f8e797..b624bc9d4 100644
--- a/gio/tests/network-address.c
+++ b/gio/tests/network-address.c
@@ -1,4 +1,5 @@
#include "config.h"
+#include "mock-resolver.h"
#include <gio/gio.h>
#include <gio/gnetworking.h>
@@ -416,6 +417,8 @@ test_loopback_sync (void)
typedef struct {
GList/*<owned GSocketAddress> */ *addrs; /* owned */
GMainLoop *loop; /* owned */
+ guint delay_ms;
+ gint expected_error_code;
} AsyncData;
static void
@@ -430,7 +433,14 @@ got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
data = user_data;
a = g_socket_address_enumerator_next_finish (enumerator, result, &error);
- g_assert_no_error (error);
+
+ if (data->expected_error_code)
+ {
+ g_assert_error (error, G_IO_ERROR, data->expected_error_code);
+ g_error_free (error);
+ }
+ else
+ g_assert_no_error (error);
if (a == NULL)
{
@@ -443,6 +453,9 @@ got_addr (GObject *source_object, GAsyncResult *result, gpointer user_data)
g_assert (G_IS_INET_SOCKET_ADDRESS (a));
data->addrs = g_list_prepend (data->addrs, a);
+ if (data->delay_ms)
+ g_usleep (data->delay_ms * 1000);
+
g_socket_address_enumerator_next_async (enumerator, NULL,
got_addr, user_data);
}
@@ -515,6 +528,217 @@ test_to_string (void)
g_object_unref (addr);
}
+static ResolveTest he_ipv4_addresses[] = {
+ {.input="1.1.1.1",},
+ {.input="2.2.2.2",},
+};
+
+static ResolveTest he_ipv6_addresses[] = {
+ {.input="ff::11",},
+ {.input="ff::22",},
+};
+
+static void
+assert_list_contains (GList *list, GInetAddress *address)
+{
+ while (list)
+ {
+ if (g_inet_address_equal (list->data, address))
+ return;
+
+ list = g_list_next (list);
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+assert_list_matches_expected (GList *result, GList *expected)
+{
+ g_assert_cmpint (g_list_length (result), ==, g_list_length (expected));
+
+ while (result)
+ {
+ GInetAddress *address = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (result->data));
+ assert_list_contains (expected, address);
+ result = g_list_next (result);
+ }
+}
+
+static void
+test_happy_eyeballs_async (void)
+{
+ GResolver *old_resolver;
+ MockResolver *mock;
+ GSocketConnectable *addr = NULL; /* owned */
+ GSocketAddressEnumerator *enumerator = NULL; /* owned */
+ AsyncData data = { 0 };
+ GList *input_ipv4_results = NULL;
+ GList *input_ipv6_results = NULL;
+ GList *input_all_results = NULL;
+ GError *ipv6_error = NULL;
+ GError *ipv4_error = NULL;
+ int i;
+
+ /* This test tries to reproduce some of the situations that
+ * RFC 8305 (Happy Eyeballs v2) is designed to handle.
+ */
+
+ /* Setup mock resolver. */
+ old_resolver = g_resolver_get_default ();
+ mock = mock_resolver_new ();
+ g_resolver_set_default (G_RESOLVER (mock));
+
+#define CLEANUP() G_STMT_START { \
+ g_list_free_full (data.addrs, (GDestroyNotify) g_object_unref); \
+ g_clear_object (&enumerator); \
+ g_clear_object (&addr); \
+ addr = g_network_address_new ("test.fake", 80); \
+ enumerator = g_socket_connectable_enumerate (addr); \
+ data.addrs = NULL; \
+ data.delay_ms = 0; \
+ data.expected_error_code = 0; \
+ g_clear_error (&ipv4_error); \
+ g_clear_error (&ipv6_error); \
+ mock_resolver_set_ipv6_error (mock, NULL); \
+ mock_resolver_set_ipv4_error (mock, NULL); \
+ mock_resolver_set_ipv4_delay (mock, 0); \
+ mock_resolver_set_ipv6_delay (mock, 0); \
+} G_STMT_END
+
+ /* init */
+ CLEANUP ();
+ for (i = 0; i < G_N_ELEMENTS (he_ipv4_addresses); ++i)
+ {
+ GInetAddress *ipv4_addr = g_inet_address_new_from_string (he_ipv4_addresses[i].input);
+ GInetAddress *ipv6_addr = g_inet_address_new_from_string (he_ipv6_addresses[i].input);
+ input_ipv4_results = g_list_append (input_ipv4_results, ipv4_addr);
+ input_ipv6_results = g_list_append (input_ipv6_results, ipv6_addr);
+ input_all_results = g_list_append (input_all_results, ipv4_addr);
+ input_all_results = g_list_append (input_all_results, ipv6_addr);
+ }
+ data.loop = g_main_loop_new (NULL, FALSE);
+ mock_resolver_set_ipv4_results (mock, input_ipv4_results);
+ mock_resolver_set_ipv6_results (mock, input_ipv6_results);
+
+ /* Sanity check first */
+ g_test_message ("Sanity check");
+ data.delay_ms = 1;
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_all_results);
+ CLEANUP ();
+
+ /* If ipv4 dns response is a bit slow we just don't get them */
+ mock_resolver_set_ipv4_delay (mock, 25);
+ g_test_message ("Testing slow ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_ipv6_results);
+ CLEANUP ();
+
+ /* If ipv6 is a bit slow it waits for them */
+ mock_resolver_set_ipv6_delay (mock, 25);
+ g_test_message ("Testing slow ipv6");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_all_results);
+ CLEANUP ();
+
+ /* If ipv6 is very slow we don't get them */
+ mock_resolver_set_ipv6_delay (mock, 200);
+ g_test_message ("Testing very slow ipv6");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_ipv4_results);
+ CLEANUP ();
+
+ /* Even if the dns response is slow we still get them if our connection attempts
+ * take long enough. */
+ data.delay_ms = 500;
+ mock_resolver_set_ipv4_delay (mock, 200);
+ g_test_message ("Testing slow ipv4 and connection");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_all_results);
+ CLEANUP ();
+
+ /* If ipv6 fails we still get ipv4. */
+ ipv6_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv6 Broken");
+ mock_resolver_set_ipv6_error (mock, ipv6_error);
+ g_test_message ("Testing failing ipv6");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_ipv4_results);
+ CLEANUP ();
+
+ /* If ipv4 fails we still get ipv6. */
+ ipv4_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv4 Broken");
+ mock_resolver_set_ipv4_error (mock, ipv4_error);
+ g_test_message ("Testing failing ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, input_ipv6_results);
+ CLEANUP ();
+
+ /* If both fail we get an error. */
+ ipv4_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv4 Broken");
+ mock_resolver_set_ipv4_error (mock, ipv4_error);
+ ipv6_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv6 Broken");
+ mock_resolver_set_ipv6_error (mock, ipv6_error);
+ data.expected_error_code = G_IO_ERROR_TIMED_OUT;
+ g_test_message ("Testing failing ipv6 and ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, NULL);
+ CLEANUP ();
+
+ /* The same with some different timings */
+ ipv4_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv4 Broken");
+ mock_resolver_set_ipv4_error (mock, ipv4_error);
+ mock_resolver_set_ipv4_delay (mock, 25);
+ ipv6_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv6 Broken");
+ mock_resolver_set_ipv6_error (mock, ipv6_error);
+ data.expected_error_code = G_IO_ERROR_TIMED_OUT;
+ g_test_message ("Testing failing ipv6 and slow ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, NULL);
+ CLEANUP ();
+
+ ipv4_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv4 Broken");
+ mock_resolver_set_ipv4_error (mock, ipv4_error);
+ ipv6_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv6 Broken");
+ mock_resolver_set_ipv6_error (mock, ipv6_error);
+ mock_resolver_set_ipv6_delay (mock, 25);
+ data.expected_error_code = G_IO_ERROR_TIMED_OUT;
+ g_test_message ("Testing failing ipv6 and slow ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, NULL);
+ CLEANUP ();
+
+ ipv4_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv4 Broken");
+ mock_resolver_set_ipv4_error (mock, ipv4_error);
+ ipv6_error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_TIMED_OUT, "IPv6 Broken");
+ mock_resolver_set_ipv6_error (mock, ipv6_error);
+ mock_resolver_set_ipv6_delay (mock, 200);
+ data.expected_error_code = G_IO_ERROR_TIMED_OUT;
+ g_test_message ("Testing failing and slow ipv6 and ipv4");
+ g_socket_address_enumerator_next_async (enumerator, NULL, got_addr, &data);
+ g_main_loop_run (data.loop);
+ assert_list_matches_expected (data.addrs, NULL);
+ CLEANUP ();
+
+ g_main_loop_unref (data.loop);
+ g_list_free_full (input_all_results, g_object_unref);
+ g_list_free (input_ipv4_results);
+ g_list_free (input_ipv6_results);
+ g_resolver_set_default (old_resolver);
+ g_object_unref (old_resolver);
+ g_object_unref (mock);
+}
+
int
main (int argc, char *argv[])
{
@@ -560,5 +784,7 @@ main (int argc, char *argv[])
g_test_add_func ("/network-address/loopback/async", test_loopback_async);
g_test_add_func ("/network-address/to-string", test_to_string);
+ g_test_add_func ("/network-address/happy-eyeballs", test_happy_eyeballs_async);
+
return g_test_run ();
}
diff --git a/gio/tests/proxy-test.c b/gio/tests/proxy-test.c
index 3855ae2f8..7e82a60f8 100644
--- a/gio/tests/proxy-test.c
+++ b/gio/tests/proxy-test.c
@@ -772,6 +772,21 @@ g_fake_resolver_lookup_by_name_async (GResolver *resolver,
g_object_unref (task);
}
+static void
+g_fake_resolver_lookup_by_name_with_flags_async (GResolver *resolver,
+ const gchar *hostname,
+ GResolverNameLookupFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_fake_resolver_lookup_by_name_async (resolver,
+ hostname,
+ cancellable,
+ callback,
+ user_data);
+}
+
static GList *
g_fake_resolver_lookup_by_name_finish (GResolver *resolver,
GAsyncResult *result,
@@ -785,9 +800,11 @@ g_fake_resolver_class_init (GFakeResolverClass *fake_class)
{
GResolverClass *resolver_class = G_RESOLVER_CLASS (fake_class);
- resolver_class->lookup_by_name = g_fake_resolver_lookup_by_name;
- resolver_class->lookup_by_name_async = g_fake_resolver_lookup_by_name_async;
- resolver_class->lookup_by_name_finish = g_fake_resolver_lookup_by_name_finish;
+ resolver_class->lookup_by_name = g_fake_resolver_lookup_by_name;
+ resolver_class->lookup_by_name_async = g_fake_resolver_lookup_by_name_async;
+ resolver_class->lookup_by_name_finish = g_fake_resolver_lookup_by_name_finish;
+ resolver_class->lookup_by_name_with_flags_async = g_fake_resolver_lookup_by_name_with_flags_async;
+ resolver_class->lookup_by_name_with_flags_finish = g_fake_resolver_lookup_by_name_finish;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]