mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-25 06:56:14 +01:00
1738fad172
It doesn't make sense for a proxy resolver to return NULL without an error on the first call. Whereas a DNS resolver would do this to indicate that a query completed successfully but found no results, a proxy resolver should return "direct://" instead. Therefore, if we are going to return NULL, we ought to have an error as well. Let's make sure this actually happens by adding some fallback errors just in case GProxyResolver feeds us weird results. Additionally, we should not return any errors except G_IO_ERROR_CANCELLED after the very first iteration. This is an API contract of GSocketAddressEnumerator. Let's add some checks to ensure this. Note that we have inadequate test coverage for GProxyAddressEnumerator. It's tested here only via GSocketClient. We could do a bit better by testing it directly as well. For example, I've added tests to see what happens when GProxyResolver returns both a valid and an invalid URI, but it's not so interesting here because GSocketClient always uses the valid result and ignores the error from GProxyAddressEnumerator. Fixes #2597
1530 lines
43 KiB
C
1530 lines
43 KiB
C
/* GLib testing framework examples and tests
|
|
*
|
|
* Copyright 2012 Red Hat, Inc.
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*
|
|
* 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.1 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#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 two GProxyResolver implementations.
|
|
*
|
|
* 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 default GProxyResolver (GTestProxyResolver) looks at its URI
|
|
* and returns [ "direct://" ] for "simple://" URIs, and
|
|
* [ proxy_a.uri, proxy_b.uri ] for most other URIs. It can also return
|
|
* invalid results for other URIs (empty://, invalid://,
|
|
* invalid-then-simple://, and simple-then-invalid://) to test error
|
|
* handling.
|
|
*
|
|
* The other GProxyResolver (GTestAltProxyResolver) always returns
|
|
* [ proxy_a.uri ].
|
|
*/
|
|
|
|
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);
|
|
|
|
static GType _g_test_proxy_resolver_get_type (void);
|
|
#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 (g_str_has_prefix (uri, "simple://"))
|
|
{
|
|
proxies[0] = g_strdup ("direct://");
|
|
proxies[1] = NULL;
|
|
}
|
|
else if (g_str_has_prefix (uri, "empty://"))
|
|
{
|
|
proxies[0] = g_strdup ("");
|
|
proxies[1] = NULL;
|
|
}
|
|
else if (g_str_has_prefix (uri, "invalid://"))
|
|
{
|
|
proxies[0] = g_strdup ("😼");
|
|
proxies[1] = NULL;
|
|
}
|
|
else if (g_str_has_prefix (uri, "invalid-then-simple://"))
|
|
{
|
|
proxies[0] = g_strdup ("😼");
|
|
proxies[1] = g_strdup ("direct://");
|
|
proxies[2] = NULL;
|
|
}
|
|
else if (g_str_has_prefix (uri, "simple-then-invalid://"))
|
|
{
|
|
proxies[0] = g_strdup ("direct://");
|
|
proxies[1] = g_strdup ("😼");
|
|
proxies[2] = 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;
|
|
GTask *task;
|
|
gchar **proxies;
|
|
|
|
proxies = g_proxy_resolver_lookup (resolver, uri, cancellable, &error);
|
|
|
|
task = g_task_new (resolver, NULL, callback, user_data);
|
|
if (proxies == NULL)
|
|
g_task_return_error (task, error);
|
|
else
|
|
g_task_return_pointer (task, proxies, (GDestroyNotify) g_strfreev);
|
|
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static gchar **
|
|
g_test_proxy_resolver_lookup_finish (GProxyResolver *resolver,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/****************************/
|
|
/* Alternate GProxyResolver */
|
|
/****************************/
|
|
|
|
typedef GTestProxyResolver GTestAltProxyResolver;
|
|
typedef GTestProxyResolverClass GTestAltProxyResolverClass;
|
|
|
|
static void g_test_alt_proxy_resolver_iface_init (GProxyResolverInterface *iface);
|
|
|
|
static GType _g_test_alt_proxy_resolver_get_type (void);
|
|
#define g_test_alt_proxy_resolver_get_type _g_test_alt_proxy_resolver_get_type
|
|
G_DEFINE_TYPE_WITH_CODE (GTestAltProxyResolver, g_test_alt_proxy_resolver, g_test_proxy_resolver_get_type (),
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_PROXY_RESOLVER,
|
|
g_test_alt_proxy_resolver_iface_init);
|
|
)
|
|
|
|
static void
|
|
g_test_alt_proxy_resolver_init (GTestProxyResolver *resolver)
|
|
{
|
|
}
|
|
|
|
static gchar **
|
|
g_test_alt_proxy_resolver_lookup (GProxyResolver *resolver,
|
|
const gchar *uri,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
gchar **proxies;
|
|
|
|
proxies = g_new (gchar *, 2);
|
|
|
|
proxies[0] = g_strdup (proxy_a.uri);
|
|
proxies[1] = NULL;
|
|
|
|
last_proxies = g_strdupv (proxies);
|
|
|
|
return proxies;
|
|
}
|
|
|
|
static void
|
|
g_test_alt_proxy_resolver_class_init (GTestProxyResolverClass *resolver_class)
|
|
{
|
|
}
|
|
|
|
static void
|
|
g_test_alt_proxy_resolver_iface_init (GProxyResolverInterface *iface)
|
|
{
|
|
iface->lookup = g_test_alt_proxy_resolver_lookup;
|
|
}
|
|
|
|
|
|
/****************************************/
|
|
/* Test proxy implementation base class */
|
|
/****************************************/
|
|
|
|
typedef struct {
|
|
GObject parent;
|
|
|
|
ProxyData *proxy_data;
|
|
} GProxyBase;
|
|
|
|
typedef struct {
|
|
GObjectClass parent_class;
|
|
} GProxyBaseClass;
|
|
|
|
static GType _g_proxy_base_get_type (void);
|
|
#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;
|
|
GTask *task;
|
|
GIOStream *proxy_io_stream;
|
|
|
|
task = g_task_new (proxy, NULL, callback, user_data);
|
|
|
|
proxy_io_stream = g_proxy_connect (proxy, io_stream, proxy_address,
|
|
cancellable, &error);
|
|
if (proxy_io_stream)
|
|
g_task_return_pointer (task, proxy_io_stream, g_object_unref);
|
|
else
|
|
g_task_return_error (task, error);
|
|
g_object_unref (task);
|
|
}
|
|
|
|
static GIOStream *
|
|
g_proxy_base_connect_finish (GProxy *proxy,
|
|
GAsyncResult *result,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
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);
|
|
|
|
static GType _g_proxy_a_get_type (void);
|
|
#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);
|
|
|
|
static GType _g_proxy_b_get_type (void);
|
|
#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 *read_source, *write_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);
|
|
g_error_free (error);
|
|
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);
|
|
|
|
read_source = g_socket_create_source (proxy->client_sock, G_IO_IN, NULL);
|
|
g_source_set_callback (read_source, (GSourceFunc)proxy_bytes, proxy, NULL);
|
|
g_source_attach (read_source, context);
|
|
|
|
write_source = g_socket_create_source (proxy->server_sock, G_IO_IN, NULL);
|
|
g_source_set_callback (write_source, (GSourceFunc)proxy_bytes, proxy, NULL);
|
|
g_source_attach (write_source, context);
|
|
|
|
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_source_destroy (read_source);
|
|
g_source_unref (read_source);
|
|
g_source_destroy (write_source);
|
|
g_source_unref (write_source);
|
|
}
|
|
|
|
g_main_loop_unref (proxy->loop);
|
|
g_main_context_unref (context);
|
|
|
|
g_object_unref (proxy->server);
|
|
g_object_unref (proxy->cancellable);
|
|
|
|
g_free (proxy->proxy_command);
|
|
g_free (proxy->supported_protocol);
|
|
g_free (proxy->uri);
|
|
|
|
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);
|
|
g_error_free (error);
|
|
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->server_addr);
|
|
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);
|
|
}
|
|
|
|
|
|
/******************************************************************/
|
|
/* Now a GResolver implementation, so the can't-resolve test will */
|
|
/* pass even if you have an evil DNS-faking ISP. */
|
|
/******************************************************************/
|
|
|
|
typedef GResolver GFakeResolver;
|
|
typedef GResolverClass GFakeResolverClass;
|
|
|
|
static GType g_fake_resolver_get_type (void);
|
|
G_DEFINE_TYPE (GFakeResolver, g_fake_resolver, G_TYPE_RESOLVER)
|
|
|
|
static void
|
|
g_fake_resolver_init (GFakeResolver *gtr)
|
|
{
|
|
}
|
|
|
|
static GList *
|
|
g_fake_resolver_lookup_by_name (GResolver *resolver,
|
|
const gchar *hostname,
|
|
GCancellable *cancellable,
|
|
GError **error)
|
|
{
|
|
if (!strcmp (hostname, "example.com"))
|
|
return g_list_prepend (NULL, g_inet_address_new_from_string ("127.0.0.1"));
|
|
else
|
|
{
|
|
/* Anything else is expected to fail. */
|
|
g_set_error (error,
|
|
G_RESOLVER_ERROR,
|
|
G_RESOLVER_ERROR_NOT_FOUND,
|
|
"Not found");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_fake_resolver_lookup_by_name_async (GResolver *resolver,
|
|
const gchar *hostname,
|
|
GCancellable *cancellable,
|
|
GAsyncReadyCallback callback,
|
|
gpointer user_data)
|
|
{
|
|
GTask *task;
|
|
|
|
task = g_task_new (resolver, cancellable, callback, user_data);
|
|
|
|
if (!strcmp (hostname, "example.com"))
|
|
{
|
|
GList *result;
|
|
|
|
result = g_list_prepend (NULL, g_inet_address_new_from_string ("127.0.0.1"));
|
|
g_task_return_pointer (task, result, (GDestroyNotify) g_resolver_free_addresses);
|
|
}
|
|
else
|
|
{
|
|
g_task_return_new_error (task,
|
|
G_RESOLVER_ERROR, G_RESOLVER_ERROR_NOT_FOUND,
|
|
"Not found");
|
|
}
|
|
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)
|
|
{
|
|
/* Note this isn't a real implementation as it ignores the flags */
|
|
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,
|
|
GError **error)
|
|
{
|
|
return g_task_propagate_pointer (G_TASK (result), error);
|
|
}
|
|
|
|
static void
|
|
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_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;
|
|
}
|
|
|
|
|
|
|
|
/****************************************/
|
|
/* 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)
|
|
{
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
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;
|
|
gsize nwrote, total;
|
|
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 (addr != NULL && !G_IS_PROXY_ADDRESS (addr));
|
|
g_object_unref (addr);
|
|
|
|
addr = g_socket_connection_get_local_address (conn, &error);
|
|
g_assert_no_error (error);
|
|
g_object_unref (addr);
|
|
|
|
g_assert (g_socket_connection_is_connected (conn));
|
|
}
|
|
|
|
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);
|
|
|
|
g_object_unref (addr);
|
|
}
|
|
|
|
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);
|
|
|
|
g_object_unref (addr);
|
|
}
|
|
|
|
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_invalid_uris_sync (gpointer fixture,
|
|
gconstpointer user_data)
|
|
{
|
|
GSocketConnection *conn;
|
|
gchar *uri;
|
|
GError *error = NULL;
|
|
|
|
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
|
|
|
|
/* The empty:// URI causes the proxy resolver to return an empty string. */
|
|
uri = g_strdup_printf ("empty://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_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
g_assert_null (conn);
|
|
g_clear_error (&error);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* The invalid:// URI causes the proxy resolver to return a cat emoji. */
|
|
uri = g_strdup_printf ("invalid://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_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
g_assert_null (conn);
|
|
g_clear_error (&error);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* If the proxy resolver returns an invalid URI before a valid URI,
|
|
* we should succeed.
|
|
*/
|
|
uri = g_strdup_printf ("invalid-then-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);
|
|
do_echo_test (conn);
|
|
g_object_unref (conn);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* If the proxy resolver returns a valid URI before an invalid URI,
|
|
* we should succeed.
|
|
*/
|
|
uri = g_strdup_printf ("simple-then-invalid://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);
|
|
do_echo_test (conn);
|
|
g_object_unref (conn);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
}
|
|
|
|
static void
|
|
test_invalid_uris_async (gpointer fixture,
|
|
gconstpointer user_data)
|
|
{
|
|
GSocketConnection *conn;
|
|
GError *error = NULL;
|
|
gchar *uri;
|
|
|
|
g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2597");
|
|
|
|
/* The empty:// URI causes the proxy resolver to return an empty string. */
|
|
uri = g_strdup_printf ("empty://127.0.0.1:%u", server.server_port);
|
|
g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
|
|
async_got_error, &error);
|
|
g_free (uri);
|
|
while (error == NULL)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
g_clear_error (&error);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* The invalid:// URI causes the proxy resolver to return a cat emoji. */
|
|
uri = g_strdup_printf ("invalid://127.0.0.1:%u", server.server_port);
|
|
g_socket_client_connect_to_uri_async (client, uri, 0, NULL,
|
|
async_got_error, &error);
|
|
g_free (uri);
|
|
while (error == NULL)
|
|
g_main_context_iteration (NULL, TRUE);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_FAILED);
|
|
g_clear_error (&error);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* If the proxy resolver returns an invalid URI before a valid URI,
|
|
* we should succeed.
|
|
*/
|
|
uri = g_strdup_printf ("invalid-then-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);
|
|
do_echo_test (conn);
|
|
g_object_unref (conn);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
|
|
/* If the proxy resolver returns a valid URI before an invalid URI,
|
|
* we should succeed.
|
|
*/
|
|
uri = g_strdup_printf ("simple-then-invalid://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);
|
|
do_echo_test (conn);
|
|
g_object_unref (conn);
|
|
g_clear_pointer (&last_proxies, g_strfreev);
|
|
}
|
|
|
|
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_assert_no_error (proxy_a.last_error);
|
|
g_assert_no_error (proxy_b.last_error);
|
|
|
|
do_echo_test (conn);
|
|
g_clear_object (&conn);
|
|
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_free (uri);
|
|
|
|
g_assert_no_error (proxy_a.last_error);
|
|
g_assert_no_error (proxy_b.last_error);
|
|
|
|
do_echo_test (conn);
|
|
g_clear_object (&conn);
|
|
teardown_test (NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
assert_override (GSocketConnection *conn)
|
|
{
|
|
g_assert_cmpint (g_strv_length (last_proxies), ==, 1);
|
|
g_assert_cmpstr (last_proxies[0], ==, proxy_a.uri);
|
|
|
|
if (conn)
|
|
g_assert_no_error (proxy_a.last_error);
|
|
else
|
|
g_assert_error (proxy_a.last_error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
|
}
|
|
|
|
static void
|
|
test_override (gpointer fixture,
|
|
gconstpointer user_data)
|
|
{
|
|
GProxyResolver *alt_resolver;
|
|
GSocketConnection *conn;
|
|
GError *error = NULL;
|
|
gchar *uri;
|
|
|
|
g_assert (g_socket_client_get_proxy_resolver (client) == g_proxy_resolver_get_default ());
|
|
alt_resolver = g_object_new (g_test_alt_proxy_resolver_get_type (), NULL);
|
|
g_socket_client_set_proxy_resolver (client, alt_resolver);
|
|
g_assert (g_socket_client_get_proxy_resolver (client) == alt_resolver);
|
|
|
|
/* Alt proxy resolver always returns Proxy A, so alpha:// should
|
|
* succeed, and simple:// and beta:// should fail.
|
|
*/
|
|
|
|
/* simple */
|
|
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_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
|
g_clear_error (&error);
|
|
assert_override (conn);
|
|
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);
|
|
assert_override (conn);
|
|
g_free (uri);
|
|
teardown_test (NULL, NULL);
|
|
|
|
/* alpha */
|
|
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_assert_no_error (error);
|
|
assert_override (conn);
|
|
do_echo_test (conn);
|
|
g_clear_object (&conn);
|
|
teardown_test (NULL, NULL);
|
|
|
|
conn = 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);
|
|
assert_override (conn);
|
|
do_echo_test (conn);
|
|
g_clear_object (&conn);
|
|
g_free (uri);
|
|
teardown_test (NULL, NULL);
|
|
|
|
/* beta */
|
|
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_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
|
|
g_clear_error (&error);
|
|
assert_override (conn);
|
|
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);
|
|
assert_override (conn);
|
|
g_free (uri);
|
|
teardown_test (NULL, NULL);
|
|
|
|
g_assert (g_socket_client_get_proxy_resolver (client) == alt_resolver);
|
|
g_socket_client_set_proxy_resolver (client, NULL);
|
|
g_assert (g_socket_client_get_proxy_resolver (client) == g_proxy_resolver_get_default ());
|
|
g_object_unref (alt_resolver);
|
|
}
|
|
|
|
static void
|
|
assert_destination_port (GSocketAddressEnumerator *etor,
|
|
guint16 port)
|
|
{
|
|
GSocketAddress *addr;
|
|
GProxyAddress *paddr;
|
|
GError *error = NULL;
|
|
|
|
while ((addr = g_socket_address_enumerator_next (etor, NULL, &error)))
|
|
{
|
|
g_assert_no_error (error);
|
|
|
|
g_assert (G_IS_PROXY_ADDRESS (addr));
|
|
paddr = G_PROXY_ADDRESS (addr);
|
|
g_assert_cmpint (g_proxy_address_get_destination_port (paddr), ==, port);
|
|
g_object_unref (addr);
|
|
}
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
static void
|
|
test_proxy_enumerator_ports (void)
|
|
{
|
|
GSocketAddressEnumerator *etor;
|
|
|
|
etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
|
|
"uri", "http://example.com/",
|
|
NULL);
|
|
assert_destination_port (etor, 0);
|
|
g_object_unref (etor);
|
|
|
|
/* Have to call this to clear last_proxies so the next call to
|
|
* g_test_proxy_resolver_lookup() won't assert.
|
|
*/
|
|
teardown_test (NULL, NULL);
|
|
|
|
etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
|
|
"uri", "http://example.com:8080/",
|
|
NULL);
|
|
assert_destination_port (etor, 8080);
|
|
g_object_unref (etor);
|
|
|
|
teardown_test (NULL, NULL);
|
|
|
|
etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
|
|
"uri", "http://example.com/",
|
|
"default-port", 80,
|
|
NULL);
|
|
assert_destination_port (etor, 80);
|
|
g_object_unref (etor);
|
|
|
|
teardown_test (NULL, NULL);
|
|
|
|
etor = g_object_new (G_TYPE_PROXY_ADDRESS_ENUMERATOR,
|
|
"uri", "http://example.com:8080/",
|
|
"default-port", 80,
|
|
NULL);
|
|
assert_destination_port (etor, 8080);
|
|
g_object_unref (etor);
|
|
|
|
teardown_test (NULL, NULL);
|
|
}
|
|
|
|
int
|
|
main (int argc,
|
|
char *argv[])
|
|
{
|
|
GResolver *fake_resolver;
|
|
GCancellable *cancellable;
|
|
gint result;
|
|
|
|
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);
|
|
|
|
fake_resolver = g_object_new (g_fake_resolver_get_type (), NULL);
|
|
g_resolver_set_default (fake_resolver);
|
|
|
|
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/invalid-uris-sync", 0, NULL, setup_test, test_invalid_uris_sync, teardown_test);
|
|
g_test_add_vtable ("/proxy/invalid-uris-async", 0, NULL, setup_test, test_invalid_uris_async, teardown_test);
|
|
g_test_add_vtable ("/proxy/dns", 0, NULL, setup_test, test_dns, teardown_test);
|
|
g_test_add_vtable ("/proxy/override", 0, NULL, setup_test, test_override, teardown_test);
|
|
g_test_add_func ("/proxy/enumerator-ports", test_proxy_enumerator_ports);
|
|
|
|
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);
|
|
|
|
g_object_unref (cancellable);
|
|
|
|
return result;
|
|
}
|