X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgtcpconnection.c;h=8ff355d46b04b006b498e8327d4464b28a8757b0;hb=2e5bd8cf47f9e1559ccc44823a2f321b8ff8c1ea;hp=bd1c4110c63e50779376aa2d32509014804a33b1;hpb=ce8361217c1c9bd458eab55554a77d24210235cc;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gtcpconnection.c b/gio/gtcpconnection.c index bd1c411..8ff355d 100644 --- a/gio/gtcpconnection.c +++ b/gio/gtcpconnection.c @@ -11,57 +11,321 @@ */ /** - * SECTION: gtcpconnection + * SECTION:gtcpconnection * @title: GTcpConnection - * @short_description: a TCP #GSocketConnection + * @short_description: A TCP GSocketConnection + * @include: gio/gio.h * @see_also: #GSocketConnection. * * This is the subclass of #GSocketConnection that is created * for TCP/IP sockets. * - * It is currently empty; it offers no additional functionality - * over its base class. - * - * Eventually, some TCP-specific socket stuff will be added. - * * Since: 2.22 - **/ + */ #include "config.h" #include "gtcpconnection.h" +#include "gasyncresult.h" +#include "gtask.h" +#include "giostream.h" #include "glibintl.h" -#include "gioalias.h" +struct _GTcpConnectionPrivate +{ + guint graceful_disconnect : 1; +}; G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection, G_TYPE_SOCKET_CONNECTION, + G_ADD_PRIVATE (GTcpConnection) g_socket_connection_factory_register_type (g_define_type_id, G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, - 0); + G_SOCKET_PROTOCOL_DEFAULT); g_socket_connection_factory_register_type (g_define_type_id, G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, - 0); + G_SOCKET_PROTOCOL_DEFAULT); g_socket_connection_factory_register_type (g_define_type_id, G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, - g_socket_protocol_id_lookup_by_name ("tcp")); + G_SOCKET_PROTOCOL_TCP); g_socket_connection_factory_register_type (g_define_type_id, G_SOCKET_FAMILY_IPV6, G_SOCKET_TYPE_STREAM, - g_socket_protocol_id_lookup_by_name ("tcp")); + G_SOCKET_PROTOCOL_TCP); ); +static gboolean g_tcp_connection_close (GIOStream *stream, + GCancellable *cancellable, + GError **error); +static void g_tcp_connection_close_async (GIOStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + + +enum +{ + PROP_0, + PROP_GRACEFUL_DISCONNECT +}; + static void g_tcp_connection_init (GTcpConnection *connection) { + connection->priv = g_tcp_connection_get_instance_private (connection); + connection->priv->graceful_disconnect = FALSE; +} + +static void +g_tcp_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GTcpConnection *connection = G_TCP_CONNECTION (object); + + switch (prop_id) + { + case PROP_GRACEFUL_DISCONNECT: + g_value_set_boolean (value, connection->priv->graceful_disconnect); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +g_tcp_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GTcpConnection *connection = G_TCP_CONNECTION (object); + + switch (prop_id) + { + case PROP_GRACEFUL_DISCONNECT: + g_tcp_connection_set_graceful_disconnect (connection, + g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } } static void g_tcp_connection_class_init (GTcpConnectionClass *class) { + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class); + + gobject_class->set_property = g_tcp_connection_set_property; + gobject_class->get_property = g_tcp_connection_get_property; + + stream_class->close_fn = g_tcp_connection_close; + stream_class->close_async = g_tcp_connection_close_async; + + g_object_class_install_property (gobject_class, PROP_GRACEFUL_DISCONNECT, + g_param_spec_boolean ("graceful-disconnect", + P_("Graceful Disconnect"), + P_("Whether or not close does a graceful disconnect"), + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +} + +static gboolean +g_tcp_connection_close (GIOStream *stream, + GCancellable *cancellable, + GError **error) +{ + GTcpConnection *connection = G_TCP_CONNECTION (stream); + GSocket *socket; + char buffer[1024]; + gssize ret; + gboolean had_error; + + socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); + had_error = FALSE; + + if (connection->priv->graceful_disconnect && + !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */) + { + if (!g_socket_shutdown (socket, FALSE, TRUE, error)) + { + error = NULL; /* Ignore further errors */ + had_error = TRUE; + } + else + { + while (TRUE) + { + ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer), + TRUE, cancellable, error); + if (ret < 0) + { + had_error = TRUE; + error = NULL; + break; + } + if (ret == 0) + break; + } + } + } + + return G_IO_STREAM_CLASS (g_tcp_connection_parent_class) + ->close_fn (stream, cancellable, error) && !had_error; +} + +/* consumes @error */ +static void +async_close_finish (GTask *task, + GError *error) +{ + GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class); + GIOStream *stream = g_task_get_source_object (task); + GCancellable *cancellable = g_task_get_cancellable (task); + + /* Close underlying stream, ignoring further errors if we already + * have one. + */ + if (error) + parent->close_fn (stream, cancellable, NULL); + else + parent->close_fn (stream, cancellable, &error); + + if (error) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); } -#define __G_TCP_CONNECTION_C__ -#include "gioaliasdef.c" + +static gboolean +close_read_ready (GSocket *socket, + GIOCondition condition, + GTask *task) +{ + GError *error = NULL; + char buffer[1024]; + gssize ret; + + ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer), + FALSE, g_task_get_cancellable (task), + &error); + if (ret < 0) + { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + { + g_error_free (error); + return TRUE; + } + else + { + async_close_finish (task, error); + g_object_unref (task); + return FALSE; + } + } + + if (ret == 0) + { + async_close_finish (task, NULL); + return FALSE; + } + + return TRUE; +} + + +static void +g_tcp_connection_close_async (GIOStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTcpConnection *connection = G_TCP_CONNECTION (stream); + GSocket *socket; + GSource *source; + GError *error; + GTask *task; + + if (connection->priv->graceful_disconnect && + !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */) + { + task = g_task_new (stream, cancellable, callback, user_data); + g_task_set_priority (task, io_priority); + + socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream)); + + error = NULL; + if (!g_socket_shutdown (socket, FALSE, TRUE, &error)) + { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + source = g_socket_create_source (socket, G_IO_IN, cancellable); + g_task_attach_source (task, source, (GSourceFunc) close_read_ready); + g_source_unref (source); + + return; + } + + G_IO_STREAM_CLASS (g_tcp_connection_parent_class) + ->close_async (stream, io_priority, cancellable, callback, user_data); +} + +/** + * g_tcp_connection_set_graceful_disconnect: + * @connection: a #GTcpConnection + * @graceful_disconnect: Whether to do graceful disconnects or not + * + * This enables graceful disconnects on close. A graceful disconnect + * means that we signal the receiving end that the connection is terminated + * and wait for it to close the connection before closing the connection. + * + * A graceful disconnect means that we can be sure that we successfully sent + * all the outstanding data to the other end, or get an error reported. + * However, it also means we have to wait for all the data to reach the + * other side and for it to acknowledge this by closing the socket, which may + * take a while. For this reason it is disabled by default. + * + * Since: 2.22 + */ +void +g_tcp_connection_set_graceful_disconnect (GTcpConnection *connection, + gboolean graceful_disconnect) +{ + graceful_disconnect = !!graceful_disconnect; + if (graceful_disconnect != connection->priv->graceful_disconnect) + { + connection->priv->graceful_disconnect = graceful_disconnect; + g_object_notify (G_OBJECT (connection), "graceful-disconnect"); + } +} + +/** + * g_tcp_connection_get_graceful_disconnect: + * @connection: a #GTcpConnection + * + * Checks if graceful disconnects are used. See + * g_tcp_connection_set_graceful_disconnect(). + * + * Returns: %TRUE if graceful disconnect is used on close, %FALSE otherwise + * + * Since: 2.22 + */ +gboolean +g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection) +{ + return connection->priv->graceful_disconnect; +}