Coverage Report

Created: 2025-07-23 06:03

/src/libsoup/libsoup/server/soup-message-body.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-message-body.c: SoupMessage request/response bodies
4
 *
5
 * Copyright (C) 2000-2003, Ximian, Inc.
6
 */
7
8
#ifdef HAVE_CONFIG_H
9
#include <config.h>
10
#endif
11
12
#include <string.h>
13
14
#include "soup-message-body.h"
15
#include "soup.h"
16
17
/**
18
 * SoupMemoryUse:
19
 * @SOUP_MEMORY_STATIC: The memory is statically allocated and
20
 *   constant; libsoup can use the passed-in buffer directly and not
21
 *   need to worry about it being modified or freed.
22
 * @SOUP_MEMORY_TAKE: The caller has allocated the memory and libsoup
23
 *   will assume ownership of it and free it with [func@GLib.free].
24
 * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller and
25
 *   libsoup will copy it into new memory leaving the caller free
26
 *   to reuse the original memory.
27
 *
28
 * The lifetime of the memory being passed.
29
 **/
30
31
/**
32
 * SoupMessageBody:
33
 * @data: (array length=length) (element-type guint8): the data
34
 * @length: length of @data
35
 *
36
 * [struct@MessageBody] represents the request or response body of a
37
 * [class@Message].
38
 *
39
 * Note that while @length always reflects the full length of the
40
 * message body, @data is normally %NULL, and will only be filled in
41
 * after [method@MessageBody.flatten] is called. For client-side
42
 * messages, this automatically happens for the response body after it
43
 * has been fully read. Likewise, for server-side
44
 * messages, the request body is automatically filled in after being
45
 * read.
46
 *
47
 * As an added bonus, when @data is filled in, it is always terminated
48
 * with a `\0` byte (which is not reflected in @length).
49
 **/
50
51
typedef struct {
52
  SoupMessageBody body;
53
  GSList *chunks, *last;
54
  GBytes *flattened;
55
  gboolean accumulate;
56
  goffset base_offset;
57
} SoupMessageBodyPrivate;
58
59
/**
60
 * soup_message_body_new:
61
 *
62
 * Creates a new [struct@MessageBody]
63
 *
64
 * [class@Message] uses this internally; you
65
 * will not normally need to call it yourself.
66
 *
67
 * Returns: a new #SoupMessageBody.
68
 **/
69
SoupMessageBody *
70
soup_message_body_new (void)
71
0
{
72
0
  SoupMessageBodyPrivate *priv;
73
74
0
  priv = g_atomic_rc_box_new0 (SoupMessageBodyPrivate);
75
0
  priv->accumulate = TRUE;
76
77
0
  return (SoupMessageBody *)priv;
78
0
}
79
80
/**
81
 * soup_message_body_set_accumulate:
82
 * @body: a #SoupMessageBody
83
 * @accumulate: whether or not to accumulate body chunks in @body
84
 *
85
 * Sets or clears the accumulate flag on @body.
86
 *
87
 * (The default value is %TRUE.) If set to %FALSE, @body's data field will not
88
 * be filled in after the body is fully sent/received, and the chunks that make
89
 * up @body may be discarded when they are no longer needed.
90
 *
91
 * If you set the flag to %FALSE on the [class@Message] request_body of a
92
 * client-side message, it will block the accumulation of chunks into
93
 * @body's data field, but it will not normally cause the chunks to
94
 * be discarded after being written like in the server-side
95
 * [class@Message] response_body case, because the request body needs to
96
 * be kept around in case the request needs to be sent a second time
97
 * due to redirection or authentication.
98
 **/
99
void
100
soup_message_body_set_accumulate (SoupMessageBody *body,
101
          gboolean         accumulate)
102
0
{
103
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
104
105
0
  priv->accumulate = accumulate;
106
0
}
107
108
/**
109
 * soup_message_body_get_accumulate:
110
 * @body: a #SoupMessageBody
111
 *
112
 * Gets the accumulate flag on @body.
113
 *
114
 * See [method@MessageBody.set_accumulate. for details.
115
 *
116
 * Returns: the accumulate flag for @body.
117
 **/
118
gboolean
119
soup_message_body_get_accumulate (SoupMessageBody *body)
120
0
{
121
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
122
123
0
  return priv->accumulate;
124
0
}
125
126
static void
127
append_buffer (SoupMessageBody *body, GBytes *buffer)
128
0
{
129
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
130
131
0
  if (priv->last) {
132
0
    priv->last = g_slist_append (priv->last, buffer);
133
0
    priv->last = priv->last->next;
134
0
  } else
135
0
    priv->chunks = priv->last = g_slist_append (NULL, buffer);
136
137
0
        g_clear_pointer (&priv->flattened, g_bytes_unref);
138
0
        body->data = NULL;
139
0
  body->length += g_bytes_get_size (buffer);
140
0
}
141
142
/**
143
 * soup_message_body_append:
144
 * @body: a #SoupMessageBody
145
 * @use: how to use @data
146
 * @data: (array length=length) (element-type guint8): data to append
147
 * @length: length of @data
148
 *
149
 * Appends @length bytes from @data to @body according to @use.
150
 **/
151
void
152
soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use,
153
        gconstpointer data, gsize length)
154
0
{
155
0
        GBytes *bytes;
156
0
        if (length > 0) {
157
0
                if (use == SOUP_MEMORY_TAKE)
158
0
                        bytes = g_bytes_new_take ((guchar*)data, length);
159
0
                else if (use == SOUP_MEMORY_STATIC)
160
0
                        bytes = g_bytes_new_static (data, length);
161
0
                else
162
0
                        bytes = g_bytes_new (data, length);
163
0
                append_buffer (body, g_steal_pointer (&bytes));
164
0
        }
165
0
  else if (use == SOUP_MEMORY_TAKE)
166
0
    g_free ((gpointer)data);
167
0
}
168
169
/**
170
 * soup_message_body_append_take: (rename-to soup_message_body_append)
171
 * @body: a #SoupMessageBody
172
 * @data: (array length=length) (transfer full): data to append
173
 * @length: length of @data
174
 *
175
 * Appends @length bytes from @data to @body.
176
 *
177
 * This function is exactly equivalent to [method@MessageBody.append]
178
 * with %SOUP_MEMORY_TAKE as second argument; it exists mainly for
179
 * convenience and simplifying language bindings.
180
 **/
181
void
182
soup_message_body_append_take (SoupMessageBody *body,
183
             guchar *data, gsize length)
184
0
{
185
0
  soup_message_body_append(body, SOUP_MEMORY_TAKE, data, length);
186
0
}
187
188
/**
189
 * soup_message_body_append_bytes:
190
 * @body: a #SoupMessageBody
191
 * @buffer: a #GBytes
192
 *
193
 * Appends the data from @buffer to @body.
194
 **/
195
void
196
soup_message_body_append_bytes (SoupMessageBody *body, GBytes *buffer)
197
0
{
198
0
  g_return_if_fail (g_bytes_get_size (buffer) > 0);
199
0
  append_buffer (body, g_bytes_ref (buffer));
200
0
}
201
202
/**
203
 * soup_message_body_truncate:
204
 * @body: a #SoupMessageBody
205
 *
206
 * Deletes all of the data in @body.
207
 **/
208
void
209
soup_message_body_truncate (SoupMessageBody *body)
210
0
{
211
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
212
213
0
  g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
214
0
  priv->chunks = priv->last = NULL;
215
0
  priv->base_offset = 0;
216
0
        g_clear_pointer (&priv->flattened, g_bytes_unref);
217
0
        body->data = NULL;
218
0
  body->length = 0;
219
0
}
220
221
/**
222
 * soup_message_body_complete:
223
 * @body: a #SoupMessageBody
224
 *
225
 * Tags @body as being complete.
226
 *
227
 * Call this when using chunked encoding after you have appended the last chunk.
228
 **/
229
void
230
soup_message_body_complete (SoupMessageBody *body)
231
0
{
232
0
  append_buffer (body, g_bytes_new_static (NULL, 0));
233
0
}
234
235
/**
236
 * soup_message_body_flatten:
237
 * @body: a #SoupMessageBody
238
 *
239
 * Fills in @body's data field with a buffer containing all of the
240
 * data in @body.
241
 *
242
 * Adds an additional `\0` byte not counted by @body's
243
 * length field.
244
 *
245
 * Returns: (transfer full): a #GBytes containing the same data as @body.
246
 *   (You must [method@GLib.Bytes.unref] this if you do not want it.)
247
 **/
248
GBytes *
249
soup_message_body_flatten (SoupMessageBody *body)
250
0
{
251
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
252
253
0
  g_return_val_if_fail (priv->accumulate == TRUE, NULL);
254
255
0
  if (!priv->flattened) {
256
#if GLIB_SIZEOF_SIZE_T < 8
257
    g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
258
#endif
259
260
0
                GByteArray *array = g_byte_array_sized_new (body->length + 1);
261
0
    for (GSList *iter = priv->chunks; iter; iter = iter->next) {
262
0
      GBytes *chunk = iter->data;
263
0
                        gsize chunk_size;
264
0
                        const guchar *chunk_data = g_bytes_get_data (chunk, &chunk_size);
265
0
                        g_byte_array_append (array, chunk_data, chunk_size);
266
0
    }
267
                // NUL terminate the array but don't reflect that in the length
268
0
                g_byte_array_append (array, (guchar*)"\0", 1);
269
0
                array->len -= 1;
270
271
0
    priv->flattened = g_byte_array_free_to_bytes (array);
272
0
                body->data = g_bytes_get_data (priv->flattened, NULL);
273
0
  }
274
275
0
  return g_bytes_ref (priv->flattened);
276
0
}
277
278
/**
279
 * soup_message_body_get_chunk:
280
 * @body: a #SoupMessageBody
281
 * @offset: an offset
282
 *
283
 * Gets a [struct@GLib.Bytes] containing data from @body starting at @offset.
284
 *
285
 * The size of the returned chunk is unspecified. You can iterate
286
 * through the entire body by first calling
287
 * [method@MessageBody.get_chunk] with an offset of 0, and then on each
288
 * successive call, increment the offset by the length of the
289
 * previously-returned chunk.
290
 *
291
 * If @offset is greater than or equal to the total length of @body,
292
 * then the return value depends on whether or not
293
 * [method@MessageBody.complete] has been called or not; if it has,
294
 * then [method@MessageBody.get_chunk] will return a 0-length chunk
295
 * (indicating the end of @body). If it has not, then
296
 * [method@MessageBody.get_chunk] will return %NULL (indicating that
297
 * @body may still potentially have more data, but that data is not
298
 * currently available).
299
 *
300
 * Returns: (nullable): a #GBytes
301
 **/
302
GBytes *
303
soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
304
0
{
305
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
306
0
  GSList *iter;
307
0
  GBytes *chunk = NULL;
308
309
0
  offset -= priv->base_offset;
310
0
  for (iter = priv->chunks; iter; iter = iter->next) {
311
0
    chunk = iter->data;
312
0
                gsize chunk_length = g_bytes_get_size (chunk);
313
314
0
    if (offset < chunk_length || offset == 0)
315
0
      break;
316
317
0
    offset -= chunk_length;
318
0
  }
319
320
0
  if (!iter)
321
0
    return NULL;
322
323
0
        return g_bytes_new_from_bytes (chunk, offset, g_bytes_get_size (chunk) - offset);
324
0
}
325
326
/**
327
 * soup_message_body_got_chunk:
328
 * @body: a #SoupMessageBody
329
 * @chunk: a #GBytes received from the network
330
 *
331
 * Handles the [struct@MessageBody] part of receiving a chunk of data from
332
 * the network.
333
 *
334
 * Normally this means appending @chunk to @body, exactly as with
335
 * [method@MessageBody.append_bytes], but if you have set @body's accumulate
336
 * flag to %FALSE, then that will not happen.
337
 *
338
 * This is a low-level method which you should not normally need to
339
 * use.
340
 **/
341
void
342
soup_message_body_got_chunk (SoupMessageBody *body, GBytes *chunk)
343
0
{
344
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
345
346
0
  if (!priv->accumulate)
347
0
    return;
348
349
0
  soup_message_body_append_bytes (body, chunk);
350
0
}
351
352
/**
353
 * soup_message_body_wrote_chunk:
354
 * @body: a #SoupMessageBody
355
 * @chunk: a #GBytes returned from [method@MessageBody.get_chunk]
356
 *
357
 * Handles the [struct@MessageBody] part of writing a chunk of data to the
358
 * network.
359
 *
360
 * Normally this is a no-op, but if you have set @body's accumulate flag to
361
 * %FALSE, then this will cause @chunk to be discarded to free up memory.
362
 *
363
 * This is a low-level method which you should not need to use, and
364
 * there are further restrictions on its proper use which are not
365
 * documented here.
366
 **/
367
void
368
soup_message_body_wrote_chunk (SoupMessageBody *body, GBytes *chunk)
369
0
{
370
0
  SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
371
0
  GBytes *chunk2;
372
373
0
  if (priv->accumulate)
374
0
    return;
375
376
0
  chunk2 = priv->chunks->data;
377
0
  g_return_if_fail (g_bytes_get_size (chunk) == g_bytes_get_size (chunk2));
378
0
  g_return_if_fail (chunk == chunk2);
379
380
0
  priv->chunks = g_slist_remove (priv->chunks, chunk2);
381
0
  if (!priv->chunks)
382
0
    priv->last = NULL;
383
384
0
  priv->base_offset += g_bytes_get_size (chunk2);
385
0
  g_bytes_unref (chunk2);
386
0
}
387
388
/**
389
 * soup_message_body_ref:
390
 * @body: a #SoupMessageBody
391
 *
392
 * Atomically increments the reference count of @body by one.
393
 *
394
 * Returns: the passed in #SoupMessageBody
395
 */
396
SoupMessageBody *
397
soup_message_body_ref (SoupMessageBody *body)
398
0
{
399
0
        g_atomic_rc_box_acquire (body);
400
401
0
  return body;
402
0
}
403
404
/**
405
 * soup_message_body_unref:
406
 * @body: a #SoupMessageBody
407
 *
408
 * Atomically decrements the reference count of @body by one.
409
 *
410
 * When the reference count reaches zero, the resources allocated by
411
 * @body are freed
412
 */
413
void
414
soup_message_body_unref (SoupMessageBody *body)
415
0
{
416
0
        g_atomic_rc_box_release_full (body, (GDestroyNotify)soup_message_body_truncate);
417
0
}
418
419
G_DEFINE_BOXED_TYPE (SoupMessageBody, soup_message_body, soup_message_body_ref, soup_message_body_unref)