gtcpconnection.c 8.96 KB
Newer Older
1 2 3 4
/* GIO - GLib Input, Output and Streaming Library
 *
 * Copyright © 2008, 2009 Codethink Limited
 *
5 6 7 8
 * This library 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.1 of the License, or (at your option) any later version.
9 10 11 12 13
 *
 * See the included COPYING file for more information.
 */

/**
14
 * SECTION:gtcpconnection
15
 * @title: GTcpConnection
Matthias Clasen's avatar
Matthias Clasen committed
16
 * @short_description: A TCP GSocketConnection
17
 * @include: gio/gio.h
18 19 20 21 22 23
 * @see_also: #GSocketConnection.
 *
 * This is the subclass of #GSocketConnection that is created
 * for TCP/IP sockets.
 *
 * Since: 2.22
24
 */
25 26 27

#include "config.h"
#include "gtcpconnection.h"
28
#include "gasyncresult.h"
29
#include "gtask.h"
30
#include "giostream.h"
31 32
#include "glibintl.h"

33 34 35 36
struct _GTcpConnectionPrivate
{
  guint graceful_disconnect : 1;
};
37 38 39

G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection,
			 G_TYPE_SOCKET_CONNECTION,
40
                         G_ADD_PRIVATE (GTcpConnection)
41 42 43
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV4,
					     G_SOCKET_TYPE_STREAM,
44
					     G_SOCKET_PROTOCOL_DEFAULT);
45 46 47
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV6,
					     G_SOCKET_TYPE_STREAM,
48
					     G_SOCKET_PROTOCOL_DEFAULT);
49 50 51
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV4,
					     G_SOCKET_TYPE_STREAM,
52
					     G_SOCKET_PROTOCOL_TCP);
53 54 55
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV6,
					     G_SOCKET_TYPE_STREAM,
56
					     G_SOCKET_PROTOCOL_TCP);
57 58
			 );

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
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
};

75 76 77
static void
g_tcp_connection_init (GTcpConnection *connection)
{
78
  connection->priv = g_tcp_connection_get_instance_private (connection);
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
  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);
    }
119 120 121 122 123
}

static void
g_tcp_connection_class_init (GTcpConnectionClass *class)
{
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
  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
143 144 145
g_tcp_connection_close (GIOStream     *stream,
			GCancellable  *cancellable,
			GError       **error)
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
{
  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)
	    {
168 169
	      ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
						    TRUE, cancellable, error);
170 171
	      if (ret < 0)
		{
172 173 174
		  had_error = TRUE;
		  error = NULL;
		  break;
175 176 177 178 179 180 181 182 183 184 185
		}
	      if (ret == 0)
		break;
	    }
	}
    }

  return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
    ->close_fn (stream, cancellable, error) && !had_error;
}

186
/* consumes @error */
187
static void
188 189
async_close_finish (GTask    *task,
                    GError   *error)
190 191
{
  GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
192 193
  GIOStream *stream = g_task_get_source_object (task);
  GCancellable *cancellable = g_task_get_cancellable (task);
194

195 196 197
  /* Close underlying stream, ignoring further errors if we already
   * have one.
   */
198
  if (error)
199
    parent->close_fn (stream, cancellable, NULL);
200
  else
201
    parent->close_fn (stream, cancellable, &error);
202

203 204
  if (error)
    g_task_return_error (task, error);
205
  else
206
    g_task_return_boolean (task, TRUE);
207 208
}

209

210
static gboolean
211 212
close_read_ready (GSocket        *socket,
		  GIOCondition    condition,
213
		  GTask          *task)
214 215 216 217 218
{
  GError *error = NULL;
  char buffer[1024];
  gssize ret;

219 220 221
  ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
                                        FALSE, g_task_get_cancellable (task),
                                        &error);
222 223 224
  if (ret < 0)
    {
      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
225 226 227 228
	{
	  g_error_free (error);
	  return TRUE;
	}
229 230
      else
	{
231 232
	  async_close_finish (task, error);
	  g_object_unref (task);
233 234 235 236 237 238
	  return FALSE;
	}
    }

  if (ret == 0)
    {
239
      async_close_finish (task, NULL);
240 241 242 243 244 245 246 247
      return FALSE;
    }

  return TRUE;
}


static void
248 249 250 251 252
g_tcp_connection_close_async (GIOStream           *stream,
			      int                  io_priority,
			      GCancellable        *cancellable,
			      GAsyncReadyCallback  callback,
			      gpointer             user_data)
253 254 255 256 257
{
  GTcpConnection *connection = G_TCP_CONNECTION (stream);
  GSocket *socket;
  GSource *source;
  GError *error;
258
  GTask *task;
259 260 261 262

  if (connection->priv->graceful_disconnect &&
      !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
    {
263
      task = g_task_new (stream, cancellable, callback, user_data);
264
      g_task_set_source_tag (task, g_tcp_connection_close_async);
265
      g_task_set_priority (task, io_priority);
266 267 268 269 270 271

      socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));

      error = NULL;
      if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
	{
272 273
	  g_task_return_error (task, error);
	  g_object_unref (task);
274 275 276 277
	  return;
	}

      source = g_socket_create_source (socket, G_IO_IN, cancellable);
278
      g_task_attach_source (task, source, (GSourceFunc) close_read_ready);
279 280 281 282 283
      g_source_unref (source);

      return;
    }

284
  G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
285 286 287 288 289 290 291 292
    ->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
 *
293
 * This enables graceful disconnects on close. A graceful disconnect
294
 * means that we signal the receiving end that the connection is terminated
295 296 297 298 299 300 301 302 303
 * 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
304
 */
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
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
327
 */
328 329 330 331 332
gboolean
g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
{
  return connection->priv->graceful_disconnect;
}