Coverage Report

Created: 2025-07-01 07:09

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