Blame libsoup/soup-body-output-stream.c

rpm-build 4f3c61
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
rpm-build 4f3c61
/*
rpm-build 4f3c61
 * soup-body-output-stream.c
rpm-build 4f3c61
 *
rpm-build 4f3c61
 * Copyright 2012 Red Hat, Inc.
rpm-build 4f3c61
 */
rpm-build 4f3c61
rpm-build 4f3c61
#ifdef HAVE_CONFIG_H
rpm-build 4f3c61
#include <config.h>
rpm-build 4f3c61
#endif
rpm-build 4f3c61
rpm-build 4f3c61
#include <string.h>
rpm-build 4f3c61
rpm-build 4f3c61
#include "soup-body-output-stream.h"
rpm-build 4f3c61
#include "soup.h"
rpm-build 4f3c61
rpm-build 4f3c61
typedef enum {
rpm-build 4f3c61
	SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE,
rpm-build 4f3c61
	SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END,
rpm-build 4f3c61
	SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK,
rpm-build 4f3c61
	SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS,
rpm-build 4f3c61
	SOUP_BODY_OUTPUT_STREAM_STATE_DONE
rpm-build 4f3c61
} SoupBodyOutputStreamState;
rpm-build 4f3c61
rpm-build 4f3c61
struct _SoupBodyOutputStreamPrivate {
rpm-build 4f3c61
	GOutputStream *base_stream;
rpm-build 4f3c61
	char           buf[20];
rpm-build 4f3c61
rpm-build 4f3c61
	SoupEncoding   encoding;
rpm-build 4f3c61
	goffset        write_length;
rpm-build 4f3c61
	goffset        written;
rpm-build 4f3c61
	SoupBodyOutputStreamState chunked_state;
rpm-build 4f3c61
	gboolean       eof;
rpm-build 4f3c61
};
rpm-build 4f3c61
rpm-build 4f3c61
enum {
rpm-build 4f3c61
	PROP_0,
rpm-build 4f3c61
rpm-build 4f3c61
	PROP_ENCODING,
rpm-build 4f3c61
	PROP_CONTENT_LENGTH
rpm-build 4f3c61
};
rpm-build 4f3c61
rpm-build 4f3c61
static void soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, gpointer interface_data);
rpm-build 4f3c61
rpm-build 4f3c61
G_DEFINE_TYPE_WITH_CODE (SoupBodyOutputStream, soup_body_output_stream, G_TYPE_FILTER_OUTPUT_STREAM,
rpm-build 4f3c61
                         G_ADD_PRIVATE (SoupBodyOutputStream)
rpm-build 4f3c61
			 G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
rpm-build 4f3c61
						soup_body_output_stream_pollable_init))
rpm-build 4f3c61
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_init (SoupBodyOutputStream *stream)
rpm-build 4f3c61
{
rpm-build 4f3c61
	stream->priv = soup_body_output_stream_get_instance_private (stream);
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_constructed (GObject *object)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (bostream));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_set_property (GObject *object, guint prop_id,
rpm-build 4f3c61
				      const GValue *value, GParamSpec *pspec)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	switch (prop_id) {
rpm-build 4f3c61
	case PROP_ENCODING:
rpm-build 4f3c61
		bostream->priv->encoding = g_value_get_enum (value);
rpm-build 4f3c61
		if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED)
rpm-build 4f3c61
			bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	case PROP_CONTENT_LENGTH:
rpm-build 4f3c61
		bostream->priv->write_length = g_value_get_uint64 (value);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_get_property (GObject *object, guint prop_id,
rpm-build 4f3c61
				      GValue *value, GParamSpec *pspec)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
rpm-build 4f3c61
rpm-build 4f3c61
	switch (prop_id) {
rpm-build 4f3c61
	case PROP_ENCODING:
rpm-build 4f3c61
		g_value_set_enum (value, bostream->priv->encoding);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
rpm-build 4f3c61
		break;
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_body_output_stream_write_raw (SoupBodyOutputStream  *bostream,
rpm-build 4f3c61
				   const void            *buffer,
rpm-build 4f3c61
				   gsize                  count,
rpm-build 4f3c61
				   gboolean               blocking,
rpm-build 4f3c61
				   GCancellable          *cancellable,
rpm-build 4f3c61
				   GError               **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	gssize nwrote, my_count;
rpm-build 4f3c61
rpm-build 4f3c61
	/* If the caller tries to write too much to a Content-Length
rpm-build 4f3c61
	 * encoded stream, we truncate at the right point, but keep
rpm-build 4f3c61
	 * accepting additional data until they stop.
rpm-build 4f3c61
	 */
rpm-build 4f3c61
	if (bostream->priv->write_length) {
rpm-build 4f3c61
		my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
rpm-build 4f3c61
		if (my_count == 0) {
rpm-build 4f3c61
			bostream->priv->eof = TRUE;
rpm-build 4f3c61
			return count;
rpm-build 4f3c61
		}
rpm-build 4f3c61
	} else
rpm-build 4f3c61
		my_count = count;
rpm-build 4f3c61
rpm-build 4f3c61
	nwrote = g_pollable_stream_write (bostream->priv->base_stream,
rpm-build 4f3c61
					  buffer, my_count,
rpm-build 4f3c61
					  blocking, cancellable, error);
rpm-build 4f3c61
rpm-build 4f3c61
	if (nwrote > 0 && bostream->priv->write_length)
rpm-build 4f3c61
		bostream->priv->written += nwrote;
rpm-build 4f3c61
rpm-build 4f3c61
	if (nwrote == my_count && my_count != count)
rpm-build 4f3c61
		nwrote = count;
rpm-build 4f3c61
rpm-build 4f3c61
	return nwrote;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_body_output_stream_write_chunked (SoupBodyOutputStream  *bostream,
rpm-build 4f3c61
				       const void            *buffer,
rpm-build 4f3c61
				       gsize                  count,
rpm-build 4f3c61
				       gboolean               blocking,
rpm-build 4f3c61
				       GCancellable          *cancellable,
rpm-build 4f3c61
				       GError               **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	char *buf = bostream->priv->buf;
rpm-build 4f3c61
	gssize nwrote, len;
rpm-build 4f3c61
rpm-build 4f3c61
again:
rpm-build 4f3c61
	len = strlen (buf);
rpm-build 4f3c61
	if (len) {
rpm-build 4f3c61
		nwrote = g_pollable_stream_write (bostream->priv->base_stream,
rpm-build 4f3c61
						  buf, len, blocking,
rpm-build 4f3c61
						  cancellable, error);
rpm-build 4f3c61
		if (nwrote < 0)
rpm-build 4f3c61
			return nwrote;
rpm-build 4f3c61
		memmove (buf, buf + nwrote, len + 1 - nwrote);
rpm-build 4f3c61
		goto again;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	switch (bostream->priv->chunked_state) {
rpm-build 4f3c61
	case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE:
rpm-build 4f3c61
		g_snprintf (buf, sizeof (bostream->priv->buf),
rpm-build 4f3c61
			    "%lx\r\n", (gulong)count);
rpm-build 4f3c61
rpm-build 4f3c61
		if (count > 0)
rpm-build 4f3c61
			bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK;
rpm-build 4f3c61
		else
rpm-build 4f3c61
			bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS;
rpm-build 4f3c61
		break;
rpm-build 4f3c61
rpm-build 4f3c61
	case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK:
rpm-build 4f3c61
		nwrote = g_pollable_stream_write (bostream->priv->base_stream,
rpm-build 4f3c61
						  buffer, count, blocking,
rpm-build 4f3c61
						  cancellable, error);
rpm-build 4f3c61
		if (nwrote < (gssize)count)
rpm-build 4f3c61
			return nwrote;
rpm-build 4f3c61
rpm-build 4f3c61
		bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END;
rpm-build 4f3c61
		break;
rpm-build 4f3c61
rpm-build 4f3c61
	case SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_END:
rpm-build 4f3c61
		strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
rpm-build 4f3c61
		bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
rpm-build 4f3c61
		break;
rpm-build 4f3c61
rpm-build 4f3c61
	case SOUP_BODY_OUTPUT_STREAM_STATE_TRAILERS:
rpm-build 4f3c61
		strncpy (buf, "\r\n", sizeof (bostream->priv->buf));
rpm-build 4f3c61
		bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_DONE;
rpm-build 4f3c61
		break;
rpm-build 4f3c61
rpm-build 4f3c61
	case SOUP_BODY_OUTPUT_STREAM_STATE_DONE:
rpm-build 4f3c61
		bostream->priv->chunked_state = SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE;
rpm-build 4f3c61
		return count;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	goto again;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_body_output_stream_write_fn (GOutputStream  *stream,
rpm-build 4f3c61
				  const void     *buffer,
rpm-build 4f3c61
				  gsize           count,
rpm-build 4f3c61
				  GCancellable   *cancellable,
rpm-build 4f3c61
				  GError        **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
rpm-build 4f3c61
rpm-build 4f3c61
	if (bostream->priv->eof)
rpm-build 4f3c61
		return count;
rpm-build 4f3c61
rpm-build 4f3c61
	switch (bostream->priv->encoding) {
rpm-build 4f3c61
	case SOUP_ENCODING_CHUNKED:
rpm-build 4f3c61
		return soup_body_output_stream_write_chunked (bostream, buffer, count,
rpm-build 4f3c61
							      TRUE, cancellable, error);
rpm-build 4f3c61
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		return soup_body_output_stream_write_raw (bostream, buffer, count,
rpm-build 4f3c61
							  TRUE, cancellable, error);
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gboolean
rpm-build 4f3c61
soup_body_output_stream_close_fn (GOutputStream  *stream,
rpm-build 4f3c61
				  GCancellable   *cancellable,
rpm-build 4f3c61
				  GError        **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
rpm-build 4f3c61
rpm-build 4f3c61
	if (bostream->priv->encoding == SOUP_ENCODING_CHUNKED &&
rpm-build 4f3c61
	    bostream->priv->chunked_state == SOUP_BODY_OUTPUT_STREAM_STATE_CHUNK_SIZE) {
rpm-build 4f3c61
		if (soup_body_output_stream_write_chunked (bostream, NULL, 0, TRUE, cancellable, error) == -1)
rpm-build 4f3c61
			return FALSE;
rpm-build 4f3c61
	}
rpm-build 4f3c61
rpm-build 4f3c61
	return G_OUTPUT_STREAM_CLASS (soup_body_output_stream_parent_class)->close_fn (stream, cancellable, error);
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gboolean
rpm-build 4f3c61
soup_body_output_stream_is_writable (GPollableOutputStream *stream)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
rpm-build 4f3c61
rpm-build 4f3c61
	return bostream->priv->eof ||
rpm-build 4f3c61
		g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static gssize
rpm-build 4f3c61
soup_body_output_stream_write_nonblocking (GPollableOutputStream  *stream,
rpm-build 4f3c61
					   const void             *buffer,
rpm-build 4f3c61
					   gsize                   count,
rpm-build 4f3c61
					   GError                **error)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
rpm-build 4f3c61
rpm-build 4f3c61
	if (bostream->priv->eof)
rpm-build 4f3c61
		return count;
rpm-build 4f3c61
rpm-build 4f3c61
	switch (bostream->priv->encoding) {
rpm-build 4f3c61
	case SOUP_ENCODING_CHUNKED:
rpm-build 4f3c61
		return soup_body_output_stream_write_chunked (bostream, buffer, count,
rpm-build 4f3c61
							      FALSE, NULL, error);
rpm-build 4f3c61
rpm-build 4f3c61
	default:
rpm-build 4f3c61
		return soup_body_output_stream_write_raw (bostream, buffer, count,
rpm-build 4f3c61
							  FALSE, NULL, error);
rpm-build 4f3c61
	}
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static GSource *
rpm-build 4f3c61
soup_body_output_stream_create_source (GPollableOutputStream *stream,
rpm-build 4f3c61
				       GCancellable *cancellable)
rpm-build 4f3c61
{
rpm-build 4f3c61
	SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (stream);
rpm-build 4f3c61
	GSource *base_source, *pollable_source;
rpm-build 4f3c61
rpm-build 4f3c61
	if (bostream->priv->eof)
rpm-build 4f3c61
		base_source = g_timeout_source_new (0);
rpm-build 4f3c61
	else
rpm-build 4f3c61
		base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (bostream->priv->base_stream), cancellable);
rpm-build 4f3c61
	g_source_set_dummy_callback (base_source);
rpm-build 4f3c61
rpm-build 4f3c61
	pollable_source = g_pollable_source_new (G_OBJECT (stream));
rpm-build 4f3c61
	g_source_add_child_source (pollable_source, base_source);
rpm-build 4f3c61
	g_source_unref (base_source);
rpm-build 4f3c61
rpm-build 4f3c61
	return pollable_source;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
rpm-build 4f3c61
{
rpm-build 4f3c61
	GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
rpm-build 4f3c61
	GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
rpm-build 4f3c61
rpm-build 4f3c61
	object_class->constructed = soup_body_output_stream_constructed;
rpm-build 4f3c61
	object_class->set_property = soup_body_output_stream_set_property;
rpm-build 4f3c61
	object_class->get_property = soup_body_output_stream_get_property;
rpm-build 4f3c61
rpm-build 4f3c61
	output_stream_class->write_fn = soup_body_output_stream_write_fn;
rpm-build 4f3c61
	output_stream_class->close_fn = soup_body_output_stream_close_fn;
rpm-build 4f3c61
rpm-build 4f3c61
	g_object_class_install_property (
rpm-build 4f3c61
		object_class, PROP_ENCODING,
rpm-build 4f3c61
		g_param_spec_enum ("encoding",
rpm-build 4f3c61
				   "Encoding",
rpm-build 4f3c61
				   "Message body encoding",
rpm-build 4f3c61
				   SOUP_TYPE_ENCODING,
rpm-build 4f3c61
				   SOUP_ENCODING_NONE,
rpm-build 4f3c61
				   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
rpm-build 4f3c61
	g_object_class_install_property (
rpm-build 4f3c61
		object_class, PROP_CONTENT_LENGTH,
rpm-build 4f3c61
		g_param_spec_uint64 ("content-length",
rpm-build 4f3c61
				     "Content-Length",
rpm-build 4f3c61
				     "Message body Content-Length",
rpm-build 4f3c61
				     0, G_MAXUINT64, 0,
rpm-build 4f3c61
				     G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
static void
rpm-build 4f3c61
soup_body_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
rpm-build 4f3c61
				       gpointer interface_data)
rpm-build 4f3c61
{
rpm-build 4f3c61
	pollable_interface->is_writable = soup_body_output_stream_is_writable;
rpm-build 4f3c61
	pollable_interface->write_nonblocking = soup_body_output_stream_write_nonblocking;
rpm-build 4f3c61
	pollable_interface->create_source = soup_body_output_stream_create_source;
rpm-build 4f3c61
}
rpm-build 4f3c61
rpm-build 4f3c61
GOutputStream *
rpm-build 4f3c61
soup_body_output_stream_new (GOutputStream *base_stream,
rpm-build 4f3c61
			     SoupEncoding   encoding,
rpm-build 4f3c61
			     goffset        content_length)
rpm-build 4f3c61
{
rpm-build 4f3c61
	return g_object_new (SOUP_TYPE_BODY_OUTPUT_STREAM,
rpm-build 4f3c61
			     "base-stream", base_stream,
rpm-build 4f3c61
			     "close-base-stream", FALSE,
rpm-build 4f3c61
			     "encoding", encoding,
rpm-build 4f3c61
			     "content-length", content_length,
rpm-build 4f3c61
			     NULL);
rpm-build 4f3c61
}