Blame libsoup/soup-content-decoder.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-content-decoder.c
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright (C) 2009 Red Hat, Inc.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#ifdef HAVE_CONFIG_H
Packit Service ca3877
#include <config.h>
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
#include "soup-content-decoder.h"
Packit Service ca3877
#include "soup-converter-wrapper.h"
Packit Service ca3877
#include "soup.h"
Packit Service ca3877
#include "soup-message-private.h"
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SECTION:soup-content-decoder
Packit Service ca3877
 * @short_description: Content-Encoding handler
Packit Service ca3877
 *
Packit Service ca3877
 * #SoupContentDecoder handles adding the "Accept-Encoding" header on
Packit Service ca3877
 * outgoing messages, and processing the "Content-Encoding" header on
Packit Service ca3877
 * incoming ones. Currently it supports the "gzip" and "deflate"
Packit Service ca3877
 * content codings.
Packit Service ca3877
 *
Packit Service ca3877
 * If you are using a plain #SoupSession (ie, not #SoupSessionAsync or
Packit Service ca3877
 * #SoupSessionSync), then a #SoupContentDecoder will automatically be
Packit Service ca3877
 * added to the session by default. (You can use
Packit Service ca3877
 * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE at construct time if you don't
Packit Service ca3877
 * want this.) If you are using one of the deprecated #SoupSession
Packit Service ca3877
 * subclasses, you can add a #SoupContentDecoder to your session with
Packit Service ca3877
 * soup_session_add_feature() or soup_session_add_feature_by_type().
Packit Service ca3877
 *
Packit Service ca3877
 * If #SoupContentDecoder successfully decodes the Content-Encoding,
Packit Service ca3877
 * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
Packit Service ca3877
 * and the message body and the chunks in the #SoupMessage::got_chunk
Packit Service ca3877
 * signals will contain the decoded data; however, the message headers
Packit Service ca3877
 * will be unchanged (and so "Content-Encoding" will still be present,
Packit Service ca3877
 * "Content-Length" will describe the original encoded length, etc).
Packit Service ca3877
 *
Packit Service ca3877
 * If "Content-Encoding" contains any encoding types that
Packit Service ca3877
 * #SoupContentDecoder doesn't recognize, then none of the encodings
Packit Service ca3877
 * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
Packit Service ca3877
 * not be set).
Packit Service ca3877
 *
Packit Service ca3877
 * (Note that currently there is no way to (automatically) use
Packit Service ca3877
 * Content-Encoding when sending a request body, or to pick specific
Packit Service ca3877
 * encoding types to support.)
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.30
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
struct _SoupContentDecoderPrivate {
Packit Service ca3877
	GHashTable *decoders;
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
typedef GConverter * (*SoupContentDecoderCreator) (void);
Packit Service ca3877
Packit Service ca3877
static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
Packit Service ca3877
Packit Service ca3877
static SoupContentProcessorInterface *soup_content_decoder_default_content_processor_interface;
Packit Service ca3877
static void soup_content_decoder_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
Packit Service ca3877
Packit Service ca3877
Packit Service ca3877
G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
Packit Service ca3877
                         G_ADD_PRIVATE (SoupContentDecoder)
Packit Service ca3877
			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
Packit Service ca3877
						soup_content_decoder_session_feature_init)
Packit Service ca3877
			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
Packit Service ca3877
						soup_content_decoder_content_processor_init))
Packit Service ca3877
Packit Service ca3877
static GSList *
Packit Service ca3877
soup_content_decoder_get_decoders_for_msg (SoupContentDecoder *decoder, SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	const char *header;
Packit Service ca3877
	GSList *encodings, *e, *decoders = NULL;
Packit Service ca3877
	SoupContentDecoderCreator converter_creator;
Packit Service ca3877
	GConverter *converter;
Packit Service ca3877
Packit Service ca3877
	header = soup_message_headers_get_list (msg->response_headers,
Packit Service ca3877
						"Content-Encoding");
Packit Service ca3877
	if (!header)
Packit Service ca3877
		return NULL;
Packit Service ca3877
Packit Service ca3877
	/* Workaround for an apache bug (bgo 613361) */
Packit Service ca3877
	if (!g_ascii_strcasecmp (header, "gzip") ||
Packit Service ca3877
	    !g_ascii_strcasecmp (header, "x-gzip")) {
Packit Service ca3877
		const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
Packit Service ca3877
Packit Service ca3877
		if (content_type &&
Packit Service ca3877
		    (!g_ascii_strcasecmp (content_type, "application/gzip") ||
Packit Service ca3877
		     !g_ascii_strcasecmp (content_type, "application/x-gzip")))
Packit Service ca3877
			return NULL;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* OK, really, no one is ever going to use more than one
Packit Service ca3877
	 * encoding, but we'll be robust.
Packit Service ca3877
	 */
Packit Service ca3877
	encodings = soup_header_parse_list (header);
Packit Service ca3877
	if (!encodings)
Packit Service ca3877
		return NULL;
Packit Service ca3877
Packit Service ca3877
	for (e = encodings; e; e = e->next) {
Packit Service ca3877
		if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
Packit Service ca3877
			soup_header_free_list (encodings);
Packit Service ca3877
			return NULL;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	for (e = encodings; e; e = e->next) {
Packit Service ca3877
		converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
Packit Service ca3877
		converter = converter_creator ();
Packit Service ca3877
Packit Service ca3877
		/* Content-Encoding lists the codings in the order
Packit Service ca3877
		 * they were applied in, so we put decoders in reverse
Packit Service ca3877
		 * order so the last-applied will be the first
Packit Service ca3877
		 * decoded.
Packit Service ca3877
		 */
Packit Service ca3877
		decoders = g_slist_prepend (decoders, converter);
Packit Service ca3877
	}
Packit Service ca3877
	soup_header_free_list (encodings);
Packit Service ca3877
Packit Service ca3877
	return decoders;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static GInputStream*
Packit Service ca3877
soup_content_decoder_content_processor_wrap_input (SoupContentProcessor *processor,
Packit Service ca3877
						   GInputStream *base_stream,
Packit Service ca3877
						   SoupMessage *msg,
Packit Service ca3877
						   GError **error)
Packit Service ca3877
{
Packit Service ca3877
	GSList *decoders, *d;
Packit Service ca3877
	GInputStream *istream;
Packit Service ca3877
Packit Service ca3877
	decoders = soup_content_decoder_get_decoders_for_msg (SOUP_CONTENT_DECODER (processor), msg);
Packit Service ca3877
	if (!decoders)
Packit Service ca3877
		return NULL;
Packit Service ca3877
Packit Service ca3877
	istream = g_object_ref (base_stream);
Packit Service ca3877
	for (d = decoders; d; d = d->next) {
Packit Service ca3877
		GConverter *decoder, *wrapper;
Packit Service ca3877
		GInputStream *filter;
Packit Service ca3877
Packit Service ca3877
		decoder = d->data;
Packit Service ca3877
		wrapper = soup_converter_wrapper_new (decoder, msg);
Packit Service ca3877
		filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
Packit Service ca3877
				       "base-stream", istream,
Packit Service ca3877
				       "converter", wrapper,
Packit Service ca3877
				       NULL);
Packit Service ca3877
		g_object_unref (istream);
Packit Service ca3877
		g_object_unref (wrapper);
Packit Service ca3877
		istream = filter;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_slist_free_full (decoders, g_object_unref);
Packit Service ca3877
Packit Service ca3877
	return istream;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_content_processor_init (SoupContentProcessorInterface *processor_interface,
Packit Service ca3877
					     gpointer interface_data)
Packit Service ca3877
{
Packit Service ca3877
	soup_content_decoder_default_content_processor_interface =
Packit Service ca3877
		g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
Packit Service ca3877
Packit Service ca3877
	processor_interface->processing_stage = SOUP_STAGE_CONTENT_ENCODING;
Packit Service ca3877
	processor_interface->wrap_input = soup_content_decoder_content_processor_wrap_input;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/* This is constant for now */
Packit Service ca3877
#define ACCEPT_ENCODING_HEADER "gzip, deflate"
Packit Service ca3877
Packit Service ca3877
static GConverter *
Packit Service ca3877
gzip_decoder_creator (void)
Packit Service ca3877
{
Packit Service ca3877
	return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static GConverter *
Packit Service ca3877
zlib_decoder_creator (void)
Packit Service ca3877
{
Packit Service ca3877
	return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_init (SoupContentDecoder *decoder)
Packit Service ca3877
{
Packit Service ca3877
	decoder->priv = soup_content_decoder_get_instance_private (decoder);
Packit Service ca3877
Packit Service ca3877
	decoder->priv->decoders = g_hash_table_new (g_str_hash, g_str_equal);
Packit Service ca3877
	/* Hardcoded for now */
Packit Service ca3877
	g_hash_table_insert (decoder->priv->decoders, "gzip",
Packit Service ca3877
			     gzip_decoder_creator);
Packit Service ca3877
	g_hash_table_insert (decoder->priv->decoders, "x-gzip",
Packit Service ca3877
			     gzip_decoder_creator);
Packit Service ca3877
	g_hash_table_insert (decoder->priv->decoders, "deflate",
Packit Service ca3877
			     zlib_decoder_creator);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_finalize (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (object);
Packit Service ca3877
Packit Service ca3877
	g_hash_table_destroy (decoder->priv->decoders);
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_content_decoder_parent_class)->finalize (object);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
Packit Service ca3877
{
Packit Service ca3877
	GObjectClass *object_class = G_OBJECT_CLASS (decoder_class);
Packit Service ca3877
Packit Service ca3877
	object_class->finalize = soup_content_decoder_finalize;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_request_queued (SoupSessionFeature *feature,
Packit Service ca3877
				     SoupSession *session,
Packit Service ca3877
				     SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	if (!soup_message_headers_get_one (msg->request_headers,
Packit Service ca3877
					   "Accept-Encoding")) {
Packit Service ca3877
		soup_message_headers_append (msg->request_headers,
Packit Service ca3877
					     "Accept-Encoding",
Packit Service ca3877
					     ACCEPT_ENCODING_HEADER);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
Packit Service ca3877
					   gpointer interface_data)
Packit Service ca3877
{
Packit Service ca3877
	feature_interface->request_queued = soup_content_decoder_request_queued;
Packit Service ca3877
}