Blame gio/gtcpconnection.c

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
}