gtcpconnection.c 9.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/* 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.
 */

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

#include "config.h"
#include "gtcpconnection.h"
27 28 29
#include "gasyncresult.h"
#include "gsimpleasyncresult.h"
#include "giostream.h"
30 31 32 33 34 35 36 37
#include "glibintl.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,
38
					     G_SOCKET_PROTOCOL_DEFAULT);
39 40 41
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV6,
					     G_SOCKET_TYPE_STREAM,
42
					     G_SOCKET_PROTOCOL_DEFAULT);
43 44 45
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV4,
					     G_SOCKET_TYPE_STREAM,
46
					     G_SOCKET_PROTOCOL_TCP);
47 48 49
  g_socket_connection_factory_register_type (g_define_type_id,
					     G_SOCKET_FAMILY_IPV6,
					     G_SOCKET_TYPE_STREAM,
50
					     G_SOCKET_PROTOCOL_TCP);
51 52
			 );

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
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
};

74 75 76
static void
g_tcp_connection_init (GTcpConnection *connection)
{
77 78 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 119
  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);
    }
120 121 122 123 124
}

static void
g_tcp_connection_class_init (GTcpConnectionClass *class)
{
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
  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
146 147 148
g_tcp_connection_close (GIOStream     *stream,
			GCancellable  *cancellable,
			GError       **error)
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
{
  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)
	    {
	      my_error = NULL;
	      ret = g_socket_receive (socket,  buffer, sizeof (buffer),
174
				      cancellable, &my_error);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
	      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);
209 210
}

211
static void
212
async_close_finish (CloseAsyncData *data,
213
                    GError         *error /* consumed */,
214
                    gboolean        in_mainloop)
215 216 217 218 219 220 221 222 223 224 225
{
  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);
226
      g_simple_async_result_take_error (data->res, error);
227 228 229 230 231 232
    }
  else
    {
      my_error = NULL;
      parent->close_fn (stream, data->cancellable, &my_error);
      if (my_error)
233
        g_simple_async_result_take_error (data->res, my_error);
234 235 236 237 238 239 240 241 242
    }

  if (in_mainloop)
    g_simple_async_result_complete (data->res);
  else
    g_simple_async_result_complete_in_idle (data->res);
}

static gboolean
243 244
close_read_ready (GSocket        *socket,
		  GIOCondition    condition,
245 246 247 248 249 250
		  CloseAsyncData *data)
{
  GError *error = NULL;
  char buffer[1024];
  gssize ret;

251 252
  ret = g_socket_receive (socket,  buffer, sizeof (buffer),
			  data->cancellable, &error);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
  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);
	  return FALSE;
	}
    }

  if (ret == 0)
    {
      async_close_finish (data, NULL, TRUE);
      return FALSE;
    }

  return TRUE;
}


static void
275 276 277 278 279
g_tcp_connection_close_async (GIOStream           *stream,
			      int                  io_priority,
			      GCancellable        *cancellable,
			      GAsyncReadyCallback  callback,
			      gpointer             user_data)
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
{
  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);
	  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);
313
      g_source_attach (source, g_main_context_get_thread_default ());
314 315 316 317 318
      g_source_unref (source);

      return;
    }

319
  G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
320 321 322 323 324 325 326 327 328
    ->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
329
 * means that we signal the receiving end that the connection is terminated
330 331 332 333 334 335 336 337 338
 * 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
339
 */
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
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
362
 */
363 364 365 366 367
gboolean
g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
{
  return connection->priv->graceful_disconnect;
}