Add support for graceful disconnect to GTcpConnection
[platform/upstream/glib.git] / gio / gtcpconnection.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2008, 2009 Codethink Limited
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published
7  * by the Free Software Foundation; either version 2 of the licence or (at
8  * your option) any later version.
9  *
10  * See the included COPYING file for more information.
11  */
12
13 /**
14  * SECTION: gtcpconnection
15  * @title: GTcpConnection
16  * @short_description: a TCP #GSocketConnection
17  * @see_also: #GSocketConnection.
18  *
19  * This is the subclass of #GSocketConnection that is created
20  * for TCP/IP sockets.
21  *
22  * It is currently empty; it offers no additional functionality
23  * over its base class.
24  *
25  * Eventually, some TCP-specific socket stuff will be added.
26  *
27  * Since: 2.22
28  **/
29
30 #include "config.h"
31 #include "gtcpconnection.h"
32 #include "gasyncresult.h"
33 #include "gsimpleasyncresult.h"
34 #include "giostream.h"
35 #include "glibintl.h"
36
37 #include "gioalias.h"
38
39 G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection,
40                          G_TYPE_SOCKET_CONNECTION,
41   g_socket_connection_factory_register_type (g_define_type_id,
42                                              G_SOCKET_FAMILY_IPV4,
43                                              G_SOCKET_TYPE_STREAM,
44                                              0);
45   g_socket_connection_factory_register_type (g_define_type_id,
46                                              G_SOCKET_FAMILY_IPV6,
47                                              G_SOCKET_TYPE_STREAM,
48                                              0);
49   g_socket_connection_factory_register_type (g_define_type_id,
50                                              G_SOCKET_FAMILY_IPV4,
51                                              G_SOCKET_TYPE_STREAM,
52                                              g_socket_protocol_id_lookup_by_name ("tcp"));
53   g_socket_connection_factory_register_type (g_define_type_id,
54                                              G_SOCKET_FAMILY_IPV6,
55                                              G_SOCKET_TYPE_STREAM,
56                                              g_socket_protocol_id_lookup_by_name ("tcp"));
57                          );
58
59 static gboolean g_tcp_connection_close       (GIOStream            *stream,
60                                               GCancellable         *cancellable,
61                                               GError              **error);
62 static void     g_tcp_connection_close_async (GIOStream            *stream,
63                                               int                   io_priority,
64                                               GCancellable         *cancellable,
65                                               GAsyncReadyCallback   callback,
66                                               gpointer              user_data);
67
68 struct _GTcpConnectionPrivate
69 {
70   guint graceful_disconnect : 1;
71 };
72
73
74 enum
75 {
76   PROP_0,
77   PROP_GRACEFUL_DISCONNECT
78 };
79
80 static void
81 g_tcp_connection_init (GTcpConnection *connection)
82 {
83   connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection,
84                                                   G_TYPE_TCP_CONNECTION,
85                                                   GTcpConnectionPrivate);
86   connection->priv->graceful_disconnect = FALSE;
87 }
88
89 static void
90 g_tcp_connection_get_property (GObject    *object,
91                                guint       prop_id,
92                                GValue     *value,
93                                GParamSpec *pspec)
94 {
95   GTcpConnection *connection = G_TCP_CONNECTION (object);
96
97   switch (prop_id)
98     {
99       case PROP_GRACEFUL_DISCONNECT:
100         g_value_set_boolean (value, connection->priv->graceful_disconnect);
101         break;
102
103       default:
104         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
105     }
106 }
107
108 static void
109 g_tcp_connection_set_property (GObject      *object,
110                                guint         prop_id,
111                                const GValue *value,
112                                GParamSpec   *pspec)
113 {
114   GTcpConnection *connection = G_TCP_CONNECTION (object);
115
116   switch (prop_id)
117     {
118       case PROP_GRACEFUL_DISCONNECT:
119         g_tcp_connection_set_graceful_disconnect (connection,
120                                                   g_value_get_boolean (value));
121         break;
122
123       default:
124         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
125     }
126 }
127
128 static void
129 g_tcp_connection_class_init (GTcpConnectionClass *class)
130 {
131   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
132   GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class);
133
134   g_type_class_add_private (class, sizeof (GTcpConnectionPrivate));
135
136   gobject_class->set_property = g_tcp_connection_set_property;
137   gobject_class->get_property = g_tcp_connection_get_property;
138
139   stream_class->close_fn = g_tcp_connection_close;
140   stream_class->close_async = g_tcp_connection_close_async;
141
142   g_object_class_install_property (gobject_class, PROP_GRACEFUL_DISCONNECT,
143                                    g_param_spec_boolean ("graceful-disconnect",
144                                                          P_("Graceful Disconnect"),
145                                                          P_("Whether or not close does a graceful disconnect"),
146                                                          FALSE,
147                                                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
148
149 }
150
151 static gboolean
152 g_tcp_connection_close (GIOStream            *stream,
153                         GCancellable         *cancellable,
154                         GError              **error)
155 {
156   GTcpConnection *connection = G_TCP_CONNECTION (stream);
157   GSocket *socket;
158   char buffer[1024];
159   gssize ret;
160   GError *my_error;
161   gboolean had_error;
162
163   socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
164   had_error = FALSE;
165
166   if (connection->priv->graceful_disconnect &&
167       !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
168     {
169       if (!g_socket_shutdown (socket, FALSE, TRUE, error))
170         {
171           error = NULL; /* Ignore further errors */
172           had_error = TRUE;
173         }
174       else
175         {
176           while (TRUE)
177             {
178               if (!g_socket_condition_wait (socket,
179                                             G_IO_IN, cancellable, error))
180                 {
181                   had_error = TRUE;
182                   error = NULL;
183                   break;
184                 }
185
186               my_error = NULL;
187               ret = g_socket_receive (socket,  buffer, sizeof (buffer),
188                                       &my_error);
189               if (ret < 0)
190                 {
191                   if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
192                     g_error_free (my_error);
193                   else
194                     {
195                       had_error = TRUE;
196                       g_propagate_error (error, my_error);
197                       error = NULL;
198                       break;
199                     }
200                 }
201               if (ret == 0)
202                 break;
203             }
204         }
205     }
206
207   return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
208     ->close_fn (stream, cancellable, error) && !had_error;
209 }
210
211 typedef struct {
212   GSimpleAsyncResult *res;
213   GCancellable *cancellable;
214 } CloseAsyncData;
215
216 static void
217 close_async_data_free (CloseAsyncData *data)
218 {
219   g_object_unref (data->res);
220   if (data->cancellable)
221     g_object_unref (data->cancellable);
222   g_free (data);
223 }
224
225 static void
226 async_close_finish (CloseAsyncData *data, GError *error, gboolean in_mainloop)
227 {
228   GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
229   GIOStream *stream;
230   GError *my_error;
231
232   stream = G_IO_STREAM (g_async_result_get_source_object (G_ASYNC_RESULT (data->res)));
233
234   /* Doesn't block, ignore error */
235   if (error)
236     {
237       parent->close_fn (stream, data->cancellable, NULL);
238       g_simple_async_result_set_from_error (data->res, error);
239     }
240   else
241     {
242       my_error = NULL;
243       parent->close_fn (stream, data->cancellable, &my_error);
244       if (my_error)
245         {
246           g_simple_async_result_set_from_error (data->res, my_error);
247           g_error_free (my_error);
248         }
249     }
250
251   if (in_mainloop)
252     g_simple_async_result_complete (data->res);
253   else
254     g_simple_async_result_complete_in_idle (data->res);
255 }
256
257 static gboolean
258 close_read_ready (GSocket *socket,
259                   GIOCondition condition,
260                   CloseAsyncData *data)
261 {
262   GError *error = NULL;
263   char buffer[1024];
264   gssize ret;
265
266   if (g_cancellable_set_error_if_cancelled (data->cancellable,
267                                             &error))
268     {
269       async_close_finish (data, error, TRUE);
270       g_error_free (error);
271       return FALSE;
272     }
273
274   ret = g_socket_receive (socket,  buffer, sizeof (buffer), &error);
275   if (ret < 0)
276     {
277       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
278         g_error_free (error);
279       else
280         {
281           async_close_finish (data, error, TRUE);
282           g_error_free (error);
283           return FALSE;
284         }
285     }
286
287   if (ret == 0)
288     {
289       async_close_finish (data, NULL, TRUE);
290       return FALSE;
291     }
292
293   return TRUE;
294 }
295
296
297 static void
298 g_tcp_connection_close_async (GIOStream        *stream,
299                               int               io_priority,
300                               GCancellable     *cancellable,
301                               GAsyncReadyCallback callback,
302                               gpointer          user_data)
303 {
304   GTcpConnection *connection = G_TCP_CONNECTION (stream);
305   CloseAsyncData *data;
306   GSocket *socket;
307   GSource *source;
308   GError *error;
309
310   if (connection->priv->graceful_disconnect &&
311       !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
312     {
313       data = g_new (CloseAsyncData, 1);
314       data->res =
315         g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
316                                    g_tcp_connection_close_async);
317       if (cancellable)
318         data->cancellable = g_object_ref (cancellable);
319       else
320         data->cancellable = NULL;
321
322       socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
323
324       error = NULL;
325       if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
326         {
327           async_close_finish (data, error, FALSE);
328           g_error_free (error);
329           close_async_data_free (data);
330           return;
331         }
332
333       source = g_socket_create_source (socket, G_IO_IN, cancellable);
334       g_source_set_callback (source,
335                              (GSourceFunc) close_read_ready,
336                              data, (GDestroyNotify)close_async_data_free);
337       g_source_attach (source, NULL);
338       g_source_unref (source);
339
340       return;
341     }
342
343  out:
344   return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
345     ->close_async (stream, io_priority, cancellable, callback, user_data);
346
347
348 }
349
350 /**
351  * g_tcp_connection_set_graceful_disconnect:
352  * @connection: a #GTcpConnection
353  * @graceful_disconnect: Whether to do graceful disconnects or not
354  *
355  * This enabled graceful disconnects on close. A graceful disconnect
356  * means that we signal the recieving end that the connection is terminated
357  * and wait for it to close the connection before closing the connection.
358  *
359  * A graceful disconnect means that we can be sure that we successfully sent
360  * all the outstanding data to the other end, or get an error reported.
361  * However, it also means we have to wait for all the data to reach the
362  * other side and for it to acknowledge this by closing the socket, which may
363  * take a while. For this reason it is disabled by default.
364  *
365  * Since: 2.22
366  **/
367 void
368 g_tcp_connection_set_graceful_disconnect (GTcpConnection *connection,
369                                           gboolean        graceful_disconnect)
370 {
371   graceful_disconnect = !!graceful_disconnect;
372   if (graceful_disconnect != connection->priv->graceful_disconnect)
373     {
374       connection->priv->graceful_disconnect = graceful_disconnect;
375       g_object_notify (G_OBJECT (connection), "graceful-disconnect");
376     }
377 }
378
379 /**
380  * g_tcp_connection_get_graceful_disconnect:
381  * @connection: a #GTcpConnection
382  *
383  * Checks if graceful disconnects are used. See
384  * g_tcp_connection_set_graceful_disconnect().
385  *
386  * Returns: %TRUE if graceful disconnect is used on close, %FALSE otherwise
387  *
388  * Since: 2.22
389  **/
390 gboolean
391 g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
392 {
393   return connection->priv->graceful_disconnect;
394 }
395
396
397 #define __G_TCP_CONNECTION_C__
398 #include "gioaliasdef.c"