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