Blame libsoup/soup-filter-input-stream.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-filter-input-stream.c
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright 2012 Red Hat, Inc.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#ifdef HAVE_CONFIG_H
Packit Service ca3877
#include <config.h>
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
#include <string.h>
Packit Service ca3877
Packit Service ca3877
#include "soup-filter-input-stream.h"
Packit Service ca3877
#include "soup.h"
Packit Service ca3877
Packit Service ca3877
/* This is essentially a subset of GDataInputStream, except that we
Packit Service ca3877
 * can do the equivalent of "fill_nonblocking()" on it. (We could use
Packit Service ca3877
 * an actual GDataInputStream, and implement the nonblocking semantics
Packit Service ca3877
 * via fill_async(), but that would be more work...)
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
struct _SoupFilterInputStreamPrivate {
Packit Service ca3877
	GByteArray *buf;
Packit Service ca3877
	gboolean need_more;
Packit Service ca3877
	gboolean in_read_until;
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
static void soup_filter_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
Packit Service ca3877
Packit Service ca3877
G_DEFINE_TYPE_WITH_CODE (SoupFilterInputStream, soup_filter_input_stream, G_TYPE_FILTER_INPUT_STREAM,
Packit Service ca3877
                         G_ADD_PRIVATE (SoupFilterInputStream)
Packit Service ca3877
			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_INPUT_STREAM,
Packit Service ca3877
						soup_filter_input_stream_pollable_init))
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_filter_input_stream_init (SoupFilterInputStream *stream)
Packit Service ca3877
{
Packit Service ca3877
	stream->priv = soup_filter_input_stream_get_instance_private (stream);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_filter_input_stream_finalize (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (object);
Packit Service ca3877
Packit Service ca3877
	g_clear_pointer (&fstream->priv->buf, g_byte_array_unref);
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_filter_input_stream_parent_class)->finalize (object);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gssize
Packit Service ca3877
read_from_buf (SoupFilterInputStream *fstream, gpointer buffer, gsize count)
Packit Service ca3877
{
Packit Service ca3877
	GByteArray *buf = fstream->priv->buf;
Packit Service ca3877
Packit Service ca3877
	if (buf->len < count)
Packit Service ca3877
		count = buf->len;
Packit Service ca3877
	memcpy (buffer, buf->data, count);
Packit Service ca3877
Packit Service ca3877
	if (count == buf->len) {
Packit Service ca3877
		g_byte_array_free (buf, TRUE);
Packit Service ca3877
		fstream->priv->buf = NULL;
Packit Service ca3877
	} else {
Packit Service ca3877
		memmove (buf->data, buf->data + count,
Packit Service ca3877
			 buf->len - count);
Packit Service ca3877
		g_byte_array_set_size (buf, buf->len - count);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	return count;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gssize
Packit Service ca3877
soup_filter_input_stream_read_fn (GInputStream  *stream,
Packit Service ca3877
				  void          *buffer,
Packit Service ca3877
				  gsize          count,
Packit Service ca3877
				  GCancellable  *cancellable,
Packit Service ca3877
				  GError       **error)
Packit Service ca3877
{
Packit Service ca3877
	SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
Packit Service ca3877
Packit Service ca3877
	if (!fstream->priv->in_read_until)
Packit Service ca3877
		fstream->priv->need_more = FALSE;
Packit Service ca3877
Packit Service ca3877
	if (fstream->priv->buf && !fstream->priv->in_read_until) {
Packit Service ca3877
		return read_from_buf (fstream, buffer, count);
Packit Service ca3877
	} else {
Packit Service ca3877
		return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
Packit Service ca3877
					       buffer, count,
Packit Service ca3877
					       TRUE, cancellable, error);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_filter_input_stream_is_readable (GPollableInputStream *stream)
Packit Service ca3877
{
Packit Service ca3877
	SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
Packit Service ca3877
Packit Service ca3877
	if (fstream->priv->buf && !fstream->priv->need_more)
Packit Service ca3877
		return TRUE;
Packit Service ca3877
	else
Packit Service ca3877
		return g_pollable_input_stream_is_readable (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (fstream)->base_stream));
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gssize
Packit Service ca3877
soup_filter_input_stream_read_nonblocking (GPollableInputStream  *stream,
Packit Service ca3877
					   void                  *buffer,
Packit Service ca3877
					   gsize                  count,
Packit Service ca3877
					   GError               **error)
Packit Service ca3877
{
Packit Service ca3877
	SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
Packit Service ca3877
Packit Service ca3877
	if (!fstream->priv->in_read_until)
Packit Service ca3877
		fstream->priv->need_more = FALSE;
Packit Service ca3877
Packit Service ca3877
	if (fstream->priv->buf && !fstream->priv->in_read_until) {
Packit Service ca3877
		return read_from_buf (fstream, buffer, count);
Packit Service ca3877
	} else {
Packit Service ca3877
		return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
Packit Service ca3877
					       buffer, count,
Packit Service ca3877
					       FALSE, NULL, error);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static GSource *
Packit Service ca3877
soup_filter_input_stream_create_source (GPollableInputStream *stream,
Packit Service ca3877
					GCancellable         *cancellable)
Packit Service ca3877
{
Packit Service ca3877
	SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
Packit Service ca3877
	GSource *base_source, *pollable_source;
Packit Service ca3877
Packit Service ca3877
	if (fstream->priv->buf && !fstream->priv->need_more)
Packit Service ca3877
		base_source = g_timeout_source_new (0);
Packit Service ca3877
	else
Packit Service ca3877
		base_source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (G_FILTER_INPUT_STREAM (fstream)->base_stream), cancellable);
Packit Service ca3877
Packit Service ca3877
	g_source_set_dummy_callback (base_source);
Packit Service ca3877
	pollable_source = g_pollable_source_new (G_OBJECT (stream));
Packit Service ca3877
	g_source_add_child_source (pollable_source, base_source);
Packit Service ca3877
	g_source_unref (base_source);
Packit Service ca3877
Packit Service ca3877
	return pollable_source;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_filter_input_stream_class_init (SoupFilterInputStreamClass *stream_class)
Packit Service ca3877
{
Packit Service ca3877
	GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
Packit Service ca3877
	GInputStreamClass *input_stream_class = G_INPUT_STREAM_CLASS (stream_class);
Packit Service ca3877
Packit Service ca3877
	object_class->finalize = soup_filter_input_stream_finalize;
Packit Service ca3877
Packit Service ca3877
	input_stream_class->read_fn = soup_filter_input_stream_read_fn;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_filter_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface,
Packit Service ca3877
					gpointer                       interface_data)
Packit Service ca3877
{
Packit Service ca3877
	pollable_interface->is_readable = soup_filter_input_stream_is_readable;
Packit Service ca3877
	pollable_interface->read_nonblocking = soup_filter_input_stream_read_nonblocking;
Packit Service ca3877
	pollable_interface->create_source = soup_filter_input_stream_create_source;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
GInputStream *
Packit Service ca3877
soup_filter_input_stream_new (GInputStream *base_stream)
Packit Service ca3877
{
Packit Service ca3877
	return g_object_new (SOUP_TYPE_FILTER_INPUT_STREAM,
Packit Service ca3877
			     "base-stream", base_stream,
Packit Service ca3877
			     "close-base-stream", FALSE,
Packit Service ca3877
			     NULL);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
gssize
Packit Service ca3877
soup_filter_input_stream_read_line (SoupFilterInputStream  *fstream,
Packit Service ca3877
				    void                   *buffer,
Packit Service ca3877
				    gsize                   length,
Packit Service ca3877
				    gboolean                blocking,
Packit Service ca3877
				    gboolean               *got_line,
Packit Service ca3877
				    GCancellable           *cancellable,
Packit Service ca3877
				    GError                **error)
Packit Service ca3877
{
Packit Service ca3877
	return soup_filter_input_stream_read_until (fstream, buffer, length,
Packit Service ca3877
						    "\n", 1, blocking,
Packit Service ca3877
						    TRUE, got_line,
Packit Service ca3877
						    cancellable, error);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
gssize
Packit Service ca3877
soup_filter_input_stream_read_until (SoupFilterInputStream  *fstream,
Packit Service ca3877
				     void                   *buffer,
Packit Service ca3877
				     gsize                   length,
Packit Service ca3877
				     const void             *boundary,
Packit Service ca3877
				     gsize                   boundary_length,
Packit Service ca3877
				     gboolean                blocking,
Packit Service ca3877
				     gboolean                include_boundary,
Packit Service ca3877
				     gboolean               *got_boundary,
Packit Service ca3877
				     GCancellable           *cancellable,
Packit Service ca3877
				     GError                **error)
Packit Service ca3877
{
Packit Service ca3877
	gssize nread, read_length;
Packit Service ca3877
	guint8 *p, *buf, *end;
Packit Service ca3877
	gboolean eof = FALSE;
Packit Service ca3877
	GError *my_error = NULL;
Packit Service ca3877
Packit Service ca3877
	g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (fstream), -1);
Packit Service ca3877
	g_return_val_if_fail (!include_boundary || (boundary_length < length), -1);
Packit Service ca3877
Packit Service ca3877
	*got_boundary = FALSE;
Packit Service ca3877
	fstream->priv->need_more = FALSE;
Packit Service ca3877
Packit Service ca3877
	if (!fstream->priv->buf || fstream->priv->buf->len < boundary_length) {
Packit Service ca3877
		guint prev_len;
Packit Service ca3877
Packit Service ca3877
	fill_buffer:
Packit Service ca3877
		if (!fstream->priv->buf)
Packit Service ca3877
			fstream->priv->buf = g_byte_array_new ();
Packit Service ca3877
		prev_len = fstream->priv->buf->len;
Packit Service ca3877
		g_byte_array_set_size (fstream->priv->buf, length);
Packit Service ca3877
		buf = fstream->priv->buf->data;
Packit Service ca3877
Packit Service ca3877
		fstream->priv->in_read_until = TRUE;
Packit Service ca3877
		nread = g_pollable_stream_read (G_INPUT_STREAM (fstream),
Packit Service ca3877
						buf + prev_len, length - prev_len,
Packit Service ca3877
						blocking,
Packit Service ca3877
						cancellable, &my_error);
Packit Service ca3877
		fstream->priv->in_read_until = FALSE;
Packit Service ca3877
		if (nread <= 0) {
Packit Service ca3877
			if (prev_len)
Packit Service ca3877
				fstream->priv->buf->len = prev_len;
Packit Service ca3877
			else {
Packit Service ca3877
				g_byte_array_free (fstream->priv->buf, TRUE);
Packit Service ca3877
				fstream->priv->buf = NULL;
Packit Service ca3877
			}
Packit Service ca3877
Packit Service ca3877
			if (nread == 0 && prev_len)
Packit Service ca3877
				eof = TRUE;
Packit Service ca3877
			else {
Packit Service ca3877
				if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
Packit Service ca3877
					fstream->priv->need_more = TRUE;
Packit Service ca3877
				if (my_error)
Packit Service ca3877
					g_propagate_error (error, my_error);
Packit Service ca3877
Packit Service ca3877
				return nread;
Packit Service ca3877
			}
Packit Service ca3877
Packit Service ca3877
			if (my_error)
Packit Service ca3877
				g_propagate_error (error, my_error);
Packit Service ca3877
		} else
Packit Service ca3877
			fstream->priv->buf->len = prev_len + nread;
Packit Service ca3877
	} else
Packit Service ca3877
		buf = fstream->priv->buf->data;
Packit Service ca3877
Packit Service ca3877
	/* Scan for the boundary within the range we can possibly return. */
Packit Service ca3877
	if (include_boundary)
Packit Service ca3877
		end = buf + MIN (fstream->priv->buf->len, length) - boundary_length;
Packit Service ca3877
	else
Packit Service ca3877
		end = buf + MIN (fstream->priv->buf->len - boundary_length, length);
Packit Service ca3877
	for (p = buf; p <= end; p++) {
Packit Service ca3877
		if (*p == *(guint8*)boundary &&
Packit Service ca3877
		    !memcmp (p, boundary, boundary_length)) {
Packit Service ca3877
			if (include_boundary)
Packit Service ca3877
				p += boundary_length;
Packit Service ca3877
			*got_boundary = TRUE;
Packit Service ca3877
			break;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (!*got_boundary && fstream->priv->buf->len < length && !eof)
Packit Service ca3877
		goto fill_buffer;
Packit Service ca3877
Packit Service ca3877
	if (eof && !*got_boundary)
Packit Service ca3877
		read_length = MIN (fstream->priv->buf->len, length);
Packit Service ca3877
	else
Packit Service ca3877
		read_length = p - buf;
Packit Service ca3877
	return read_from_buf (fstream, buffer, read_length);
Packit Service ca3877
}