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