GSocketClient: add proxy-resolver property

Add a proxy-resolver property to GSocketClient, to allow overriding
proxy resolution in situations where you need to force a particular
proxy rather than using the system defaults.

https://bugzilla.gnome.org/show_bug.cgi?id=691105
This commit is contained in:
Dan Winship 2013-01-27 13:53:36 -05:00
parent d200208d2b
commit 7c49869eae
5 changed files with 302 additions and 15 deletions

View File

@ -1958,6 +1958,7 @@ g_socket_client_set_protocol
g_socket_client_set_socket_type
g_socket_client_set_timeout
g_socket_client_set_enable_proxy
g_socket_client_set_proxy_resolver
g_socket_client_set_tls
g_socket_client_set_tls_validation_flags
g_socket_client_get_family
@ -1966,6 +1967,7 @@ g_socket_client_get_protocol
g_socket_client_get_socket_type
g_socket_client_get_timeout
g_socket_client_get_enable_proxy
g_socket_client_get_proxy_resolver
g_socket_client_get_tls
g_socket_client_get_tls_validation_flags
g_socket_client_add_application_proxy

View File

@ -47,7 +47,8 @@ enum
{
PROP_0,
PROP_URI,
PROP_CONNECTABLE
PROP_CONNECTABLE,
PROP_PROXY_RESOLVER
};
struct _GProxyAddressEnumeratorPrivate
@ -60,6 +61,7 @@ struct _GProxyAddressEnumeratorPrivate
GList *dest_ips;
/* Proxy enumeration */
GProxyResolver *proxy_resolver;
gchar **proxies;
gchar **next_proxy;
GSocketAddressEnumerator *addr_enum;
@ -180,8 +182,7 @@ g_proxy_address_enumerator_next (GSocketAddressEnumerator *enumerator,
if (priv->proxies == NULL)
{
GProxyResolver *resolver = g_proxy_resolver_get_default ();
priv->proxies = g_proxy_resolver_lookup (resolver,
priv->proxies = g_proxy_resolver_lookup (priv->proxy_resolver,
priv->dest_uri,
cancellable,
error);
@ -530,8 +531,7 @@ g_proxy_address_enumerator_next_async (GSocketAddressEnumerator *enumerator,
if (priv->proxies == NULL)
{
GProxyResolver *resolver = g_proxy_resolver_get_default ();
g_proxy_resolver_lookup_async (resolver,
g_proxy_resolver_lookup_async (priv->proxy_resolver,
priv->dest_uri,
cancellable,
proxy_lookup_cb,
@ -586,6 +586,10 @@ g_proxy_address_enumerator_get_property (GObject *object,
g_value_set_object (value, priv->connectable);
break;
case PROP_PROXY_RESOLVER:
g_value_set_object (value, priv->proxy_resolver);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -643,6 +647,15 @@ g_proxy_address_enumerator_set_property (GObject *object,
priv->connectable = g_value_dup_object (value);
break;
case PROP_PROXY_RESOLVER:
if (priv->proxy_resolver)
g_object_unref (priv->proxy_resolver);
priv->proxy_resolver = g_value_get_object (value);
if (!priv->proxy_resolver)
priv->proxy_resolver = g_proxy_resolver_get_default ();
g_object_ref (priv->proxy_resolver);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@ -656,6 +669,9 @@ g_proxy_address_enumerator_finalize (GObject *object)
if (priv->connectable)
g_object_unref (priv->connectable);
if (priv->proxy_resolver)
g_object_unref (priv->proxy_resolver);
g_free (priv->dest_uri);
g_free (priv->dest_hostname);
@ -720,4 +736,21 @@ g_proxy_address_enumerator_class_init (GProxyAddressEnumeratorClass *proxy_enume
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* GProxyAddressEnumerator:proxy-resolver:
*
* The proxy resolver to use.
*
* Since: 2.36
*/
g_object_class_install_property (object_class,
PROP_PROXY_RESOLVER,
g_param_spec_object ("proxy-resolver",
P_("Proxy resolver"),
P_("The proxy resolver to use."),
G_TYPE_PROXY_RESOLVER,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
}

View File

@ -41,6 +41,7 @@
#include <gio/gnetworkaddress.h>
#include <gio/gnetworkservice.h>
#include <gio/gproxy.h>
#include <gio/gproxyresolver.h>
#include <gio/gsocketaddress.h>
#include <gio/gtcpconnection.h>
#include <gio/gtcpwrapperconnection.h>
@ -94,7 +95,8 @@ enum
PROP_TIMEOUT,
PROP_ENABLE_PROXY,
PROP_TLS,
PROP_TLS_VALIDATION_FLAGS
PROP_TLS_VALIDATION_FLAGS,
PROP_PROXY_RESOLVER
};
struct _GSocketClientPrivate
@ -108,6 +110,7 @@ struct _GSocketClientPrivate
GHashTable *app_proxies;
gboolean tls;
GTlsCertificateFlags tls_validation_flags;
GProxyResolver *proxy_resolver;
};
static GSocket *
@ -227,8 +230,8 @@ g_socket_client_finalize (GObject *object)
{
GSocketClient *client = G_SOCKET_CLIENT (object);
if (client->priv->local_address)
g_object_unref (client->priv->local_address);
g_clear_object (&client->priv->local_address);
g_clear_object (&client->priv->proxy_resolver);
if (G_OBJECT_CLASS (g_socket_client_parent_class)->finalize)
(*G_OBJECT_CLASS (g_socket_client_parent_class)->finalize) (object);
@ -278,6 +281,10 @@ g_socket_client_get_property (GObject *object,
g_value_set_flags (value, g_socket_client_get_tls_validation_flags (client));
break;
case PROP_PROXY_RESOLVER:
g_value_set_object (value, g_socket_client_get_proxy_resolver (client));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@ -325,6 +332,10 @@ g_socket_client_set_property (GObject *object,
g_socket_client_set_tls_validation_flags (client, g_value_get_flags (value));
break;
case PROP_PROXY_RESOLVER:
g_socket_client_set_proxy_resolver (client, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@ -579,6 +590,8 @@ g_socket_client_get_enable_proxy (GSocketClient *client)
* #GProxyResolver to determine if a proxy protocol such as SOCKS is
* needed, and automatically do the necessary proxy negotiation.
*
* See also g_socket_client_set_proxy_resolver().
*
* Since: 2.26
*/
void
@ -686,6 +699,64 @@ g_socket_client_set_tls_validation_flags (GSocketClient *client,
}
}
/**
* g_socket_client_get_proxy_resolver:
* @client: a #GSocketClient.
*
* Gets the #GProxyResolver being used by @client. Normally, this will
* be the resolver returned by g_proxy_resolver_get_default(), but you
* can override it with g_socket_client_set_proxy_resolver().
*
* Returns: (transfer none): The #GProxyResolver being used by
* @client.
*
* Since: 2.36
*/
GProxyResolver *
g_socket_client_get_proxy_resolver (GSocketClient *client)
{
if (client->priv->proxy_resolver)
return client->priv->proxy_resolver;
else
return g_proxy_resolver_get_default ();
}
/**
* g_socket_client_set_proxy_resolver:
* @client: a #GSocketClient.
* @proxy_resolver: (allow-none): a #GProxyResolver, or %NULL for the
* default.
*
* Overrides the #GProxyResolver used by @client. You can call this if
* you want to use specific proxies, rather than using the system
* default proxy settings.
*
* Note that whether or not the proxy resolver is actually used
* depends on the setting of #GSocketClient:enable-proxy, which is not
* changed by this function (but which is %TRUE by default)
*
* Since: 2.36
*/
void
g_socket_client_set_proxy_resolver (GSocketClient *client,
GProxyResolver *proxy_resolver)
{
/* We have to be careful to avoid calling
* g_proxy_resolver_get_default() until we're sure we need it,
* because trying to load the default proxy resolver module will
* break some test programs that aren't expecting it (eg,
* tests/gsettings).
*/
if (client->priv->proxy_resolver)
g_object_unref (client->priv->proxy_resolver);
client->priv->proxy_resolver = proxy_resolver;
if (client->priv->proxy_resolver)
g_object_ref (client->priv->proxy_resolver);
}
static void
g_socket_client_class_init (GSocketClientClass *class)
{
@ -881,6 +952,22 @@ g_socket_client_class_init (GSocketClientClass *class)
G_PARAM_CONSTRUCT |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
/**
* GSocketClient:proxy-resolver:
*
* The proxy resolver to use
*
* Since: 2.36
*/
g_object_class_install_property (gobject_class, PROP_PROXY_RESOLVER,
g_param_spec_object ("proxy-resolver",
P_("Proxy resolver"),
P_("The proxy resolver to use"),
G_TYPE_PROXY_RESOLVER,
G_PARAM_CONSTRUCT |
G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS));
}
static void
@ -936,7 +1023,15 @@ g_socket_client_connect (GSocketClient *client,
last_error = NULL;
if (can_use_proxy (client))
enumerator = g_socket_connectable_proxy_enumerate (connectable);
{
enumerator = g_socket_connectable_proxy_enumerate (connectable);
if (client->priv->proxy_resolver &&
G_IS_PROXY_ADDRESS_ENUMERATOR (enumerator))
{
g_object_set (G_OBJECT (enumerator), "proxy-resolver",
client->priv->proxy_resolver);
}
}
else
enumerator = g_socket_connectable_enumerate (connectable);
@ -1605,9 +1700,17 @@ g_socket_client_connect_async (GSocketClient *client,
data->connectable = g_object_ref (connectable);
if (can_use_proxy (client))
{
data->enumerator = g_socket_connectable_proxy_enumerate (connectable);
if (client->priv->proxy_resolver &&
G_IS_PROXY_ADDRESS_ENUMERATOR (data->enumerator))
{
g_object_set (G_OBJECT (data->enumerator), "proxy-resolver",
client->priv->proxy_resolver);
}
}
else
data->enumerator = g_socket_connectable_enumerate (connectable);
data->enumerator = g_socket_connectable_enumerate (connectable);
data->task = g_task_new (client, cancellable, callback, user_data);
g_task_set_task_data (data->task, data, (GDestroyNotify)g_socket_client_async_connect_data_free);

View File

@ -117,6 +117,11 @@ GTlsCertificateFlags g_socket_client_get_tls_validation_flags (GSocket
GLIB_AVAILABLE_IN_ALL
void g_socket_client_set_tls_validation_flags (GSocketClient *client,
GTlsCertificateFlags flags);
GLIB_AVAILABLE_IN_2_36
GProxyResolver *g_socket_client_get_proxy_resolver (GSocketClient *client);
GLIB_AVAILABLE_IN_2_36
void g_socket_client_set_proxy_resolver (GSocketClient *client,
GProxyResolver *proxy_resolver);
GLIB_AVAILABLE_IN_ALL
GSocketConnection * g_socket_client_connect (GSocketClient *client,

View File

@ -26,7 +26,7 @@
/* Overview:
*
* We have an echo server, two proxy servers, two GProxy
* implementations, and a GProxyResolver implementation.
* implementations, and two GProxyResolver implementations.
*
* The echo server runs at @server.server_addr (on
* @server.server_port).
@ -42,9 +42,10 @@
* 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.
* The default GProxyResolver (GTestProxyResolver) looks at its URI
* and returns [ "direct://" ] for "simple://" URIs, and [
* proxy_a.uri, proxy_b.uri ] for other URIs. The other GProxyResolver
* (GTestAltProxyResolver) always returns [ proxy_a.uri ].
*/
typedef struct {
@ -163,7 +164,7 @@ g_test_proxy_resolver_lookup_async (GProxyResolver *resolver,
GTask *task;
gchar **proxies;
proxies = g_test_proxy_resolver_lookup (resolver, uri, cancellable, &error);
proxies = g_proxy_resolver_lookup (resolver, uri, cancellable, &error);
task = g_task_new (resolver, NULL, callback, user_data);
if (proxies == NULL)
@ -196,6 +197,56 @@ g_test_proxy_resolver_iface_init (GProxyResolverInterface *iface)
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 */
@ -1093,6 +1144,98 @@ test_dns (gpointer fixture,
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);
}
int
main (int argc,
char *argv[])
@ -1132,6 +1275,7 @@ main (int argc,
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);
g_test_add_vtable ("/proxy/override", 0, NULL, setup_test, test_override, teardown_test);
result = g_test_run();