[libsoup] Added SoupContentProcessor support to SoupMessage, SoupContentDecoder & SoupContentSniffer



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]