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