* 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.
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Ryan Lortie <desrt@desrt.ca>
* Alexander Larsson <alexl@redhat.com>
#include <gio/gsocketaddressenumerator.h>
#include <gio/gsocketconnectable.h>
#include <gio/gsocketconnection.h>
+#include <gio/gioprivate.h>
#include <gio/gproxyaddressenumerator.h>
#include <gio/gproxyaddress.h>
-#include <gio/gsimpleasyncresult.h>
+#include <gio/gtask.h>
#include <gio/gcancellable.h>
#include <gio/gioerror.h>
#include <gio/gsocket.h>
#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>
*/
-G_DEFINE_TYPE (GSocketClient, g_socket_client, G_TYPE_OBJECT);
-
enum
{
EVENT,
PROP_TIMEOUT,
PROP_ENABLE_PROXY,
PROP_TLS,
- PROP_TLS_VALIDATION_FLAGS
+ PROP_TLS_VALIDATION_FLAGS,
+ PROP_PROXY_RESOLVER
};
struct _GSocketClientPrivate
GHashTable *app_proxies;
gboolean tls;
GTlsCertificateFlags tls_validation_flags;
+ GProxyResolver *proxy_resolver;
};
+G_DEFINE_TYPE_WITH_PRIVATE (GSocketClient, g_socket_client, G_TYPE_OBJECT)
+
static GSocket *
create_socket (GSocketClient *client,
GSocketAddress *dest_address,
static void
g_socket_client_init (GSocketClient *client)
{
- client->priv = G_TYPE_INSTANCE_GET_PRIVATE (client,
- G_TYPE_SOCKET_CLIENT,
- GSocketClientPrivate);
+ client->priv = g_socket_client_get_instance_private (client);
client->priv->type = G_SOCKET_TYPE_STREAM;
client->priv->app_proxies = g_hash_table_new_full (g_str_hash,
g_str_equal,
{
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);
+ G_OBJECT_CLASS (g_socket_client_parent_class)->finalize (object);
g_hash_table_unref (client->priv->app_proxies);
}
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);
}
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);
}
/**
* g_socket_client_set_local_address:
* @client: a #GSocketClient.
- * @address: a #GSocketAddress, or %NULL
+ * @address: (allow-none): a #GSocketAddress, or %NULL
*
* Sets the local address of the socket client.
* The sockets created by this object will bound to the
* #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
}
}
+/**
+ * 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)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
- g_type_class_add_private (class, sizeof (GSocketClientPrivate));
-
gobject_class->finalize = g_socket_client_finalize;
gobject_class->set_property = g_socket_client_set_property;
gobject_class->get_property = g_socket_client_get_property;
* information about a network connection in the UI. The meanings of
* the different @event values are as follows:
*
- * <variablelist>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_RESOLVING:</term>
- * <listitem><para>
- * @client is about to look up @connectable in DNS.
- * @connection will be %NULL.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_RESOLVED:</term>
- * <listitem><para>
- * @client has successfully resolved @connectable in DNS.
- * @connection will be %NULL.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_CONNECTING:</term>
- * <listitem><para>
- * @client is about to make a connection to a remote host;
- * either a proxy server or the destination server itself.
- * @connection is the #GSocketConnection, which is not yet
- * connected.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_CONNECTED:</term>
- * <listitem><para>
- * @client has successfully connected to a remote host.
- * @connection is the connected #GSocketConnection.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_PROXY_NEGOTIATING:</term>
- * <listitem><para>
- * @client is about to negotiate with a proxy to get it to
- * connect to @connectable. @connection is the
- * #GSocketConnection to the proxy server.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_PROXY_NEGOTIATED:</term>
- * <listitem><para>
- * @client has negotiated a connection to @connectable through
- * a proxy server. @connection is the stream returned from
- * g_proxy_connect(), which may or may not be a
- * #GSocketConnection.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_TLS_HANDSHAKING:</term>
- * <listitem><para>
- * @client is about to begin a TLS handshake. @connection is a
- * #GTlsClientConnection.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_TLS_HANDSHAKED:</term>
- * <listitem><para>
- * @client has successfully completed the TLS handshake.
- * @connection is a #GTlsClientConnection.
- * </para></listitem>
- * </varlistentry>
- * <varlistentry>
- * <term>%G_SOCKET_CLIENT_COMPLETE:</term>
- * <listitem><para>
- * @client has either successfully connected to @connectable
- * (in which case @connection is the #GSocketConnection that
- * it will be returning to the caller) or has failed (in which
- * case @connection is %NULL and the client is about to return
- * an error).
- * </para></listitem>
- * </varlistentry>
- * </variablelist>
+ * - %G_SOCKET_CLIENT_RESOLVING: @client is about to look up @connectable
+ * in DNS. @connection will be %NULL.
+ *
+ * - %G_SOCKET_CLIENT_RESOLVED: @client has successfully resolved
+ * @connectable in DNS. @connection will be %NULL.
+ *
+ * - %G_SOCKET_CLIENT_CONNECTING: @client is about to make a connection
+ * to a remote host; either a proxy server or the destination server
+ * itself. @connection is the #GSocketConnection, which is not yet
+ * connected. Since GLib 2.40, you can access the remote
+ * address via g_socket_connection_get_remote_address().
+ *
+ * - %G_SOCKET_CLIENT_CONNECTED: @client has successfully connected
+ * to a remote host. @connection is the connected #GSocketConnection.
+ *
+ * - %G_SOCKET_CLIENT_PROXY_NEGOTIATING: @client is about to negotiate
+ * with a proxy to get it to connect to @connectable. @connection is
+ * the #GSocketConnection to the proxy server.
+ *
+ * - %G_SOCKET_CLIENT_PROXY_NEGOTIATED: @client has negotiated a
+ * connection to @connectable through a proxy server. @connection is
+ * the stream returned from g_proxy_connect(), which may or may not
+ * be a #GSocketConnection.
+ *
+ * - %G_SOCKET_CLIENT_TLS_HANDSHAKING: @client is about to begin a TLS
+ * handshake. @connection is a #GTlsClientConnection.
+ *
+ * - %G_SOCKET_CLIENT_TLS_HANDSHAKED: @client has successfully completed
+ * the TLS handshake. @connection is a #GTlsClientConnection.
+ *
+ * - %G_SOCKET_CLIENT_COMPLETE: @client has either successfully connected
+ * to @connectable (in which case @connection is the #GSocketConnection
+ * that it will be returning to the caller) or has failed (in which
+ * case @connection is %NULL and the client is about to return an error).
*
* Each event except %G_SOCKET_CLIENT_COMPLETE may be emitted
* multiple times (or not at all) for a given connectable (in
* particular, if @client ends up attempting to connect to more than
- * one address). However, if @client emits the #GSocketClient:event
+ * one address). However, if @client emits the #GSocketClient::event
* signal at all for a given connectable, that it will always emit
* it with %G_SOCKET_CLIENT_COMPLETE when it is done.
*
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
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,
+ NULL);
+ }
+ }
else
enumerator = g_socket_connectable_enumerate (connectable);
}
connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
+ g_socket_connection_set_cached_remote_address ((GSocketConnection*)connection, address);
g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTING, connectable, connection);
if (g_socket_connection_connect (G_SOCKET_CONNECTION (connection),
address, cancellable, &last_error))
{
+ g_socket_connection_set_cached_remote_address ((GSocketConnection*)connection, NULL);
g_socket_client_emit_event (client, G_SOCKET_CLIENT_CONNECTED, connectable, connection);
}
else
g_set_error_literal (&last_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Trying to proxy over non-TCP connection is not supported."));
+ _("Proxying over a non-TCP connection is not supported."));
g_object_unref (connection);
connection = NULL;
typedef struct
{
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
+ GTask *task;
GSocketClient *client;
GSocketConnectable *connectable;
} GSocketClientAsyncConnectData;
static void
-g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
+g_socket_client_async_connect_data_free (GSocketClientAsyncConnectData *data)
{
- g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, data->connection);
+ g_clear_object (&data->connectable);
+ g_clear_object (&data->enumerator);
+ g_clear_object (&data->proxy_addr);
+ g_clear_object (&data->current_addr);
+ g_clear_object (&data->current_socket);
+ g_clear_object (&data->connection);
- if (data->last_error)
- {
- g_simple_async_result_take_error (data->result, data->last_error);
- }
- else
- {
- g_assert (data->connection);
+ g_clear_error (&data->last_error);
- if (!G_IS_SOCKET_CONNECTION (data->connection))
- {
- GSocketConnection *wrapper_connection;
+ g_slice_free (GSocketClientAsyncConnectData, data);
+}
- wrapper_connection = g_tcp_wrapper_connection_new (data->connection,
- data->current_socket);
- g_object_unref (data->connection);
- data->connection = (GIOStream *)wrapper_connection;
- }
+static void
+g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
+{
+ g_assert (data->connection);
+
+ if (!G_IS_SOCKET_CONNECTION (data->connection))
+ {
+ GSocketConnection *wrapper_connection;
- g_simple_async_result_set_op_res_gpointer (data->result,
- data->connection,
- g_object_unref);
+ wrapper_connection = g_tcp_wrapper_connection_new (data->connection,
+ data->current_socket);
+ g_object_unref (data->connection);
+ data->connection = (GIOStream *)wrapper_connection;
}
- g_simple_async_result_complete (data->result);
- g_object_unref (data->result);
- g_object_unref (data->connectable);
- g_object_unref (data->enumerator);
- if (data->cancellable)
- g_object_unref (data->cancellable);
- if (data->current_addr)
- g_object_unref (data->current_addr);
- if (data->current_socket)
- g_object_unref (data->current_socket);
- if (data->proxy_addr)
- g_object_unref (data->proxy_addr);
- g_slice_free (GSocketClientAsyncConnectData, data);
+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, data->connection);
+ g_task_return_pointer (data->task, data->connection, g_object_unref);
+ data->connection = NULL;
+ g_object_unref (data->task);
}
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_RESOLVING, data->connectable, NULL);
g_socket_address_enumerator_next_async (data->enumerator,
- data->cancellable,
+ g_task_get_cancellable (data->task),
g_socket_client_enumerator_callback,
data);
}
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_TLS_HANDSHAKING, data->connectable, G_IO_STREAM (tlsconn));
g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn),
G_PRIORITY_DEFAULT,
- data->cancellable,
+ g_task_get_cancellable (data->task),
g_socket_client_tls_handshake_callback,
data);
}
GProxy *proxy;
const gchar *protocol;
+ if (g_task_return_error_if_cancelled (data->task))
+ {
+ g_object_unref (data->task);
+ return;
+ }
+
if (!g_socket_connection_connect_finish (G_SOCKET_CONNECTION (source),
result, &error))
{
return;
}
+ g_socket_connection_set_cached_remote_address ((GSocketConnection*)data->connection, NULL);
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTED, data->connectable, data->connection);
/* wrong, but backward compatible */
g_set_error_literal (&data->last_error,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Trying to proxy over non-TCP connection is not supported."));
+ _("Proxying over a non-TCP connection is not supported."));
enumerator_next_async (data);
}
g_proxy_connect_async (proxy,
data->connection,
data->proxy_addr,
- data->cancellable,
+ g_task_get_cancellable (data->task),
g_socket_client_proxy_connect_callback,
data);
g_object_unref (proxy);
GSocketClientAsyncConnectData *data = user_data;
GSocketAddress *address = NULL;
GSocket *socket;
- GError *tmp_error = NULL;
+ GError *error = NULL;
- if (g_cancellable_is_cancelled (data->cancellable))
+ if (g_task_return_error_if_cancelled (data->task))
{
- g_clear_error (&data->last_error);
- g_cancellable_set_error_if_cancelled (data->cancellable, &data->last_error);
- g_socket_client_async_connect_complete (data);
+ g_object_unref (data->task);
return;
}
address = g_socket_address_enumerator_next_finish (data->enumerator,
- result, &tmp_error);
-
+ result, &error);
if (address == NULL)
{
- if (tmp_error)
- set_last_error (data, tmp_error);
- else if (data->last_error == NULL)
- g_set_error_literal (&data->last_error, G_IO_ERROR, G_IO_ERROR_FAILED,
- _("Unknown error on connect"));
-
- g_socket_client_async_connect_complete (data);
+ g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_COMPLETE, data->connectable, NULL);
+ if (!error)
+ {
+ if (data->last_error)
+ {
+ error = data->last_error;
+ data->last_error = NULL;
+ }
+ else
+ {
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unknown error on connect"));
+ }
+ }
+ g_task_return_error (data->task, error);
+ g_object_unref (data->task);
return;
}
data->current_addr = address;
data->connection = (GIOStream *) g_socket_connection_factory_create_connection (socket);
+ g_socket_connection_set_cached_remote_address ((GSocketConnection*)data->connection, address);
g_socket_client_emit_event (data->client, G_SOCKET_CLIENT_CONNECTING, data->connectable, data->connection);
g_socket_connection_connect_async (G_SOCKET_CONNECTION (data->connection),
- address, data->cancellable,
+ address,
+ g_task_get_cancellable (data->task),
g_socket_client_connected_callback, data);
}
g_return_if_fail (G_IS_SOCKET_CLIENT (client));
data = g_slice_new0 (GSocketClientAsyncConnectData);
-
- data->result = g_simple_async_result_new (G_OBJECT (client),
- callback, user_data,
- g_socket_client_connect_async);
data->client = client;
- if (cancellable)
- data->cancellable = g_object_ref (cancellable);
- else
- data->cancellable = NULL;
- data->last_error = NULL;
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,
+ NULL);
+ }
+ }
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);
enumerator_next_async (data);
}
&error);
if (connectable == NULL)
{
- g_simple_async_report_take_gerror_in_idle (G_OBJECT (client),
- callback, user_data, error);
+ g_task_report_error (client, callback, user_data,
+ g_socket_client_connect_to_host_async,
+ error);
}
else
{
connectable = g_network_address_parse_uri (uri, default_port, &error);
if (connectable == NULL)
{
- g_simple_async_report_take_gerror_in_idle (G_OBJECT (client),
- callback, user_data, error);
+ g_task_report_error (client, callback, user_data,
+ g_socket_client_connect_to_uri_async,
+ error);
}
else
{
GAsyncResult *result,
GError **error)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
-
- if (g_simple_async_result_propagate_error (simple, error))
- return NULL;
+ g_return_val_if_fail (g_task_is_valid (result, client), NULL);
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+ return g_task_propagate_pointer (G_TASK (result), error);
}
/**