Coverage Report

Created: 2025-07-09 06:18

/src/libsoup/libsoup/soup-client-input-stream.c
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
/*
3
 * soup-client-input-stream.c
4
 *
5
 * Copyright 2010-2012 Red Hat, Inc.
6
 */
7
8
#ifdef HAVE_CONFIG_H
9
#include <config.h>
10
#endif
11
12
#include "soup-client-input-stream.h"
13
#include "soup.h"
14
#include "soup-message-private.h"
15
#include "soup-message-metrics-private.h"
16
#include "soup-misc.h"
17
18
struct _SoupClientInputStream {
19
  SoupFilterInputStream parent_instance;
20
};
21
22
typedef struct {
23
  SoupMessage  *msg;
24
        SoupMessageMetrics *metrics;
25
} SoupClientInputStreamPrivate;
26
27
enum {
28
  SIGNAL_EOF,
29
  LAST_SIGNAL
30
};
31
32
static guint signals[LAST_SIGNAL] = { 0 };
33
34
enum {
35
  PROP_0,
36
37
  PROP_MESSAGE,
38
39
        LAST_PROPERTY
40
};
41
42
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
43
44
static GPollableInputStreamInterface *soup_client_input_stream_parent_pollable_interface;
45
static void soup_client_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
46
47
G_DEFINE_FINAL_TYPE_WITH_CODE (SoupClientInputStream, soup_client_input_stream, SOUP_TYPE_FILTER_INPUT_STREAM,
48
                               G_ADD_PRIVATE (SoupClientInputStream)
49
             G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
50
                  soup_client_input_stream_pollable_init))
51
52
static void
53
soup_client_input_stream_init (SoupClientInputStream *stream)
54
0
{
55
0
}
56
57
static void
58
soup_client_input_stream_finalize (GObject *object)
59
0
{
60
0
  SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object);
61
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
62
63
0
  g_clear_object (&priv->msg);
64
65
0
  G_OBJECT_CLASS (soup_client_input_stream_parent_class)->finalize (object);
66
0
}
67
68
static void
69
soup_client_input_stream_set_property (GObject *object, guint prop_id,
70
               const GValue *value, GParamSpec *pspec)
71
0
{
72
0
  SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object);
73
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
74
75
0
  switch (prop_id) {
76
0
  case PROP_MESSAGE:
77
0
    priv->msg = g_value_dup_object (value);
78
0
                priv->metrics = soup_message_get_metrics (priv->msg);
79
0
    break;
80
0
  default:
81
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
82
0
    break;
83
0
  }
84
0
}
85
86
static void
87
soup_client_input_stream_get_property (GObject *object, guint prop_id,
88
               GValue *value, GParamSpec *pspec)
89
0
{
90
0
  SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (object);
91
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
92
93
0
  switch (prop_id) {
94
0
  case PROP_MESSAGE:
95
0
    g_value_set_object (value, priv->msg);
96
0
    break;
97
0
  default:
98
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
99
0
    break;
100
0
  }
101
0
}
102
103
static gssize
104
soup_client_input_stream_read_fn (GInputStream  *stream,
105
          void          *buffer,
106
          gsize          count,
107
          GCancellable  *cancellable,
108
          GError       **error)
109
0
{
110
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
111
0
  gssize nread;
112
113
0
        if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error))
114
0
                return -1;
115
116
0
  nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)->
117
0
    read_fn (stream, buffer, count, cancellable, error);
118
119
0
        if (priv->metrics && nread > 0)
120
0
                priv->metrics->response_body_size += nread;
121
122
0
  if (nread == 0)
123
0
    g_signal_emit (stream, signals[SIGNAL_EOF], 0);
124
125
0
  return nread;
126
0
}
127
128
static gssize
129
soup_client_input_stream_skip (GInputStream  *stream,
130
                               gsize          count,
131
                               GCancellable  *cancellable,
132
                               GError       **error)
133
0
{
134
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
135
0
        gssize nread;
136
137
0
        if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error))
138
0
                return -1;
139
140
0
        nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)->
141
0
                skip (stream, count, cancellable, error);
142
143
0
        if (priv->metrics && nread > 0)
144
0
          priv->metrics->response_body_size += nread;
145
146
0
        if (nread == 0)
147
0
                g_signal_emit (stream, signals[SIGNAL_EOF], 0);
148
149
0
        return nread;
150
0
}
151
152
static gssize
153
soup_client_input_stream_read_nonblocking (GPollableInputStream  *stream,
154
             void                  *buffer,
155
             gsize                  count,
156
             GError               **error)
157
0
{
158
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
159
0
  gssize nread;
160
161
0
        if (g_cancellable_set_error_if_cancelled (soup_message_io_get_cancellable (priv->msg), error))
162
0
                return -1;
163
164
0
  nread = soup_client_input_stream_parent_pollable_interface->
165
0
    read_nonblocking (stream, buffer, count, error);
166
167
0
        if (priv->metrics && nread > 0)
168
0
          priv->metrics->response_body_size += nread;
169
170
0
  if (nread == 0)
171
0
    g_signal_emit (stream, signals[SIGNAL_EOF], 0);
172
173
0
  return nread;
174
0
}
175
176
static gboolean
177
soup_client_input_stream_close_fn (GInputStream  *stream,
178
           GCancellable  *cancellable,
179
           GError       **error)
180
0
{
181
0
  SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream);
182
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
183
0
  gboolean success;
184
185
0
  success = soup_message_io_skip (priv->msg, TRUE, cancellable, error);
186
0
  soup_message_io_finished (priv->msg);
187
0
  return success;
188
0
}
189
190
static gboolean
191
close_async_ready (SoupMessage *msg, gpointer user_data)
192
0
{
193
0
  GTask *task = user_data;
194
0
  SoupClientInputStream *cistream = g_task_get_source_object (task);
195
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
196
0
  GError *error = NULL;
197
198
0
  if (!soup_message_io_skip (priv->msg, FALSE, g_task_get_cancellable (task), &error) &&
199
0
      g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
200
0
    g_error_free (error);
201
0
    return TRUE;
202
0
  }
203
204
0
  soup_message_io_finished (priv->msg);
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
0
        g_object_unref (task);
211
212
0
  return FALSE;
213
0
}
214
215
static void
216
soup_client_input_stream_close_async (GInputStream        *stream,
217
              gint                 priority,
218
              GCancellable        *cancellable,
219
              GAsyncReadyCallback  callback,
220
              gpointer             user_data)
221
0
{
222
0
  SoupClientInputStream *cistream = SOUP_CLIENT_INPUT_STREAM (stream);
223
0
        SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (cistream);
224
0
  GTask *task;
225
0
  GSource *source;
226
227
0
  task = g_task_new (stream, cancellable, callback, user_data);
228
0
  g_task_set_source_tag (task, soup_client_input_stream_close_async);
229
0
  g_task_set_priority (task, priority);
230
231
0
  if (close_async_ready (priv->msg, task) == G_SOURCE_CONTINUE) {
232
                /* When SoupClientInputStream is created we always have a body input stream,
233
                 * and we finished writing, so it's safe to pass NULL for the streams
234
                 */
235
0
    source = soup_message_io_data_get_source ((SoupMessageIOData *)soup_message_get_io_data (priv->msg),
236
0
                G_OBJECT (priv->msg),
237
0
                                                          NULL, NULL,
238
0
                cancellable, NULL, NULL);
239
240
0
    g_task_attach_source (task, source, (GSourceFunc) close_async_ready);
241
0
    g_source_unref (source);
242
0
  }
243
0
}
244
245
static gboolean
246
soup_client_input_stream_close_finish (GInputStream  *stream,
247
               GAsyncResult  *result,
248
               GError       **error)
249
0
{
250
0
  return g_task_propagate_boolean (G_TASK (result), error);
251
0
}
252
253
static void
254
soup_client_input_stream_class_init (SoupClientInputStreamClass *stream_class)
255
0
{
256
0
  GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
257
0
  GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
258
259
0
  object_class->finalize = soup_client_input_stream_finalize;
260
0
  object_class->set_property = soup_client_input_stream_set_property;
261
0
  object_class->get_property = soup_client_input_stream_get_property;
262
263
0
  input_stream_class->read_fn = soup_client_input_stream_read_fn;
264
0
  input_stream_class->skip = soup_client_input_stream_skip;
265
0
  input_stream_class->close_fn = soup_client_input_stream_close_fn;
266
0
  input_stream_class->close_async = soup_client_input_stream_close_async;
267
0
  input_stream_class->close_finish = soup_client_input_stream_close_finish;
268
269
0
  signals[SIGNAL_EOF] =
270
0
    g_signal_new ("eof",
271
0
            G_OBJECT_CLASS_TYPE (object_class),
272
0
            G_SIGNAL_RUN_LAST,
273
0
            0,
274
0
            NULL, NULL,
275
0
            NULL,
276
0
            G_TYPE_NONE, 0);
277
278
0
        properties[PROP_MESSAGE] =
279
0
    g_param_spec_object ("message",
280
0
             "Message",
281
0
             "Message",
282
0
             SOUP_TYPE_MESSAGE,
283
0
             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
284
0
             G_PARAM_STATIC_STRINGS);
285
286
0
        g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
287
0
}
288
289
static void
290
soup_client_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
291
          gpointer interface_data)
292
0
{
293
0
  soup_client_input_stream_parent_pollable_interface =
294
0
    g_type_interface_peek_parent (pollable_interface);
295
296
0
  pollable_interface->read_nonblocking = soup_client_input_stream_read_nonblocking;
297
0
}
298
299
GInputStream *
300
soup_client_input_stream_new (GInputStream *base_stream,
301
            SoupMessage  *msg)
302
0
{
303
0
  return g_object_new (SOUP_TYPE_CLIENT_INPUT_STREAM,
304
0
           "base-stream", base_stream,
305
0
           "message", msg,
306
0
           NULL);
307
0
}