1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2008, 2009 Codethink Limited
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at 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  * @include: gio/gio.h
18  * @see_also: #GSocketConnection.
19  *
20  * This is the subclass of #GSocketConnection that is created
21  * for TCP/IP sockets.
22  *
23  * Since: 2.22
24  */
25 
26 #include "config.h"
27 #include "gtcpconnection.h"
28 #include "gasyncresult.h"
29 #include "gtask.h"
30 #include "giostream.h"
31 #include "glibintl.h"
32 
33 struct _GTcpConnectionPrivate
34 {
35   guint graceful_disconnect : 1;
36 };
37 
38 G_DEFINE_TYPE_WITH_CODE (GTcpConnection, g_tcp_connection,
39 			 G_TYPE_SOCKET_CONNECTION,
40                          G_ADD_PRIVATE (GTcpConnection)
41   g_socket_connection_factory_register_type (g_define_type_id,
42 					     G_SOCKET_FAMILY_IPV4,
43 					     G_SOCKET_TYPE_STREAM,
44 					     G_SOCKET_PROTOCOL_DEFAULT);
45   g_socket_connection_factory_register_type (g_define_type_id,
46 					     G_SOCKET_FAMILY_IPV6,
47 					     G_SOCKET_TYPE_STREAM,
48 					     G_SOCKET_PROTOCOL_DEFAULT);
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_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_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 
69 enum
70 {
71   PROP_0,
72   PROP_GRACEFUL_DISCONNECT
73 };
74 
75 static void
g_tcp_connection_init(GTcpConnection * connection)76 g_tcp_connection_init (GTcpConnection *connection)
77 {
78   connection->priv = g_tcp_connection_get_instance_private (connection);
79   connection->priv->graceful_disconnect = FALSE;
80 }
81 
82 static void
g_tcp_connection_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)83 g_tcp_connection_get_property (GObject    *object,
84 			       guint       prop_id,
85 			       GValue     *value,
86 			       GParamSpec *pspec)
87 {
88   GTcpConnection *connection = G_TCP_CONNECTION (object);
89 
90   switch (prop_id)
91     {
92       case PROP_GRACEFUL_DISCONNECT:
93 	g_value_set_boolean (value, connection->priv->graceful_disconnect);
94 	break;
95 
96       default:
97 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98     }
99 }
100 
101 static void
g_tcp_connection_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)102 g_tcp_connection_set_property (GObject      *object,
103 			       guint         prop_id,
104 			       const GValue *value,
105 			       GParamSpec   *pspec)
106 {
107   GTcpConnection *connection = G_TCP_CONNECTION (object);
108 
109   switch (prop_id)
110     {
111       case PROP_GRACEFUL_DISCONNECT:
112 	g_tcp_connection_set_graceful_disconnect (connection,
113 						  g_value_get_boolean (value));
114 	break;
115 
116       default:
117 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118     }
119 }
120 
121 static void
g_tcp_connection_class_init(GTcpConnectionClass * class)122 g_tcp_connection_class_init (GTcpConnectionClass *class)
123 {
124   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
125   GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class);
126 
127   gobject_class->set_property = g_tcp_connection_set_property;
128   gobject_class->get_property = g_tcp_connection_get_property;
129 
130   stream_class->close_fn = g_tcp_connection_close;
131   stream_class->close_async = g_tcp_connection_close_async;
132 
133   g_object_class_install_property (gobject_class, PROP_GRACEFUL_DISCONNECT,
134 				   g_param_spec_boolean ("graceful-disconnect",
135 							 P_("Graceful Disconnect"),
136 							 P_("Whether or not close does a graceful disconnect"),
137 							 FALSE,
138 							 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139 
140 }
141 
142 static gboolean
g_tcp_connection_close(GIOStream * stream,GCancellable * cancellable,GError ** error)143 g_tcp_connection_close (GIOStream     *stream,
144 			GCancellable  *cancellable,
145 			GError       **error)
146 {
147   GTcpConnection *connection = G_TCP_CONNECTION (stream);
148   GSocket *socket;
149   char buffer[1024];
150   gssize ret;
151   gboolean had_error;
152 
153   socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
154   had_error = FALSE;
155 
156   if (connection->priv->graceful_disconnect &&
157       !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
158     {
159       if (!g_socket_shutdown (socket, FALSE, TRUE, error))
160 	{
161 	  error = NULL; /* Ignore further errors */
162 	  had_error = TRUE;
163 	}
164       else
165 	{
166 	  while (TRUE)
167 	    {
168 	      ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
169 						    TRUE, cancellable, error);
170 	      if (ret < 0)
171 		{
172 		  had_error = TRUE;
173 		  error = NULL;
174 		  break;
175 		}
176 	      if (ret == 0)
177 		break;
178 	    }
179 	}
180     }
181 
182   return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
183     ->close_fn (stream, cancellable, error) && !had_error;
184 }
185 
186 /* consumes @error */
187 static void
async_close_finish(GTask * task,GError * error)188 async_close_finish (GTask    *task,
189                     GError   *error)
190 {
191   GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
192   GIOStream *stream = g_task_get_source_object (task);
193   GCancellable *cancellable = g_task_get_cancellable (task);
194 
195   /* Close underlying stream, ignoring further errors if we already
196    * have one.
197    */
198   if (error)
199     parent->close_fn (stream, cancellable, NULL);
200   else
201     parent->close_fn (stream, cancellable, &error);
202 
203   if (error)
204     g_task_return_error (task, error);
205   else
206     g_task_return_boolean (task, TRUE);
207 }
208 
209 
210 static gboolean
close_read_ready(GSocket * socket,GIOCondition condition,GTask * task)211 close_read_ready (GSocket        *socket,
212 		  GIOCondition    condition,
213 		  GTask          *task)
214 {
215   GError *error = NULL;
216   char buffer[1024];
217   gssize ret;
218 
219   ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
220                                         FALSE, g_task_get_cancellable (task),
221                                         &error);
222   if (ret < 0)
223     {
224       if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
225 	{
226 	  g_error_free (error);
227 	  return TRUE;
228 	}
229       else
230 	{
231 	  async_close_finish (task, error);
232 	  g_object_unref (task);
233 	  return FALSE;
234 	}
235     }
236 
237   if (ret == 0)
238     {
239       async_close_finish (task, NULL);
240       return FALSE;
241     }
242 
243   return TRUE;
244 }
245 
246 
247 static void
g_tcp_connection_close_async(GIOStream * stream,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)248 g_tcp_connection_close_async (GIOStream           *stream,
249 			      int                  io_priority,
250 			      GCancellable        *cancellable,
251 			      GAsyncReadyCallback  callback,
252 			      gpointer             user_data)
253 {
254   GTcpConnection *connection = G_TCP_CONNECTION (stream);
255   GSocket *socket;
256   GSource *source;
257   GError *error;
258   GTask *task;
259 
260   if (connection->priv->graceful_disconnect &&
261       !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
262     {
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       socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
268 
269       error = NULL;
270       if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
271 	{
272 	  g_task_return_error (task, error);
273 	  g_object_unref (task);
274 	  return;
275 	}
276 
277       source = g_socket_create_source (socket, G_IO_IN, cancellable);
278       g_task_attach_source (task, source, (GSourceFunc) close_read_ready);
279       g_source_unref (source);
280 
281       return;
282     }
283 
284   G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
285     ->close_async (stream, io_priority, cancellable, callback, user_data);
286 }
287 
288 /**
289  * g_tcp_connection_set_graceful_disconnect:
290  * @connection: a #GTcpConnection
291  * @graceful_disconnect: Whether to do graceful disconnects or not
292  *
293  * This enables graceful disconnects on close. A graceful disconnect
294  * means that we signal the receiving end that the connection is terminated
295  * and wait for it to close the connection before closing the connection.
296  *
297  * A graceful disconnect means that we can be sure that we successfully sent
298  * all the outstanding data to the other end, or get an error reported.
299  * However, it also means we have to wait for all the data to reach the
300  * other side and for it to acknowledge this by closing the socket, which may
301  * take a while. For this reason it is disabled by default.
302  *
303  * Since: 2.22
304  */
305 void
g_tcp_connection_set_graceful_disconnect(GTcpConnection * connection,gboolean graceful_disconnect)306 g_tcp_connection_set_graceful_disconnect (GTcpConnection *connection,
307 					  gboolean        graceful_disconnect)
308 {
309   graceful_disconnect = !!graceful_disconnect;
310   if (graceful_disconnect != connection->priv->graceful_disconnect)
311     {
312       connection->priv->graceful_disconnect = graceful_disconnect;
313       g_object_notify (G_OBJECT (connection), "graceful-disconnect");
314     }
315 }
316 
317 /**
318  * g_tcp_connection_get_graceful_disconnect:
319  * @connection: a #GTcpConnection
320  *
321  * Checks if graceful disconnects are used. See
322  * g_tcp_connection_set_graceful_disconnect().
323  *
324  * Returns: %TRUE if graceful disconnect is used on close, %FALSE otherwise
325  *
326  * Since: 2.22
327  */
328 gboolean
g_tcp_connection_get_graceful_disconnect(GTcpConnection * connection)329 g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
330 {
331   return connection->priv->graceful_disconnect;
332 }
333