summaryrefslogtreecommitdiff
path: root/libsoup/soup-message-body.c
diff options
context:
space:
mode:
Diffstat (limited to 'libsoup/soup-message-body.c')
-rw-r--r--libsoup/soup-message-body.c438
1 files changed, 63 insertions, 375 deletions
diff --git a/libsoup/soup-message-body.c b/libsoup/soup-message-body.c
index eb6d5f5a..82c6b887 100644
--- a/libsoup/soup-message-body.c
+++ b/libsoup/soup-message-body.c
@@ -21,12 +21,6 @@
*
* #SoupMessageBody represents the request or response body of a
* #SoupMessage.
- *
- * In addition to #SoupMessageBody, libsoup also defines a "smaller"
- * data buffer type, #SoupBuffer, which is primarily used as a
- * component of #SoupMessageBody. In particular, when using chunked
- * encoding to transmit or receive a message, each chunk is
- * represented as a #SoupBuffer.
**/
/**
@@ -34,311 +28,12 @@
* @SOUP_MEMORY_STATIC: The memory is statically allocated and
* constant; libsoup can use the passed-in buffer directly and not
* need to worry about it being modified or freed.
- * @SOUP_MEMORY_TAKE: The caller has allocated the memory for the
- * #SoupBuffer's use; libsoup will assume ownership of it and free it
- * (with g_free()) when it is done with it.
- * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller; the
- * #SoupBuffer will copy it into new memory, leaving the caller free
+ * @SOUP_MEMORY_TAKE: The caller has allocated the memory and libsoup
+ * will assume ownership of it and free it with g_free().
+ * @SOUP_MEMORY_COPY: The passed-in data belongs to the caller and
+ * libsoup will copy it into new memory leaving the caller free
* to reuse the original memory.
- * @SOUP_MEMORY_TEMPORARY: The passed-in data belongs to the caller,
- * but will remain valid for the lifetime of the #SoupBuffer. The
- * difference between this and @SOUP_MEMORY_STATIC is that if you copy
- * a @SOUP_MEMORY_TEMPORARY buffer, it will make a copy of the memory
- * as well, rather than reusing the original memory.
- *
- * Describes how #SoupBuffer should use the data passed in by the
- * caller.
- *
- * See also soup_buffer_new_with_owner(), which allows to you create a
- * buffer containing data which is owned by another object.
- **/
-
-/* Internal SoupMemoryUse values */
-enum {
- SOUP_MEMORY_SUBBUFFER = SOUP_MEMORY_TEMPORARY + 1,
- SOUP_MEMORY_OWNED
-};
-
-/**
- * SoupBuffer:
- * @data: (type gpointer): the data
- * @length: length of @data
- *
- * A data buffer, generally used to represent a chunk of a
- * #SoupMessageBody.
- *
- * @data is a #char because that's generally convenient; in some
- * situations you may need to cast it to #guchar or another type.
- **/
-
-typedef struct {
- SoupBuffer buffer;
- SoupMemoryUse use;
- guint refcount;
-
- gpointer owner;
- GDestroyNotify owner_dnotify;
-} SoupBufferPrivate;
-
-/**
- * soup_buffer_new:
- * @use: how @data is to be used by the buffer
- * @data: (array length=length) (element-type guint8): data
- * @length: length of @data
- *
- * Creates a new #SoupBuffer containing @length bytes from @data.
- *
- * Return value: the new #SoupBuffer.
- **/
-SoupBuffer *
-soup_buffer_new (SoupMemoryUse use, gconstpointer data, gsize length)
-{
- SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
-
- if (use == SOUP_MEMORY_COPY) {
- data = g_memdup (data, length);
- use = SOUP_MEMORY_TAKE;
- }
-
- priv->buffer.data = data;
- priv->buffer.length = length;
- priv->use = use;
- priv->refcount = 1;
-
- if (use == SOUP_MEMORY_TAKE) {
- priv->owner = (gpointer)data;
- priv->owner_dnotify = g_free;
- }
-
- return (SoupBuffer *)priv;
-}
-
-/**
- * soup_buffer_new_take: (rename-to soup_buffer_new)
- * @data: (array length=length) (transfer full): data
- * @length: length of @data
- *
- * Creates a new #SoupBuffer containing @length bytes from @data.
- *
- * This function is exactly equivalent to soup_buffer_new() with
- * %SOUP_MEMORY_TAKE as first argument; it exists mainly for
- * convenience and simplifying language bindings.
- *
- * Return value: the new #SoupBuffer.
- *
- * Since: 2.32
- **/
-SoupBuffer *
-soup_buffer_new_take (guchar *data, gsize length)
-{
- return soup_buffer_new (SOUP_MEMORY_TAKE, data, length);
-}
-
-/**
- * soup_buffer_new_subbuffer:
- * @parent: the parent #SoupBuffer
- * @offset: offset within @parent to start at
- * @length: number of bytes to copy from @parent
- *
- * Creates a new #SoupBuffer containing @length bytes "copied" from
- * @parent starting at @offset. (Normally this will not actually copy
- * any data, but will instead simply reference the same data as
- * @parent does.)
- *
- * Return value: the new #SoupBuffer.
- **/
-SoupBuffer *
-soup_buffer_new_subbuffer (SoupBuffer *parent, gsize offset, gsize length)
-{
- SoupBufferPrivate *priv;
-
- /* Normally this is just a ref, but if @parent is TEMPORARY,
- * it will do an actual copy.
- */
- parent = soup_buffer_copy (parent);
-
- priv = g_slice_new0 (SoupBufferPrivate);
- priv->buffer.data = parent->data + offset;
- priv->buffer.length = length;
- priv->use = SOUP_MEMORY_SUBBUFFER;
- priv->owner = parent;
- priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
- priv->refcount = 1;
-
- return (SoupBuffer *)priv;
-}
-
-/**
- * soup_buffer_new_with_owner:
- * @data: (array length=length) (element-type guint8): data
- * @length: length of @data
- * @owner: pointer to an object that owns @data
- * @owner_dnotify: (allow-none): a function to free/unref @owner when
- * the buffer is freed
- *
- * Creates a new #SoupBuffer containing @length bytes from @data. When
- * the #SoupBuffer is freed, it will call @owner_dnotify, passing
- * @owner to it. You must ensure that @data will remain valid until
- * @owner_dnotify is called.
- *
- * For example, you could use this to create a buffer containing data
- * returned from libxml without needing to do an extra copy:
- *
- * <informalexample><programlisting>
- * xmlDocDumpMemory (doc, &xmlbody, &len);
- * return soup_buffer_new_with_owner (xmlbody, len, xmlbody,
- * (GDestroyNotify)xmlFree);
- * </programlisting></informalexample>
- *
- * In this example, @data and @owner are the same, but in other cases
- * they would be different (eg, @owner would be a object, and @data
- * would be a pointer to one of the object's fields).
- *
- * Return value: the new #SoupBuffer.
- **/
-SoupBuffer *
-soup_buffer_new_with_owner (gconstpointer data, gsize length,
- gpointer owner, GDestroyNotify owner_dnotify)
-{
- SoupBufferPrivate *priv = g_slice_new0 (SoupBufferPrivate);
-
- priv->buffer.data = data;
- priv->buffer.length = length;
- priv->use = SOUP_MEMORY_OWNED;
- priv->owner = owner;
- priv->owner_dnotify = owner_dnotify;
- priv->refcount = 1;
-
- return (SoupBuffer *)priv;
-}
-
-/**
- * soup_buffer_get_owner:
- * @buffer: a #SoupBuffer created with soup_buffer_new_with_owner()
- *
- * Gets the "owner" object for a buffer created with
- * soup_buffer_new_with_owner().
- *
- * Return value: (transfer none): the owner pointer
- **/
-gpointer
-soup_buffer_get_owner (SoupBuffer *buffer)
-{
- SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
-
- g_return_val_if_fail ((int)priv->use == (int)SOUP_MEMORY_OWNED, NULL);
- return priv->owner;
-}
-
-/**
- * soup_buffer_get_data:
- * @buffer: a #SoupBuffer
- * @data: (out) (array length=length) (transfer none): the pointer
- * to the buffer data is stored here
- * @length: (out): the length of the buffer data is stored here
- *
- * This function exists for use by language bindings, because it's not
- * currently possible to get the right effect by annotating the fields
- * of #SoupBuffer.
- *
- * Since: 2.32
- */
-void
-soup_buffer_get_data (SoupBuffer *buffer,
- const guint8 **data,
- gsize *length)
-{
- *data = (const guint8 *)buffer->data;
- *length = buffer->length;
-}
-
-/**
- * soup_buffer_copy:
- * @buffer: a #SoupBuffer
- *
- * Makes a copy of @buffer. In reality, #SoupBuffer is a refcounted
- * type, and calling soup_buffer_copy() will normally just increment
- * the refcount on @buffer and return it. However, if @buffer was
- * created with #SOUP_MEMORY_TEMPORARY memory, then soup_buffer_copy()
- * will actually return a copy of it, so that the data in the copy
- * will remain valid after the temporary buffer is freed.
- *
- * Return value: the new (or newly-reffed) buffer
- **/
-SoupBuffer *
-soup_buffer_copy (SoupBuffer *buffer)
-{
- SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
-
- /* For non-TEMPORARY buffers, this is just a ref */
- if (priv->use != SOUP_MEMORY_TEMPORARY) {
- g_atomic_int_inc (&priv->refcount);
- return buffer;
- }
-
- /* For TEMPORARY buffers, we need to do a real copy the first
- * time, and then after that, we just keep returning the copy.
- * We store the copy in priv->owner, which is technically
- * backwards, but it saves us from having to keep an extra
- * pointer in SoupBufferPrivate.
- */
-
- if (!priv->owner) {
- priv->owner = soup_buffer_new (SOUP_MEMORY_COPY,
- buffer->data,
- buffer->length);
- priv->owner_dnotify = (GDestroyNotify)soup_buffer_free;
- }
- return soup_buffer_copy (priv->owner);
-}
-
-/**
- * soup_buffer_free:
- * @buffer: a #SoupBuffer
- *
- * Frees @buffer. (In reality, as described in the documentation for
- * soup_buffer_copy(), this is actually an "unref" operation, and may
- * or may not actually free @buffer.)
**/
-void
-soup_buffer_free (SoupBuffer *buffer)
-{
- SoupBufferPrivate *priv = (SoupBufferPrivate *)buffer;
-
- if (!g_atomic_int_dec_and_test (&priv->refcount))
- return;
-
- if (priv->owner_dnotify)
- priv->owner_dnotify (priv->owner);
- g_slice_free (SoupBufferPrivate, priv);
-}
-
-/**
- * soup_buffer_get_as_bytes:
- * @buffer: a #SoupBuffer
- *
- * Creates a #GBytes pointing to the same memory as @buffer. The
- * #GBytes will hold a reference on @buffer to ensure that it is not
- * freed while the #GBytes is still valid.
- *
- * Returns: (transfer full): a new #GBytes which has the same content
- * as the #SoupBuffer.
- *
- * Since: 2.40
- */
-GBytes *
-soup_buffer_get_as_bytes (SoupBuffer *buffer)
-{
- SoupBuffer *copy;
-
- copy = soup_buffer_copy (buffer);
- return g_bytes_new_with_free_func (copy->data, copy->length,
- (GDestroyNotify)soup_buffer_free,
- copy);
-}
-
-G_DEFINE_BOXED_TYPE (SoupBuffer, soup_buffer, soup_buffer_copy, soup_buffer_free)
-
/**
* SoupMessageBody:
@@ -362,7 +57,7 @@ G_DEFINE_BOXED_TYPE (SoupBuffer, soup_buffer, soup_buffer_copy, soup_buffer_free
typedef struct {
SoupMessageBody body;
GSList *chunks, *last;
- SoupBuffer *flattened;
+ GBytes *flattened;
gboolean accumulate;
goffset base_offset;
int ref_count;
@@ -451,7 +146,7 @@ soup_message_body_get_accumulate (SoupMessageBody *body)
}
static void
-append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+append_buffer (SoupMessageBody *body, GBytes *buffer)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
@@ -461,12 +156,9 @@ append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
} else
priv->chunks = priv->last = g_slist_append (NULL, buffer);
- if (priv->flattened) {
- soup_buffer_free (priv->flattened);
- priv->flattened = NULL;
- body->data = NULL;
- }
- body->length += buffer->length;
+ g_clear_pointer (&priv->flattened, g_bytes_unref);
+ body->data = NULL;
+ body->length += g_bytes_get_size (buffer);
}
/**
@@ -482,8 +174,16 @@ void
soup_message_body_append (SoupMessageBody *body, SoupMemoryUse use,
gconstpointer data, gsize length)
{
- if (length > 0)
- append_buffer (body, soup_buffer_new (use, data, length));
+ GBytes *bytes;
+ if (length > 0) {
+ if (use == SOUP_MEMORY_TAKE)
+ bytes = g_bytes_new_take ((guchar*)data, length);
+ else if (use == SOUP_MEMORY_STATIC)
+ bytes = g_bytes_new_static (data, length);
+ else
+ bytes = g_bytes_new (data, length);
+ append_buffer (body, g_steal_pointer (&bytes));
+ }
else if (use == SOUP_MEMORY_TAKE)
g_free ((gpointer)data);
}
@@ -510,20 +210,17 @@ soup_message_body_append_take (SoupMessageBody *body,
}
/**
- * soup_message_body_append_buffer:
+ * soup_message_body_append_bytes:
* @body: a #SoupMessageBody
- * @buffer: a #SoupBuffer
+ * @buffer: a #GBytes
*
- * Appends the data from @buffer to @body. (#SoupMessageBody uses
- * #SoupBuffers internally, so this is normally a constant-time
- * operation that doesn't actually require copying the data in
- * @buffer.)
+ * Appends the data from @buffer to @body.
**/
void
-soup_message_body_append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
+soup_message_body_append_bytes (SoupMessageBody *body, GBytes *buffer)
{
- g_return_if_fail (buffer->length > 0);
- append_buffer (body, soup_buffer_copy (buffer));
+ g_return_if_fail (g_bytes_get_size (buffer) > 0);
+ append_buffer (body, g_bytes_ref (buffer));
}
/**
@@ -537,15 +234,11 @@ soup_message_body_truncate (SoupMessageBody *body)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
- g_slist_free_full (priv->chunks, (GDestroyNotify)soup_buffer_free);
+ g_slist_free_full (priv->chunks, (GDestroyNotify)g_bytes_unref);
priv->chunks = priv->last = NULL;
priv->base_offset = 0;
-
- if (priv->flattened) {
- soup_buffer_free (priv->flattened);
- priv->flattened = NULL;
- body->data = NULL;
- }
+ g_clear_pointer (&priv->flattened, g_bytes_unref);
+ body->data = NULL;
body->length = 0;
}
@@ -559,7 +252,7 @@ soup_message_body_truncate (SoupMessageBody *body)
void
soup_message_body_complete (SoupMessageBody *body)
{
- append_buffer (body, soup_buffer_new (SOUP_MEMORY_STATIC, NULL, 0));
+ append_buffer (body, g_bytes_new_static (NULL, 0));
}
/**
@@ -570,16 +263,13 @@ soup_message_body_complete (SoupMessageBody *body)
* data in @body (plus an additional '\0' byte not counted by @body's
* length field).
*
- * Return value: a #SoupBuffer containing the same data as @body.
- * (You must free this buffer if you do not want it.)
+ * Return: (transfer full): a #GBytes containing the same data as @body.
+ * (You must g_bytes_unref() this if you do not want it.)
**/
-SoupBuffer *
+GBytes *
soup_message_body_flatten (SoupMessageBody *body)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
- char *buf, *ptr;
- GSList *iter;
- SoupBuffer *chunk;
g_return_val_if_fail (priv->accumulate == TRUE, NULL);
@@ -588,20 +278,22 @@ soup_message_body_flatten (SoupMessageBody *body)
g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
#endif
- buf = ptr = g_malloc (body->length + 1);
- for (iter = priv->chunks; iter; iter = iter->next) {
- chunk = iter->data;
- memcpy (ptr, chunk->data, chunk->length);
- ptr += chunk->length;
+ GByteArray *array = g_byte_array_sized_new (body->length + 1);
+ for (GSList *iter = priv->chunks; iter; iter = iter->next) {
+ GBytes *chunk = iter->data;
+ gsize chunk_size;
+ const guchar *chunk_data = g_bytes_get_data (chunk, &chunk_size);
+ g_byte_array_append (array, chunk_data, chunk_size);
}
- *ptr = '\0';
+ // NUL terminate the array but don't reflect that in the length
+ g_byte_array_append (array, (guchar*)"\0", 1);
+ array->len -= 1;
- priv->flattened = soup_buffer_new (SOUP_MEMORY_TAKE,
- buf, body->length);
- body->data = priv->flattened->data;
+ priv->flattened = g_byte_array_free_to_bytes (array);
+ body->data = g_bytes_get_data (priv->flattened, NULL);
}
- return soup_buffer_copy (priv->flattened);
+ return g_bytes_ref (priv->flattened);
}
/**
@@ -609,7 +301,7 @@ soup_message_body_flatten (SoupMessageBody *body)
* @body: a #SoupMessageBody
* @offset: an offset
*
- * Gets a #SoupBuffer containing data from @body starting at @offset.
+ * Gets a #GBytes containing data from @body starting at @offset.
* The size of the returned chunk is unspecified. You can iterate
* through the entire body by first calling
* soup_message_body_get_chunk() with an offset of 0, and then on each
@@ -625,44 +317,40 @@ soup_message_body_flatten (SoupMessageBody *body)
* @body may still potentially have more data, but that data is not
* currently available).
*
- * Return value: (nullable): a #SoupBuffer, or %NULL.
+ * Return value: (nullable): a #GBytes, or %NULL.
**/
-SoupBuffer *
+GBytes *
soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
GSList *iter;
- SoupBuffer *chunk = NULL;
+ GBytes *chunk = NULL;
offset -= priv->base_offset;
for (iter = priv->chunks; iter; iter = iter->next) {
chunk = iter->data;
+ gsize chunk_length = g_bytes_get_size (chunk);
- if (offset < chunk->length || offset == 0)
+ if (offset < chunk_length || offset == 0)
break;
- offset -= chunk->length;
+ offset -= chunk_length;
}
if (!iter)
return NULL;
- if (offset == 0)
- return soup_buffer_copy (chunk);
- else {
- return soup_buffer_new_subbuffer (chunk, offset,
- chunk->length - offset);
- }
+ return g_bytes_new_from_bytes (chunk, offset, g_bytes_get_size (chunk) - offset);
}
/**
* soup_message_body_got_chunk:
* @body: a #SoupMessageBody
- * @chunk: a #SoupBuffer received from the network
+ * @chunk: a #GBytes received from the network
*
* Handles the #SoupMessageBody part of receiving a chunk of data from
* the network. Normally this means appending @chunk to @body, exactly
- * as with soup_message_body_append_buffer(), but if you have set
+ * as with soup_message_body_append_bytes(), but if you have set
* @body's accumulate flag to %FALSE, then that will not happen.
*
* This is a low-level method which you should not normally need to
@@ -671,20 +359,20 @@ soup_message_body_get_chunk (SoupMessageBody *body, goffset offset)
* Since: 2.24
**/
void
-soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+soup_message_body_got_chunk (SoupMessageBody *body, GBytes *chunk)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
if (!priv->accumulate)
return;
- soup_message_body_append_buffer (body, chunk);
+ soup_message_body_append_bytes (body, chunk);
}
/**
* soup_message_body_wrote_chunk:
* @body: a #SoupMessageBody
- * @chunk: a #SoupBuffer returned from soup_message_body_get_chunk()
+ * @chunk: a #GBytes returned from soup_message_body_get_chunk()
*
* Handles the #SoupMessageBody part of writing a chunk of data to the
* network. Normally this is a no-op, but if you have set @body's
@@ -698,24 +386,24 @@ soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
* Since: 2.24
**/
void
-soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+soup_message_body_wrote_chunk (SoupMessageBody *body, GBytes *chunk)
{
SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
- SoupBuffer *chunk2;
+ GBytes *chunk2;
if (priv->accumulate)
return;
chunk2 = priv->chunks->data;
- g_return_if_fail (chunk->length == chunk2->length);
- g_return_if_fail (chunk == chunk2 || ((SoupBufferPrivate *)chunk2)->use == SOUP_MEMORY_TEMPORARY);
+ g_return_if_fail (g_bytes_get_size (chunk) == g_bytes_get_size (chunk2));
+ g_return_if_fail (chunk == chunk2);
priv->chunks = g_slist_remove (priv->chunks, chunk2);
if (!priv->chunks)
priv->last = NULL;
- priv->base_offset += chunk2->length;
- soup_buffer_free (chunk2);
+ priv->base_offset += g_bytes_get_size (chunk2);
+ g_bytes_unref (chunk2);
}
static SoupMessageBody *