|
Packit |
ae235b |
/* GIO - GLib Input, Output and Streaming Library
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Copyright © 2008, 2009 Codethink Limited
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* This library is free software; you can redistribute it and/or
|
|
Packit |
ae235b |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
ae235b |
* License as published by the Free Software Foundation; either
|
|
Packit |
ae235b |
* version 2.1 of the License, or (at your option) any later version.
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* See the included COPYING file for more information.
|
|
Packit |
ae235b |
*/
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
/**
|
|
Packit |
ae235b |
* SECTION:gtcpconnection
|
|
Packit |
ae235b |
* @title: GTcpConnection
|
|
Packit |
ae235b |
* @short_description: A TCP GSocketConnection
|
|
Packit |
ae235b |
* @include: gio/gio.h
|
|
Packit |
ae235b |
* @see_also: #GSocketConnection.
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* This is the subclass of #GSocketConnection that is created
|
|
Packit |
ae235b |
* for TCP/IP sockets.
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Since: 2.22
|
|
Packit |
ae235b |
*/
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
#include "config.h"
|
|
Packit |
ae235b |
#include "gtcpconnection.h"
|
|
Packit |
ae235b |
#include "gasyncresult.h"
|
|
Packit |
ae235b |
#include "gtask.h"
|
|
Packit |
ae235b |
#include "giostream.h"
|
|
Packit |
ae235b |
#include "glibintl.h"
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
struct _GTcpConnectionPrivate
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
guint graceful_disconnect : 1;
|
|
Packit |
ae235b |
};
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection,
|
|
Packit |
ae235b |
G_TYPE_SOCKET_CONNECTION,
|
|
Packit |
ae235b |
G_ADD_PRIVATE (GTcpConnection)
|
|
Packit |
ae235b |
g_socket_connection_factory_register_type (g_define_type_id,
|
|
Packit |
ae235b |
G_SOCKET_FAMILY_IPV4,
|
|
Packit |
ae235b |
G_SOCKET_TYPE_STREAM,
|
|
Packit |
ae235b |
G_SOCKET_PROTOCOL_DEFAULT);
|
|
Packit |
ae235b |
g_socket_connection_factory_register_type (g_define_type_id,
|
|
Packit |
ae235b |
G_SOCKET_FAMILY_IPV6,
|
|
Packit |
ae235b |
G_SOCKET_TYPE_STREAM,
|
|
Packit |
ae235b |
G_SOCKET_PROTOCOL_DEFAULT);
|
|
Packit |
ae235b |
g_socket_connection_factory_register_type (g_define_type_id,
|
|
Packit |
ae235b |
G_SOCKET_FAMILY_IPV4,
|
|
Packit |
ae235b |
G_SOCKET_TYPE_STREAM,
|
|
Packit |
ae235b |
G_SOCKET_PROTOCOL_TCP);
|
|
Packit |
ae235b |
g_socket_connection_factory_register_type (g_define_type_id,
|
|
Packit |
ae235b |
G_SOCKET_FAMILY_IPV6,
|
|
Packit |
ae235b |
G_SOCKET_TYPE_STREAM,
|
|
Packit |
ae235b |
G_SOCKET_PROTOCOL_TCP);
|
|
Packit |
ae235b |
);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static gboolean g_tcp_connection_close (GIOStream *stream,
|
|
Packit |
ae235b |
GCancellable *cancellable,
|
|
Packit |
ae235b |
GError **error);
|
|
Packit |
ae235b |
static void g_tcp_connection_close_async (GIOStream *stream,
|
|
Packit |
ae235b |
int io_priority,
|
|
Packit |
ae235b |
GCancellable *cancellable,
|
|
Packit |
ae235b |
GAsyncReadyCallback callback,
|
|
Packit |
ae235b |
gpointer user_data);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
enum
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
PROP_0,
|
|
Packit |
ae235b |
PROP_GRACEFUL_DISCONNECT
|
|
Packit |
ae235b |
};
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
g_tcp_connection_init (GTcpConnection *connection)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
connection->priv = g_tcp_connection_get_instance_private (connection);
|
|
Packit |
ae235b |
connection->priv->graceful_disconnect = FALSE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
g_tcp_connection_get_property (GObject *object,
|
|
Packit |
ae235b |
guint prop_id,
|
|
Packit |
ae235b |
GValue *value,
|
|
Packit |
ae235b |
GParamSpec *pspec)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GTcpConnection *connection = G_TCP_CONNECTION (object);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
switch (prop_id)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
case PROP_GRACEFUL_DISCONNECT:
|
|
Packit |
ae235b |
g_value_set_boolean (value, connection->priv->graceful_disconnect);
|
|
Packit |
ae235b |
break;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
default:
|
|
Packit |
ae235b |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
g_tcp_connection_set_property (GObject *object,
|
|
Packit |
ae235b |
guint prop_id,
|
|
Packit |
ae235b |
const GValue *value,
|
|
Packit |
ae235b |
GParamSpec *pspec)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GTcpConnection *connection = G_TCP_CONNECTION (object);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
switch (prop_id)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
case PROP_GRACEFUL_DISCONNECT:
|
|
Packit |
ae235b |
g_tcp_connection_set_graceful_disconnect (connection,
|
|
Packit |
ae235b |
g_value_get_boolean (value));
|
|
Packit |
ae235b |
break;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
default:
|
|
Packit |
ae235b |
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
g_tcp_connection_class_init (GTcpConnectionClass *class)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
|
Packit |
ae235b |
GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
gobject_class->set_property = g_tcp_connection_set_property;
|
|
Packit |
ae235b |
gobject_class->get_property = g_tcp_connection_get_property;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
stream_class->close_fn = g_tcp_connection_close;
|
|
Packit |
ae235b |
stream_class->close_async = g_tcp_connection_close_async;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
g_object_class_install_property (gobject_class, PROP_GRACEFUL_DISCONNECT,
|
|
Packit |
ae235b |
g_param_spec_boolean ("graceful-disconnect",
|
|
Packit |
ae235b |
P_("Graceful Disconnect"),
|
|
Packit |
ae235b |
P_("Whether or not close does a graceful disconnect"),
|
|
Packit |
ae235b |
FALSE,
|
|
Packit |
ae235b |
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static gboolean
|
|
Packit |
ae235b |
g_tcp_connection_close (GIOStream *stream,
|
|
Packit |
ae235b |
GCancellable *cancellable,
|
|
Packit |
ae235b |
GError **error)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GTcpConnection *connection = G_TCP_CONNECTION (stream);
|
|
Packit |
ae235b |
GSocket *socket;
|
|
Packit |
ae235b |
char buffer[1024];
|
|
Packit |
ae235b |
gssize ret;
|
|
Packit |
ae235b |
gboolean had_error;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
|
|
Packit |
ae235b |
had_error = FALSE;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
if (connection->priv->graceful_disconnect &&
|
|
Packit |
ae235b |
!g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
if (!g_socket_shutdown (socket, FALSE, TRUE, error))
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
error = NULL; /* Ignore further errors */
|
|
Packit |
ae235b |
had_error = TRUE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
else
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
while (TRUE)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
|
|
Packit |
ae235b |
TRUE, cancellable, error);
|
|
Packit |
ae235b |
if (ret < 0)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
had_error = TRUE;
|
|
Packit |
ae235b |
error = NULL;
|
|
Packit |
ae235b |
break;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
if (ret == 0)
|
|
Packit |
ae235b |
break;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
|
|
Packit |
ae235b |
->close_fn (stream, cancellable, error) && !had_error;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
/* consumes @error */
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
async_close_finish (GTask *task,
|
|
Packit |
ae235b |
GError *error)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
|
|
Packit |
ae235b |
GIOStream *stream = g_task_get_source_object (task);
|
|
Packit |
ae235b |
GCancellable *cancellable = g_task_get_cancellable (task);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
/* Close underlying stream, ignoring further errors if we already
|
|
Packit |
ae235b |
* have one.
|
|
Packit |
ae235b |
*/
|
|
Packit |
ae235b |
if (error)
|
|
Packit |
ae235b |
parent->close_fn (stream, cancellable, NULL);
|
|
Packit |
ae235b |
else
|
|
Packit |
ae235b |
parent->close_fn (stream, cancellable, &error);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
if (error)
|
|
Packit |
ae235b |
g_task_return_error (task, error);
|
|
Packit |
ae235b |
else
|
|
Packit |
ae235b |
g_task_return_boolean (task, TRUE);
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static gboolean
|
|
Packit |
ae235b |
close_read_ready (GSocket *socket,
|
|
Packit |
ae235b |
GIOCondition condition,
|
|
Packit |
ae235b |
GTask *task)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GError *error = NULL;
|
|
Packit |
ae235b |
char buffer[1024];
|
|
Packit |
ae235b |
gssize ret;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
|
|
Packit |
ae235b |
FALSE, g_task_get_cancellable (task),
|
|
Packit |
ae235b |
&error);
|
|
Packit |
ae235b |
if (ret < 0)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
g_error_free (error);
|
|
Packit |
ae235b |
return TRUE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
else
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
async_close_finish (task, error);
|
|
Packit |
ae235b |
g_object_unref (task);
|
|
Packit |
ae235b |
return FALSE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
if (ret == 0)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
async_close_finish (task, NULL);
|
|
Packit |
ae235b |
return FALSE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
return TRUE;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
static void
|
|
Packit |
ae235b |
g_tcp_connection_close_async (GIOStream *stream,
|
|
Packit |
ae235b |
int io_priority,
|
|
Packit |
ae235b |
GCancellable *cancellable,
|
|
Packit |
ae235b |
GAsyncReadyCallback callback,
|
|
Packit |
ae235b |
gpointer user_data)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
GTcpConnection *connection = G_TCP_CONNECTION (stream);
|
|
Packit |
ae235b |
GSocket *socket;
|
|
Packit |
ae235b |
GSource *source;
|
|
Packit |
ae235b |
GError *error;
|
|
Packit |
ae235b |
GTask *task;
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
if (connection->priv->graceful_disconnect &&
|
|
Packit |
ae235b |
!g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
task = g_task_new (stream, cancellable, callback, user_data);
|
|
Packit |
ae235b |
g_task_set_source_tag (task, g_tcp_connection_close_async);
|
|
Packit |
ae235b |
g_task_set_priority (task, io_priority);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
error = NULL;
|
|
Packit |
ae235b |
if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
g_task_return_error (task, error);
|
|
Packit |
ae235b |
g_object_unref (task);
|
|
Packit |
ae235b |
return;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
source = g_socket_create_source (socket, G_IO_IN, cancellable);
|
|
Packit |
ae235b |
g_task_attach_source (task, source, (GSourceFunc) close_read_ready);
|
|
Packit |
ae235b |
g_source_unref (source);
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
return;
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
|
|
Packit |
ae235b |
->close_async (stream, io_priority, cancellable, callback, user_data);
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
/**
|
|
Packit |
ae235b |
* g_tcp_connection_set_graceful_disconnect:
|
|
Packit |
ae235b |
* @connection: a #GTcpConnection
|
|
Packit |
ae235b |
* @graceful_disconnect: Whether to do graceful disconnects or not
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* This enables graceful disconnects on close. A graceful disconnect
|
|
Packit |
ae235b |
* means that we signal the receiving end that the connection is terminated
|
|
Packit |
ae235b |
* and wait for it to close the connection before closing the connection.
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* A graceful disconnect means that we can be sure that we successfully sent
|
|
Packit |
ae235b |
* all the outstanding data to the other end, or get an error reported.
|
|
Packit |
ae235b |
* However, it also means we have to wait for all the data to reach the
|
|
Packit |
ae235b |
* other side and for it to acknowledge this by closing the socket, which may
|
|
Packit |
ae235b |
* take a while. For this reason it is disabled by default.
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Since: 2.22
|
|
Packit |
ae235b |
*/
|
|
Packit |
ae235b |
void
|
|
Packit |
ae235b |
g_tcp_connection_set_graceful_disconnect (GTcpConnection *connection,
|
|
Packit |
ae235b |
gboolean graceful_disconnect)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
graceful_disconnect = !!graceful_disconnect;
|
|
Packit |
ae235b |
if (graceful_disconnect != connection->priv->graceful_disconnect)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
connection->priv->graceful_disconnect = graceful_disconnect;
|
|
Packit |
ae235b |
g_object_notify (G_OBJECT (connection), "graceful-disconnect");
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
}
|
|
Packit |
ae235b |
|
|
Packit |
ae235b |
/**
|
|
Packit |
ae235b |
* g_tcp_connection_get_graceful_disconnect:
|
|
Packit |
ae235b |
* @connection: a #GTcpConnection
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Checks if graceful disconnects are used. See
|
|
Packit |
ae235b |
* g_tcp_connection_set_graceful_disconnect().
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Returns: %TRUE if graceful disconnect is used on close, %FALSE otherwise
|
|
Packit |
ae235b |
*
|
|
Packit |
ae235b |
* Since: 2.22
|
|
Packit |
ae235b |
*/
|
|
Packit |
ae235b |
gboolean
|
|
Packit |
ae235b |
g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
|
|
Packit |
ae235b |
{
|
|
Packit |
ae235b |
return connection->priv->graceful_disconnect;
|
|
Packit |
ae235b |
}
|