1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; 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 * In addition to #SoupMessageBody, libsoup also defines a "smaller"
26 * data buffer type, #SoupBuffer, which is primarily used as a
27 * component of #SoupMessageBody. In particular, when using chunked
28 * encoding to transmit or receive a message, each chunk is
29 * represented as a #SoupBuffer.
30 **/
31
32 /**
33 * SoupMemoryUse:
34 * @SOUP_MEMORY_STATIC: The memory is statically allocated and
35 * constant; libsoup can use the passed-in buffer directly and not
36 * need to worry about it being modified or freed.
37 * @SOUP_MEMORY_TAKE: The caller has allocated the memory for the
38 * #SoupBuffer's use; libsoup will assume ownership of it and free it
39 * (with g_free()) when it is done with it.
40 * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller; the
41 * #SoupBuffer will copy it into new memory, leaving the caller free
42 * to reuse the original memory.
43 * @SOUP_MEMORY_TEMPORARY: The passed-in data belongs to the caller,
44 * but will remain valid for the lifetime of the #SoupBuffer. The
45 * difference between this and @SOUP_MEMORY_STATIC is that if you copy
46 * a @SOUP_MEMORY_TEMPORARY buffer, it will make a copy of the memory
47 * as well, rather than reusing the original memory.
48 *
49 * Describes how #SoupBuffer should use the data passed in by the
50 * caller.
51 *
52 * See also soup_buffer_new_with_owner(), which allows to you create a
53 * buffer containing data which is owned by another object.
54 **/
55
56 /* Internal SoupMemoryUse values */
57 enum {
58 SOUP_MEMORY_SUBBUFFER = SOUP_MEMORY_TEMPORARY + 1,
59 SOUP_MEMORY_OWNED
60 };
61
62 /**
63 * SoupBuffer:
64 * @data: (type gpointer): the data
65 * @length: length of @data
66 *
67 * A data buffer, generally used to represent a chunk of a
68 * #SoupMessageBody.
69 *
70 * @data is a #char because that's generally convenient; in some
71 * situations you may need to cast it to #guchar or another type.
72 **/
73
74 typedef struct {
75 SoupBuffer buffer;
76 SoupMemoryUse use;
77 guint refcount;
78
79 gpointer owner;
80 GDestroyNotify owner_dnotify;
81 } SoupBufferPrivate;
82
83 /**
84 * soup_buffer_new:
85 * @use: how @data is to be used by the buffer
86 * @data: (array length=length) (element-type guint8): data
87 * @length: length of @data
88 *
89 * Creates a new #SoupBuffer containing @length bytes from @data.
90 *
91 * Return value: the new #SoupBuffer.
92 **/
93 SoupBuffer *
soup_buffer_new(SoupMemoryUse use,gconstpointer data,gsize length)94 soup_buffer_new (SoupMemoryUse use, gconstpointer data, gsize length)
95 {
96 SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
97
98 if (use == SOUP_MEMORY_COPY) {
99 data = g_memdup (data, length);
100 use = SOUP_MEMORY_TAKE;
101 }
102
103 priv->buffer.data = data;
104 priv->buffer.length = length;
105 priv->use = use;
106 priv->refcount = 1;
107
108 if (use == SOUP_MEMORY_TAKE) {
109 priv->owner = (gpointer)data;
110 priv->owner_dnotify = g_free;
111 }
112
113 return (SoupBuffer *)priv;
114 }
115
116 /**
117 * soup_buffer_new_take: (rename-to soup_buffer_new)
118 * @data: (array length=length) (transfer full): data
119 * @length: length of @data
120 *
121 * Creates a new #SoupBuffer containing @length bytes from @data.
122 *
123 * This function is exactly equivalent to soup_buffer_new() with
124 * %SOUP_MEMORY_TAKE as first argument; it exists mainly for
125 * convenience and simplifying language bindings.
126 *
127 * Return value: the new #SoupBuffer.
128 *
129 * Since: 2.32
130 **/
131 SoupBuffer *
soup_buffer_new_take(guchar * data,gsize length)132 soup_buffer_new_take (guchar *data, gsize length)
133 {
134 return soup_buffer_new (SOUP_MEMORY_TAKE, data, length);
135 }
136
137 /**
138 * soup_buffer_new_subbuffer:
139 * @parent: the parent #SoupBuffer
140 * @offset: offset within @parent to start at
141 * @length: number of bytes to copy from @parent
142 *
143 * Creates a new #SoupBuffer containing @length bytes "copied" from
144 * @parent starting at @offset. (Normally this will not actually copy
145 * any data, but will instead simply reference the same data as
146 * @parent does.)
147 *
148 * Return value: the new #SoupBuffer.
149 **/
150 SoupBuffer *
soup_buffer_new_subbuffer(SoupBuffer * parent,gsize offset,gsize length)151 soup_buffer_new_subbuffer (SoupBuffer *parent, gsize offset, gsize length)
152 {
153 SoupBufferPrivate *priv;
154
155 /* Normally this is just a ref, but if @parent is TEMPORARY,
156 * it will do an actual copy.
157 */
158 parent = soup_buffer_copy (parent);
159
160 priv = g_slice_new0 (SoupBufferPrivate);
161 priv->buffer.data = parent->data + offset;
162 priv->buffer.length = length;
163 priv->use = SOUP_MEMORY_SUBBUFFER;
164 priv->owner = parent;
165 priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
166 priv->refcount = 1;
167
168 return (SoupBuffer *)priv;
169 }
170
171 /**
172 * soup_buffer_new_with_owner:
173 * @data: (array length=length) (element-type guint8): data
174 * @length: length of @data
175 * @owner: pointer to an object that owns @data
176 * @owner_dnotify: (allow-none): a function to free/unref @owner when
177 * the buffer is freed
178 *
179 * Creates a new #SoupBuffer containing @length bytes from @data. When
180 * the #SoupBuffer is freed, it will call @owner_dnotify, passing
181 * @owner to it. You must ensure that @data will remain valid until
182 * @owner_dnotify is called.
183 *
184 * For example, you could use this to create a buffer containing data
185 * returned from libxml without needing to do an extra copy:
186 *
187 * <informalexample><programlisting>
188 * xmlDocDumpMemory (doc, &xmlbody, &len);
189 * return soup_buffer_new_with_owner (xmlbody, len, xmlbody,
190 * (GDestroyNotify)xmlFree);
191 * </programlisting></informalexample>
192 *
193 * In this example, @data and @owner are the same, but in other cases
194 * they would be different (eg, @owner would be a object, and @data
195 * would be a pointer to one of the object's fields).
196 *
197 * Return value: the new #SoupBuffer.
198 **/
199 SoupBuffer *
soup_buffer_new_with_owner(gconstpointer data,gsize length,gpointer owner,GDestroyNotify owner_dnotify)200 soup_buffer_new_with_owner (gconstpointer data, gsize length,
201 gpointer owner, GDestroyNotify owner_dnotify)
202 {
203 SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
204
205 priv->buffer.data = data;
206 priv->buffer.length = length;
207 priv->use = SOUP_MEMORY_OWNED;
208 priv->owner = owner;
209 priv->owner_dnotify = owner_dnotify;
210 priv->refcount = 1;
211
212 return (SoupBuffer *)priv;
213 }
214
215 /**
216 * soup_buffer_get_owner:
217 * @buffer: a #SoupBuffer created with soup_buffer_new_with_owner()
218 *
219 * Gets the "owner" object for a buffer created with
220 * soup_buffer_new_with_owner().
221 *
222 * Return value: (transfer none): the owner pointer
223 **/
224 gpointer
soup_buffer_get_owner(SoupBuffer * buffer)225 soup_buffer_get_owner (SoupBuffer *buffer)
226 {
227 SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
228
229 g_return_val_if_fail ((int)priv->use == (int)SOUP_MEMORY_OWNED, NULL);
230 return priv->owner;
231 }
232
233 /**
234 * soup_buffer_get_data:
235 * @buffer: a #SoupBuffer
236 * @data: (out) (array length=length) (transfer none): the pointer
237 * to the buffer data is stored here
238 * @length: (out): the length of the buffer data is stored here
239 *
240 * This function exists for use by language bindings, because it's not
241 * currently possible to get the right effect by annotating the fields
242 * of #SoupBuffer.
243 *
244 * Since: 2.32
245 */
246 void
soup_buffer_get_data(SoupBuffer * buffer,const guint8 ** data,gsize * length)247 soup_buffer_get_data (SoupBuffer *buffer,
248 const guint8 **data,
249 gsize *length)
250 {
251 *data = (const guint8 *)buffer->data;
252 *length = buffer->length;
253 }
254
255 /**
256 * soup_buffer_copy:
257 * @buffer: a #SoupBuffer
258 *
259 * Makes a copy of @buffer. In reality, #SoupBuffer is a refcounted
260 * type, and calling soup_buffer_copy() will normally just increment
261 * the refcount on @buffer and return it. However, if @buffer was
262 * created with #SOUP_MEMORY_TEMPORARY memory, then soup_buffer_copy()
263 * will actually return a copy of it, so that the data in the copy
264 * will remain valid after the temporary buffer is freed.
265 *
266 * Return value: the new (or newly-reffed) buffer
267 **/
268 SoupBuffer *
soup_buffer_copy(SoupBuffer * buffer)269 soup_buffer_copy (SoupBuffer *buffer)
270 {
271 SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
272
273 /* For non-TEMPORARY buffers, this is just a ref */
274 if (priv->use != SOUP_MEMORY_TEMPORARY) {
275 g_atomic_int_inc (&priv->refcount);
276 return buffer;
277 }
278
279 /* For TEMPORARY buffers, we need to do a real copy the first
280 * time, and then after that, we just keep returning the copy.
281 * We store the copy in priv->owner, which is technically
282 * backwards, but it saves us from having to keep an extra
283 * pointer in SoupBufferPrivate.
284 */
285
286 if (!priv->owner) {
287 priv->owner = soup_buffer_new (SOUP_MEMORY_COPY,
288 buffer->data,
289 buffer->length);
290 priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
291 }
292 return soup_buffer_copy (priv->owner);
293 }
294
295 /**
296 * soup_buffer_free:
297 * @buffer: a #SoupBuffer
298 *
299 * Frees @buffer. (In reality, as described in the documentation for
300 * soup_buffer_copy(), this is actually an "unref" operation, and may
301 * or may not actually free @buffer.)
302 **/
303 void
soup_buffer_free(SoupBuffer * buffer)304 soup_buffer_free (SoupBuffer *buffer)
305 {
306 SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
307
308 if (!g_atomic_int_dec_and_test (&priv->refcount))
309 return;
310
311 if (priv->owner_dnotify)
312 priv->owner_dnotify (priv->owner);
313 g_slice_free (SoupBufferPrivate, priv);
314 }
315
316 /**
317 * soup_buffer_get_as_bytes:
318 * @buffer: a #SoupBuffer
319 *
320 * Creates a #GBytes pointing to the same memory as @buffer. The
321 * #GBytes will hold a reference on @buffer to ensure that it is not
322 * freed while the #GBytes is still valid.
323 *
324 * Returns: (transfer full): a new #GBytes which has the same content
325 * as the #SoupBuffer.
326 *
327 * Since: 2.40
328 */
329 GBytes *
soup_buffer_get_as_bytes(SoupBuffer * buffer)330 soup_buffer_get_as_bytes (SoupBuffer *buffer)
331 {
332 SoupBuffer *copy;
333
334 copy = soup_buffer_copy (buffer);
335 return g_bytes_new_with_free_func (copy->data, copy->length,
336 (GDestroyNotify)soup_buffer_free,
337 copy);
338 }
339
340 G_DEFINE_BOXED_TYPE (SoupBuffer, soup_buffer, soup_buffer_copy, soup_buffer_free)
341
342
343 /**
344 * SoupMessageBody:
345 * @data: the data
346 * @length: length of @data
347 *
348 * A #SoupMessage request or response body.
349 *
350 * Note that while @length always reflects the full length of the
351 * message body, @data is normally %NULL, and will only be filled in
352 * after soup_message_body_flatten() is called. For client-side
353 * messages, this automatically happens for the response body after it
354 * has been fully read, unless you set the
355 * %SOUP_MESSAGE_OVERWRITE_CHUNKS flags. Likewise, for server-side
356 * messages, the request body is automatically filled in after being
357 * read.
358 *
359 * As an added bonus, when @data is filled in, it is always terminated
360 * with a '\0' byte (which is not reflected in @length).
361 **/
362
363 typedef struct {
364 SoupMessageBody body;
365 GSList *chunks, *last;
366 SoupBuffer *flattened;
367 gboolean accumulate;
368 goffset base_offset;
369 int ref_count;
370 } SoupMessageBodyPrivate;
371
372 /**
373 * soup_message_body_new:
374 *
375 * Creates a new #SoupMessageBody. #SoupMessage uses this internally; you
376 * will not normally need to call it yourself.
377 *
378 * Return value: a new #SoupMessageBody.
379 **/
380 SoupMessageBody *
soup_message_body_new(void)381 soup_message_body_new (void)
382 {
383 SoupMessageBodyPrivate *priv;
384
385 priv = g_slice_new0 (SoupMessageBodyPrivate);
386 priv->accumulate = TRUE;
387 priv->ref_count = 1;
388
389 return (SoupMessageBody *)priv;
390 }
391
392 /**
393 * soup_message_body_set_accumulate:
394 * @body: a #SoupMessageBody
395 * @accumulate: whether or not to accumulate body chunks in @body
396 *
397 * Sets or clears the accumulate flag on @body. (The default value is
398 * %TRUE.) If set to %FALSE, @body's %data field will not be filled in
399 * after the body is fully sent/received, and the chunks that make up
400 * @body may be discarded when they are no longer needed.
401 *
402 * In particular, if you set this flag to %FALSE on an "incoming"
403 * message body (that is, the #SoupMessage:response_body of a
404 * client-side message, or #SoupMessage:request_body of a server-side
405 * message), this will cause each chunk of the body to be discarded
406 * after its corresponding #SoupMessage::got_chunk signal is emitted.
407 * (This is equivalent to setting the deprecated
408 * %SOUP_MESSAGE_OVERWRITE_CHUNKS flag on the message.)
409 *
410 * If you set this flag to %FALSE on the #SoupMessage:response_body of
411 * a server-side message, it will cause each chunk of the body to be
412 * discarded after its corresponding #SoupMessage::wrote_chunk signal
413 * is emitted.
414 *
415 * If you set the flag to %FALSE on the #SoupMessage:request_body of a
416 * client-side message, it will block the accumulation of chunks into
417 * @body's %data field, but it will not normally cause the chunks to
418 * be discarded after being written like in the server-side
419 * #SoupMessage:response_body case, because the request body needs to
420 * be kept around in case the request needs to be sent a second time
421 * due to redirection or authentication. However, if you set the
422 * %SOUP_MESSAGE_CAN_REBUILD flag on the message, then the chunks will
423 * be discarded, and you will be responsible for recreating the
424 * request body after the #SoupMessage::restarted signal is emitted.
425 *
426 * Since: 2.24
427 **/
428 void
soup_message_body_set_accumulate(SoupMessageBody * body,gboolean accumulate)429 soup_message_body_set_accumulate (SoupMessageBody *body,
430 gboolean accumulate)
431 {
432 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
433
434 priv->accumulate = accumulate;
435 }
436
437 /**
438 * soup_message_body_get_accumulate:
439 * @body: a #SoupMessageBody
440 *
441 * Gets the accumulate flag on @body; see
442 * soup_message_body_set_accumulate() for details.
443 *
444 * Return value: the accumulate flag for @body.
445 *
446 * Since: 2.24
447 **/
448 gboolean
soup_message_body_get_accumulate(SoupMessageBody * body)449 soup_message_body_get_accumulate (SoupMessageBody *body)
450 {
451 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
452
453 return priv->accumulate;
454 }
455
456 static void
append_buffer(SoupMessageBody * body,SoupBuffer * buffer)457 append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
458 {
459 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
460
461 if (priv->last) {
462 priv->last = g_slist_append (priv->last, buffer);
463 priv->last = priv->last->next;
464 } else
465 priv->chunks = priv->last = g_slist_append (NULL, buffer);
466
467 if (priv->flattened) {
468 soup_buffer_free (priv->flattened);
469 priv->flattened = NULL;
470 body->data = NULL;
471 }
472 body->length += buffer->length;
473 }
474
475 /**
476 * soup_message_body_append:
477 * @body: a #SoupMessageBody
478 * @use: how to use @data
479 * @data: (array length=length) (element-type guint8): data to append
480 * @length: length of @data
481 *
482 * Appends @length bytes from @data to @body according to @use.
483 **/
484 void
soup_message_body_append(SoupMessageBody * body,SoupMemoryUse use,gconstpointer data,gsize length)485 soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use,
486 gconstpointer data, gsize length)
487 {
488 if (length > 0)
489 append_buffer (body, soup_buffer_new (use, data, length));
490 else if (use == SOUP_MEMORY_TAKE)
491 g_free ((gpointer)data);
492 }
493
494 /**
495 * soup_message_body_append_take: (rename-to soup_message_body_append)
496 * @body: a #SoupMessageBody
497 * @data: (array length=length) (transfer full): data to append
498 * @length: length of @data
499 *
500 * Appends @length bytes from @data to @body.
501 *
502 * This function is exactly equivalent to soup_message_body_append()
503 * with %SOUP_MEMORY_TAKE as second argument; it exists mainly for
504 * convenience and simplifying language bindings.
505 *
506 * Since: 2.32
507 **/
508 void
soup_message_body_append_take(SoupMessageBody * body,guchar * data,gsize length)509 soup_message_body_append_take (SoupMessageBody *body,
510 guchar *data, gsize length)
511 {
512 soup_message_body_append(body, SOUP_MEMORY_TAKE, data, length);
513 }
514
515 /**
516 * soup_message_body_append_buffer:
517 * @body: a #SoupMessageBody
518 * @buffer: a #SoupBuffer
519 *
520 * Appends the data from @buffer to @body. (#SoupMessageBody uses
521 * #SoupBuffers internally, so this is normally a constant-time
522 * operation that doesn't actually require copying the data in
523 * @buffer.)
524 **/
525 void
soup_message_body_append_buffer(SoupMessageBody * body,SoupBuffer * buffer)526 soup_message_body_append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
527 {
528 g_return_if_fail (buffer->length > 0);
529 append_buffer (body, soup_buffer_copy (buffer));
530 }
531
532 /**
533 * soup_message_body_truncate:
534 * @body: a #SoupMessageBody
535 *
536 * Deletes all of the data in @body.
537 **/
538 void
soup_message_body_truncate(SoupMessageBody * body)539 soup_message_body_truncate (SoupMessageBody *body)
540 {
541 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
542
543 g_slist_free_full (priv->chunks, (GDestroyNotify)soup_buffer_free);
544 priv->chunks = priv->last = NULL;
545 priv->base_offset = 0;
546
547 if (priv->flattened) {
548 soup_buffer_free (priv->flattened);
549 priv->flattened = NULL;
550 body->data = NULL;
551 }
552 body->length = 0;
553 }
554
555 /**
556 * soup_message_body_complete:
557 * @body: a #SoupMessageBody
558 *
559 * Tags @body as being complete; Call this when using chunked encoding
560 * after you have appended the last chunk.
561 **/
562 void
soup_message_body_complete(SoupMessageBody * body)563 soup_message_body_complete (SoupMessageBody *body)
564 {
565 append_buffer (body, soup_buffer_new (SOUP_MEMORY_STATIC, NULL, 0));
566 }
567
568 /**
569 * soup_message_body_flatten:
570 * @body: a #SoupMessageBody
571 *
572 * Fills in @body's data field with a buffer containing all of the
573 * data in @body (plus an additional '\0' byte not counted by @body's
574 * length field).
575 *
576 * Return value: a #SoupBuffer containing the same data as @body.
577 * (You must free this buffer if you do not want it.)
578 **/
579 SoupBuffer *
soup_message_body_flatten(SoupMessageBody * body)580 soup_message_body_flatten (SoupMessageBody *body)
581 {
582 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
583 char *buf, *ptr;
584 GSList *iter;
585 SoupBuffer *chunk;
586
587 g_return_val_if_fail (priv->accumulate == TRUE, NULL);
588
589 if (!priv->flattened) {
590 #if GLIB_SIZEOF_SIZE_T < 8
591 g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
592 #endif
593
594 buf = ptr = g_malloc (body->length + 1);
595 for (iter = priv->chunks; iter; iter = iter->next) {
596 chunk = iter->data;
597 memcpy (ptr, chunk->data, chunk->length);
598 ptr += chunk->length;
599 }
600 *ptr = '\0';
601
602 priv->flattened = soup_buffer_new (SOUP_MEMORY_TAKE,
603 buf, body->length);
604 body->data = priv->flattened->data;
605 }
606
607 return soup_buffer_copy (priv->flattened);
608 }
609
610 /**
611 * soup_message_body_get_chunk:
612 * @body: a #SoupMessageBody
613 * @offset: an offset
614 *
615 * Gets a #SoupBuffer containing data from @body starting at @offset.
616 * The size of the returned chunk is unspecified. You can iterate
617 * through the entire body by first calling
618 * soup_message_body_get_chunk() with an offset of 0, and then on each
619 * successive call, increment the offset by the length of the
620 * previously-returned chunk.
621 *
622 * If @offset is greater than or equal to the total length of @body,
623 * then the return value depends on whether or not
624 * soup_message_body_complete() has been called or not; if it has,
625 * then soup_message_body_get_chunk() will return a 0-length chunk
626 * (indicating the end of @body). If it has not, then
627 * soup_message_body_get_chunk() will return %NULL (indicating that
628 * @body may still potentially have more data, but that data is not
629 * currently available).
630 *
631 * Return value: (nullable): a #SoupBuffer, or %NULL.
632 **/
633 SoupBuffer *
soup_message_body_get_chunk(SoupMessageBody * body,goffset offset)634 soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
635 {
636 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
637 GSList *iter;
638 SoupBuffer *chunk = NULL;
639
640 offset -= priv->base_offset;
641 for (iter = priv->chunks; iter; iter = iter->next) {
642 chunk = iter->data;
643
644 if (offset < chunk->length || offset == 0)
645 break;
646
647 offset -= chunk->length;
648 }
649
650 if (!iter)
651 return NULL;
652
653 if (offset == 0)
654 return soup_buffer_copy (chunk);
655 else {
656 return soup_buffer_new_subbuffer (chunk, offset,
657 chunk->length - offset);
658 }
659 }
660
661 /**
662 * soup_message_body_got_chunk:
663 * @body: a #SoupMessageBody
664 * @chunk: a #SoupBuffer received from the network
665 *
666 * Handles the #SoupMessageBody part of receiving a chunk of data from
667 * the network. Normally this means appending @chunk to @body, exactly
668 * as with soup_message_body_append_buffer(), but if you have set
669 * @body's accumulate flag to %FALSE, then that will not happen.
670 *
671 * This is a low-level method which you should not normally need to
672 * use.
673 *
674 * Since: 2.24
675 **/
676 void
soup_message_body_got_chunk(SoupMessageBody * body,SoupBuffer * chunk)677 soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
678 {
679 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
680
681 if (!priv->accumulate)
682 return;
683
684 soup_message_body_append_buffer (body, chunk);
685 }
686
687 /**
688 * soup_message_body_wrote_chunk:
689 * @body: a #SoupMessageBody
690 * @chunk: a #SoupBuffer returned from soup_message_body_get_chunk()
691 *
692 * Handles the #SoupMessageBody part of writing a chunk of data to the
693 * network. Normally this is a no-op, but if you have set @body's
694 * accumulate flag to %FALSE, then this will cause @chunk to be
695 * discarded to free up memory.
696 *
697 * This is a low-level method which you should not need to use, and
698 * there are further restrictions on its proper use which are not
699 * documented here.
700 *
701 * Since: 2.24
702 **/
703 void
soup_message_body_wrote_chunk(SoupMessageBody * body,SoupBuffer * chunk)704 soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk)
705 {
706 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
707 SoupBuffer *chunk2;
708
709 if (priv->accumulate)
710 return;
711
712 chunk2 = priv->chunks->data;
713 g_return_if_fail (chunk->length == chunk2->length);
714 g_return_if_fail (chunk == chunk2 || ((SoupBufferPrivate *)chunk2)->use == SOUP_MEMORY_TEMPORARY);
715
716 priv->chunks = g_slist_remove (priv->chunks, chunk2);
717 if (!priv->chunks)
718 priv->last = NULL;
719
720 priv->base_offset += chunk2->length;
721 soup_buffer_free (chunk2);
722 }
723
724 static SoupMessageBody *
soup_message_body_copy(SoupMessageBody * body)725 soup_message_body_copy (SoupMessageBody *body)
726 {
727 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
728
729 g_atomic_int_inc (&priv->ref_count);
730 return body;
731 }
732
733 /**
734 * soup_message_body_free:
735 * @body: a #SoupMessageBody
736 *
737 * Frees @body. You will not normally need to use this, as
738 * #SoupMessage frees its associated message bodies automatically.
739 **/
740 void
soup_message_body_free(SoupMessageBody * body)741 soup_message_body_free (SoupMessageBody *body)
742 {
743 SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
744
745 if (!g_atomic_int_dec_and_test (&priv->ref_count))
746 return;
747
748 soup_message_body_truncate (body);
749 g_slice_free (SoupMessageBodyPrivate, priv);
750 }
751
752 G_DEFINE_BOXED_TYPE (SoupMessageBody, soup_message_body, soup_message_body_copy, soup_message_body_free)
753