Coverage Report

Created: 2025-06-13 06:55

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