|
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 |
}
|