Branch data Line data Source code
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 : 2245 : 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 : 120 : g_tcp_connection_init (GTcpConnection *connection)
75 : : {
76 : 120 : connection->priv = g_tcp_connection_get_instance_private (connection);
77 : 120 : connection->priv->graceful_disconnect = FALSE;
78 : 120 : }
79 : :
80 : : static void
81 : 0 : g_tcp_connection_get_property (GObject *object,
82 : : guint prop_id,
83 : : GValue *value,
84 : : GParamSpec *pspec)
85 : : {
86 : 0 : GTcpConnection *connection = G_TCP_CONNECTION (object);
87 : :
88 : 0 : switch (prop_id)
89 : : {
90 : 0 : case PROP_GRACEFUL_DISCONNECT:
91 : 0 : g_value_set_boolean (value, connection->priv->graceful_disconnect);
92 : 0 : break;
93 : :
94 : 0 : default:
95 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
96 : : }
97 : 0 : }
98 : :
99 : : static void
100 : 0 : g_tcp_connection_set_property (GObject *object,
101 : : guint prop_id,
102 : : const GValue *value,
103 : : GParamSpec *pspec)
104 : : {
105 : 0 : GTcpConnection *connection = G_TCP_CONNECTION (object);
106 : :
107 : 0 : switch (prop_id)
108 : : {
109 : 0 : case PROP_GRACEFUL_DISCONNECT:
110 : 0 : g_tcp_connection_set_graceful_disconnect (connection,
111 : : g_value_get_boolean (value));
112 : 0 : break;
113 : :
114 : 0 : default:
115 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116 : : }
117 : 0 : }
118 : :
119 : : static void
120 : 9 : g_tcp_connection_class_init (GTcpConnectionClass *class)
121 : : {
122 : 9 : GObjectClass *gobject_class = G_OBJECT_CLASS (class);
123 : 9 : GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class);
124 : :
125 : 9 : gobject_class->set_property = g_tcp_connection_set_property;
126 : 9 : gobject_class->get_property = g_tcp_connection_get_property;
127 : :
128 : 9 : stream_class->close_fn = g_tcp_connection_close;
129 : 9 : 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 : 9 : 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 : 9 : }
144 : :
145 : : static gboolean
146 : 57 : g_tcp_connection_close (GIOStream *stream,
147 : : GCancellable *cancellable,
148 : : GError **error)
149 : : {
150 : 57 : GTcpConnection *connection = G_TCP_CONNECTION (stream);
151 : : GSocket *socket;
152 : : char buffer[1024];
153 : : gssize ret;
154 : : gboolean had_error;
155 : :
156 : 57 : socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
157 : 57 : had_error = FALSE;
158 : :
159 : 57 : if (connection->priv->graceful_disconnect &&
160 : 0 : !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
161 : : {
162 : 0 : if (!g_socket_shutdown (socket, FALSE, TRUE, error))
163 : : {
164 : 0 : error = NULL; /* Ignore further errors */
165 : 0 : had_error = TRUE;
166 : : }
167 : : else
168 : : {
169 : : while (TRUE)
170 : : {
171 : 0 : ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
172 : : TRUE, cancellable, error);
173 : 0 : if (ret < 0)
174 : : {
175 : 0 : had_error = TRUE;
176 : 0 : error = NULL;
177 : 0 : break;
178 : : }
179 : 0 : if (ret == 0)
180 : 0 : break;
181 : : }
182 : : }
183 : : }
184 : :
185 : 57 : return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
186 : 57 : ->close_fn (stream, cancellable, error) && !had_error;
187 : : }
188 : :
189 : : /* consumes @error */
190 : : static void
191 : 0 : async_close_finish (GTask *task,
192 : : GError *error)
193 : : {
194 : 0 : GIOStreamClass *parent = G_IO_STREAM_CLASS (g_tcp_connection_parent_class);
195 : 0 : GIOStream *stream = g_task_get_source_object (task);
196 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
197 : :
198 : : /* Close underlying stream, ignoring further errors if we already
199 : : * have one.
200 : : */
201 : 0 : if (error)
202 : 0 : parent->close_fn (stream, cancellable, NULL);
203 : : else
204 : 0 : parent->close_fn (stream, cancellable, &error);
205 : :
206 : 0 : if (error)
207 : 0 : g_task_return_error (task, error);
208 : : else
209 : 0 : g_task_return_boolean (task, TRUE);
210 : :
211 : 0 : g_object_unref (task);
212 : 0 : }
213 : :
214 : :
215 : : static gboolean
216 : 0 : close_read_ready (GSocket *socket,
217 : : GIOCondition condition,
218 : : GTask *task)
219 : : {
220 : 0 : GError *error = NULL;
221 : : char buffer[1024];
222 : : gssize ret;
223 : :
224 : 0 : ret = g_socket_receive_with_blocking (socket, buffer, sizeof (buffer),
225 : : FALSE, g_task_get_cancellable (task),
226 : : &error);
227 : 0 : if (ret < 0)
228 : : {
229 : 0 : if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
230 : : {
231 : 0 : g_error_free (error);
232 : 0 : return TRUE;
233 : : }
234 : : else
235 : : {
236 : 0 : async_close_finish (task, error);
237 : 0 : return FALSE;
238 : : }
239 : : }
240 : :
241 : 0 : if (ret == 0)
242 : : {
243 : 0 : async_close_finish (task, NULL);
244 : 0 : return FALSE;
245 : : }
246 : :
247 : 0 : return TRUE;
248 : : }
249 : :
250 : :
251 : : static void
252 : 11 : g_tcp_connection_close_async (GIOStream *stream,
253 : : int io_priority,
254 : : GCancellable *cancellable,
255 : : GAsyncReadyCallback callback,
256 : : gpointer user_data)
257 : : {
258 : 11 : GTcpConnection *connection = G_TCP_CONNECTION (stream);
259 : : GSocket *socket;
260 : : GSource *source;
261 : : GError *error;
262 : : GTask *task;
263 : :
264 : 11 : if (connection->priv->graceful_disconnect &&
265 : 0 : !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
266 : : {
267 : 0 : task = g_task_new (stream, cancellable, callback, user_data);
268 : 0 : g_task_set_source_tag (task, g_tcp_connection_close_async);
269 : 0 : g_task_set_priority (task, io_priority);
270 : :
271 : 0 : socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
272 : :
273 : 0 : error = NULL;
274 : 0 : if (!g_socket_shutdown (socket, FALSE, TRUE, &error))
275 : : {
276 : 0 : g_task_return_error (task, error);
277 : 0 : g_object_unref (task);
278 : 0 : return;
279 : : }
280 : :
281 : 0 : source = g_socket_create_source (socket, G_IO_IN, cancellable);
282 : 0 : g_task_attach_source (task, source, (GSourceFunc) close_read_ready);
283 : 0 : g_source_unref (source);
284 : :
285 : 0 : return;
286 : : }
287 : :
288 : 11 : G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
289 : 11 : ->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 : 0 : g_tcp_connection_set_graceful_disconnect (GTcpConnection *connection,
311 : : gboolean graceful_disconnect)
312 : : {
313 : 0 : graceful_disconnect = !!graceful_disconnect;
314 : 0 : if (graceful_disconnect != connection->priv->graceful_disconnect)
315 : : {
316 : 0 : connection->priv->graceful_disconnect = graceful_disconnect;
317 : 0 : g_object_notify (G_OBJECT (connection), "graceful-disconnect");
318 : : }
319 : 0 : }
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 : 0 : g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
334 : : {
335 : 0 : return connection->priv->graceful_disconnect;
336 : : }
|