1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-content-decoder.c
5 * Copyright (C) 2009 Red Hat, Inc.
15 #include "soup-content-decoder.h"
16 #include "soup-enum-types.h"
17 #include "soup-message.h"
18 #include "soup-message-private.h"
19 #include "soup-session-feature.h"
23 * SECTION:soup-content-decoder
24 * @short_description: Content-Encoding handler
26 * #SoupContentDecoder handles the "Accept-Encoding" header on
27 * outgoing messages, and the "Content-Encoding" header on incoming
28 * ones. If you add it to a session with soup_session_add_feature() or
29 * soup_session_add_feature_by_type(), the session will automatically
30 * use Content-Encoding as appropriate.
32 * (Note that currently there is no way to (automatically) use
33 * Content-Encoding when sending a request body, or to pick specific
34 * encoding types to support.)
36 * If #SoupContentDecoder successfully decodes the Content-Encoding,
37 * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
38 * and the message body and the chunks in the #SoupMessage::got_chunk
39 * signals will contain the decoded data; however, the message headers
40 * will be unchanged (and so "Content-Encoding" will still be present,
41 * "Content-Length" will describe the original encoded length, etc).
43 * If "Content-Encoding" contains any encoding types that
44 * #SoupContentDecoder doesn't recognize, then none of the encodings
45 * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
51 struct _SoupContentDecoderPrivate {
55 typedef GConverter * (*SoupContentDecoderCreator) (void);
57 static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
59 G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
60 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
61 soup_content_decoder_session_feature_init))
63 /* This is constant for now */
64 #define ACCEPT_ENCODING_HEADER "gzip, deflate"
67 gzip_decoder_creator (void)
69 return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP);
73 zlib_decoder_creator (void)
75 return (GConverter *)g_zlib_decompressor_new (G_ZLIB_COMPRESSOR_FORMAT_ZLIB);
79 soup_content_decoder_init (SoupContentDecoder *decoder)
81 decoder->priv = G_TYPE_INSTANCE_GET_PRIVATE (decoder,
82 SOUP_TYPE_CONTENT_DECODER,
83 SoupContentDecoderPrivate);
85 decoder->priv->decoders = g_hash_table_new (g_str_hash, g_str_equal);
86 /* Hardcoded for now */
87 g_hash_table_insert (decoder->priv->decoders, "gzip",
88 gzip_decoder_creator);
89 g_hash_table_insert (decoder->priv->decoders, "x-gzip",
90 gzip_decoder_creator);
91 g_hash_table_insert (decoder->priv->decoders, "deflate",
92 zlib_decoder_creator);
96 soup_content_decoder_finalize (GObject *object)
98 SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (object);
100 g_hash_table_destroy (decoder->priv->decoders);
102 G_OBJECT_CLASS (soup_content_decoder_parent_class)->finalize (object);
106 soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
108 GObjectClass *object_class = G_OBJECT_CLASS (decoder_class);
110 g_type_class_add_private (decoder_class, sizeof (SoupContentDecoderPrivate));
112 object_class->finalize = soup_content_decoder_finalize;
116 soup_content_decoder_got_headers_cb (SoupMessage *msg, SoupContentDecoder *decoder)
118 SoupMessagePrivate *msgpriv = SOUP_MESSAGE_GET_PRIVATE (msg);
120 GSList *encodings, *e;
121 SoupContentDecoderCreator converter_creator;
122 GConverter *converter;
124 header = soup_message_headers_get_list (msg->response_headers,
129 /* Workaround for an apache bug (bgo 613361) */
130 if (!g_ascii_strcasecmp (header, "gzip") ||
131 !g_ascii_strcasecmp (header, "x-gzip")) {
132 const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL);
135 (!g_ascii_strcasecmp (content_type, "application/gzip") ||
136 !g_ascii_strcasecmp (content_type, "application/x-gzip")))
140 /* OK, really, no one is ever going to use more than one
141 * encoding, but we'll be robust.
143 encodings = soup_header_parse_list (header);
147 for (e = encodings; e; e = e->next) {
148 if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) {
149 soup_header_free_list (encodings);
154 /* msgpriv->decoders should be empty at this point anyway, but
155 * clean it up if it's not.
157 g_slist_free_full (msgpriv->decoders, g_object_unref);
159 for (e = encodings; e; e = e->next) {
160 converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data);
161 converter = converter_creator ();
163 /* Content-Encoding lists the codings in the order
164 * they were applied in, so we put decoders in reverse
165 * order so the last-applied will be the first
168 msgpriv->decoders = g_slist_prepend (msgpriv->decoders, converter);
170 soup_header_free_list (encodings);
174 soup_content_decoder_request_queued (SoupSessionFeature *feature,
175 SoupSession *session,
178 SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (feature);
180 if (!soup_message_headers_get_one (msg->request_headers,
181 "Accept-Encoding")) {
182 soup_message_headers_append (msg->request_headers,
184 ACCEPT_ENCODING_HEADER);
187 g_signal_connect (msg, "got-headers",
188 G_CALLBACK (soup_content_decoder_got_headers_cb),
193 soup_content_decoder_request_unqueued (SoupSessionFeature *feature,
194 SoupSession *session,
197 g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature);
201 soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
202 gpointer interface_data)
204 feature_interface->request_queued = soup_content_decoder_request_queued;
205 feature_interface->request_unqueued = soup_content_decoder_request_unqueued;