mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-13 15:56:23 +01:00
5cd86fbda6
The whole protocol name thing is pretty weird. The getprotobyname functions seem to only specify one mapping for name <-> ids, so all families/types must use the same values. Plus the values used for the protocols are standardized by IANA, so are always the same. So, we drop using names for protocols, intead introducing an enum with a few commonly availible and used protocols.
399 lines
10 KiB
C
399 lines
10 KiB
C
/* GIO - GLib Input, Output and Streaming Library
|
|
*
|
|
* Copyright © 2008, 2009 Codethink Limited
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the licence or (at
|
|
* your option) any later version.
|
|
*
|
|
* See the included COPYING file for more information.
|
|
*/
|
|
|
|
/**
|
|
* SECTION: gtcpconnection
|
|
* @title: GTcpConnection
|
|
* @short_description: a TCP #GSocketConnection
|
|
* @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 "gsimpleasyncresult.h"
|
|
#include "giostream.h"
|
|
#include "glibintl.h"
|
|
|
|
#include "gioalias.h"
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection,
|
|
G_TYPE_SOCKET_CONNECTION,
|
|
g_socket_connection_factory_register_type (g_define_type_id,
|
|
G_SOCKET_FAMILY_IPV4,
|
|
G_SOCKET_TYPE_STREAM,
|
|
G_SOCKET_PROTOCOL_DEFAULT);
|
|
g_socket_connection_factory_register_type (g_define_type_id,
|
|
G_SOCKET_FAMILY_IPV6,
|
|
G_SOCKET_TYPE_STREAM,
|
|
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_TCP);
|
|
g_socket_connection_factory_register_type (g_define_type_id,
|
|
G_SOCKET_FAMILY_IPV6,
|
|
G_SOCKET_TYPE_STREAM,
|
|
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);
|
|
|
|
struct _GTcpConnectionPrivate
|
|
{
|
|
guint graceful_disconnect : 1;
|
|
};
|
|
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_GRACEFUL_DISCONNECT
|
|
};
|
|
|
|
static void
|
|
g_tcp_connection_init (GTcpConnection *connection)
|
|
{
|
|
connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection,
|
|
G_TYPE_TCP_CONNECTION,
|
|
GTcpConnectionPrivate);
|
|
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);
|
|
|
|
g_type_class_add_private (class, sizeof (GTcpConnectionPrivate));
|
|
|
|
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;
|
|
GError *my_error;
|
|
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)
|
|
{
|
|
if (!g_socket_condition_wait (socket,
|
|
G_IO_IN, cancellable, error))
|
|
{
|
|
had_error = TRUE;
|
|
error = NULL;
|
|
break;
|
|
}
|
|
|
|
my_error = NULL;
|
|
ret = g_socket_receive (socket, buffer, sizeof (buffer),
|
|
&my_error);
|
|
if (ret < 0)
|
|
{
|
|
if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
|
g_error_free (my_error);
|
|
else
|
|
{
|
|
had_error = TRUE;
|
|
g_propagate_error (error, my_error);
|
|
error = NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (ret == 0)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
|
|
->close_fn (stream, cancellable, error) && !had_error;
|
|
}
|
|
|
|
typedef struct {
|
|
GSimpleAsyncResult *res;
|
|
GCancellable *cancellable;
|
|
} CloseAsyncData;
|
|
|
|
static void
|
|
close_async_data_free (CloseAsyncData *data)
|
|
{
|
|
g_object_unref (data->res);
|
|
if (data->cancellable)
|
|
g_object_unref (data->cancellable);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
async_close_finish (CloseAsyncData *data, GError *error, gboolean in_mainloop)
|
|
{
|
|
GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
|
|
GIOStream *stream;
|
|
GError *my_error;
|
|
|
|
stream = G_IO_STREAM (g_async_result_get_source_object (G_ASYNC_RESULT (data->res)));
|
|
|
|
/* Doesn't block, ignore error */
|
|
if (error)
|
|
{
|
|
parent->close_fn (stream, data->cancellable, NULL);
|
|
g_simple_async_result_set_from_error (data->res, error);
|
|
}
|
|
else
|
|
{
|
|
my_error = NULL;
|
|
parent->close_fn (stream, data->cancellable, &my_error);
|
|
if (my_error)
|
|
{
|
|
g_simple_async_result_set_from_error (data->res, my_error);
|
|
g_error_free (my_error);
|
|
}
|
|
}
|
|
|
|
if (in_mainloop)
|
|
g_simple_async_result_complete (data->res);
|
|
else
|
|
g_simple_async_result_complete_in_idle (data->res);
|
|
}
|
|
|
|
static gboolean
|
|
close_read_ready (GSocket *socket,
|
|
GIOCondition condition,
|
|
CloseAsyncData *data)
|
|
{
|
|
GError *error = NULL;
|
|
char buffer[1024];
|
|
gssize ret;
|
|
|
|
if (g_cancellable_set_error_if_cancelled (data->cancellable,
|
|
&error))
|
|
{
|
|
async_close_finish (data, error, TRUE);
|
|
g_error_free (error);
|
|
return FALSE;
|
|
}
|
|
|
|
ret = g_socket_receive (socket, buffer, sizeof (buffer), &error);
|
|
if (ret < 0)
|
|
{
|
|
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
|
|
g_error_free (error);
|
|
else
|
|
{
|
|
async_close_finish (data, error, TRUE);
|
|
g_error_free (error);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (ret == 0)
|
|
{
|
|
async_close_finish (data, NULL, TRUE);
|
|
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);
|
|
CloseAsyncData *data;
|
|
GSocket *socket;
|
|
GSource *source;
|
|
GError *error;
|
|
|
|
if (connection->priv->graceful_disconnect &&
|
|
!g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
|
|
{
|
|
data = g_new (CloseAsyncData, 1);
|
|
data->res =
|
|
g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
|
|
g_tcp_connection_close_async);
|
|
if (cancellable)
|
|
data->cancellable = g_object_ref (cancellable);
|
|
else
|
|
data->cancellable = NULL;
|
|
|
|
socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
|
|
|
|
error = NULL;
|
|
if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
|
|
{
|
|
async_close_finish (data, error, FALSE);
|
|
g_error_free (error);
|
|
close_async_data_free (data);
|
|
return;
|
|
}
|
|
|
|
source = g_socket_create_source (socket, G_IO_IN, cancellable);
|
|
g_source_set_callback (source,
|
|
(GSourceFunc) close_read_ready,
|
|
data, (GDestroyNotify)close_async_data_free);
|
|
g_source_attach (source, NULL);
|
|
g_source_unref (source);
|
|
|
|
return;
|
|
}
|
|
|
|
out:
|
|
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 enabled graceful disconnects on close. A graceful disconnect
|
|
* means that we signal the recieving 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;
|
|
}
|
|
|
|
|
|
#define __G_TCP_CONNECTION_C__
|
|
#include "gioaliasdef.c"
|