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