Coverage Report

Created: 2025-06-13 06:21

/src/glib/gio/gtcpconnection.c
Line
Count
Source (jump to first uncovered line)
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
0
{
76
0
  connection->priv = g_tcp_connection_get_instance_private (connection);
77
0
  connection->priv->graceful_disconnect = FALSE;
78
0
}
79
80
static void
81
g_tcp_connection_get_property (GObject    *object,
82
             guint       prop_id,
83
             GValue     *value,
84
             GParamSpec *pspec)
85
0
{
86
0
  GTcpConnection *connection = G_TCP_CONNECTION (object);
87
88
0
  switch (prop_id)
89
0
    {
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
0
    }
97
0
}
98
99
static void
100
g_tcp_connection_set_property (GObject      *object,
101
             guint         prop_id,
102
             const GValue *value,
103
             GParamSpec   *pspec)
104
0
{
105
0
  GTcpConnection *connection = G_TCP_CONNECTION (object);
106
107
0
  switch (prop_id)
108
0
    {
109
0
      case PROP_GRACEFUL_DISCONNECT:
110
0
  g_tcp_connection_set_graceful_disconnect (connection,
111
0
              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
0
    }
117
0
}
118
119
static void
120
g_tcp_connection_class_init (GTcpConnectionClass *class)
121
0
{
122
0
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
123
0
  GIOStreamClass *stream_class = G_IO_STREAM_CLASS (class);
124
125
0
  gobject_class->set_property = g_tcp_connection_set_property;
126
0
  gobject_class->get_property = g_tcp_connection_get_property;
127
128
0
  stream_class->close_fn = g_tcp_connection_close;
129
0
  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
0
  g_object_class_install_property (gobject_class, PROP_GRACEFUL_DISCONNECT,
139
0
           g_param_spec_boolean ("graceful-disconnect", NULL, NULL,
140
0
               FALSE,
141
0
               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
142
143
0
}
144
145
static gboolean
146
g_tcp_connection_close (GIOStream     *stream,
147
      GCancellable  *cancellable,
148
      GError       **error)
149
0
{
150
0
  GTcpConnection *connection = G_TCP_CONNECTION (stream);
151
0
  GSocket *socket;
152
0
  char buffer[1024];
153
0
  gssize ret;
154
0
  gboolean had_error;
155
156
0
  socket = g_socket_connection_get_socket (G_SOCKET_CONNECTION (stream));
157
0
  had_error = FALSE;
158
159
0
  if (connection->priv->graceful_disconnect &&
160
0
      !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
161
0
    {
162
0
      if (!g_socket_shutdown (socket, FALSE, TRUE, error))
163
0
  {
164
0
    error = NULL; /* Ignore further errors */
165
0
    had_error = TRUE;
166
0
  }
167
0
      else
168
0
  {
169
0
    while (TRUE)
170
0
      {
171
0
        ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
172
0
                TRUE, cancellable, error);
173
0
        if (ret < 0)
174
0
    {
175
0
      had_error = TRUE;
176
0
      error = NULL;
177
0
      break;
178
0
    }
179
0
        if (ret == 0)
180
0
    break;
181
0
      }
182
0
  }
183
0
    }
184
185
0
  return G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
186
0
    ->close_fn (stream, cancellable, error) && !had_error;
187
0
}
188
189
/* consumes @error */
190
static void
191
async_close_finish (GTask    *task,
192
                    GError   *error)
193
0
{
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
0
  else
204
0
    parent->close_fn (stream, cancellable, &error);
205
206
0
  if (error)
207
0
    g_task_return_error (task, error);
208
0
  else
209
0
    g_task_return_boolean (task, TRUE);
210
211
0
  g_object_unref (task);
212
0
}
213
214
215
static gboolean
216
close_read_ready (GSocket        *socket,
217
      GIOCondition    condition,
218
      GTask          *task)
219
0
{
220
0
  GError *error = NULL;
221
0
  char buffer[1024];
222
0
  gssize ret;
223
224
0
  ret = g_socket_receive_with_blocking (socket,  buffer, sizeof (buffer),
225
0
                                        FALSE, g_task_get_cancellable (task),
226
0
                                        &error);
227
0
  if (ret < 0)
228
0
    {
229
0
      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
230
0
  {
231
0
    g_error_free (error);
232
0
    return TRUE;
233
0
  }
234
0
      else
235
0
  {
236
0
    async_close_finish (task, error);
237
0
    return FALSE;
238
0
  }
239
0
    }
240
241
0
  if (ret == 0)
242
0
    {
243
0
      async_close_finish (task, NULL);
244
0
      return FALSE;
245
0
    }
246
247
0
  return TRUE;
248
0
}
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
0
{
258
0
  GTcpConnection *connection = G_TCP_CONNECTION (stream);
259
0
  GSocket *socket;
260
0
  GSource *source;
261
0
  GError *error;
262
0
  GTask *task;
263
264
0
  if (connection->priv->graceful_disconnect &&
265
0
      !g_cancellable_is_cancelled (cancellable) /* Cancelled -> close fast */)
266
0
    {
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
0
  {
276
0
    g_task_return_error (task, error);
277
0
    g_object_unref (task);
278
0
    return;
279
0
  }
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
0
    }
287
288
0
  G_IO_STREAM_CLASS (g_tcp_connection_parent_class)
289
0
    ->close_async (stream, io_priority, cancellable, callback, user_data);
290
0
}
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
0
{
313
0
  graceful_disconnect = !!graceful_disconnect;
314
0
  if (graceful_disconnect != connection->priv->graceful_disconnect)
315
0
    {
316
0
      connection->priv->graceful_disconnect = graceful_disconnect;
317
0
      g_object_notify (G_OBJECT (connection), "graceful-disconnect");
318
0
    }
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
g_tcp_connection_get_graceful_disconnect (GTcpConnection *connection)
334
0
{
335
0
  return connection->priv->graceful_disconnect;
336
0
}