[glib] gio: add a proxy test program



commit 2a37bc0dc6cc013d01836b45c2eea6b1bd6d80e5
Author: Dan Winship <danw gnome org>
Date:   Sun Apr 22 15:18:50 2012 -0400

    gio: add a proxy test program
    
    Test GProxy, GProxyResolver, GProxyAddress, and
    GProxyAddressEnumerator, plus GSocketClient's proxy-resolving
    codepaths.

 gio/tests/Makefile.am  |    4 +
 gio/tests/proxy-test.c | 1077 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1081 insertions(+), 0 deletions(-)
---
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 9829aa0..d565df5 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -59,6 +59,7 @@ TEST_PROGS +=	 		\
 	network-monitor		\
 	fileattributematcher	\
 	resources		\
+	proxy-test		\
 	$(NULL)
 
 if HAVE_DBUS_DAEMON
@@ -453,6 +454,9 @@ gapplication_example_actions_LDADD   = $(progs_ldadd)
 gmenumodel_SOURCES = gmenumodel.c gdbus-sessionbus.h gdbus-sessionbus.c
 gmenumodel_LDADD = $(progs_ldadd)
 
+proxy_test_SOURCES = proxy-test.c
+proxy_test_LDADD   = $(progs_ldadd)
+
 schema_tests = \
 	schema-tests/array-default-not-in-choices.gschema.xml		\
 	schema-tests/bad-choice.gschema.xml				\
diff --git a/gio/tests/proxy-test.c b/gio/tests/proxy-test.c
new file mode 100644
index 0000000..079b9ad
--- /dev/null
+++ b/gio/tests/proxy-test.c
@@ -0,0 +1,1077 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+
+#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_34
+#include <gio/gio.h>
+
+/* Overview:
+ *
+ * We have an echo server, two proxy servers, two GProxy
+ * implementations, and a GProxyResolver implementation.
+ *
+ * The echo server runs at @server.server_addr (on
+ * @server.server_port).
+ *
+ * The two proxy servers, A and B, run on @proxy_a.port and
+ * @proxy_b.port, with @proxy_a.uri and @proxy_b.uri pointing to them.
+ * The "negotiation" with the two proxies is just sending the single
+ * letter "a" or "b" and receiving it back in uppercase; the proxy
+ * then connects to @server_addr.
+ *
+ * Proxy A supports "alpha://" URIs, and does not support hostname
+ * resolution, and Proxy B supports "beta://" URIs, and does support
+ * hostname resolution (but it just ignores the hostname and always
+ * connects to @server_addr anyway).
+ *
+ * The GProxyResolver (GTestProxyResolver) looks at its URI and
+ * returns [ "direct://" ] for "simple://" URIs, and [ proxy_a.uri,
+ * proxy_b.uri ] for other URIs.
+ */
+
+typedef struct {
+  gchar *proxy_command;
+  gchar *supported_protocol;
+
+  GSocket *server;
+  GThread *thread;
+  GCancellable *cancellable;
+  gchar *uri;
+  gushort port;
+
+  GSocket *client_sock, *server_sock;
+  GMainLoop *loop;
+
+  GError *last_error;
+} ProxyData;
+
+static ProxyData proxy_a, proxy_b;
+
+typedef struct {
+  GSocket *server;
+  GThread *server_thread;
+  GCancellable *cancellable;
+  GSocketAddress *server_addr;
+  gushort server_port;
+} ServerData;
+
+static ServerData server;
+
+static gchar **last_proxies;
+
+static GSocketClient *client;
+
+
+/**************************************/
+/* Test GProxyResolver implementation */
+/**************************************/
+
+typedef struct {
+  GObject parent_instance;
+} GTestProxyResolver;
+
+typedef struct {
+  GObjectClass parent_class;
+} GTestProxyResolverClass;
+
+static void g_test_proxy_resolver_iface_init (GProxyResolverInterface *iface);
+
+#define g_test_proxy_resolver_get_type _g_test_proxy_resolver_get_type
+G_DEFINE_TYPE_WITH_CODE (GTestProxyResolver, g_test_proxy_resolver, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER,
+						g_test_proxy_resolver_iface_init)
+			 g_io_extension_point_implement (G_PROXY_RESOLVER_EXTENSION_POINT_NAME,
+							 g_define_type_id,
+							 "test",
+							 0))
+
+static void
+g_test_proxy_resolver_init (GTestProxyResolver *resolver)
+{
+}
+
+static gboolean
+g_test_proxy_resolver_is_supported (GProxyResolver *resolver)
+{
+  return TRUE;
+}
+
+static gchar **
+g_test_proxy_resolver_lookup (GProxyResolver  *resolver,
+			      const gchar     *uri,
+			      GCancellable    *cancellable,
+			      GError         **error)
+{
+  gchar **proxies;
+
+  g_assert (last_proxies == NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  proxies = g_new (gchar *, 3);
+
+  if (!strncmp (uri, "simple://", 4))
+    {
+      proxies[0] = g_strdup ("direct://");
+      proxies[1] = NULL;
+    }
+  else
+    {
+      /* Proxy A can only deal with "alpha://" URIs, not
+       * "beta://", but we always return both URIs
+       * anyway so we can test error handling when the first
+       * fails.
+       */
+      proxies[0] = g_strdup (proxy_a.uri);
+      proxies[1] = g_strdup (proxy_b.uri);
+      proxies[2] = NULL;
+    }
+
+  last_proxies = g_strdupv (proxies);
+
+  return proxies;
+}
+
+static void
+g_test_proxy_resolver_lookup_async (GProxyResolver      *resolver,
+				    const gchar         *uri,
+				    GCancellable        *cancellable,
+				    GAsyncReadyCallback  callback,
+				    gpointer             user_data)
+{
+  GError *error = NULL;
+  GSimpleAsyncResult *simple;
+  gchar **proxies;
+
+  proxies = g_test_proxy_resolver_lookup (resolver, uri, cancellable, &error);
+
+  simple = g_simple_async_result_new (G_OBJECT (resolver),
+				      callback, user_data,
+				      g_test_proxy_resolver_lookup_async);
+
+  if (proxies == NULL)
+    g_simple_async_result_take_error (simple, error);
+  else
+    g_simple_async_result_set_op_res_gpointer (simple, proxies, (GDestroyNotify) g_strfreev);
+
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static gchar **
+g_test_proxy_resolver_lookup_finish (GProxyResolver     *resolver,
+				     GAsyncResult       *result,
+				     GError            **error)
+{
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      gchar **proxies;
+
+      if (g_simple_async_result_propagate_error (simple, error))
+        return NULL;
+
+      proxies = g_simple_async_result_get_op_res_gpointer (simple);
+      return g_strdupv (proxies);
+    }
+
+  return NULL;
+}
+
+static void
+g_test_proxy_resolver_class_init (GTestProxyResolverClass *resolver_class)
+{
+}
+
+static void
+g_test_proxy_resolver_iface_init (GProxyResolverInterface *iface)
+{
+  iface->is_supported = g_test_proxy_resolver_is_supported;
+  iface->lookup = g_test_proxy_resolver_lookup;
+  iface->lookup_async = g_test_proxy_resolver_lookup_async;
+  iface->lookup_finish = g_test_proxy_resolver_lookup_finish;
+}
+
+
+/****************************************/
+/* Test proxy implementation base class */
+/****************************************/
+
+typedef struct {
+  GObject parent;
+
+  ProxyData *proxy_data;
+} GProxyBase;
+
+typedef struct {
+  GObjectClass parent_class;
+} GProxyBaseClass;
+
+#define g_proxy_base_get_type _g_proxy_base_get_type
+G_DEFINE_ABSTRACT_TYPE (GProxyBase, g_proxy_base, G_TYPE_OBJECT)
+
+static void
+g_proxy_base_init (GProxyBase *proxy)
+{
+}
+
+static GIOStream *
+g_proxy_base_connect (GProxy            *proxy,
+		      GIOStream         *io_stream,
+		      GProxyAddress     *proxy_address,
+		      GCancellable      *cancellable,
+		      GError           **error)
+{
+  ProxyData *data = ((GProxyBase *) proxy)->proxy_data;
+  const gchar *protocol;
+  GOutputStream *ostream;
+  GInputStream *istream;
+  gchar response;
+
+  g_assert_no_error (data->last_error);
+
+  protocol = g_proxy_address_get_destination_protocol (proxy_address);
+  if (strcmp (protocol, data->supported_protocol) != 0)
+    {
+      g_set_error_literal (&data->last_error,
+			   G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+			   "Unsupported protocol");
+      goto fail;
+    }
+
+  ostream = g_io_stream_get_output_stream (io_stream);
+  if (g_output_stream_write (ostream, data->proxy_command, 1, cancellable,
+			     &data->last_error) != 1)
+    goto fail;
+
+  istream = g_io_stream_get_input_stream (io_stream);
+  if (g_input_stream_read (istream, &response, 1, cancellable,
+			   &data->last_error) != 1)
+    goto fail;
+
+  if (response != g_ascii_toupper (*data->proxy_command))
+    {
+      g_set_error_literal (&data->last_error,
+			   G_IO_ERROR, G_IO_ERROR_FAILED,
+			   "Failed");
+      goto fail;
+    }
+
+  return g_object_ref (io_stream);
+
+ fail:
+  g_propagate_error (error, g_error_copy (data->last_error));
+  return NULL;
+}
+
+static void
+g_proxy_base_connect_async (GProxy               *proxy,
+			    GIOStream            *io_stream,
+			    GProxyAddress        *proxy_address,
+			    GCancellable         *cancellable,
+			    GAsyncReadyCallback   callback,
+			    gpointer              user_data)
+{
+  GError *error = NULL;
+  GSimpleAsyncResult *simple;
+  GIOStream *proxy_io_stream;
+
+  simple = g_simple_async_result_new (G_OBJECT (proxy),
+				      callback, user_data,
+				      g_proxy_base_connect_async);
+
+  proxy_io_stream = g_proxy_connect (proxy, io_stream, proxy_address,
+				     cancellable, &error);
+  if (proxy_io_stream)
+    {
+      g_simple_async_result_set_op_res_gpointer (simple, proxy_io_stream,
+						 g_object_unref);
+    }
+  else
+    g_simple_async_result_take_error (simple, error);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static GIOStream *
+g_proxy_base_connect_finish (GProxy        *proxy,
+			     GAsyncResult  *result,
+			     GError       **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static void
+g_proxy_base_class_init (GProxyBaseClass *class)
+{
+}
+
+
+/********************************************/
+/* Test proxy implementation #1 ("Proxy A") */
+/********************************************/
+
+typedef GProxyBase GProxyA;
+typedef GProxyBaseClass GProxyAClass;
+
+static void g_proxy_a_iface_init (GProxyInterface *proxy_iface);
+
+#define g_proxy_a_get_type _g_proxy_a_get_type
+G_DEFINE_TYPE_WITH_CODE (GProxyA, g_proxy_a, g_proxy_base_get_type (),
+			 G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
+						g_proxy_a_iface_init)
+			 g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
+							 g_define_type_id,
+							 "proxy-a",
+							 0))
+
+static void
+g_proxy_a_init (GProxyA *proxy)
+{
+  ((GProxyBase *) proxy)->proxy_data = &proxy_a;
+}
+
+static gboolean
+g_proxy_a_supports_hostname (GProxy *proxy)
+{
+  return FALSE;
+}
+
+static void
+g_proxy_a_class_init (GProxyAClass *class)
+{
+}
+
+static void
+g_proxy_a_iface_init (GProxyInterface *proxy_iface)
+{
+  proxy_iface->connect = g_proxy_base_connect;
+  proxy_iface->connect_async = g_proxy_base_connect_async;
+  proxy_iface->connect_finish = g_proxy_base_connect_finish;
+  proxy_iface->supports_hostname = g_proxy_a_supports_hostname;
+}
+
+/********************************************/
+/* Test proxy implementation #2 ("Proxy B") */
+/********************************************/
+
+typedef GProxyBase GProxyB;
+typedef GProxyBaseClass GProxyBClass;
+
+static void g_proxy_b_iface_init (GProxyInterface *proxy_iface);
+
+#define g_proxy_b_get_type _g_proxy_b_get_type
+G_DEFINE_TYPE_WITH_CODE (GProxyB, g_proxy_b, g_proxy_base_get_type (),
+			 G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
+						g_proxy_b_iface_init)
+			 g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
+							 g_define_type_id,
+							 "proxy-b",
+							 0))
+
+static void
+g_proxy_b_init (GProxyB *proxy)
+{
+  ((GProxyBase *) proxy)->proxy_data = &proxy_b;
+}
+
+static gboolean
+g_proxy_b_supports_hostname (GProxy *proxy)
+{
+  return TRUE;
+}
+
+static void
+g_proxy_b_class_init (GProxyBClass *class)
+{
+}
+
+static void
+g_proxy_b_iface_init (GProxyInterface *proxy_iface)
+{
+  proxy_iface->connect = g_proxy_base_connect;
+  proxy_iface->connect_async = g_proxy_base_connect_async;
+  proxy_iface->connect_finish = g_proxy_base_connect_finish;
+  proxy_iface->supports_hostname = g_proxy_b_supports_hostname;
+}
+
+
+/***********************************/
+/* The proxy server implementation */
+/***********************************/
+
+static gboolean
+proxy_bytes (GSocket      *socket,
+	     GIOCondition  condition,
+	     gpointer      user_data)
+{
+  ProxyData *proxy = user_data;
+  gssize nread, nwrote, total;
+  gchar buffer[8];
+  GSocket *out_socket;
+  GError *error = NULL;
+
+  nread = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
+					  TRUE, NULL, &error);
+  if (nread == -1)
+    {
+      g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED);
+      return FALSE;
+    }
+  else
+    g_assert_no_error (error);
+
+  if (nread == 0)
+    {
+      g_main_loop_quit (proxy->loop);
+      return FALSE;
+    }
+
+  if (socket == proxy->client_sock)
+    out_socket = proxy->server_sock;
+  else
+    out_socket = proxy->client_sock;
+
+  for (total = 0; total < nread; total += nwrote)
+    {
+      nwrote = g_socket_send_with_blocking (out_socket,
+					    buffer + total, nread - total,
+					    TRUE, NULL, &error);
+      g_assert_no_error (error);
+    }
+
+  return TRUE;
+}
+
+static gpointer
+proxy_thread (gpointer user_data)
+{
+  ProxyData *proxy = user_data;
+  GError *error = NULL;
+  gssize nread, nwrote;
+  gchar command[2] = { 0, 0 };
+  GMainContext *context;
+  GSource *source;
+
+  context = g_main_context_new ();
+  proxy->loop = g_main_loop_new (context, FALSE);
+
+  while (TRUE)
+    {
+      proxy->client_sock = g_socket_accept (proxy->server, proxy->cancellable, &error);
+      if (!proxy->client_sock)
+	{
+	  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+	  break;
+	}
+      else
+	g_assert_no_error (error);
+
+      nread = g_socket_receive (proxy->client_sock, command, 1, NULL, &error);
+      g_assert_no_error (error);
+
+      if (nread == 0)
+	{
+	  g_clear_object (&proxy->client_sock);
+	  continue;
+	}
+
+      g_assert_cmpint (nread, ==, 1);
+      g_assert_cmpstr (command, ==, proxy->proxy_command);
+
+      *command = g_ascii_toupper (*command);
+      nwrote = g_socket_send (proxy->client_sock, command, 1, NULL, &error);
+      g_assert_no_error (error);
+      g_assert_cmpint (nwrote, ==, 1);
+
+      proxy->server_sock = g_socket_new (G_SOCKET_FAMILY_IPV4,
+					 G_SOCKET_TYPE_STREAM,
+					 G_SOCKET_PROTOCOL_DEFAULT,
+					 &error);
+      g_assert_no_error (error);
+      g_socket_connect (proxy->server_sock, server.server_addr, NULL, &error);
+      g_assert_no_error (error);
+
+      source = g_socket_create_source (proxy->client_sock, G_IO_IN, NULL);
+      g_source_set_callback (source, (GSourceFunc)proxy_bytes, proxy, NULL);
+      g_source_attach (source, context);
+      g_source_unref (source);
+
+      source = g_socket_create_source (proxy->server_sock, G_IO_IN, NULL);
+      g_source_set_callback (source, (GSourceFunc)proxy_bytes, proxy, NULL);
+      g_source_attach (source, context);
+      g_source_unref (source);
+
+      g_main_loop_run (proxy->loop);
+
+      g_socket_close (proxy->client_sock, &error);
+      g_assert_no_error (error);
+      g_clear_object (&proxy->client_sock);
+
+      g_socket_close (proxy->server_sock, &error);
+      g_assert_no_error (error);
+      g_clear_object (&proxy->server_sock);
+    }
+
+  g_main_loop_unref (proxy->loop);
+  g_main_context_unref (context);
+
+  g_object_unref (proxy->server);
+  g_object_unref (proxy->cancellable);
+
+  return NULL;
+}
+
+static void
+create_proxy (ProxyData    *proxy,
+	      gchar         proxy_protocol,
+	      const gchar  *destination_protocol,
+	      GCancellable *cancellable)
+{
+  GError *error = NULL;
+  GSocketAddress *addr;
+  GInetAddress *iaddr;
+
+  proxy->proxy_command = g_strdup_printf ("%c", proxy_protocol);
+  proxy->supported_protocol = g_strdup (destination_protocol);
+  proxy->cancellable = g_object_ref (cancellable);
+
+  proxy->server = g_socket_new (G_SOCKET_FAMILY_IPV4,
+				G_SOCKET_TYPE_STREAM,
+				G_SOCKET_PROTOCOL_DEFAULT,
+				&error);
+  g_assert_no_error (error);
+
+  iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
+  addr = g_inet_socket_address_new (iaddr, 0);
+  g_object_unref (iaddr);
+
+  g_socket_bind (proxy->server, addr, TRUE, &error);
+  g_assert_no_error (error);
+  g_object_unref (addr);
+
+  addr = g_socket_get_local_address (proxy->server, &error);
+  proxy->port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
+  proxy->uri = g_strdup_printf ("proxy-%c://127.0.0.1:%u",
+				g_ascii_tolower (proxy_protocol),
+				proxy->port);
+  g_object_unref (addr);
+
+  g_socket_listen (proxy->server, &error);
+  g_assert_no_error (error);
+
+  proxy->thread = g_thread_new ("proxy", proxy_thread, proxy);
+}
+
+
+
+/**************************/
+/* The actual echo server */
+/**************************/
+
+static gpointer
+echo_server_thread (gpointer user_data)
+{
+  ServerData *data = user_data;
+  GSocket *sock;
+  GError *error = NULL;
+  gssize nread, nwrote;
+  gchar buf[128];
+
+  while (TRUE)
+    {
+      sock = g_socket_accept (data->server, data->cancellable, &error);
+      if (!sock)
+	{
+	  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+	  break;
+	}
+      else
+	g_assert_no_error (error);
+
+      while (TRUE)
+	{
+	  nread = g_socket_receive (sock, buf, sizeof (buf), NULL, &error);
+	  g_assert_no_error (error);
+	  g_assert_cmpint (nread, >=, 0);
+
+	  if (nread == 0)
+	    break;
+
+	  nwrote = g_socket_send (sock, buf, nread, NULL, &error);
+	  g_assert_no_error (error);
+	  g_assert_cmpint (nwrote, ==, nread);
+	}
+
+      g_socket_close (sock, &error);
+      g_assert_no_error (error);
+      g_object_unref (sock);
+    }
+
+  g_object_unref (data->server);
+  g_object_unref (data->cancellable);
+
+  return NULL;
+}
+
+static void
+create_server (ServerData *data, GCancellable *cancellable)
+{
+  GError *error = NULL;
+  GSocketAddress *addr;
+  GInetAddress *iaddr;
+
+  data->cancellable = g_object_ref (cancellable);
+
+  data->server = g_socket_new (G_SOCKET_FAMILY_IPV4,
+			       G_SOCKET_TYPE_STREAM,
+			       G_SOCKET_PROTOCOL_DEFAULT,
+			       &error);
+  g_assert_no_error (error);
+
+  g_socket_set_blocking (data->server, TRUE);
+  iaddr = g_inet_address_new_loopback (G_SOCKET_FAMILY_IPV4);
+  addr = g_inet_socket_address_new (iaddr, 0);
+  g_object_unref (iaddr);
+
+  g_socket_bind (data->server, addr, TRUE, &error);
+  g_assert_no_error (error);
+  g_object_unref (addr);
+
+  data->server_addr = g_socket_get_local_address (data->server, &error);
+  g_assert_no_error (error);
+
+  data->server_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (data->server_addr));
+
+  g_socket_listen (data->server, &error);
+  g_assert_no_error (error);
+
+  data->server_thread = g_thread_new ("server", echo_server_thread, data);
+}
+
+
+/****************************************/
+/* We made it! Now for the actual test! */
+/****************************************/
+
+static void
+setup_test (gpointer fixture,
+	    gconstpointer user_data)
+{
+}
+
+static void
+teardown_test (gpointer fixture,
+	       gconstpointer user_data)
+{
+  if (last_proxies)
+    {
+      g_strfreev (last_proxies);
+      last_proxies = NULL;
+    }
+  g_clear_error (&proxy_a.last_error);
+  g_clear_error (&proxy_b.last_error);
+}
+
+
+static const gchar *testbuf = "0123456789abcdef";
+
+static void
+do_echo_test (GSocketConnection *conn)
+{
+  GIOStream *iostream = G_IO_STREAM (conn);
+  GInputStream *istream = g_io_stream_get_input_stream (iostream);
+  GOutputStream *ostream = g_io_stream_get_output_stream (iostream);
+  gssize nread, total;
+  gsize nwrote;
+  gchar buf[128];
+  GError *error = NULL;
+
+  g_output_stream_write_all (ostream, testbuf, strlen (testbuf),
+			     &nwrote, NULL, &error);
+  g_assert_no_error (error);
+  g_assert_cmpint (nwrote, ==, strlen (testbuf));
+
+  for (total = 0; total < nwrote; total += nread)
+    {
+      nread = g_input_stream_read (istream,
+				   buf + total, sizeof (buf) - total,
+				   NULL, &error);
+      g_assert_no_error (error);
+      g_assert_cmpint (nread, >, 0);
+    }
+
+  buf[total] = '\0';
+  g_assert_cmpstr (buf, ==, testbuf);
+}
+
+static void
+async_got_conn (GObject      *source,
+		GAsyncResult *result,
+		gpointer      user_data)
+{
+  GSocketConnection **conn = user_data;
+  GError *error = NULL;
+
+  *conn = g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
+					  result, &error);
+  g_assert_no_error (error);
+}
+
+static void
+async_got_error (GObject      *source,
+		 GAsyncResult *result,
+		 gpointer      user_data)
+{
+  GError **error = user_data;
+
+  g_assert (error != NULL && *error == NULL);
+  g_socket_client_connect_finish (G_SOCKET_CLIENT (source),
+				  result, error);
+  g_assert (*error != NULL);
+}
+
+
+static void
+assert_direct (GSocketConnection *conn)
+{
+  GSocketAddress *addr;
+  GError *error = NULL;
+
+  g_assert_cmpint (g_strv_length (last_proxies), ==, 1);
+  g_assert_cmpstr (last_proxies[0], ==, "direct://");
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+
+  addr = g_socket_connection_get_remote_address (conn, &error);
+  g_assert_no_error (error);
+  g_assert (!G_IS_PROXY_ADDRESS (addr));
+}
+
+static void
+test_direct_sync (gpointer fixture,
+		  gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  gchar *uri;
+  GError *error = NULL;
+
+  /* The simple:// URI should not require any proxy. */
+
+  uri = g_strdup_printf ("simple://127.0.0.1:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  g_free (uri);
+  g_assert_no_error (error);
+
+  assert_direct (conn);
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+test_direct_async (gpointer fixture,
+		   gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  gchar *uri;
+
+  /* The simple:// URI should not require any proxy. */
+  uri = g_strdup_printf ("simple://127.0.0.1:%u", server.server_port);
+  conn = NULL;
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_conn, &conn);
+  g_free (uri);
+  while (conn == NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  assert_direct (conn);
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+assert_single (GSocketConnection *conn)
+{
+  GSocketAddress *addr;
+  const gchar *proxy_uri;
+  gushort proxy_port;
+  GError *error = NULL;
+
+  g_assert_cmpint (g_strv_length (last_proxies), ==, 2);
+  g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
+  g_assert_cmpstr (last_proxies[1], ==, proxy_b.uri);
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+
+  addr = g_socket_connection_get_remote_address (conn, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_PROXY_ADDRESS (addr));
+  proxy_uri = g_proxy_address_get_uri (G_PROXY_ADDRESS (addr));
+  g_assert_cmpstr (proxy_uri, ==, proxy_a.uri);
+  proxy_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
+  g_assert_cmpint (proxy_port, ==, proxy_a.port);
+}
+
+static void
+test_single_sync (gpointer fixture,
+		  gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  GError *error = NULL;
+  gchar *uri;
+
+  /* The alpha:// URI should be proxied via Proxy A */
+  uri = g_strdup_printf ("alpha://127.0.0.1:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  g_free (uri);
+  g_assert_no_error (error);
+
+  assert_single (conn);
+
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+test_single_async (gpointer fixture,
+		   gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  gchar *uri;
+
+  /* The alpha:// URI should be proxied via Proxy A */
+  uri = g_strdup_printf ("alpha://127.0.0.1:%u", server.server_port);
+  conn = NULL;
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_conn, &conn);
+  g_free (uri);
+  while (conn == NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  assert_single (conn);
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+assert_multiple (GSocketConnection *conn)
+{
+  GSocketAddress *addr;
+  const gchar *proxy_uri;
+  gushort proxy_port;
+  GError *error = NULL;
+
+  g_assert_cmpint (g_strv_length (last_proxies), ==, 2);
+  g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
+  g_assert_cmpstr (last_proxies[1], ==, proxy_b.uri);
+  g_assert_error (proxy_a.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+  g_assert_no_error (proxy_b.last_error);
+
+  addr = g_socket_connection_get_remote_address (conn, &error);
+  g_assert_no_error (error);
+  g_assert (G_IS_PROXY_ADDRESS (addr));
+  proxy_uri = g_proxy_address_get_uri (G_PROXY_ADDRESS (addr));
+  g_assert_cmpstr (proxy_uri, ==, proxy_b.uri);
+  proxy_port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (addr));
+  g_assert_cmpint (proxy_port, ==, proxy_b.port);
+}
+
+static void
+test_multiple_sync (gpointer fixture,
+		    gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  GError *error = NULL;
+  gchar *uri;
+
+  /* The beta:// URI should be proxied via Proxy B, after failing
+   * via Proxy A.
+   */
+  uri = g_strdup_printf ("beta://127.0.0.1:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  g_free (uri);
+  g_assert_no_error (error);
+
+  assert_multiple (conn);
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+test_multiple_async (gpointer fixture,
+		     gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  gchar *uri;
+
+  /* The beta:// URI should be proxied via Proxy B, after failing
+   * via Proxy A.
+   */
+  uri = g_strdup_printf ("beta://127.0.0.1:%u", server.server_port);
+  conn = NULL;
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_conn, &conn);
+  g_free (uri);
+  while (conn == NULL)
+    g_main_context_iteration (NULL, TRUE);
+
+  assert_multiple (conn);
+  do_echo_test (conn);
+  g_object_unref (conn);
+}
+
+static void
+test_dns (gpointer fixture,
+	  gconstpointer user_data)
+{
+  GSocketConnection *conn;
+  GError *error = NULL;
+  gchar *uri;
+
+  /* The simple:// and alpha:// URIs should fail with a DNS error,
+   * but the beta:// URI should succeed, because we pass it to
+   * Proxy B without trying to resolve it first
+   */
+
+  /* simple */
+  uri = g_strdup_printf ("simple://no-such-host.xx:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
+  g_clear_error (&error);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+  teardown_test (NULL, NULL);
+
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_error, &error);
+  while (error == NULL)
+    g_main_context_iteration (NULL, TRUE);
+  g_assert_error (error, G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND);
+  g_clear_error (&error);
+  g_free (uri);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+  teardown_test (NULL, NULL);
+
+  /* alpha */
+  uri = g_strdup_printf ("alpha://no-such-host.xx:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  /* Since Proxy A fails, @client will try Proxy B too, which won't
+   * load an alpha:// URI.
+   */
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+  g_clear_error (&error);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_error (proxy_b.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+  teardown_test (NULL, NULL);
+
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_error, &error);
+  while (error == NULL)
+    g_main_context_iteration (NULL, TRUE);
+  g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+  g_clear_error (&error);
+  g_free (uri);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_error (proxy_b.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
+  teardown_test (NULL, NULL);
+
+  /* beta */
+  uri = g_strdup_printf ("beta://no-such-host.xx:%u", server.server_port);
+  conn = g_socket_client_connect_to_uri (client, uri, 0, NULL, &error);
+  g_assert_no_error (error);
+  g_clear_object (&conn);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+  teardown_test (NULL, NULL);
+
+  g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
+					async_got_conn, &conn);
+  while (conn == NULL)
+    g_main_context_iteration (NULL, TRUE);
+  g_clear_object (&conn);
+  g_free (uri);
+
+  g_assert_no_error (proxy_a.last_error);
+  g_assert_no_error (proxy_b.last_error);
+  teardown_test (NULL, NULL);
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+  GCancellable *cancellable;
+  gint result;
+
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  /* Register stuff. The dummy g_proxy_get_default_for_protocol() call
+   * is to force _g_io_modules_ensure_extension_points_registered() to
+   * get called, so we can then register a proxy resolver extension
+   * point.
+   */
+  g_proxy_get_default_for_protocol ("foo");
+  g_test_proxy_resolver_get_type ();
+  g_proxy_a_get_type ();
+  g_proxy_b_get_type ();
+  g_setenv ("GIO_USE_PROXY_RESOLVER", "test", TRUE);
+
+  cancellable = g_cancellable_new ();
+  create_server (&server, cancellable);
+  create_proxy (&proxy_a, 'a', "alpha", cancellable);
+  create_proxy (&proxy_b, 'b', "beta", cancellable);
+
+  client = g_socket_client_new ();
+  g_assert_cmpint (g_socket_client_get_enable_proxy (client), ==, TRUE);
+
+  g_test_add_vtable ("/proxy/direct_sync", 0, NULL, setup_test, test_direct_sync, teardown_test);
+  g_test_add_vtable ("/proxy/direct_async", 0, NULL, setup_test, test_direct_async, teardown_test);
+  g_test_add_vtable ("/proxy/single_sync", 0, NULL, setup_test, test_single_sync, teardown_test);
+  g_test_add_vtable ("/proxy/single_async", 0, NULL, setup_test, test_single_async, teardown_test);
+  g_test_add_vtable ("/proxy/multiple_sync", 0, NULL, setup_test, test_multiple_sync, teardown_test);
+  g_test_add_vtable ("/proxy/multiple_async", 0, NULL, setup_test, test_multiple_async, teardown_test);
+  g_test_add_vtable ("/proxy/dns", 0, NULL, setup_test, test_dns, teardown_test);
+
+  result = g_test_run();
+
+  g_object_unref (client);
+
+  g_cancellable_cancel (cancellable);
+  g_thread_join (proxy_a.thread);
+  g_thread_join (proxy_b.thread);
+  g_thread_join (server.server_thread);
+
+  return result;
+}
+



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