diff options
Diffstat (limited to 'libsoup/soup-content-decoder.c')
-rw-r--r-- | libsoup/soup-content-decoder.c | 207 |
1 files changed, 123 insertions, 84 deletions
diff --git a/libsoup/soup-content-decoder.c b/libsoup/soup-content-decoder.c index 85dcef49..bce78c01 100644 --- a/libsoup/soup-content-decoder.c +++ b/libsoup/soup-content-decoder.c @@ -10,6 +10,7 @@ #endif #include "soup-content-decoder.h" +#include "soup-converter-wrapper.h" #include "soup.h" #include "soup-message-private.h" @@ -17,15 +18,18 @@ * 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. + * #SoupContentDecoder handles adding the "Accept-Encoding" header on + * outgoing messages, and processing the "Content-Encoding" header on + * incoming ones. Currently it supports the "gzip" and "deflate" + * content codings. * - * (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 you are using a plain #SoupSession (ie, not #SoupSessionAsync or + * #SoupSessionSync), then a #SoupContentDecoder will automatically be + * added to the session by default. (You can use + * %SOUP_SESSION_REMOVE_FEATURE_BY_TYPE at construct time if you don't + * want this.) If you are using one of the deprecated #SoupSession + * subclasses, you can add a #SoupContentDecoder to your session with + * soup_session_add_feature() or soup_session_add_feature_by_type(). * * If #SoupContentDecoder successfully decodes the Content-Encoding, * it will set the %SOUP_MESSAGE_CONTENT_DECODED flag on the message, @@ -39,7 +43,11 @@ * will be decoded (and the %SOUP_MESSAGE_CONTENT_DECODED flag will * not be set). * - * Since: 2.28.2 + * (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.) + * + * Since: 2.30 **/ struct _SoupContentDecoderPrivate { @@ -50,9 +58,114 @@ typedef GConverter * (*SoupContentDecoderCreator) (void); static void soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); +static SoupContentProcessorInterface *soup_content_decoder_default_content_processor_interface; +static void soup_content_decoder_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data); + + 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)) + soup_content_decoder_session_feature_init) + G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR, + soup_content_decoder_content_processor_init)) + +static GSList * +soup_content_decoder_get_decoders_for_msg (SoupContentDecoder *decoder, SoupMessage *msg) +{ + const char *header; + GSList *encodings, *e, *decoders = NULL; + SoupContentDecoderCreator converter_creator; + GConverter *converter; + + header = soup_message_headers_get_list (msg->response_headers, + "Content-Encoding"); + if (!header) + return NULL; + + /* Workaround for an apache bug (bgo 613361) */ + if (!g_ascii_strcasecmp (header, "gzip") || + !g_ascii_strcasecmp (header, "x-gzip")) { + const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); + + if (content_type && + (!g_ascii_strcasecmp (content_type, "application/gzip") || + !g_ascii_strcasecmp (content_type, "application/x-gzip"))) + return NULL; + } + + /* 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 NULL; + + for (e = encodings; e; e = e->next) { + if (!g_hash_table_lookup (decoder->priv->decoders, e->data)) { + soup_header_free_list (encodings); + return NULL; + } + } + + for (e = encodings; e; e = e->next) { + converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data); + converter = converter_creator (); + + /* 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. + */ + decoders = g_slist_prepend (decoders, converter); + } + soup_header_free_list (encodings); + + return decoders; +} + +static GInputStream* +soup_content_decoder_content_processor_wrap_input (SoupContentProcessor *processor, + GInputStream *base_stream, + SoupMessage *msg, + GError **error) +{ + GSList *decoders, *d; + GInputStream *istream; + + decoders = soup_content_decoder_get_decoders_for_msg (SOUP_CONTENT_DECODER (processor), msg); + if (!decoders) + return NULL; + + istream = g_object_ref (base_stream); + for (d = decoders; d; d = d->next) { + GConverter *decoder, *wrapper; + GInputStream *filter; + + decoder = d->data; + wrapper = soup_converter_wrapper_new (decoder, msg); + filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM, + "base-stream", istream, + "converter", wrapper, + NULL); + g_object_unref (istream); + g_object_unref (wrapper); + istream = filter; + } + + g_slist_free_full (decoders, g_object_unref); + + return istream; +} + +static void +soup_content_decoder_content_processor_init (SoupContentProcessorInterface *processor_interface, + gpointer interface_data) +{ + soup_content_decoder_default_content_processor_interface = + g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR); + + processor_interface->processing_stage = SOUP_STAGE_CONTENT_ENCODING; + processor_interface->wrap_input = soup_content_decoder_content_processor_wrap_input; +} /* This is constant for now */ #define ACCEPT_ENCODING_HEADER "gzip, deflate" @@ -107,89 +220,16 @@ soup_content_decoder_class_init (SoupContentDecoderClass *decoder_class) } 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; - SoupContentDecoderCreator converter_creator; - GConverter *converter; - - header = soup_message_headers_get_list (msg->response_headers, - "Content-Encoding"); - if (!header) - return; - - /* Workaround for an apache bug (bgo 613361) */ - if (!g_ascii_strcasecmp (header, "gzip") || - !g_ascii_strcasecmp (header, "x-gzip")) { - const char *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - - if (content_type && - (!g_ascii_strcasecmp (content_type, "application/gzip") || - !g_ascii_strcasecmp (content_type, "application/x-gzip"))) - 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->decoders, e->data)) { - soup_header_free_list (encodings); - return; - } - } - - /* msgpriv->decoders should be empty at this point anyway, but - * clean it up if it's not. - */ - g_slist_free_full (msgpriv->decoders, g_object_unref); - msgpriv->decoders = NULL; - - for (e = encodings; e; e = e->next) { - converter_creator = g_hash_table_lookup (decoder->priv->decoders, e->data); - converter = converter_creator (); - - /* 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, converter); - } - soup_header_free_list (encodings); -} - -static void soup_content_decoder_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 -soup_content_decoder_request_unqueued (SoupSessionFeature *feature, - SoupSession *session, - SoupMessage *msg) -{ - g_signal_handlers_disconnect_by_func (msg, soup_content_decoder_got_headers_cb, feature); } static void @@ -197,5 +237,4 @@ soup_content_decoder_session_feature_init (SoupSessionFeatureInterface *feature_ gpointer interface_data) { feature_interface->request_queued = soup_content_decoder_request_queued; - feature_interface->request_unqueued = soup_content_decoder_request_unqueued; } |