diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 1cea4f31a..31d3bb2b8 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1885,6 +1885,11 @@ g_socket_client_get_type gsocketconnection GSocketConnection GSocketConnection +g_socket_connection_connect +g_socket_connection_connect_async +g_socket_connection_connect_finish + +g_socket_connection_is_connected g_socket_connection_get_local_address g_socket_connection_get_remote_address g_socket_connection_get_socket diff --git a/gio/gio.symbols b/gio/gio.symbols index 3ea45b7aa..a23f8ad78 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1022,6 +1022,10 @@ g_socket_connection_factory_register_type g_socket_connection_get_local_address g_socket_connection_get_remote_address g_socket_connection_get_socket +g_socket_connection_is_connected +g_socket_connection_connect +g_socket_connection_connect_async +g_socket_connection_connect_finish g_socket_listener_get_type g_socket_listener_accept g_socket_listener_accept_async diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c index da534e72c..88f2b36d2 100644 --- a/gio/gsocketclient.c +++ b/gio/gsocketclient.c @@ -845,10 +845,14 @@ g_socket_client_connect (GSocketClient *client, continue; } - if (g_socket_connect (socket, address, cancellable, &last_error)) - connection = (GIOStream *)g_socket_connection_factory_create_connection (socket); - else - clarify_connect_error (last_error, connectable, address); + connection = (GIOStream *)g_socket_connection_factory_create_connection (socket); + if (!g_socket_connection_connect (G_SOCKET_CONNECTION (connection), + address, cancellable, &last_error)) + { + clarify_connect_error (last_error, connectable, address); + g_object_unref (connection); + connection = NULL; + } if (connection && G_IS_PROXY_ADDRESS (address) && @@ -1264,11 +1268,30 @@ g_socket_client_proxy_connect_callback (GObject *object, } static void -g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) +g_socket_client_connected_callback (GObject *source, + GAsyncResult *result, + gpointer user_data) { + GSocketClientAsyncConnectData *data = user_data; + GError *error = NULL; GProxy *proxy; const gchar *protocol; + if (!g_socket_connection_connect_finish (G_SOCKET_CONNECTION (source), + result, &error)) + { + clarify_connect_error (error, data->connectable, + data->current_addr); + set_last_error (data, error); + + /* try next one */ + enumerator_next_async (data); + return; + } + + /* wrong, but backward compatible */ + g_socket_set_blocking (data->current_socket, TRUE); + if (!data->proxy_addr) { g_socket_client_tls_handshake (data); @@ -1278,7 +1301,7 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) protocol = g_proxy_address_get_protocol (data->proxy_addr); proxy = g_proxy_get_default_for_protocol (protocol); - /* The connection should not be anything else then TCP Connection, + /* The connection should not be anything other than TCP, * but let's put a safety guard in case */ if (!G_IS_TCP_CONNECTION (data->connection)) @@ -1321,54 +1344,6 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data) } } -static void -g_socket_client_socket_connected (GSocketClientAsyncConnectData *data) -{ - g_socket_set_blocking (data->current_socket, TRUE); - - data->connection = (GIOStream *) - g_socket_connection_factory_create_connection (data->current_socket); - - g_socket_client_proxy_connect (data); -} - -static gboolean -g_socket_client_socket_callback (GSocket *socket, - GIOCondition condition, - GSocketClientAsyncConnectData *data) -{ - GError *error = NULL; - - if (g_cancellable_is_cancelled (data->cancellable)) - { - /* Cancelled, return done with last error being cancelled */ - g_clear_error (&data->last_error); - g_cancellable_set_error_if_cancelled (data->cancellable, - &data->last_error); - - g_socket_client_async_connect_complete (data); - return FALSE; - } - else - { - /* socket is ready for writing means connect done, did it succeed? */ - if (!g_socket_check_connect_result (data->current_socket, &error)) - { - clarify_connect_error (error, data->connectable, - data->current_addr); - set_last_error (data, error); - - /* try next one */ - enumerator_next_async (data); - - return FALSE; - } - } - - g_socket_client_socket_connected (data); - return FALSE; -} - static void g_socket_client_enumerator_callback (GObject *object, GAsyncResult *result, @@ -1409,44 +1384,20 @@ g_socket_client_enumerator_callback (GObject *object, g_clear_error (&data->last_error); socket = create_socket (data->client, address, &data->last_error); - if (socket != NULL) + if (socket == NULL) { - g_socket_set_blocking (socket, FALSE); - if (g_socket_connect (socket, address, data->cancellable, &tmp_error)) - { - data->current_socket = socket; - g_socket_client_socket_connected (data); - - g_object_unref (address); - return; - } - else if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_PENDING)) - { - GSource *source; - - data->current_socket = socket; - data->current_addr = address; - g_error_free (tmp_error); - - source = g_socket_create_source (socket, G_IO_OUT, - data->cancellable); - g_source_set_callback (source, - (GSourceFunc) g_socket_client_socket_callback, - data, NULL); - g_source_attach (source, g_main_context_get_thread_default ()); - g_source_unref (source); - return; - } - else - { - clarify_connect_error (tmp_error, data->connectable, address); - data->last_error = tmp_error; - g_object_unref (socket); - } + g_object_unref (address); + enumerator_next_async (data); + return; } - g_object_unref (address); - enumerator_next_async (data); + data->current_socket = socket; + data->current_addr = address; + data->connection = (GIOStream *) g_socket_connection_factory_create_connection (socket); + + g_socket_connection_connect_async (G_SOCKET_CONNECTION (data->connection), + address, data->cancellable, + g_socket_client_connected_callback, data); } /** diff --git a/gio/gsocketconnection.c b/gio/gsocketconnection.c index 6f2f102a0..ee391ab18 100644 --- a/gio/gsocketconnection.c +++ b/gio/gsocketconnection.c @@ -113,6 +113,165 @@ g_socket_connection_get_output_stream (GIOStream *io_stream) return connection->priv->output_stream; } +/** + * g_socket_connection_is_connected: + * @connection: a #GSocketConnection + * + * Checks if @connection is connected. This is equivalent to calling + * g_socket_is_connected() on @connection's underlying #GSocket. + * + * Returns: whether @connection is connected + * + * Since: 2.32 + */ +gboolean +g_socket_connection_is_connected (GSocketConnection *connection) +{ + return g_socket_is_connected (connection->priv->socket); +} + +/** + * g_socket_connection_connect: + * @connection: a #GSocketConnection + * @address: a #GSocketAddress specifying the remote address. + * @cancellable: (allow-none): a %GCancellable or %NULL + * @error: #GError for error reporting, or %NULL to ignore. + * + * Connect @connection to the specified remote address. + * + * Returns: %TRUE if the connection succeeded, %FALSE on error + * + * Since: 2.32 + */ +gboolean +g_socket_connection_connect (GSocketConnection *connection, + GSocketAddress *address, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (G_IS_SOCKET_CONNECTION (connection), FALSE); + g_return_val_if_fail (G_IS_SOCKET_ADDRESS (address), FALSE); + + return g_socket_connect (connection->priv->socket, address, + cancellable, error); +} + +static gboolean g_socket_connection_connect_callback (GSocket *socket, + GIOCondition condition, + gpointer user_data); + +/** + * g_socket_connection_connect_async: + * @connection: a #GSocketConnection + * @address: a #GSocketAddress specifying the remote address. + * @cancellable: (allow-none): a %GCancellable or %NULL + * @callback: (scope async): a #GAsyncReadyCallback + * @user_data: (closure): user data for the callback + * + * Asynchronously connect @connection to the specified remote address. + * + * This clears the #GSocket:blocking flag on @connection's underlying + * socket if it is currently set. + * + * Use g_socket_connection_connect_finish() to retrieve the result. + * + * Since: 2.32 + */ +void +g_socket_connection_connect_async (GSocketConnection *connection, + GSocketAddress *address, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + GError *tmp_error = NULL; + + g_return_if_fail (G_IS_SOCKET_CONNECTION (connection)); + g_return_if_fail (G_IS_SOCKET_ADDRESS (address)); + + simple = g_simple_async_result_new (G_OBJECT (connection), + callback, user_data, + g_socket_connection_connect_async); + + g_socket_set_blocking (connection->priv->socket, FALSE); + + if (g_socket_connect (connection->priv->socket, address, + cancellable, &tmp_error)) + { + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete_in_idle (simple); + } + else if (g_error_matches (tmp_error, G_IO_ERROR, G_IO_ERROR_PENDING)) + { + GSource *source; + + g_error_free (tmp_error); + source = g_socket_create_source (connection->priv->socket, + G_IO_OUT, cancellable); + g_source_set_callback (source, + (GSourceFunc) g_socket_connection_connect_callback, + simple, NULL); + g_source_attach (source, g_main_context_get_thread_default ()); + g_source_unref (source); + } + else + { + g_simple_async_result_take_error (simple, tmp_error); + g_simple_async_result_complete_in_idle (simple); + } +} + +static gboolean +g_socket_connection_connect_callback (GSocket *socket, + GIOCondition condition, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + GSocketConnection *connection; + GError *error = NULL; + + connection = G_SOCKET_CONNECTION (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (connection); + + if (g_socket_check_connect_result (connection->priv->socket, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + g_object_unref (simple); + return FALSE; +} + +/** + * g_socket_connection_connect_finish: + * @connection: a #GSocketConnection + * @result: the #GAsyncResult + * @error: #GError for error reporting, or %NULL to ignore. + * + * Gets the result of a g_socket_connection_connect_async() call. + * + * Returns: %TRUE if the connection succeeded, %FALSE on error + * + * Since: 2.32 + */ +gboolean +g_socket_connection_connect_finish (GSocketConnection *connection, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail (G_IS_SOCKET_CONNECTION (connection), FALSE); + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (connection), g_socket_connection_connect_async), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + return TRUE; +} + /** * g_socket_connection_get_socket: * @connection: a #GSocketConnection diff --git a/gio/gsocketconnection.h b/gio/gsocketconnection.h index 1edc2f44c..34a0cb184 100644 --- a/gio/gsocketconnection.h +++ b/gio/gsocketconnection.h @@ -72,11 +72,26 @@ struct _GSocketConnection GType g_socket_connection_get_type (void) G_GNUC_CONST; +gboolean g_socket_connection_is_connected (GSocketConnection *connection); +gboolean g_socket_connection_connect (GSocketConnection *connection, + GSocketAddress *address, + GCancellable *cancellable, + GError **error); +void g_socket_connection_connect_async (GSocketConnection *connection, + GSocketAddress *address, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean g_socket_connection_connect_finish (GSocketConnection *connection, + GAsyncResult *result, + GError **error); + GSocket *g_socket_connection_get_socket (GSocketConnection *connection); GSocketAddress *g_socket_connection_get_local_address (GSocketConnection *connection, GError **error); GSocketAddress *g_socket_connection_get_remote_address (GSocketConnection *connection, GError **error); + void g_socket_connection_factory_register_type (GType g_type, GSocketFamily family, GSocketType type,