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
33struct _GTcpConnectionPrivate
34{
35 guint graceful_disconnect : 1;
36};
37
38G_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
59static gboolean g_tcp_connection_close (GIOStream *stream,
60 GCancellable *cancellable,
61 GError **error);
62static void g_tcp_connection_close_async (GIOStream *stream,
63 int io_priority,
64 GCancellable *cancellable,
65 GAsyncReadyCallback callback,
66 gpointer user_data);
67
68
69enum
70{
71 PROP_0,
72 PROP_GRACEFUL_DISCONNECT
73};
74
75static void
76g_tcp_connection_init (GTcpConnection *connection)
77{
78 connection->priv = g_tcp_connection_get_instance_private (self: connection);
79 connection->priv->graceful_disconnect = FALSE;
80}
81
82static void
83g_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, v_boolean: connection->priv->graceful_disconnect);
94 break;
95
96 default:
97 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98 }
99}
100
101static void
102g_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 graceful_disconnect: g_value_get_boolean (value));
114 break;
115
116 default:
117 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
118 }
119}
120
121static void
122g_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 (oclass: gobject_class, property_id: PROP_GRACEFUL_DISCONNECT,
134 pspec: g_param_spec_boolean (name: "graceful-disconnect",
135 P_("Graceful Disconnect"),
136 P_("Whether or not close does a graceful disconnect"),
137 FALSE,
138 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139
140}
141
142static gboolean
143g_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, size: 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 */
187static void
188async_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
210static gboolean
211close_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, size: sizeof (buffer),
220 FALSE, cancellable: g_task_get_cancellable (task),
221 error: &error);
222 if (ret < 0)
223 {
224 if (g_error_matches (error, G_IO_ERROR, code: 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 (object: 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
247static void
248g_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 (source_object: stream, cancellable, callback, callback_data: user_data);
264 g_task_set_source_tag (task, g_tcp_connection_close_async);
265 g_task_set_priority (task, priority: 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: &error))
271 {
272 g_task_return_error (task, error);
273 g_object_unref (object: task);
274 return;
275 }
276
277 source = g_socket_create_source (socket, condition: G_IO_IN, cancellable);
278 g_task_attach_source (task, source, callback: (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 */
305void
306g_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), property_name: "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 */
328gboolean
329g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
330{
331 return connection->priv->graceful_disconnect;
332}
333

source code of gtk/subprojects/glib/gio/gtcpconnection.c