Coverage Report

Created: 2025-11-16 06:19

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsoup/libsoup/http2/soup-body-input-stream-http2.c
Line
Count
Source
1
/* GIO - GLib Input, Output and Streaming Library
2
 * 
3
 * Copyright (C) 2006-2007 Red Hat, Inc.
4
 * Copyright 2021 Igalia S.L.
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General
17
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 *
19
 * Author: Christian Kellner <gicmo@gnome.org> 
20
 */
21
22
#include "config.h"
23
24
#include "soup-body-input-stream-http2.h"
25
#include <glib/gi18n-lib.h>
26
27
/*
28
 * SoupBodyInputStreamHttp2
29
 * @short_description: Streaming input operations on memory chunks
30
 *
31
 * [type@BodyInputStreamHttp2] is a class for using arbitrary
32
 * memory chunks as input for GIO streaming input operations.
33
 *
34
 * It differs from #GMemoryInputStream in that it frees older chunks
35
 * after they have been read, returns #G_IO_ERROR_WOULDBLOCK at the end
36
 * of data until soup_body_input_stream_http2_complete() is called, and implements
37
 * g_pollable_input_stream_is_readable().
38
 */
39
40
struct _SoupBodyInputStreamHttp2 {
41
        GInputStream parent_instance;
42
};
43
44
typedef struct {
45
        GQueue *chunks;
46
        gsize start_offset;
47
        gsize len;
48
        gsize pos;
49
        gboolean completed;
50
        GCancellable *need_more_data_cancellable;
51
} SoupBodyInputStreamHttp2Private;
52
53
static void soup_body_input_stream_http2_pollable_iface_init (GPollableInputStreamInterface *iface);
54
55
0
G_DEFINE_FINAL_TYPE_WITH_CODE (SoupBodyInputStreamHttp2, soup_body_input_stream_http2, G_TYPE_INPUT_STREAM,
56
0
                               G_ADD_PRIVATE (SoupBodyInputStreamHttp2)
57
0
                               G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
58
0
                                                      soup_body_input_stream_http2_pollable_iface_init);)
59
0
60
0
enum {
61
0
        NEED_MORE_DATA,
62
0
        READ_DATA,
63
0
        LAST_SIGNAL
64
0
};
65
0
66
0
static guint signals [LAST_SIGNAL] = { 0 };
67
0
68
0
/**
69
0
 * soup_body_input_stream_http2_new:
70
0
 *
71
0
 * Creates a new empty [type@BodyInputStreamHttp2].
72
0
 *
73
0
 * Returns: a new #GInputStream
74
0
 */
75
0
GInputStream *
76
0
soup_body_input_stream_http2_new (void)
77
0
{
78
0
        return G_INPUT_STREAM (g_object_new (SOUP_TYPE_BODY_INPUT_STREAM_HTTP2, NULL));
79
0
}
80
81
gsize
82
soup_body_input_stream_http2_get_buffer_size (SoupBodyInputStreamHttp2 *stream)
83
0
{
84
0
        SoupBodyInputStreamHttp2Private *priv;
85
86
0
        g_return_val_if_fail (SOUP_IS_BODY_INPUT_STREAM_HTTP2 (stream), 0);
87
88
0
        priv = soup_body_input_stream_http2_get_instance_private (stream);
89
90
0
        g_assert (priv->len >= priv->pos);
91
0
        return priv->len - priv->pos;
92
0
}
93
94
void
95
soup_body_input_stream_http2_add_data (SoupBodyInputStreamHttp2 *stream,
96
                                       const guint8             *data,
97
                                       gsize                     size)
98
0
{
99
0
        SoupBodyInputStreamHttp2Private *priv;
100
101
0
        g_return_if_fail (SOUP_IS_BODY_INPUT_STREAM_HTTP2 (stream));
102
0
        g_return_if_fail (data != NULL);
103
104
0
        priv = soup_body_input_stream_http2_get_instance_private (stream);
105
106
0
        g_queue_push_tail (priv->chunks, g_bytes_new (data, size));
107
0
        priv->len += size;
108
0
        if (priv->need_more_data_cancellable) {
109
0
                g_cancellable_cancel (priv->need_more_data_cancellable);
110
0
                g_clear_object (&priv->need_more_data_cancellable);
111
0
        }
112
0
}
113
114
gboolean
115
soup_body_input_stream_http2_is_blocked (SoupBodyInputStreamHttp2 *stream)
116
0
{
117
0
        SoupBodyInputStreamHttp2Private *priv;
118
119
0
        g_return_val_if_fail (SOUP_IS_BODY_INPUT_STREAM_HTTP2 (stream), FALSE);
120
121
0
        priv = soup_body_input_stream_http2_get_instance_private (stream);
122
0
        return priv->need_more_data_cancellable != NULL;
123
0
}
124
125
static gboolean
126
have_more_data_coming (SoupBodyInputStreamHttp2 *stream)
127
0
{
128
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (stream);
129
130
0
        return !priv->completed || priv->pos < priv->len;
131
0
}
132
133
static gssize
134
soup_body_input_stream_http2_read_real (GInputStream  *stream,
135
                                        gboolean       blocking,
136
                                        void          *buffer,
137
                                        gsize          read_count,
138
                                        GCancellable  *cancellable,
139
                                        GError       **error)
140
0
{
141
0
        SoupBodyInputStreamHttp2 *memory_stream;
142
0
        SoupBodyInputStreamHttp2Private *priv;
143
0
        GList *l;
144
0
        GBytes *chunk;
145
0
        gsize len;
146
0
        gsize offset, start, rest, size;
147
0
        gsize count;
148
149
0
        memory_stream = SOUP_BODY_INPUT_STREAM_HTTP2 (stream);
150
0
        priv = soup_body_input_stream_http2_get_instance_private (memory_stream);
151
152
        /* We have a list of chunked bytes that we continually read from.
153
         * Once a chunk is fully read it is removed from our list and we
154
         * keep the offset of where the chunks start.
155
         */
156
157
0
        count = MIN (read_count, priv->len - priv->pos);
158
159
0
        offset = priv->start_offset;
160
0
        for (l = g_queue_peek_head_link(priv->chunks); l; l = l->next) {
161
0
                chunk = (GBytes *)l->data;
162
0
                len = g_bytes_get_size (chunk);
163
164
0
                if (offset + len > priv->pos)
165
0
                        break;
166
167
0
                offset += len;
168
0
        }
169
170
0
        priv->start_offset = offset;
171
0
        start = priv->pos - offset;
172
0
        rest = count;
173
174
0
        while (l && rest > 0) {
175
0
                GList *next = l->next;
176
177
0
                const guint8 *chunk_data;
178
0
                chunk = (GBytes *)l->data;
179
180
0
                chunk_data = g_bytes_get_data (chunk, &len);
181
182
0
                size = MIN (rest, len - start);
183
184
0
                memcpy ((guint8 *)buffer + (count - rest), chunk_data + start, size);
185
0
                rest -= size;
186
187
                /* Remove fully read chunk from list, note that we are always near the start of the list */
188
0
                if (start + size == len) {
189
0
                        priv->start_offset += len;
190
0
                        g_queue_delete_link (priv->chunks, l);
191
0
                        g_bytes_unref (chunk);
192
0
                }
193
194
0
                start = 0;
195
0
                l = next;
196
0
        }
197
198
0
        gsize bytes_read = count - rest;
199
0
        priv->pos += bytes_read;
200
201
0
        if (bytes_read > 0)
202
0
                g_signal_emit (memory_stream, signals[READ_DATA], 0, (guint64)bytes_read);
203
204
        /* When doing blocking reads we must always request more data.
205
         * Even when doing non-blocking, a read consuming data may trigger a new WINDOW_UPDATE. */
206
0
        if (have_more_data_coming (memory_stream) && bytes_read == 0) {
207
0
                GError *read_error = NULL;
208
0
                g_signal_emit (memory_stream, signals[NEED_MORE_DATA], 0,
209
0
                        blocking, cancellable, &read_error);
210
211
0
                if (read_error) {
212
0
                        g_propagate_error (error, read_error);
213
0
                        return -1;
214
0
                }
215
216
0
                if (blocking) {
217
0
                        return soup_body_input_stream_http2_read_real (
218
0
                                stream, blocking, buffer, read_count, cancellable, error
219
0
                        );
220
0
                }
221
0
        }
222
223
0
        return count;
224
0
}
225
226
static gssize
227
soup_body_input_stream_http2_read (GInputStream  *stream,
228
                                   void          *buffer,
229
                                   gsize          count,
230
                                   GCancellable  *cancellable,
231
                                   GError       **error)
232
0
{
233
0
        return soup_body_input_stream_http2_read_real (stream, TRUE, buffer, count, cancellable, error);
234
0
}
235
236
static gssize
237
soup_body_input_stream_http2_read_nonblocking (GPollableInputStream  *stream,
238
                                               void                  *buffer,
239
                                               gsize                  count,
240
                                               GError               **error)
241
0
{
242
0
        SoupBodyInputStreamHttp2 *memory_stream = SOUP_BODY_INPUT_STREAM_HTTP2 (stream);
243
0
        GError *inner_error = NULL;
244
245
0
        gsize read = soup_body_input_stream_http2_read_real (G_INPUT_STREAM (stream), FALSE, buffer, count, NULL, &inner_error);
246
247
0
        if (read == 0 && have_more_data_coming (memory_stream) && !inner_error) {
248
0
                g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK, _("Operation would block"));
249
0
                return -1;
250
0
        }
251
252
0
        if (inner_error)
253
0
                g_propagate_error (error, inner_error);
254
255
0
        return read;
256
0
}
257
258
void
259
soup_body_input_stream_http2_complete (SoupBodyInputStreamHttp2 *stream)
260
0
{
261
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (stream);
262
0
        priv->completed = TRUE;
263
0
        if (priv->need_more_data_cancellable) {
264
0
                g_cancellable_cancel (priv->need_more_data_cancellable);
265
0
                g_clear_object (&priv->need_more_data_cancellable);
266
0
        }
267
0
}
268
269
static gssize
270
soup_body_input_stream_http2_skip (GInputStream  *stream,
271
                                   gsize          count,
272
                                   GCancellable  *cancellable,
273
                                   GError       **error)
274
0
{
275
0
        SoupBodyInputStreamHttp2 *memory_stream;
276
0
        SoupBodyInputStreamHttp2Private *priv;
277
278
0
        memory_stream = SOUP_BODY_INPUT_STREAM_HTTP2 (stream);
279
0
        priv = soup_body_input_stream_http2_get_instance_private (memory_stream);
280
281
0
        count = MIN (count, priv->len - priv->pos);
282
0
        priv->pos += count;
283
0
        if (count)
284
0
                g_signal_emit (memory_stream, signals[READ_DATA], 0, (guint64)count);
285
286
        /* Remove all skipped chunks */
287
0
        gsize offset = priv->start_offset;
288
0
        for (GList *l = g_queue_peek_head_link(priv->chunks); l; l = l->next) {
289
0
                GBytes *chunk = (GBytes *)l->data;
290
0
                gsize chunk_len = g_bytes_get_size (chunk);
291
292
0
                if (offset + chunk_len <= priv->pos) {
293
0
                        g_queue_delete_link (priv->chunks, l);
294
0
                        g_bytes_unref (chunk);
295
0
                        offset += chunk_len;
296
0
                }
297
0
                break;
298
0
        }
299
0
        priv->start_offset = offset;
300
301
0
        return count;
302
0
}
303
304
static gboolean
305
soup_body_input_stream_http2_close (GInputStream  *stream,
306
                                    GCancellable  *cancellable,
307
                                    GError       **error)
308
0
{
309
0
        return TRUE;
310
0
}
311
312
static void
313
soup_body_input_stream_http2_skip_async (GInputStream *stream,
314
                                     gsize count,
315
                                     int io_priority,
316
                                     GCancellable *cancellable,
317
                                     GAsyncReadyCallback callback,
318
                                     gpointer user_data)
319
0
{
320
0
        GTask *task;
321
0
        gssize nskipped;
322
0
        GError *error = NULL;
323
324
0
        nskipped = G_INPUT_STREAM_GET_CLASS (stream)->skip (stream, count, cancellable, &error);
325
0
        task = g_task_new (stream, cancellable, callback, user_data);
326
0
        g_task_set_source_tag (task, soup_body_input_stream_http2_skip_async);
327
328
0
        if (error)
329
0
                g_task_return_error (task, error);
330
0
        else
331
0
                g_task_return_int (task, nskipped);
332
0
        g_object_unref (task);
333
0
}
334
335
static gssize
336
soup_body_input_stream_http2_skip_finish (GInputStream  *stream,
337
                                          GAsyncResult  *result,
338
                                          GError       **error)
339
0
{
340
0
        g_return_val_if_fail (g_task_is_valid (result, stream), -1);
341
342
0
        return g_task_propagate_int (G_TASK (result), error);
343
0
}
344
345
static void
346
soup_body_input_stream_http2_close_async (GInputStream        *stream,
347
                                          int                  io_priority,
348
                                          GCancellable        *cancellable,
349
                                          GAsyncReadyCallback  callback,
350
                                          gpointer             user_data)
351
0
{
352
0
        GTask *task;
353
354
0
        task = g_task_new (stream, cancellable, callback, user_data);
355
0
        g_task_set_source_tag (task, soup_body_input_stream_http2_close_async);
356
0
        g_task_return_boolean (task, TRUE);
357
0
        g_object_unref (task);
358
0
}
359
360
static gboolean
361
soup_body_input_stream_http2_close_finish (GInputStream  *stream,
362
                                           GAsyncResult  *result,
363
                                           GError       **error)
364
0
{
365
0
        return TRUE;
366
0
}
367
368
static gboolean
369
soup_body_input_stream_http2_is_readable (GPollableInputStream *stream)
370
0
{
371
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (SOUP_BODY_INPUT_STREAM_HTTP2 (stream));
372
373
0
        return priv->pos < priv->len || priv->completed;
374
0
}
375
376
static GSource *
377
soup_body_input_stream_http2_create_source (GPollableInputStream *stream,
378
                                            GCancellable         *cancellable)
379
0
{
380
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (SOUP_BODY_INPUT_STREAM_HTTP2 (stream));
381
0
        GSource *base_source, *pollable_source;
382
383
0
        if (priv->pos < priv->len) {
384
0
                base_source = g_timeout_source_new (0);
385
0
        } else {
386
0
                if (!priv->need_more_data_cancellable)
387
0
                        priv->need_more_data_cancellable = g_cancellable_new ();
388
0
                base_source = g_cancellable_source_new (priv->need_more_data_cancellable);
389
0
        }
390
391
0
        pollable_source = g_pollable_source_new_full (stream, base_source, cancellable);
392
0
        g_source_set_name (pollable_source, "SoupMemoryStreamSource");
393
0
        g_source_unref (base_source);
394
395
0
        return pollable_source;
396
0
}
397
398
static void
399
soup_body_input_stream_http2_dispose (GObject *object)
400
0
{
401
0
        SoupBodyInputStreamHttp2 *stream = SOUP_BODY_INPUT_STREAM_HTTP2 (object);
402
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (stream);
403
404
0
        priv->completed = TRUE;
405
0
        if (priv->need_more_data_cancellable) {
406
0
    g_cancellable_cancel (priv->need_more_data_cancellable);
407
0
                g_clear_object (&priv->need_more_data_cancellable);
408
0
        }
409
410
0
        G_OBJECT_CLASS (soup_body_input_stream_http2_parent_class)->dispose (object);
411
0
}
412
413
static void
414
soup_body_input_stream_http2_finalize (GObject *object)
415
0
{
416
0
        SoupBodyInputStreamHttp2 *stream = SOUP_BODY_INPUT_STREAM_HTTP2 (object);
417
0
        SoupBodyInputStreamHttp2Private *priv = soup_body_input_stream_http2_get_instance_private (stream);
418
419
0
        g_queue_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
420
421
0
        G_OBJECT_CLASS (soup_body_input_stream_http2_parent_class)->finalize (object);
422
0
}
423
424
static void
425
soup_body_input_stream_http2_pollable_iface_init (GPollableInputStreamInterface *iface)
426
0
{
427
0
        iface->is_readable = soup_body_input_stream_http2_is_readable;
428
0
        iface->create_source = soup_body_input_stream_http2_create_source;
429
0
        iface->read_nonblocking = soup_body_input_stream_http2_read_nonblocking;
430
0
}
431
432
static void
433
soup_body_input_stream_http2_init (SoupBodyInputStreamHttp2 *stream)
434
0
{
435
0
        SoupBodyInputStreamHttp2Private *priv;
436
437
0
        priv = soup_body_input_stream_http2_get_instance_private (stream);
438
0
        priv->chunks = g_queue_new ();
439
0
}
440
441
static void
442
soup_body_input_stream_http2_class_init (SoupBodyInputStreamHttp2Class *klass)
443
0
{
444
0
        GObjectClass *object_class;
445
0
        GInputStreamClass *istream_class;
446
447
0
        object_class = G_OBJECT_CLASS (klass);
448
0
        object_class->finalize = soup_body_input_stream_http2_finalize;
449
0
        object_class->dispose = soup_body_input_stream_http2_dispose;
450
451
0
        istream_class = G_INPUT_STREAM_CLASS (klass);
452
0
        istream_class->read_fn = soup_body_input_stream_http2_read;
453
0
        istream_class->skip = soup_body_input_stream_http2_skip;
454
0
        istream_class->close_fn = soup_body_input_stream_http2_close;
455
456
0
        istream_class->skip_async = soup_body_input_stream_http2_skip_async;
457
0
        istream_class->skip_finish = soup_body_input_stream_http2_skip_finish;
458
0
        istream_class->close_async = soup_body_input_stream_http2_close_async;
459
0
        istream_class->close_finish = soup_body_input_stream_http2_close_finish;
460
461
0
        signals[NEED_MORE_DATA] =
462
0
                g_signal_new ("need-more-data",
463
0
                              G_OBJECT_CLASS_TYPE (object_class),
464
0
                              G_SIGNAL_RUN_FIRST,
465
0
                              0,
466
0
                              NULL, NULL,
467
0
                              NULL,
468
0
                              G_TYPE_ERROR,
469
0
                              2, G_TYPE_BOOLEAN,
470
0
                              G_TYPE_CANCELLABLE);
471
472
0
        signals[READ_DATA] =
473
0
                g_signal_new ("read-data",
474
0
                              G_OBJECT_CLASS_TYPE (object_class),
475
0
                              G_SIGNAL_RUN_FIRST,
476
0
                              0,
477
0
                              NULL, NULL,
478
0
                              NULL,
479
0
                              G_TYPE_NONE, 1,
480
0
                              G_TYPE_UINT64);
481
0
}