[libsoup/coding: 1/2] Content-Encoding support
- From: Dan Winship <danw src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [libsoup/coding: 1/2] Content-Encoding support
- Date: Tue, 27 Oct 2009 17:38:41 +0000 (UTC)
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]