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-coding-gzip.h"
17 #include "soup-enum-types.h"
18 #include "soup-message.h"
19 #include "soup-message-private.h"
20 #include "soup-session-feature.h"
24 * SECTION:soup-content-decoder
25 * @short_description: Content-Encoding handler
27 * #SoupContentDecoder handles the "Accept-Encoding" header on
28 * outgoing messages, and the "Content-Encoding" header on incoming
29 * ones. If you add it to a session with soup_session_add_feature() or
30 * soup_session_add_feature_by_type(), the session will automatically
31 * use Content-Encoding as appropriate.
33 * (Note that currently there is no way to (automatically) use
34 * Content-Encoding when sending a request body, or to pick specific
35 * encoding types to support.)
37 * If #SoupContentDecoder successfully decodes the Content-Encoding,
38 * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message,
39 * and the message body and the chunks in the #SoupMessage::got_chunk
40 * signals will contain the decoded data; however, the message headers
41 * will be unchanged (and so "Content-Encoding" will still be present,
42 * "Content-Length" will describe the original encoded length, etc).
44 * If "Content-Encoding" contains any encoding types that
45 * #SoupContentDecoder doesn't recognize, then none of the encodings
46 * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will
52 struct _SoupContentDecoderPrivate {
56 static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
58 static void request_queued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
59 static void request_unqueued (SoupSessionFeature *feature, SoupSession *session, SoupMessage *msg);
61 static void finalize (GObject *object);
63 G_DEFINE_TYPE_WITH_CODE (SoupContentDecoder, soup_content_decoder, G_TYPE_OBJECT,
64 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
65 soup_content_decoder_session_feature_init))
67 /* This is constant for now */
68 #define ACCEPT_ENCODING_HEADER "gzip"
71 soup_content_decoder_init (SoupContentDecoder *decoder)
73 decoder->priv = G_TYPE_INSTANCE_GET_PRIVATE (decoder,
74 SOUP_TYPE_CONTENT_DECODER,
75 SoupContentDecoderPrivate);
77 decoder->priv->codings = g_hash_table_new (g_str_hash, g_str_equal);
78 /* Hardcoded for now */
79 g_hash_table_insert (decoder->priv->codings, "gzip",
80 GSIZE_TO_POINTER (SOUP_TYPE_CODING_GZIP));
81 g_hash_table_insert (decoder->priv->codings, "x-gzip",
82 GSIZE_TO_POINTER (SOUP_TYPE_CODING_GZIP));
86 soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class)
88 GObjectClass *object_class = G_OBJECT_CLASS (decoder_class);
90 g_type_class_add_private (decoder_class, sizeof (SoupContentDecoderPrivate));
92 object_class->finalize = finalize;
96 soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface,
97 gpointer interface_data)
99 feature_interface->request_queued = request_queued;
100 feature_interface->request_unqueued = request_unqueued;
104 finalize (GObject *object)
106 SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (object);
108 g_hash_table_destroy (decoder->priv->codings);
110 G_OBJECT_CLASS (soup_content_decoder_parent_class)->finalize (object);
114 soup_content_decoder_got_headers_cb (SoupMessage *msg, SoupContentDecoder *decoder)
116 SoupMessagePrivate *msgpriv = SOUP_MESSAGE_GET_PRIVATE (msg);
118 GSList *encodings, *e;
122 header = soup_message_headers_get_list (msg->response_headers,
127 /* OK, really, no one is ever going to use more than one
128 * encoding, but we'll be robust.
130 encodings = soup_header_parse_list (header);
134 for (e = encodings; e; e = e->next) {
135 if (!g_hash_table_lookup (decoder->priv->codings, e->data)) {
136 soup_header_free_list (encodings);
141 /* msgpriv->decoders should be empty at this point anyway, but
142 * clean it up if it's not.
144 while (msgpriv->decoders) {
145 g_object_unref (msgpriv->decoders->data);
146 msgpriv->decoders = g_slist_delete_link (msgpriv->decoders, msgpriv->decoders);
149 for (e = encodings; e; e = e->next) {
150 coding_type = (GType) GPOINTER_TO_SIZE (g_hash_table_lookup (decoder->priv->codings, e->data));
151 coding = g_object_new (coding_type,
152 SOUP_CODING_DIRECTION, SOUP_CODING_DECODE,
155 /* Content-Encoding lists the codings in the order
156 * they were applied in, so we put decoders in reverse
157 * order so the last-applied will be the first
160 msgpriv->decoders = g_slist_prepend (msgpriv->decoders, coding);
162 soup_header_free_list (encodings);
164 soup_message_set_flags (msg, msgpriv->msg_flags | SOUP_MESSAGE_CONTENT_DECODED);
168 request_queued (SoupSessionFeature *feature, SoupSession *session,
171 SoupContentDecoder *decoder = SOUP_CONTENT_DECODER (feature);
173 if (!soup_message_headers_get_one (msg->request_headers,
174 "Accept-Encoding")) {
175 soup_message_headers_append (msg->request_headers,
177 ACCEPT_ENCODING_HEADER);
180 g_signal_connect (msg, "got-headers",
181 G_CALLBACK (soup_content_decoder_got_headers_cb),
186 request_unqueued (SoupSessionFeature *feature, SoupSession *session,
189 g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature);