[libsoup/coding: 1/2] Content-Encoding support



commit bcda64751ce13b8c5513b6f8ec3f2a8814d0d23c
Author: Dan Winship <danw gnome org>
Date:   Sat Mar 15 09:52:35 2008 -0400

    Content-Encoding support

 libsoup/Makefile.am            |   10 ++
 libsoup/soup-coding-deflate.c  |  139 +++++++++++++++++++++
 libsoup/soup-coding-deflate.h  |   33 +++++
 libsoup/soup-coding-gzip.c     |   49 ++++++++
 libsoup/soup-coding-gzip.h     |   33 +++++
 libsoup/soup-coding-zlib.c     |  147 +++++++++++++++++++++++
 libsoup/soup-coding-zlib.h     |   34 ++++++
 libsoup/soup-coding.c          |  258 ++++++++++++++++++++++++++++++++++++++++
 libsoup/soup-coding.h          |   92 ++++++++++++++
 libsoup/soup-content-decoder.c |  168 ++++++++++++++++++++++++++
 libsoup/soup-content-decoder.h |   44 +++++++
 libsoup/soup-message-io.c      |   41 ++++++-
 libsoup/soup-message-private.h |    1 +
 libsoup/soup-message.c         |   12 ++
 libsoup/soup-message.h         |    3 +-
 libsoup/soup.h                 |    1 +
 tests/get.c                    |   31 +++++-
 tests/httpd.conf.in            |    8 ++
 18 files changed, 1099 insertions(+), 5 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index c25b534..4786498 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -56,6 +56,7 @@ soup_headers =			\
 	soup-auth-domain.h	\
 	soup-auth-domain-basic.h  \
 	soup-auth-domain-digest.h \
+	soup-content-decoder.h  \
 	soup-content-sniffer.h  \
 	soup-cookie.h		\
 	soup-cookie-jar.h	\
@@ -120,8 +121,17 @@ libsoup_2_4_la_SOURCES =		\
 	soup-auth-manager.c		\
 	soup-auth-manager-ntlm.h	\
 	soup-auth-manager-ntlm.c	\
+	soup-coding.h			\
+	soup-coding.c			\
+	soup-coding-deflate.h		\
+	soup-coding-deflate.c		\
+	soup-coding-gzip.h		\
+	soup-coding-gzip.c		\
+	soup-coding-zlib.h		\
+	soup-coding-zlib.c		\
 	soup-connection.h		\
 	soup-connection.c		\
+	soup-content-decoder.c		\
 	soup-content-sniffer.c		\
 	soup-cookie.c			\
 	soup-cookie-jar.c		\
diff --git a/libsoup/soup-coding-deflate.c b/libsoup/soup-coding-deflate.c
new file mode 100644
index 0000000..68e8955
--- /dev/null
+++ b/libsoup/soup-coding-deflate.c
@@ -0,0 +1,139 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-coding-deflate.c: "deflate" coding
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-coding-deflate.h"
+
+typedef struct {
+	gboolean decode_inited, havebyte;
+	guchar byte[2];
+
+} SoupCodingDeflatePrivate;
+#define SOUP_CODING_DEFLATE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CODING_DEFLATE, SoupCodingDeflatePrivate))
+
+G_DEFINE_TYPE (SoupCodingDeflate, soup_coding_deflate, SOUP_TYPE_CODING_ZLIB)
+
+static SoupCodingClass *zlib_class;
+
+static void             constructed (GObject *object);
+static SoupCodingStatus apply_into  (SoupCoding *coding,
+				     gconstpointer input, gsize input_length,
+				     gsize *input_used,
+				     gpointer output, gsize output_length,
+				     gsize *output_used,
+				     gboolean done, GError **error);
+
+static void
+soup_coding_deflate_init (SoupCodingDeflate *deflate)
+{
+}
+
+static void
+soup_coding_deflate_class_init (SoupCodingDeflateClass *deflate_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (deflate_class);
+	SoupCodingClass *coding_class = SOUP_CODING_CLASS (deflate_class);
+
+	g_type_class_add_private (deflate_class, sizeof (SoupCodingDeflatePrivate));
+
+	coding_class->name = "deflate";
+
+	zlib_class = SOUP_CODING_CLASS (soup_coding_deflate_parent_class);
+
+	object_class->constructed = constructed;
+
+	coding_class->apply_into  = apply_into;
+}
+
+static void
+constructed (GObject *object)
+{
+	if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE)
+		soup_coding_zlib_init_encode (SOUP_CODING_ZLIB (object), 15);
+}
+
+static SoupCodingStatus
+apply_into (SoupCoding *coding,
+	    gconstpointer input, gsize input_length, gsize *input_used,
+	    gpointer output, gsize output_length, gsize *output_used,
+	    gboolean done, GError **error)
+{
+	SoupCodingDeflatePrivate *priv =
+		SOUP_CODING_DEFLATE_GET_PRIVATE (coding);
+
+	if (coding->direction == SOUP_CODING_DECODE && !priv->decode_inited) {
+		/* The "deflate" content/transfer coding is supposed
+		 * to be the RFC1950 "zlib" format wrapping the
+		 * RFC1951 "deflate" method. However, apparently many
+		 * implementations have gotten this wrong and used raw
+		 * "deflate" instead. So we check the first two bytes
+		 * to see if they look like a zlib header, and fall
+		 * back to raw deflate if they don't. This is slightly
+		 * tricky because in the pathological case, we might
+		 * be called with only a single byte on the first
+		 * call.
+		 */
+
+		if (input_length == 0) {
+			*input_used = *output_used = 0;
+			return SOUP_CODING_STATUS_OK;
+		} else if (input_length == 1 && !priv->havebyte) {
+			priv->byte[0] = ((guchar *)input)[0];
+			priv->havebyte = TRUE;
+			*input_used = 1;
+			*output_used = 0;
+			return SOUP_CODING_STATUS_OK;
+		}
+
+		/* OK, one way or another we have two bytes now. */
+		if (priv->havebyte)
+			priv->byte[1] = ((guchar *)input)[0];
+		else {
+			priv->byte[0] = ((guchar *)input)[0];
+			priv->byte[1] = ((guchar *)input)[1];
+		}
+
+		/* A "zlib" stream starts with 0x8 in the lower three
+		 * bits of the first byte, and the first two bytes
+		 * together taken as a big-endian integer are
+		 * divisible by 31.
+		 */
+		if (((priv->byte[0] & 0x8) == 0x8) &&
+		    ((priv->byte[0] << 8 & priv->byte[1]) % 31 == 0)) {
+			/* "zlib" */
+			soup_coding_zlib_init_decode (SOUP_CODING_ZLIB (coding), 15);
+		} else {
+			/* "deflate" */
+			soup_coding_zlib_init_decode (SOUP_CODING_ZLIB (coding), -15);
+		}
+
+		priv->decode_inited = TRUE;
+
+		if (priv->havebyte) {
+			gsize dummy_input_used;
+
+			/* Push the previously-read byte into the
+			 * coder and then force the caller to call us
+			 * again with the same input data.
+			 */
+			*input_used = 0;
+			return zlib_class->apply_into (
+				coding, priv->byte, 1, &dummy_input_used,
+				output, output_length, output_used,
+				done, error);
+		}
+	}
+
+	return zlib_class->apply_into (
+		coding, input, input_length, input_used,
+		output, output_length, output_used,
+		done, error);
+}
diff --git a/libsoup/soup-coding-deflate.h b/libsoup/soup-coding-deflate.h
new file mode 100644
index 0000000..748c44b
--- /dev/null
+++ b/libsoup/soup-coding-deflate.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CODING_DEFLATE_H
+#define SOUP_CODING_DEFLATE_H 1
+
+#include "soup-coding-zlib.h"
+
+#define SOUP_TYPE_CODING_DEFLATE            (soup_coding_deflate_get_type ())
+#define SOUP_CODING_DEFLATE(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CODING_DEFLATE, SoupCodingDeflate))
+#define SOUP_CODING_DEFLATE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CODING_DEFLATE, SoupCodingDeflateClass))
+#define SOUP_IS_CODING_DEFLATE(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CODING_DEFLATE))
+#define SOUP_IS_CODING_DEFLATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CODING_DEFLATE))
+#define SOUP_CODING_DEFLATE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CODING_DEFLATE, SoupCodingDeflateClass))
+
+typedef struct {
+	SoupCodingZLib parent;
+
+} SoupCodingDeflate;
+
+typedef struct {
+	SoupCodingZLibClass  parent_class;
+
+} SoupCodingDeflateClass;
+
+GType soup_coding_deflate_get_type (void);
+
+SoupCoding *soup_coding_deflate_new (SoupCodingDirection direction);
+
+#endif /* SOUP_CODING_DEFLATE_H */
diff --git a/libsoup/soup-coding-gzip.c b/libsoup/soup-coding-gzip.c
new file mode 100644
index 0000000..193a66f
--- /dev/null
+++ b/libsoup/soup-coding-gzip.c
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-coding-gzip.c: "gzip" coding
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-coding-gzip.h"
+
+G_DEFINE_TYPE (SoupCodingGzip, soup_coding_gzip, SOUP_TYPE_CODING_ZLIB)
+
+static void constructed (GObject *object);
+
+static void
+soup_coding_gzip_init (SoupCodingGzip *gzip)
+{
+}
+
+static void
+soup_coding_gzip_class_init (SoupCodingGzipClass *gzip_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (gzip_class);
+	SoupCodingClass *coding_class = SOUP_CODING_CLASS (gzip_class);
+
+	coding_class->name = "gzip";
+
+	object_class->constructed = constructed;
+}
+
+static void
+constructed (GObject *object)
+{
+	guint flags;
+
+	/* More zlib magic numbers: 15 just because, and 16 to indicate
+	 * gzip format.
+	 */
+	flags = 15 | 16;
+
+	if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE)
+		soup_coding_zlib_init_encode (SOUP_CODING_ZLIB (object), flags);
+	else
+		soup_coding_zlib_init_decode (SOUP_CODING_ZLIB (object), flags);
+}
diff --git a/libsoup/soup-coding-gzip.h b/libsoup/soup-coding-gzip.h
new file mode 100644
index 0000000..51d3467
--- /dev/null
+++ b/libsoup/soup-coding-gzip.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CODING_GZIP_H
+#define SOUP_CODING_GZIP_H 1
+
+#include "soup-coding-zlib.h"
+
+#define SOUP_TYPE_CODING_GZIP            (soup_coding_gzip_get_type ())
+#define SOUP_CODING_GZIP(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CODING_GZIP, SoupCodingGzip))
+#define SOUP_CODING_GZIP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CODING_GZIP, SoupCodingGzipClass))
+#define SOUP_IS_CODING_GZIP(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CODING_GZIP))
+#define SOUP_IS_CODING_GZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CODING_GZIP))
+#define SOUP_CODING_GZIP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CODING_GZIP, SoupCodingGzipClass))
+
+typedef struct {
+	SoupCodingZLib parent;
+
+} SoupCodingGzip;
+
+typedef struct {
+	SoupCodingZLibClass  parent_class;
+
+} SoupCodingGzipClass;
+
+GType soup_coding_gzip_get_type (void);
+
+SoupCoding *soup_coding_gzip_new (SoupCodingDirection direction);
+
+#endif /* SOUP_CODING_GZIP_H */
diff --git a/libsoup/soup-coding-zlib.c b/libsoup/soup-coding-zlib.c
new file mode 100644
index 0000000..a1db0ab
--- /dev/null
+++ b/libsoup/soup-coding-zlib.c
@@ -0,0 +1,147 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-coding-zlib.c: Base class for gzip and deflate codings
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-coding-zlib.h"
+
+#include <zlib.h>
+
+typedef struct {
+	z_stream stream;
+
+} SoupCodingZLibPrivate;
+#define SOUP_CODING_ZLIB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_CODING_ZLIB, SoupCodingZLibPrivate))
+
+G_DEFINE_TYPE (SoupCodingZLib, soup_coding_zlib, SOUP_TYPE_CODING)
+
+static SoupCodingStatus apply_into (SoupCoding *coding,
+				    gconstpointer input, gsize input_length,
+				    gsize *input_used,
+				    gpointer output, gsize output_length,
+				    gsize *output_used,
+				    gboolean done, GError **error);
+
+static void finalize (GObject *object);
+
+static void
+soup_coding_zlib_init (SoupCodingZLib *coding)
+{
+	SoupCodingZLibPrivate *priv = SOUP_CODING_ZLIB_GET_PRIVATE (coding);
+
+	priv->stream.zalloc = Z_NULL;
+	priv->stream.zfree = Z_NULL;
+	priv->stream.opaque = Z_NULL;
+}
+
+static void
+soup_coding_zlib_class_init (SoupCodingZLibClass *zlib_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (zlib_class);
+	SoupCodingClass *coding_class = SOUP_CODING_CLASS (zlib_class);
+
+	g_type_class_add_private (zlib_class, sizeof (SoupCodingZLibPrivate));
+
+	object_class->finalize = finalize;
+
+	coding_class->apply_into = apply_into;
+}
+
+static void
+finalize (GObject *object)
+{
+	SoupCodingZLibPrivate *priv = SOUP_CODING_ZLIB_GET_PRIVATE (object);
+
+	if (SOUP_CODING (object)->direction == SOUP_CODING_ENCODE)
+		deflateEnd (&priv->stream);
+	else
+		inflateEnd (&priv->stream);
+
+	G_OBJECT_CLASS (soup_coding_zlib_parent_class)->finalize (object);
+}
+
+void
+soup_coding_zlib_init_encode (SoupCodingZLib *coding, int flags)
+{
+	SoupCodingZLibPrivate *priv = SOUP_CODING_ZLIB_GET_PRIVATE (coding);
+
+	priv->stream.next_in = Z_NULL;
+	priv->stream.avail_in = 0;
+	/* All of these values are the defaults according to the zlib
+	 * documentation. "flags" distinguishes between "zlib" and
+	 * "gzip" formats.
+	 */
+	deflateInit2 (&priv->stream, Z_DEFAULT_COMPRESSION,
+		      Z_DEFLATED, flags, 8, Z_DEFAULT_STRATEGY);
+}
+
+void
+soup_coding_zlib_init_decode (SoupCodingZLib *coding, int flags)
+{
+	SoupCodingZLibPrivate *priv = SOUP_CODING_ZLIB_GET_PRIVATE (coding);
+
+	priv->stream.next_in = Z_NULL;
+	priv->stream.avail_in = 0;
+	inflateInit2 (&priv->stream, flags);
+}
+
+static SoupCodingStatus
+apply_into (SoupCoding *coding,
+	    gconstpointer input, gsize input_length, gsize *input_used,
+	    gpointer output, gsize output_length, gsize *output_used,
+	    gboolean done, GError **error)
+{
+	SoupCodingZLibPrivate *priv = SOUP_CODING_ZLIB_GET_PRIVATE (coding);
+	int ret;
+
+	priv->stream.avail_in = input_length;
+	priv->stream.next_in  = (gpointer)input;
+	priv->stream.total_in = 0;
+
+	priv->stream.avail_out = output_length;
+	priv->stream.next_out  = output;
+	priv->stream.total_out = 0;
+
+	if (coding->direction == SOUP_CODING_ENCODE)
+		ret = deflate (&priv->stream, done ? Z_FINISH : Z_NO_FLUSH);
+	else
+		ret = inflate (&priv->stream, Z_SYNC_FLUSH);
+
+	*input_used = priv->stream.total_in;
+	*output_used = priv->stream.total_out;
+
+	switch (ret) {
+	case Z_NEED_DICT:
+	case Z_DATA_ERROR:
+		g_set_error_literal (error, SOUP_CODING_ERROR,
+				     SOUP_CODING_ERROR_DATA_ERROR,
+				     priv->stream.msg ? priv->stream.msg : "Bad data");
+		return SOUP_CODING_STATUS_ERROR;
+
+	case Z_BUF_ERROR:
+	case Z_STREAM_ERROR:
+	case Z_MEM_ERROR:
+		g_set_error_literal (error, SOUP_CODING_ERROR,
+				     SOUP_CODING_ERROR_INTERNAL_ERROR,
+				     priv->stream.msg ? priv->stream.msg : "Internal error");
+		return SOUP_CODING_STATUS_ERROR;
+
+	case Z_STREAM_END:
+		return SOUP_CODING_STATUS_COMPLETE;
+
+	case Z_OK:
+	default:
+		if (*output_used == output_length &&
+		    *input_used < input_length)
+			return SOUP_CODING_STATUS_NEED_SPACE;
+		else
+			return SOUP_CODING_STATUS_OK;
+	}
+}
diff --git a/libsoup/soup-coding-zlib.h b/libsoup/soup-coding-zlib.h
new file mode 100644
index 0000000..0c9c226
--- /dev/null
+++ b/libsoup/soup-coding-zlib.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CODING_ZLIB_H
+#define SOUP_CODING_ZLIB_H 1
+
+#include "soup-coding.h"
+
+#define SOUP_TYPE_CODING_ZLIB            (soup_coding_zlib_get_type ())
+#define SOUP_CODING_ZLIB(object)         (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CODING_ZLIB, SoupCodingZLib))
+#define SOUP_CODING_ZLIB_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CODING_ZLIB, SoupCodingZLibClass))
+#define SOUP_IS_CODING_ZLIB(object)      (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CODING_ZLIB))
+#define SOUP_IS_CODING_ZLIB_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CODING_ZLIB))
+#define SOUP_CODING_ZLIB_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CODING_ZLIB, SoupCodingZLibClass))
+
+typedef struct {
+	SoupCoding parent;
+
+} SoupCodingZLib;
+
+typedef struct {
+	SoupCodingClass  parent_class;
+
+} SoupCodingZLibClass;
+
+GType soup_coding_zlib_get_type (void);
+
+void soup_coding_zlib_init_encode (SoupCodingZLib *coding, int flags);
+void soup_coding_zlib_init_decode (SoupCodingZLib *coding, int flags);
+
+#endif /* SOUP_CODING_ZLIB_H */
diff --git a/libsoup/soup-coding.c b/libsoup/soup-coding.c
new file mode 100644
index 0000000..74c4826
--- /dev/null
+++ b/libsoup/soup-coding.c
@@ -0,0 +1,258 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-coding.c: Data encoding/decoding class
+ *
+ * Copyright (C) 2005 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-coding.h"
+#include "soup-enum-types.h"
+#include "soup-session-feature.h"
+
+static void soup_coding_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SoupCoding, soup_coding, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+						soup_coding_session_feature_init))
+
+enum {
+	PROP_0,
+
+	PROP_DIRECTION,
+
+	LAST_PROP
+};
+
+static void set_property (GObject *object, guint prop_id,
+			  const GValue *value, GParamSpec *pspec);
+static void get_property (GObject *object, guint prop_id,
+			  GValue *value, GParamSpec *pspec);
+
+static SoupBuffer *apply (SoupCoding *coding,
+			  gconstpointer input, gsize input_length,
+			  gboolean done, GError **error);
+
+static void
+soup_coding_class_init (SoupCodingClass *coding_class)
+{
+	GObjectClass *object_class = (GObjectClass *)coding_class;
+
+	object_class->set_property = set_property;
+	object_class->get_property = get_property;
+
+	coding_class->apply = apply;
+
+	/* properties */
+	g_object_class_install_property (
+		object_class, PROP_DIRECTION,
+#if 0
+		g_param_spec_enum (SOUP_CODING_DIRECTION,
+#else
+		g_param_spec_uint (SOUP_CODING_DIRECTION,
+#endif
+				   "Direction",
+				   "Whether to encode or decode",
+				   0, 2,
+				   SOUP_CODING_ENCODE,
+				   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+soup_coding_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+				  gpointer interface_data)
+{
+	;
+}
+
+static void
+soup_coding_init (SoupCoding *coding)
+{
+	;
+}
+
+static void
+set_property (GObject *object, guint prop_id,
+	      const GValue *value, GParamSpec *pspec)
+{
+	SoupCoding *coding = SOUP_CODING (object);
+
+	switch (prop_id) {
+	case PROP_DIRECTION:
+#if 0
+		coding->direction = g_value_get_enum (value);
+#else
+		coding->direction = g_value_get_uint (value);
+#endif
+		break;
+	default:
+		break;
+	}
+}
+
+static void
+get_property (GObject *object, guint prop_id,
+	      GValue *value, GParamSpec *pspec)
+{
+	SoupCoding *coding = SOUP_CODING (object);
+
+	switch (prop_id) {
+	case PROP_DIRECTION:
+#if 0
+		g_value_set_enum (value, coding->direction);
+#else
+		g_value_set_uint (value, coding->direction);
+#endif
+		break;
+	default:
+		break;
+	}
+}
+
+static SoupBuffer *
+apply (SoupCoding *coding,
+       gconstpointer input, gsize input_length,
+       gboolean done, GError **error)
+{
+	gsize outbuf_length, outbuf_used, outbuf_cur, input_used, input_cur;
+	char *outbuf;
+	SoupCodingStatus status;
+
+	if (coding->direction == SOUP_CODING_ENCODE)
+		outbuf_length = MAX (input_length / 2, 1024);
+	else
+		outbuf_length = MAX (input_length * 2, 1024);
+	outbuf = g_malloc (outbuf_length);
+	outbuf_cur = input_cur = 0;
+
+	do {
+		status = soup_coding_apply_into (
+			coding,
+			(guchar *)input + input_cur, input_length - input_cur,
+			&input_used,
+			outbuf + outbuf_cur, outbuf_length - outbuf_cur,
+			&outbuf_used,
+			done, error);
+		input_cur += input_used;
+		outbuf_cur += outbuf_used;
+
+		switch (status) {
+		case SOUP_CODING_STATUS_OK:
+		case SOUP_CODING_STATUS_COMPLETE:
+			break;
+
+		case SOUP_CODING_STATUS_NEED_SPACE:
+			outbuf_length *= 2;
+			outbuf = g_realloc (outbuf, outbuf_length);
+			break;
+
+		case SOUP_CODING_STATUS_ERROR:
+		default:
+			g_free (outbuf);
+			return NULL;
+		}
+	} while (input_cur < input_length ||
+		 (done && status != SOUP_CODING_STATUS_COMPLETE));
+
+	if (outbuf_cur)
+		return soup_buffer_new (SOUP_MEMORY_TAKE, outbuf, outbuf_cur);
+	else {
+		g_free (outbuf);
+		return NULL;
+	}
+}
+
+/**
+ * soup_coding_apply:
+ * @coding: a #SoupCoding
+ * @input: input data
+ * @input_length: length of @input
+ * @done: %TRUE if this is the last piece of data to encode/decode
+ * @error: error pointer
+ *
+ * Applies @coding to @input_length bytes of data from @input, and
+ * returns a new #SoupBuffer containing the encoded/decoded data. If
+ * @done is %FALSE, the encoder may buffer some or all of the data in
+ * @input rather than outputting it right away. If @done is %TRUE, the
+ * encoder will flush any buffered data, and (if possible) verify that
+ * the input has reached the end of the stream.
+ *
+ * Return value: a #SoupBuffer containing the encoded/decoded data, or
+ * %NULL if no data can be returned at this point, or if an error
+ * occurred. (If you pass %NULL for @error, there is no way to
+ * distinguish the latter two cases).
+ **/
+SoupBuffer *
+soup_coding_apply (SoupCoding *coding,
+		   gconstpointer input, gsize input_length,
+		   gboolean done, GError **error)
+{
+	g_return_val_if_fail (SOUP_IS_CODING (coding), NULL);
+
+	return SOUP_CODING_GET_CLASS (coding)->apply (
+		coding, input, input_length, done, error);
+}
+
+/**
+ * SoupCodingStatus:
+ * @SOUP_CODING_STATUS_OK: Success
+ * @SOUP_CODING_STATUS_ERROR: An error occurred
+ * @SOUP_CODING_STATUS_NEED_SPACE: Output buffer was too small to
+ * output any data.
+ * @SOUP_CODING_STATUS_COMPLETE: The stream end has been reached and
+ * the output buffer contains the last bytes of encoded/decoded data.
+ *
+ * The result from a call to soup_coding_apply_into().
+ **/
+
+/**
+ * soup_coding_apply_into:
+ * @coding: a #SoupCoding
+ * @input: input data
+ * @input_length: length of @input
+ * @input_used: on return, contains the number of bytes of @input that
+ * were encoded/decoded.
+ * @output: output buffer
+ * @output_length: length of @output
+ * @output_used: on return, contains the number of bytes of @output that
+ * were filled with encoded/decoded data.
+ * @done: %TRUE if this is the last piece of data to encode/decode
+ * @error: error pointer
+ *
+ * Applies @coding to @input_length bytes of data from @input, and
+ * outputs between %0 and @output_length encoded/decoded bytes into
+ * @output. @input and @output may not overlap.
+ *
+ * Return value: the status; %SOUP_CODING_STATUS_OK on intermediate
+ * success, %SOUP_CODING_STATUS_COMPLETE if the stream has been fully
+ * encoded/decoded, %SOUP_CODING_STATUS_NEED_SPACE if a larger
+ * @output_length is required to make progress, or
+ * %SOUP_CODING_STATUS_ERROR on error (in which case @error will be
+ * set).
+ **/
+SoupCodingStatus
+soup_coding_apply_into (SoupCoding *coding,
+			gconstpointer input, gsize input_length, gsize *input_used,
+			gpointer output, gsize output_length, gsize *output_used,
+			gboolean done, GError **error)
+{
+	g_return_val_if_fail (SOUP_IS_CODING (coding), 0);
+
+	return SOUP_CODING_GET_CLASS (coding)->apply_into (
+		coding, input, input_length, input_used,
+		output, output_length, output_used,
+		done, error);
+}
+
+GQuark
+soup_coding_error_quark (void)
+{
+	static GQuark error;
+	if (!error)
+		error = g_quark_from_static_string ("soup_coding_error_quark");
+	return error;
+}
diff --git a/libsoup/soup-coding.h b/libsoup/soup-coding.h
new file mode 100644
index 0000000..a00d014
--- /dev/null
+++ b/libsoup/soup-coding.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2005, Novell, Inc.
+ * Copyright (C) 2008, Red Hat, Inc.
+ */
+
+#ifndef SOUP_CODING_H
+#define SOUP_CODING_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+#define SOUP_TYPE_CODING            (soup_coding_get_type ())
+#define SOUP_CODING(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CODING, SoupCoding))
+#define SOUP_CODING_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CODING, SoupCodingClass))
+#define SOUP_IS_CODING(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CODING))
+#define SOUP_IS_CODING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CODING))
+#define SOUP_CODING_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CODING, SoupCodingClass))
+
+typedef enum {
+	SOUP_CODING_ENCODE,
+	SOUP_CODING_DECODE
+} SoupCodingDirection;
+
+typedef enum {
+	SOUP_CODING_STATUS_OK,
+	SOUP_CODING_STATUS_ERROR,
+	SOUP_CODING_STATUS_NEED_SPACE,
+	SOUP_CODING_STATUS_COMPLETE,
+} SoupCodingStatus;
+
+typedef struct {
+	GObject parent;
+
+	SoupCodingDirection direction;
+} SoupCoding;
+
+typedef struct {
+	GObjectClass parent_class;
+
+	const char *name;
+
+	SoupBuffer *     (*apply)      (SoupCoding     *coding,
+					gconstpointer   input,
+					gsize           input_length,
+					gboolean        done,
+					GError        **error);
+	SoupCodingStatus (*apply_into) (SoupCoding     *coding,
+					gconstpointer   input,
+					gsize           input_length,
+					gsize          *input_used,
+					gpointer        output,
+					gsize           output_length,
+					gsize          *output_used,
+					gboolean        done,
+					GError        **error);
+
+	/* Padding for future expansion */
+	void (*_libsoup_reserved1) (void);
+	void (*_libsoup_reserved2) (void);
+	void (*_libsoup_reserved3) (void);
+	void (*_libsoup_reserved4) (void);
+} SoupCodingClass;
+
+#define SOUP_CODING_DIRECTION "direction"
+
+GType             soup_coding_get_type   (void);
+
+SoupBuffer       *soup_coding_apply      (SoupCoding     *coding,
+					  gconstpointer   input,
+					  gsize           input_length,
+					  gboolean        done,
+					  GError        **error);
+SoupCodingStatus  soup_coding_apply_into (SoupCoding     *coding,
+					  gconstpointer   input,
+					  gsize           input_length,
+					  gsize          *input_used,
+					  gpointer        output,
+					  gsize           output_length,
+					  gsize          *output_used,
+					  gboolean        done,
+					  GError        **error);
+
+#define SOUP_CODING_ERROR soup_coding_error_quark()
+GQuark soup_coding_error_quark (void);
+
+typedef enum {
+	SOUP_CODING_ERROR_DATA_ERROR,
+	SOUP_CODING_ERROR_INTERNAL_ERROR
+} SoupCodingError;
+
+#endif /* SOUP_CODING_H */
diff --git a/libsoup/soup-content-decoder.c b/libsoup/soup-content-decoder.c
new file mode 100644
index 0000000..43eb0d8
--- /dev/null
+++ b/libsoup/soup-content-decoder.c
@@ -0,0 +1,168 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-content-decoder.c
+ *
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "soup-content-decoder.h"
+#include "soup-coding-deflate.h"
+#include "soup-coding-gzip.h"
+#include "soup-enum-types.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-session-feature.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-content-decoder
+ * @short_description: Content-Encoding handler
+ *
+ * #SoupContentDecoder handles the "Accept-Encoding" header on
+ * outgoing messages, and the "Content-Encoding" header on incoming
+ * ones. If you add it to a session with soup_session_add_feature() or
+ * soup_session_add_feature_by_type(), the session will automatically
+ * use Content-Encoding as appropriate.
+ *
+ * (Note that currently there is no way to (automatically) use
+ * Content-Encoding when sending a request body, or to pick specific
+ * encoding types to support.)
+ *
+ * If #SoupContentDecoder successfully decodes the Content-Encoding,
+ * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
+ * and the message body and the chunks in the #SoupMessage::got_chunk
+ * signals will contain the decoded data; however, the message headers
+ * will be unchanged (and so "Content-Encoding" will still be present,
+ * "Content-Length" will describe the original encoded length, etc).
+ *
+ * If "Content-Encoding" contains any encoding types that
+ * #SoupContentDecoder doesn't recognize, then none of the encodings
+ * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
+ * not be set).
+ *
+ * Since: 2.28.2
+ **/
+
+struct _SoupContentDecoderPrivate {
+	GHashTable *codings;
+};
+
+static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+
+static void request_queued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+static void request_unqueued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
+
+G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
+						soup_content_decoder_session_feature_init))
+
+/* This is constant for now */
+#define ACCEPT_ENCODING_HEADER "gzip,deflate"
+
+static void
+soup_content_decoder_init (SoupContentDecoder *decoder)
+{
+	decoder->priv = G_TYPE_INSTANCE_GET_PRIVATE (decoder,
+						     SOUP_TYPE_CONTENT_DECODER,
+						     SoupContentDecoderPrivate);
+
+	decoder->priv->codings = g_hash_table_new (g_str_hash, g_str_equal);
+	/* Hardcoded for now */
+	g_hash_table_insert (decoder->priv->codings, "deflate",
+			     GSIZE_TO_POINTER (SOUP_TYPE_CODING_DEFLATE));
+	g_hash_table_insert (decoder->priv->codings, "gzip",
+			     GSIZE_TO_POINTER (SOUP_TYPE_CODING_GZIP));
+}
+
+static void
+soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
+{
+  g_type_class_add_private (decoder_class, sizeof (SoupContentDecoderPrivate));
+}
+
+static void
+soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
+					   gpointer interface_data)
+{
+	feature_interface->request_queued = request_queued;
+	feature_interface->request_unqueued = request_unqueued;
+}
+
+static void
+soup_content_decoder_got_headers_cb (SoupMessage *msg, SoupContentDecoder *decoder)
+{
+	SoupMessagePrivate *msgpriv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	const char *header;
+	GSList *encodings, *e;
+	GType coding_type;
+	SoupCoding *coding; 
+
+	header = soup_message_headers_get_list (msg->response_headers,
+						"Content-Encoding");
+	if (!header)
+		return;
+
+	/* OK, really, no one is ever going to use more than one
+	 * encoding, but we'll be robust.
+	 */
+	encodings = soup_header_parse_list (header);
+	if (!encodings)
+		return;
+
+	for (e = encodings; e; e = e->next) {
+		if (!g_hash_table_lookup (decoder->priv->codings, e->data)) {
+			soup_header_free_list (encodings);
+			return;
+		}
+	}
+
+	msgpriv->decoders = NULL;
+	for (e = encodings; e; e = e->next) {
+		coding_type = (GType) GPOINTER_TO_SIZE (g_hash_table_lookup (decoder->priv->codings, e->data));
+		coding = g_object_new (coding_type,
+				       SOUP_CODING_DIRECTION, SOUP_CODING_DECODE,
+				       NULL);
+
+		/* Content-Encoding lists the codings in the order
+		 * they were applied in, so we put decoders in reverse
+		 * order so the last-applied will be the first
+		 * decoded.
+		 */
+		msgpriv->decoders = g_slist_prepend (msgpriv->decoders, coding);
+	}
+	soup_header_free_list (encodings);
+
+	soup_message_set_flags (msg, msgpriv->msg_flags | SOUP_MESSAGE_CONTENT_DECODED);
+}
+
+static void
+request_queued (SoupSessionFeature *feature, SoupSession *session,
+		SoupMessage *msg)
+{
+	SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (feature);
+
+	if (!soup_message_headers_get_one (msg->request_headers,
+					   "Accept-Encoding")) {
+		soup_message_headers_append (msg->request_headers,
+					     "Accept-Encoding",
+					     ACCEPT_ENCODING_HEADER);
+	}
+
+	g_signal_connect (msg, "got-headers",
+			  G_CALLBACK (soup_content_decoder_got_headers_cb),
+			  decoder);
+}
+
+static void
+request_unqueued (SoupSessionFeature *feature, SoupSession *session,
+		  SoupMessage *msg)
+{
+	g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature);
+}
diff --git a/libsoup/soup-content-decoder.h b/libsoup/soup-content-decoder.h
new file mode 100644
index 0000000..e0b2238
--- /dev/null
+++ b/libsoup/soup-content-decoder.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CONTENT_DECODER_H
+#define SOUP_CONTENT_DECODER_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-body.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONTENT_DECODER            (soup_content_decoder_get_type ())
+#define SOUP_CONTENT_DECODER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoder))
+#define SOUP_CONTENT_DECODER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoderClass))
+#define SOUP_IS_CONTENT_DECODER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_CONTENT_DECODER))
+#define SOUP_IS_CONTENT_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_CONTENT_DECODER))
+#define SOUP_CONTENT_DECODER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONTENT_DECODER, SoupContentDecoderClass))
+
+typedef struct _SoupContentDecoderPrivate SoupContentDecoderPrivate;
+
+typedef struct {
+	GObject parent;
+
+	SoupContentDecoderPrivate *priv;
+} SoupContentDecoder;
+
+typedef struct {
+	GObjectClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_libsoup_reserved1) (void);
+	void (*_libsoup_reserved2) (void);
+	void (*_libsoup_reserved3) (void);
+	void (*_libsoup_reserved4) (void);
+	void (*_libsoup_reserved5) (void);
+} SoupContentDecoderClass;
+
+GType               soup_content_decoder_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_CONTENT_DECODER_H */
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index aae4e28..5e4ae6c 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -12,6 +12,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "soup-coding.h"
 #include "soup-connection.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
@@ -345,6 +346,38 @@ read_metadata (SoupMessage *msg, gboolean to_blank)
 	return TRUE;
 }
 
+static SoupBuffer *
+content_decode (SoupMessage *msg, SoupBuffer *buf)
+{
+	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	SoupCoding *decoder;
+	SoupBuffer *decoded;
+	GError *error = NULL;
+	GSList *d;
+
+	for (d = priv->decoders; d; d = d->next) {
+		decoder = d->data;
+
+		decoded = soup_coding_apply (decoder, buf->data, buf->length,
+					     FALSE, &error);
+		if (error) {
+			// FIXME!
+			g_warning ("Content-Decoding error: %s\n", error->message);
+			g_error_free (error);
+			break;
+		}
+		if (buf)
+			soup_buffer_free (buf);
+
+		if (decoded)
+			buf = decoded;
+		else
+			return NULL;
+	}
+
+	return buf;
+}
+
 /* Reads as much message body data as is available on io->sock (but no
  * further than the end of the current message body or chunk). On a
  * successful read, emits "got_chunk" (possibly multiple times), and
@@ -395,10 +428,14 @@ read_body_chunk (SoupMessage *msg)
 
 		if (status == SOUP_SOCKET_OK && nread) {
 			buffer->length = nread;
-			soup_message_body_got_chunk (io->read_body, buffer);
-
 			io->read_length -= nread;
 
+			buffer = content_decode (msg, buffer);
+			if (!buffer)
+				continue;
+
+			soup_message_body_got_chunk (io->read_body, buffer);
+
 			if (io->need_content_sniffed) {
 				soup_message_body_append_buffer (io->sniff_data, buffer);
 				soup_buffer_free (buffer);
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 7375bd1..ee6221d 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -40,6 +40,7 @@ typedef struct {
 	SoupAuth          *auth, *proxy_auth;
 
 	GSList            *disabled_features;
+	GSList            *decoders;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
 
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 020577a..93c2522 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -168,6 +168,11 @@ finalize (GObject *object)
 
 	g_slist_free (priv->disabled_features);
 
+	while (priv->decoders) {
+		g_object_unref (priv->decoders->data);
+		priv->decoders = g_slist_delete_link (priv->decoders, priv->decoders);
+	}
+
 	soup_message_body_free (msg->request_body);
 	soup_message_headers_free (msg->request_headers);
 	soup_message_body_free (msg->response_body);
@@ -1227,6 +1232,12 @@ soup_message_cleanup_response (SoupMessage *req)
 						   SOUP_ENCODING_CONTENT_LENGTH);
 	}
 
+	while (priv->decoders) {
+		g_object_unref (priv->decoders->data);
+		priv->decoders = g_slist_delete_link (priv->decoders, priv->decoders);
+	}
+	priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED;
+
 	req->status_code = SOUP_STATUS_NONE;
 	if (req->reason_phrase) {
 		g_free (req->reason_phrase);
@@ -1237,6 +1248,7 @@ soup_message_cleanup_response (SoupMessage *req)
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_STATUS_CODE);
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_REASON_PHRASE);
 	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_HTTP_VERSION);
+	g_object_notify (G_OBJECT (req), SOUP_MESSAGE_FLAGS);
 }
 
 /**
diff --git a/libsoup/soup-message.h b/libsoup/soup-message.h
index 4bbcb1b..4fc9122 100644
--- a/libsoup/soup-message.h
+++ b/libsoup/soup-message.h
@@ -101,10 +101,11 @@ void             soup_message_set_uri             (SoupMessage       *msg,
 SoupAddress     *soup_message_get_address         (SoupMessage       *msg);
 
 typedef enum {
+	SOUP_MESSAGE_NO_REDIRECT      = (1 << 1),
 #ifndef LIBSOUP_DISABLE_DEPRECATED
 	SOUP_MESSAGE_OVERWRITE_CHUNKS = (1 << 3),
 #endif
-	SOUP_MESSAGE_NO_REDIRECT      = (1 << 1)
+	SOUP_MESSAGE_CONTENT_DECODED  = (1 << 4)
 } SoupMessageFlags;
 
 void           soup_message_set_flags           (SoupMessage        *msg,
diff --git a/libsoup/soup.h b/libsoup/soup.h
index f3b0b3d..e727361 100644
--- a/libsoup/soup.h
+++ b/libsoup/soup.h
@@ -15,6 +15,7 @@ extern "C" {
 #include <libsoup/soup-auth-domain.h>
 #include <libsoup/soup-auth-domain-basic.h>
 #include <libsoup/soup-auth-domain-digest.h>
+#include <libsoup/soup-content-decoder.h>
 #include <libsoup/soup-content-sniffer.h>
 #include <libsoup/soup-cookie.h>
 #include <libsoup/soup-cookie-jar.h>
diff --git a/tests/get.c b/tests/get.c
index b0e5c57..ca072e3 100644
--- a/tests/get.c
+++ b/tests/get.c
@@ -48,6 +48,11 @@ get_url (const char *url)
 
 		printf ("%s %s HTTP/1.%d\n\n", method, path,
 			soup_message_get_http_version (msg));
+		soup_message_headers_iter_init (&iter, msg->request_headers);
+		while (soup_message_headers_iter_next (&iter, &hname, &value))
+			printf ("%s: %s\r\n", hname, value);
+		printf ("\n");
+
 		printf ("HTTP/1.%d %d %s\n",
 			soup_message_get_http_version (msg),
 			msg->status_code, msg->reason_phrase);
@@ -83,7 +88,7 @@ usage (void)
 int
 main (int argc, char **argv)
 {
-	const char *cafile = NULL, *url;
+	const char *cafile = NULL, *cookiefile = NULL, *url;
 	SoupURI *proxy = NULL, *parsed;
 	gboolean synchronous = FALSE;
 	int opt;
@@ -93,12 +98,16 @@ main (int argc, char **argv)
 
 	method = SOUP_METHOD_GET;
 
-	while ((opt = getopt (argc, argv, "c:dhp:s")) != -1) {
+	while ((opt = getopt (argc, argv, "c:C:dhp:s")) != -1) {
 		switch (opt) {
 		case 'c':
 			cafile = optarg;
 			break;
 
+		case 'C':
+			cookiefile = optarg;
+			break;
+
 		case 'd':
 			debug = TRUE;
 			break;
@@ -145,6 +154,7 @@ main (int argc, char **argv)
 #ifdef HAVE_GNOME
 			SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
 #endif
+			SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
 			SOUP_SESSION_USER_AGENT, "get ",
 			NULL);
 	} else {
@@ -153,6 +163,7 @@ main (int argc, char **argv)
 #ifdef HAVE_GNOME
 			SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_GNOME_FEATURES_2_26,
 #endif
+			SOUP_SESSION_ADD_FEATURE_BY_TYPE, SOUP_TYPE_CONTENT_DECODER,
 			SOUP_SESSION_USER_AGENT, "get ",
 			NULL);
 	}
@@ -167,6 +178,22 @@ main (int argc, char **argv)
 			      NULL);
 	}
 
+	if (cookiefile) {
+		SoupCookieJar *jar;
+
+#ifdef HAVE_GNOME
+		if (g_str_has_suffix (cookiefile, ".sqlite"))
+			jar = soup_cookie_jar_sqlite_new (cookiefile, TRUE);
+		else
+#endif
+			jar = soup_cookie_jar_text_new (cookiefile, TRUE);
+
+		if (jar) {
+			soup_session_add_feature (session, SOUP_SESSION_FEATURE (jar));
+			g_object_unref (jar);
+		}
+	}
+
 	if (!synchronous)
 		loop = g_main_loop_new (NULL, TRUE);
 
diff --git a/tests/httpd.conf.in b/tests/httpd.conf.in
index b893fdc..7cba8c8 100644
--- a/tests/httpd.conf.in
+++ b/tests/httpd.conf.in
@@ -20,6 +20,7 @@ LoadModule auth_digest_module   @APACHE_MODULE_DIR@/mod_auth_digest.so
 LoadModule authn_file_module    @APACHE_MODULE_DIR@/mod_authn_file.so
 LoadModule authz_host_module    @APACHE_MODULE_DIR@/mod_authz_host.so
 LoadModule authz_user_module    @APACHE_MODULE_DIR@/mod_authz_user.so
+LoadModule deflate_module       @APACHE_MODULE_DIR@/mod_deflate.so
 LoadModule dir_module           @APACHE_MODULE_DIR@/mod_dir.so
 LoadModule mime_module          @APACHE_MODULE_DIR@/mod_mime.so
 @IF_HAVE_PHP LoadModule php5_module          @APACHE_PHP_MODULE_DIR@/@APACHE_PHP_MODULE@
@@ -282,3 +283,10 @@ Alias /Digest @srcdir@
   # test RFC2069-style Digest
   AuthDigestQop none
 </Location>
+
+# Compression
+Alias /compressed @srcdir@
+
+<Location /compressed>
+  SetOutputFilter DEFLATE
+</Location>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]