[libsoup] Added SoupContentProcessor support to SoupMessage, SoupContentDecoder & SoupContentSniffer
- From: Sergio Villar Senin <svillar src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup] Added SoupContentProcessor support to SoupMessage, SoupContentDecoder & SoupContentSniffer
- Date: Fri, 2 Nov 2012 16:54:18 +0000 (UTC)
commit c79bf2770c8a239c7495d92e5c885e1dea42fc6f
Author: Sergio Villar Senin <svillar igalia com>
Date: Tue Aug 21 16:44:43 2012 +0200
Added SoupContentProcessor support to SoupMessage, SoupContentDecoder & SoupContentSniffer
soup-message-io uses the content processors registered for each SoupMessage
to properly setup the stack of streams used to read a particular resource
either downloaded from the network or read from a local cached file. Note
that server-side messages do not have content processor support yet.
SoupContentDecoder becomes a content processor and wraps the given base
stream with a list of decoders when required.
SoupContentSniffer becomes a content processor working at the
SOUP_STAGE_BODY_DATA stage.
https://bugzilla.gnome.org/show_bug.cgi?id=682112
libsoup/soup-content-decoder.c | 182 +++++++++++++++++++--------------
libsoup/soup-content-sniffer-stream.c | 12 --
libsoup/soup-content-sniffer-stream.h | 4 -
libsoup/soup-content-sniffer.c | 35 ++++++-
libsoup/soup-message-io.c | 94 +++++++++++------
libsoup/soup-message-private.h | 8 ++-
libsoup/soup-message.c | 4 -
7 files changed, 210 insertions(+), 129 deletions(-)
---
diff --git a/libsoup/soup-content-decoder.c b/libsoup/soup-content-decoder.c
index 85dcef4..fa7629a 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"
@@ -50,9 +51,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 +213,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 +230,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;
}
diff --git a/libsoup/soup-content-sniffer-stream.c b/libsoup/soup-content-sniffer-stream.c
index 173f902..d358a19 100644
--- a/libsoup/soup-content-sniffer-stream.c
+++ b/libsoup/soup-content-sniffer-stream.c
@@ -336,18 +336,6 @@ soup_content_sniffer_stream_pollable_init (GPollableInputStreamInterface *pollab
pollable_interface->create_source = soup_content_sniffer_stream_create_source;
}
-GInputStream *
-soup_content_sniffer_stream_new (SoupContentSniffer *sniffer,
- SoupMessage *msg,
- GInputStream *base_stream)
-{
- return g_object_new (SOUP_TYPE_CONTENT_SNIFFER_STREAM,
- "base-stream", base_stream,
- "message", msg,
- "sniffer", sniffer,
- NULL);
-}
-
gboolean
soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
gboolean blocking,
diff --git a/libsoup/soup-content-sniffer-stream.h b/libsoup/soup-content-sniffer-stream.h
index fb4889c..ab230f3 100644
--- a/libsoup/soup-content-sniffer-stream.h
+++ b/libsoup/soup-content-sniffer-stream.h
@@ -36,10 +36,6 @@ struct _SoupContentSnifferStreamClass {
GType soup_content_sniffer_stream_get_type (void) G_GNUC_CONST;
-GInputStream *soup_content_sniffer_stream_new (SoupContentSniffer *sniffer,
- SoupMessage *msg,
- GInputStream *base_stream);
-
gboolean soup_content_sniffer_stream_is_ready (SoupContentSnifferStream *sniffer,
gboolean blocking,
GCancellable *cancellable,
diff --git a/libsoup/soup-content-sniffer.c b/libsoup/soup-content-sniffer.c
index 0986bbe..012c0dc 100644
--- a/libsoup/soup-content-sniffer.c
+++ b/libsoup/soup-content-sniffer.c
@@ -13,6 +13,8 @@
#include "soup-content-sniffer.h"
#include "soup.h"
+#include "soup-content-processor.h"
+#include "soup-content-sniffer-stream.h"
#include "soup-message-private.h"
/**
@@ -31,9 +33,40 @@
static void soup_content_sniffer_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
+static SoupContentProcessorInterface *soup_content_sniffer_default_content_processor_interface;
+static void soup_content_sniffer_content_processor_init (SoupContentProcessorInterface *interface, gpointer interface_data);
+
+
G_DEFINE_TYPE_WITH_CODE (SoupContentSniffer, soup_content_sniffer, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
- soup_content_sniffer_session_feature_init))
+ soup_content_sniffer_session_feature_init)
+ G_IMPLEMENT_INTERFACE (SOUP_TYPE_CONTENT_PROCESSOR,
+ soup_content_sniffer_content_processor_init))
+
+
+static GInputStream *
+soup_content_sniffer_content_processor_wrap_input (SoupContentProcessor *processor,
+ GInputStream *base_stream,
+ SoupMessage *msg,
+ GError **error)
+{
+ return g_object_new (SOUP_TYPE_CONTENT_SNIFFER_STREAM,
+ "base-stream", base_stream,
+ "message", msg,
+ "sniffer", SOUP_CONTENT_SNIFFER (processor),
+ NULL);
+}
+
+static void
+soup_content_sniffer_content_processor_init (SoupContentProcessorInterface *processor_interface,
+ gpointer interface_data)
+{
+ soup_content_sniffer_default_content_processor_interface =
+ g_type_default_interface_peek (SOUP_TYPE_CONTENT_PROCESSOR);
+
+ processor_interface->processing_stage = SOUP_STAGE_BODY_DATA;
+ processor_interface->wrap_input = soup_content_sniffer_content_processor_wrap_input;
+}
static void
soup_content_sniffer_init (SoupContentSniffer *content_sniffer)
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 52af22b..e851ac4 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -16,8 +16,8 @@
#include "soup-body-output-stream.h"
#include "soup-client-input-stream.h"
#include "soup-connection.h"
+#include "soup-content-processor.h"
#include "soup-content-sniffer-stream.h"
-#include "soup-converter-wrapper.h"
#include "soup-filter-input-stream.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
@@ -221,38 +221,53 @@ read_headers (SoupMessage *msg, GCancellable *cancellable, GError **error)
return TRUE;
}
-static void
-setup_body_istream (SoupMessage *msg)
+static gint
+processing_stage_cmp (gconstpointer a,
+ gconstpointer b)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
- SoupMessageIOData *io = priv->io_data;
- GConverter *decoder, *wrapper;
- GInputStream *filter;
- GSList *d;
-
- io->body_istream =
- soup_body_input_stream_new (G_INPUT_STREAM (io->istream),
- io->read_encoding,
- io->read_length);
-
- for (d = priv->decoders; d; d = d->next) {
- decoder = d->data;
- wrapper = soup_converter_wrapper_new (decoder, msg);
- filter = g_object_new (G_TYPE_CONVERTER_INPUT_STREAM,
- "base-stream", io->body_istream,
- "converter", wrapper,
- NULL);
- g_object_unref (io->body_istream);
- g_object_unref (wrapper);
- io->body_istream = filter;
- }
+ SoupProcessingStage stage_a = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (a));
+ SoupProcessingStage stage_b = soup_content_processor_get_processing_stage (SOUP_CONTENT_PROCESSOR (b));
+
+ if (stage_a > stage_b)
+ return 1;
+ if (stage_a == stage_b)
+ return 0;
+ return -1;
+}
- if (priv->sniffer) {
- filter = soup_content_sniffer_stream_new (priv->sniffer,
- msg, io->body_istream);
- g_object_unref (io->body_istream);
- io->body_istream = filter;
+GInputStream *
+soup_message_setup_body_istream (GInputStream *body_stream,
+ SoupMessage *msg,
+ SoupSession *session,
+ SoupProcessingStage start_at_stage)
+{
+ GInputStream *istream;
+ GSList *p, *processors;
+
+ istream = g_object_ref (body_stream);
+
+ processors = soup_session_get_features (session, SOUP_TYPE_CONTENT_PROCESSOR);
+ processors = g_slist_sort (processors, processing_stage_cmp);
+
+ for (p = processors; p; p = p->next) {
+ GInputStream *wrapper;
+ SoupContentProcessor *processor;
+
+ processor = SOUP_CONTENT_PROCESSOR (p->data);
+ if (soup_message_disables_feature (msg, p->data) ||
+ soup_content_processor_get_processing_stage (processor) < start_at_stage)
+ continue;
+
+ wrapper = soup_content_processor_wrap_input (processor, istream, msg, NULL);
+ if (wrapper) {
+ g_object_unref (istream);
+ istream = wrapper;
+ }
}
+
+ g_slist_free (processors);
+
+ return istream;
}
/*
@@ -570,8 +585,23 @@ io_read (SoupMessage *msg, GCancellable *cancellable, GError **error)
case SOUP_MESSAGE_IO_STATE_BODY_START:
- if (!io->body_istream)
- setup_body_istream (msg);
+ if (!io->body_istream) {
+ GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream),
+ io->read_encoding,
+ io->read_length);
+
+ /* TODO: server-side messages do not have a io->item. This means
+ * that we cannot use content processors for them right now.
+ */
+ if (io->mode == SOUP_MESSAGE_IO_CLIENT) {
+ io->body_istream = soup_message_setup_body_istream (body_istream, msg,
+ io->item->session,
+ SOUP_STAGE_MESSAGE_BODY);
+ g_object_unref (body_istream);
+ } else {
+ io->body_istream = body_istream;
+ }
+ }
if (priv->sniffer) {
SoupContentSnifferStream *sniffer_stream = SOUP_CONTENT_SNIFFER_STREAM (io->body_istream);
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 69490e9..8665007 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -8,7 +8,9 @@
#include "soup-message.h"
#include "soup-auth.h"
+#include "soup-content-processor.h"
#include "soup-content-sniffer.h"
+#include "soup-session.h"
typedef struct {
gpointer io_data;
@@ -31,7 +33,6 @@ typedef struct {
SoupAuth *auth, *proxy_auth;
GSList *disabled_features;
- GSList *decoders;
SoupURI *first_party;
@@ -126,4 +127,9 @@ void soup_message_network_event (SoupMessage *msg,
GSocketClientEvent event,
GIOStream *connection);
+GInputStream *soup_message_setup_body_istream (GInputStream *body_stream,
+ SoupMessage *msg,
+ SoupSession *session,
+ SoupProcessingStage start_at_stage);
+
#endif /* SOUP_MESSAGE_PRIVATE_H */
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 2271cd0..5ff836f 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -162,8 +162,6 @@ soup_message_finalize (GObject *object)
g_slist_free (priv->disabled_features);
- g_slist_free_full (priv->decoders, g_object_unref);
-
g_clear_object (&priv->tls_certificate);
soup_message_body_free (msg->request_body);
@@ -1413,8 +1411,6 @@ soup_message_cleanup_response (SoupMessage *req)
SOUP_ENCODING_CONTENT_LENGTH);
}
- g_slist_free_full (priv->decoders, g_object_unref);
- priv->decoders = NULL;
priv->msg_flags &= ~SOUP_MESSAGE_CONTENT_DECODED;
req->status_code = SOUP_STATUS_NONE;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]