[Evolution-hackers] Content-Encoding support for libsoup [patch]



Hello evolution-hackers!

Here's my attempt to add support for the HTTP Content-Encoding header to libsoup.  It includes both encoding and decoding messages with the gzip, deflate, and bzip2 algorithms, and can support layered Content-Encodings if called upon to do so.

This is my first evolution-related patch, so I would appreciate experienced eyes pointing out how many things I've done horribly wrong.

The patch should apply cleanly against the current CVS head.  It does not alter any existing interfaces, and adds several new ones:

soup-headers:
    soup_header_tokenize() - convert a "1#token" header values into a GSList of single tokens.
    soup_header_tokenize_list() - do the same to a list of "1#token" header values.

soup-message:
    soup_message_set_encoded_request() - like soup_message_set_request, but apply a list of content encodings to the message body
    soup_message_set_encoded_response() - likewise
    soup_message_encode_request - apply a given encoding to a buffered request body
    soup_message_decode_request - try to remove all content-encodings from a buffered request body
    soup_message_encode_response - apply a given encoding to a buffered response body
    soup_message_decode_response - try to remove all content-encodings from a buffered response body

Semantic changes:
    soup_soap_message_persist() now sets the "Accept-Encoding" header.
    soup_soap_message_parse_response() now automatically tests for Content-Encodings and tries to decode them.

Extensibility:
Adding more content-codings is straightforward; see soup-message-contentencoding.c, particularly the soup_message_content_codecs[] array (which is used to automatically build the Accept-Encoding header in s_s_m_persist).


diff -urN libsoup/configure.in libsoup-ADB/configure.in
--- libsoup/configure.in	2004-08-26 16:28:33 -0400
+++ libsoup-ADB/configure.in	2004-10-26 17:21:54 -0400
@@ -234,6 +234,12 @@
 AC_SUBST(LIBGNUTLS_LIBS)
 AC_SUBST(LIBGNUTLS_LIBS_STATIC)
 
+dnl **********************************
+dnl *** Compression library checks ***
+dnl **********************************
+AC_CHECK_LIB(z, uncompress)
+AC_CHECK_LIB(bz2, BZ2_bzBuffToBuffDecompress)
+
 dnl ***************
 dnl *** gtk-doc ***
 dnl ***************
diff -urN libsoup/libsoup/Makefile.am libsoup-ADB/libsoup/Makefile.am
--- libsoup/libsoup/Makefile.am	2004-02-27 11:15:01 -0500
+++ libsoup-ADB/libsoup/Makefile.am	2004-10-28 20:30:31 -0400
@@ -80,6 +80,7 @@
 	soup-md5-utils.c		\
 	soup-message.c			\
 	soup-message-client-io.c	\
+	soup-message-contentencoding.c	\
 	soup-message-filter.c		\
 	soup-message-handlers.c		\
 	soup-message-io.c		\
diff -urN libsoup/libsoup/soup-headers.c libsoup-ADB/libsoup/soup-headers.c
--- libsoup/libsoup/soup-headers.c	2003-09-22 16:10:41 -0400
+++ libsoup-ADB/libsoup/soup-headers.c	2004-10-28 14:59:04 -0400
@@ -198,6 +198,83 @@
 	return TRUE;
 }
 
+/*
+ * HTTP header tokenization
+ */
+
+GSList 
+*soup_header_tokenize(const char *str)
+{
+	/*
+	 * A lot of HTTP headers have the content model "1#token"
+	 * (or something immediately reducible thereto).
+	 * Per RFC2616 S2.1 (ABNF), the rule is:
+	 *   ( *LWS token *( *LWS "," *LWS token ))
+	 * where "token" is:
+	 *   1*<any CHAR except CTLs or separators>
+	 * CTL            = <any US-ASCII control character
+	 *                (octets 0 - 31) and DEL (127)>
+	 * separators     = "(" | ")" | "<" | ">" | "@"
+	 *                | "," | ";" | ":" | "\" | <">
+	 *                | "/" | "[" | "]" | "?" | "="
+	 *                | "{" | "}" | SP | HT
+	 *
+	 * Fun, huh?
+	 *
+	 * Quotes are not honored in tokens, so be sure the header
+	 * you're tokening is really supposed to be 1#token before
+	 * you use this function.
+	 */
+	GSList *ret = NULL;
+	const char *iptr = str;
+
+	do {
+		char *eptr;
+		char *token;
+		while ( (*iptr=='\x0D') || (*iptr=='\x0A') || 
+			(*iptr=='\x09') || (*iptr=='\x20') ) iptr++;
+		if (*iptr=='\0')
+			break;
+		eptr = strchr(iptr, ',');
+		if (eptr) {
+			char *ws;
+			if (eptr > iptr) {
+				token = g_malloc(eptr+1-iptr);
+				strncpy(token, iptr, eptr-iptr);
+				token[eptr-iptr] = '\0';
+				/* We assume any WS between ','s is padding
+				   between ','s, so we kill the string there.
+				   Tokens aren't allowed to include WS anyway. */
+				ws = token + strcspn(token, "\x0D\x0A\x09\x20");
+				*ws = '\0';
+				ret = g_slist_append(ret, token);
+			}
+			iptr = eptr+1;
+		} else {
+			token = g_malloc(strlen(iptr)+1);
+			strncpy(token, iptr, strlen(iptr));
+			token[strlen(iptr)] = '\0';
+			char *ws = token + strcspn(token, "\x0D\x0A\x09\x20");
+			*ws = '\0';
+			ret = g_slist_append(ret, token);
+			iptr += strlen(iptr);
+		}
+	} while (*iptr!='\0');
+
+	return ret;
+}
+
+GSList 
+*soup_header_tokenize_list(const GSList *str_list)
+{
+	GSList *ret;
+	char *hdr;
+	if (str_list==NULL)
+		return NULL;
+	hdr = str_list->data;
+	ret = soup_header_tokenize(hdr);
+	return g_slist_concat(ret, soup_header_tokenize_list(str_list->next));
+}
 
 /*
  * HTTP parameterized header parsing
diff -urN libsoup/libsoup/soup-headers.h libsoup-ADB/libsoup/soup-headers.h
--- libsoup/libsoup/soup-headers.h	2003-08-20 13:49:58 -0400
+++ libsoup-ADB/libsoup/soup-headers.h	2004-10-27 13:55:53 -0400
@@ -30,6 +30,11 @@
 					     guint            *status_code,
 					     char            **status_phrase);
 
+/* HTTP header tokenization */
+GSList     *soup_header_tokenize            (const char       *str);
+
+GSList     *soup_header_tokenize_list       (const GSList     *str_list);
+
 /* HTTP parameterized header parsing */
 
 char       *soup_header_param_decode_token  (char            **in);
diff -urN libsoup/libsoup/soup-message-contentencoding.c libsoup-ADB/libsoup/soup-message-contentencoding.c
--- libsoup/libsoup/soup-message-contentencoding.c	1969-12-31 19:00:00 -0500
+++ libsoup-ADB/libsoup/soup-message-contentencoding.c	2004-10-28 21:41:12 -0400
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-message-contentencoding.c: HTTP message content-encoding 
+ *
+ * Copyright (C) 2004, Adam D. Bradley <artdodge cs bu edu>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_LIBBZ2
+#include <bzlib.h>
+#endif
+
+#include "soup-message.h"
+
+/* wrap glib mem functions for use by zlib */
+static void *g_zalloc_wrapper(voidpf opaque, uInt items, uInt size) {
+	void *ptr = g_malloc0(items * size);
+	return ptr;
+}
+
+static void g_zfree_wrapper(voidpf opaque, voidpf address) {
+	g_free(address);
+}
+
+
+
+
+/** gzip and deflate encoding **/
+
+/* gzip==1 : use gzip headers, gzip==0: use zlib headers */
+static int soup_message_zlib_encode (SoupDataBuffer *msg, int gzip)
+{
+#ifdef HAVE_LIBZ
+	z_stream stream;
+	char *new_body;
+	
+	stream.next_in = msg->body;
+	stream.avail_in = msg->length;
+	stream.total_in = 0;
+	
+	new_body = stream.next_out = g_malloc(msg->length + 65); /* overly generous */
+	stream.avail_out = msg->length + 64;
+	stream.total_out = 0;
+	
+	stream.zalloc = g_zalloc_wrapper;
+	stream.zfree = g_zfree_wrapper;
+	stream.opaque = NULL;
+	
+	stream.data_type = Z_ASCII;
+	
+	if (deflateInit2(
+		    &stream, 
+		    3 /* compression level, 1-9, default 6 (Z_DEFAULT_COMPRESSION) */, 
+		    Z_DEFLATED, 
+		    ((gzip!=0)?16:0)+15 /* gzip headers? (16) + window size (15b) */, 
+		    3 /* memory level, 1-9, default 8 */, 
+		    Z_DEFAULT_STRATEGY
+		    ) != Z_OK) {
+		/* failed to initialize, bail out */
+		deflateEnd(&stream);
+		return -1;
+	}
+	
+	while (stream.avail_in != 0) {
+		switch(deflate(&stream, Z_FINISH)) {
+		case Z_STREAM_END: /* success */
+			break;
+			
+		case Z_OK: /* need more buffer space !?  bail out... */
+			deflateEnd(&stream);
+			return -1;
+			
+		default: /* other error cases */
+			deflateEnd(&stream);
+			return -1;
+		}
+	}
+	
+	g_free(msg->body);
+	msg->body = new_body;
+	msg->length = stream.total_out;
+	
+	deflateEnd(&stream);
+	return 0;
+#else
+	/* no zlib, can't do it */
+	return -1;
+#endif
+}
+
+static int soup_message_gzip_encode (SoupDataBuffer *msg)
+{
+	return soup_message_zlib_encode (msg, 1);
+}
+
+static int soup_message_deflate_encode (SoupDataBuffer *msg)
+{
+	return soup_message_zlib_encode (msg, 0);
+}
+
+
+
+
+/** gzip and deflate decoding **/
+
+static int soup_message_zlib_decode (SoupDataBuffer *msg)
+{
+#ifdef HAVE_LIBZ
+	/* zlib inflate() auto-detects head/tailers, etc */
+	char *unc_body = NULL;
+	int factor = 4;
+	int unc_size = msg->length * factor;
+	
+	z_stream stream;
+	stream.next_in = msg->body;
+	stream.avail_in = msg->length;
+	stream.total_in = 0;
+	unc_body = stream.next_out = g_malloc(unc_size + 1);
+	stream.avail_out = unc_size;
+	stream.total_out = 0;
+	stream.zalloc = g_zalloc_wrapper;
+	stream.zfree = g_zfree_wrapper;
+	stream.opaque = NULL;
+	
+	if (inflateInit2(&stream, 32/*auto-detect*/+15/*max*/) != Z_OK) {
+		inflateEnd(&stream);
+		return -1; /* fail */
+	}
+	do {
+		int z_res = inflate(&stream, Z_FINISH);
+		if (z_res == Z_STREAM_END) {
+			/* all done */
+			break;
+		} else if (((z_res == Z_OK) && 
+			    (stream.avail_out==0)) ||
+			   (z_res==Z_BUF_ERROR)) {
+			/* need more buffer */
+			if ((unc_size >= 1048576) && 
+			    (factor >= 16)) {
+				inflateEnd(&stream);
+				g_free(unc_body);
+				return -1; /* fail */
+			}
+			factor *= 4;
+			unc_size = (msg->length * factor);
+			unc_body = g_realloc(unc_body, unc_size + 1);
+			stream.next_out = unc_body + stream.total_out;
+			stream.avail_out = unc_size - stream.total_out;
+			continue; /* try again */
+		} else {
+			/* unexpected error condition - bail out */
+			inflateEnd(&stream);
+			g_free(unc_body);
+			return -1; /* fail */
+		}
+	} while (1);
+	
+	g_free(msg->body);
+	msg->body = unc_body;
+	msg->length = stream.total_out;
+	
+	return 0; /* success; pop off the GSL */
+#else
+	return -1; /* no zlib, we can't do it */
+#endif
+}
+
+
+
+
+/** bzip2 encoding **/
+
+static int soup_message_bzip2_encode (SoupDataBuffer *msg)
+{
+#ifdef HAVE_LIBBZ2
+		char *new_body = g_malloc(msg->length + 65); /* overly generous */
+		int new_body_len;
+		switch(BZ2_bzBuffToBuffCompress(
+			       new_body, &new_body_len,
+			       msg->body, msg->length,
+			       1, 0, 1)) {
+		case BZ_OK:
+			break; /* success */
+
+		default:
+			/* assume we failed */
+			g_free(new_body);
+			return -1;
+		}
+
+		g_free(msg->body);
+		msg->body = new_body;
+		msg->length = new_body_len;
+
+		return 0;
+#else
+		/* no bz2lib, can't do it... issue a warning? */
+		return -1;
+#endif
+}
+
+
+/** bzip2 decoding **/
+
+static int soup_message_bzip2_decode (SoupDataBuffer *msg)
+{
+#ifdef HAVE_LIBBZ2
+	char *unc_body = NULL;
+	int factor = 4;
+	int unc_size = msg->length * factor;
+	unc_body = g_malloc(unc_size + 1);
+
+	fprintf(stderr, "In\n");
+	
+	do {
+		fprintf(stderr, "Trying bounce\n");
+		int z_res = 
+			BZ2_bzBuffToBuffDecompress(
+				unc_body, &unc_size,
+				msg->body, msg->length,
+				0, 0);
+		if (z_res == BZ_OUTBUFF_FULL) {
+			/* need more buffer */
+			fprintf(stderr, "Needs Buffer\n");
+
+			if ((unc_size >= 1048576) && 
+			    (factor >= 16)) {
+				/* freak out if we're looking at a 
+				 * huge decompressed stream
+				 * TODO : make this configurable? */
+				g_free(unc_body);
+				fprintf(stderr, "Too much\n");
+				return -1;
+			}
+			factor *= 4;
+			unc_size = (msg->length * factor);
+			g_free(unc_body);
+			unc_body = g_malloc(unc_size + 1);
+			continue; /* try again */
+		} else if (z_res == BZ_OK) {
+			/* success */
+			break;
+		} else {
+			/* unexpected error condition - bail out */
+			g_free(unc_body);
+			fprintf(stderr, "ERROR: [%d]\n", z_res);
+			return -1;
+		}
+	} while (1);
+	
+	fprintf(stderr, "Done\n");
+
+	g_free(msg->body);
+	msg->body = unc_body;
+	msg->length = unc_size;
+	
+	return 0;
+#else
+	return -1; /* failed */
+#endif
+}
+
+
+
+
+SoupContentEncoderDecoder soup_message_content_codecs[] = {
+	{ "gzip", soup_message_gzip_encode, soup_message_zlib_decode },
+	{ "x-gzip", soup_message_gzip_encode, soup_message_zlib_decode },
+
+	/* gzip decoder auto-detects deflate format */
+	{ "deflate", soup_message_deflate_encode, soup_message_zlib_decode },
+
+	{ "x-bzip2", soup_message_bzip2_encode, soup_message_bzip2_decode },
+
+	/* we don't have an implementation of "compress" (aka "x-compress") */
+	{ NULL, NULL, NULL }
+};
+
diff -urN libsoup/libsoup/soup-message.c libsoup-ADB/libsoup/soup-message.c
--- libsoup/libsoup/soup-message.c	2004-08-26 11:33:33 -0400
+++ libsoup-ADB/libsoup/soup-message.c	2004-10-28 22:27:46 -0400
@@ -3,11 +3,26 @@
  * soup-message.c: HTTP request/response
  *
  * Copyright (C) 2000-2003, Ximian, Inc.
+ *
+ * Content coding/decoding hooks by Adam D. Bradley <artdodge cs bu edu>, 20041028
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+#ifdef HAVE_LIBBZ2
+#include <bzlib.h>
+#endif
+
 #include <string.h>
 
 #include "soup-auth.h"
+#include "soup-headers.h"
 #include "soup-marshal.h"
 #include "soup-message.h"
 #include "soup-message-private.h"
@@ -254,6 +269,45 @@
 }
 
 /**
+ * soup_message_set_encoded_request:
+ * @msg: the message
+ * @content_type: MIME Content-Type of the body
+ * @req_owner: the #SoupOwnership of the passed data buffer.
+ * @req_body: a data buffer containing the body of the message request.
+ * @req_length: the byte length of @req_body.
+ * @content_encodings: GSList of string keys for content-encodings to be applied to the body.  Understood values: "gzip", "x-gzip", "deflate", "x-bzip2".
+ *
+ * Convenience function to set the request body of a #SoupMessage and apply content-encodings
+ */
+void
+soup_message_set_encoded_request (SoupMessage   *msg,
+				  const char    *content_type,
+				  SoupOwnership  req_owner,
+				  char          *req_body,
+				  gulong         req_length,
+				  GSList        *content_encodings)
+{
+	GSList *ce_iter;
+
+	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+	g_return_if_fail (content_type != NULL);
+	g_return_if_fail (req_body != NULL || req_length == 0);
+
+	soup_message_add_header (msg->request_headers,
+				 "Content-Type", content_type);
+
+	msg->request.owner = req_owner;
+	msg->request.body = req_body;
+	msg->request.length = req_length;
+
+	for (ce_iter = content_encodings ; 
+	     ce_iter != NULL ; 
+	     ce_iter = ce_iter->next) {
+		soup_message_encode_request(msg, ce_iter->data);
+	}
+}
+
+/**
  * soup_message_set_request:
  * @msg: the message
  * @content_type: MIME Content-Type of the body
@@ -270,15 +324,159 @@
 			  char          *req_body,
 			  gulong         req_length)
 {
+	soup_message_set_encoded_request(msg, content_type, 
+					 req_owner, req_body, req_length, 
+					 NULL);
+}
+
+
+/* encoding/decoding of message bodies (Content-Encoding), 
+   used on requests and responses by s_m_e_{request,response} respectively */
+static int soup_message_encode (SoupDataBuffer *msg, 
+				const char *encoding)
+{
+	int i;
+	for (i=0 ; soup_message_content_codecs[i].token != NULL ; i++) {
+		if (g_strcasecmp(encoding, soup_message_content_codecs[i].token) == 0) {
+			return soup_message_content_codecs[i].encode(msg);
+		}
+	}
+	return -1; /* unknown key, report failure */
+}
+
+static GSList *soup_message_decode (SoupDataBuffer *msg, 
+				    GSList *ce)
+{
+	int i;
+	const char *enc;
+
+	if ((!ce) || (!ce->data))
+		return NULL;
+	
+	if ((ce->next=soup_message_decode(msg, ce->next))!=NULL)
+		return ce;
+	
+	/* ce->next will be NULL if we succeeded in all outer decodes */
+	
+	enc = ce->data;
+
+	for (i=0 ; soup_message_content_codecs[i].token!=NULL ; i++) {
+		if (g_strcasecmp(enc, soup_message_content_codecs[i].token)==0) {
+			if (soup_message_content_codecs[i].decode(msg)==0) {
+				/* success */
+				g_free(ce->data);
+				g_slist_free(ce);
+				return NULL;
+			} else {
+				/* failed, leave encoding record in place */
+				return ce;
+			}
+		}
+	}
+	return ce; /* couldn't find a codec, nothing done */
+}
+
+/**
+ * soup_message_encode_request
+ * @msg: the message
+ * @encoding: key to content-coding to apply
+ *
+ * Applies content-codings in-place to a #SoupMessage request body
+ */
+void
+soup_message_encode_request (SoupMessage *msg,
+			     const char  *encoding)
+{
+	if (msg->request.length < 1)
+		return; /* Don't bother encoding an empty payload */
+
+	if (soup_message_encode(&(msg->request), encoding)==0)
+		soup_message_add_header(msg->request_headers, 
+					"Content-Encoding", encoding);
+}
+
+/**
+ * soup_message_decode_request
+ * @msg: the message
+ *
+ * Unwraps content-codings applied to the #SoupMessage request body
+ */
+int
+soup_message_decode_request (SoupMessage *msg)
+{
+	GSList *l_ce, *new_ce;
+	const char *ct;
+
+	l_ce = soup_header_tokenize_list(
+		soup_message_get_header_list(
+			msg->request_headers, 
+			"Content-Encoding"));
+
+	/* Check for RFC3229 instance-manipulations */
+	g_return_val_if_fail(soup_message_get_header(msg->request_headers, "IM")==NULL, -1);
+	
+	/* Check for conventional instance-manipulations */
+	g_return_val_if_fail(soup_message_get_header(msg->request_headers, "Content-Range")==NULL, -1);
+	if ((ct = soup_message_get_header(msg->request_headers, "Content_Type"))!=NULL) {
+		g_return_val_if_fail(g_strncasecmp(ct, "multipart/byterangs", 20)!=0, -1);
+	}
+
+	new_ce = soup_message_decode(&(msg->request), l_ce);
+	soup_message_remove_header(msg->request_headers, "Content-Encoding");
+	if (new_ce) {
+		GSList *ce_it;
+		char *content_encoding;
+		int len = 0;
+		for (ce_it=new_ce ; ce_it!=NULL ; ce_it=ce_it->next)
+			len += (strlen((char *) ce_it->data)+2);
+		content_encoding = g_malloc0(len);
+		for (ce_it=new_ce ; ce_it!=NULL ; ce_it=ce_it->next) {
+			sprintf(content_encoding+strlen(content_encoding), "%s%s",
+				(char *) ce_it->data,
+				((ce_it->next!=NULL)?", ":""));
+		}
+		soup_message_add_header(msg->request_headers, "Content-Encoding", content_encoding);
+		return -1;
+	} else
+		return 0;
+}
+
+/**
+ * soup_message_set_encoded_response:
+ * @msg: the message
+ * @content_type: MIME Content-Type of the body
+ * @resp_owner: the #SoupOwnership of the passed data buffer.
+ * @resp_body: a data buffer containing the body of the message response.
+ * @resp_length: the byte length of @resp_body.
+ * @content_encoding: GSList of string keys for content-encodings to be applied to the body.  Understood values: "gzip", "x-gzip", "deflate", "x-bzip2".
+ * 
+ * Convenience function to set the response body of a #SoupMessage
+ */
+void
+soup_message_set_encoded_response (SoupMessage   *msg,
+				   const char    *content_type,
+				   SoupOwnership  resp_owner,
+				   char          *resp_body,
+				   gulong         resp_length,
+				   GSList        *content_encodings)
+{
+	GSList *ce_iter;
+
 	g_return_if_fail (SOUP_IS_MESSAGE (msg));
 	g_return_if_fail (content_type != NULL);
-	g_return_if_fail (req_body != NULL || req_length == 0);
+	g_return_if_fail (resp_body != NULL || resp_length == 0);
 
-	soup_message_add_header (msg->request_headers,
+	soup_message_add_header (msg->response_headers,
 				 "Content-Type", content_type);
-	msg->request.owner = req_owner;
-	msg->request.body = req_body;
-	msg->request.length = req_length;
+	msg->response.owner = resp_owner;
+	msg->response.body = resp_body;
+	msg->response.length = resp_length;
+
+	for (ce_iter = content_encodings ;
+	     ce_iter != NULL ;
+	     ce_iter = ce_iter->next) {
+		soup_message_encode_response(msg, ce_iter->data);
+	}
 }
 
 /**
@@ -298,15 +496,74 @@
 			   char          *resp_body,
 			   gulong         resp_length)
 {
-	g_return_if_fail (SOUP_IS_MESSAGE (msg));
-	g_return_if_fail (content_type != NULL);
-	g_return_if_fail (resp_body != NULL || resp_length == 0);
+	soup_message_set_encoded_response(msg, content_type, 
+					  resp_owner, resp_body, resp_length, 
+					  NULL);
+}
 
-	soup_message_add_header (msg->response_headers,
-				 "Content-Type", content_type);
-	msg->response.owner = resp_owner;
-	msg->response.body = resp_body;
-	msg->response.length = resp_length;
+/**
+ * soup_message_encode_response
+ * @msg: the message
+ * @encoding: key to content-coding to apply
+ *
+ * Applies content-codings in-place to a #SoupMessage response body
+ */
+void
+soup_message_encode_response (SoupMessage *msg,
+			      const char  *encoding)
+{
+	g_return_if_fail(msg->response.length >= 1); /* Don't bother encoding an empty payload */
+
+	if (soup_message_encode(&(msg->response), encoding)==0)
+		soup_message_add_header(msg->response_headers, "Content-Encoding", encoding);
+}
+
+/**
+ * soup_message_decode_response
+ * @msg: the message
+ *
+ * Unwraps content-codings applied to the #SoupMessage response body
+ */
+int
+soup_message_decode_response (SoupMessage *msg)
+{
+	GSList *l_ce, *new_ce;
+	const char *ct;
+
+	l_ce = soup_header_tokenize_list(
+		soup_message_get_header_list(
+			msg->response_headers, 
+			"Content-Encoding"));
+
+	/* Check for RFC3229 instance-manipulations */
+	g_return_val_if_fail(msg->status_code != 226, -1);
+	g_return_val_if_fail(soup_message_get_header(msg->request_headers, "IM")==NULL, -1);
+	
+	/* Check for conventional instance-manipulations */
+	g_return_val_if_fail(msg->status_code != 206, -1);
+	g_return_val_if_fail(soup_message_get_header(msg->request_headers, "Content-Range")==NULL, -1);
+	if ((ct = soup_message_get_header(msg->request_headers, "Content_Type"))!=NULL) {
+		g_return_val_if_fail(g_strncasecmp(ct, "multipart/byterangs", 20)!=0, -1);
+	}
+
+	new_ce = soup_message_decode(&(msg->response), l_ce);
+	soup_message_remove_header(msg->response_headers, "Content-Encoding");
+	if (new_ce) {
+		GSList *ce_it;
+		char *content_encoding;
+		int len = 0;
+		for (ce_it=new_ce ; ce_it!=NULL ; ce_it=ce_it->next)
+			len += (strlen((char *) ce_it->data)+2);
+		content_encoding = g_malloc0(len);
+		for (ce_it=new_ce ; ce_it!=NULL ; ce_it=ce_it->next) {
+			sprintf(content_encoding+strlen(content_encoding), "%s%s",
+				(char *) ce_it->data,
+				((ce_it->next!=NULL)?", ":""));
+		}
+		soup_message_add_header(msg->response_headers, "Content-Encoding", content_encoding);
+		return -1;
+	} else
+		return 0;
 }
 
 /**
diff -urN libsoup/libsoup/soup-message.h libsoup-ADB/libsoup/soup-message.h
--- libsoup/libsoup/soup-message.h	2004-08-26 11:33:33 -0400
+++ libsoup-ADB/libsoup/soup-message.h	2004-10-28 21:32:41 -0400
@@ -46,6 +46,14 @@
 	guint          length;
 } SoupDataBuffer;
 
+typedef struct {
+	const char *token;
+	int    (*encode) (SoupDataBuffer *);
+	int    (*decode) (SoupDataBuffer *);
+} SoupContentEncoderDecoder;
+
+extern SoupContentEncoderDecoder soup_message_content_codecs[];
+
 struct SoupMessage {
 	GObject parent;
 
@@ -90,18 +98,42 @@
 SoupMessage   *soup_message_new_from_uri        (const char        *method,
 						 const SoupUri     *uri);
 
+void           soup_message_set_encoded_request (SoupMessage       *msg,
+						 const char        *content_type,
+						 SoupOwnership      req_owner,
+						 char              *req_body,
+						 gulong             req_length,
+						 GSList            *content_encodings);
+
 void           soup_message_set_request         (SoupMessage       *msg,
 						 const char        *content_type,
 						 SoupOwnership      req_owner,
 						 char              *req_body,
 						 gulong             req_length);
 
+void           soup_message_encode_request      (SoupMessage       *msg,
+						 const char        *encoding);
+
+int            soup_message_decode_request      (SoupMessage       *msg);
+
+void           soup_message_set_encoded_response (SoupMessage       *msg,
+						  const char        *content_type,
+						  SoupOwnership      resp_owner,
+						  char              *resp_body,
+						  gulong             resp_length,
+						  GSList            *content_encodings);
+
 void           soup_message_set_response        (SoupMessage       *msg,
 						 const char        *content_type,
 						 SoupOwnership      resp_owner,
 						 char              *resp_body,
 						 gulong             resp_length);
 
+void           soup_message_encode_response     (SoupMessage       *msg,
+						 const char        *encoding);
+
+int            soup_message_decode_response     (SoupMessage       *msg);
+
 void           soup_message_add_header          (GHashTable        *hash,
 						 const char        *name,
 						 const char        *value);
diff -urN libsoup/libsoup/soup-soap-message.c libsoup-ADB/libsoup/soup-soap-message.c
--- libsoup/libsoup/soup-soap-message.c	2004-08-26 11:33:33 -0400
+++ libsoup-ADB/libsoup/soup-soap-message.c	2004-10-29 00:11:35 -0400
@@ -3,7 +3,12 @@
  * Copyright (C) 2003, Novell, Inc.
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <string.h>
+
 #include "soup-misc.h"
 #include "soup-soap-message.h"
 #include "soup-uri.h"
@@ -682,16 +687,32 @@
 void
 soup_soap_message_persist (SoupSoapMessage *msg)
 {
+	static char *accept_encoding = NULL;
 	xmlChar *body;
 	int len;
 
 	g_return_if_fail (SOUP_IS_SOAP_MESSAGE (msg));
 
 	xmlDocDumpMemory (msg->priv->doc, &body, &len);
-
+	
 	/* serialize to SoupMessage class */
 	soup_message_set_request (SOUP_MESSAGE (msg), "text/xml",
 				  SOUP_BUFFER_SYSTEM_OWNED, body, len);
+
+	/* Add an "Accept-Encoding" header to the request,
+	   since we (in principle) understand Content-Encodings */
+	if ((accept_encoding==NULL) && (soup_message_content_codecs[0].token!=NULL)) {
+		int i;
+		accept_encoding = g_malloc0(32);
+		for (i=0 ; soup_message_content_codecs[i].token!=NULL ; i++) {
+			sprintf(accept_encoding+strlen(accept_encoding),
+				"%s, ", soup_message_content_codecs[i].token);
+		}
+		accept_encoding[strlen(accept_encoding)-2] = '\0';
+	}
+	if (accept_encoding)
+		soup_message_add_header (SOUP_MESSAGE(msg)->request_headers,
+					 "Accept-Encoding", accept_encoding);
 }
 
 /**
@@ -758,6 +779,8 @@
 
 	g_return_val_if_fail (SOUP_IS_SOAP_MESSAGE (msg), NULL);
 
+	g_return_val_if_fail(soup_message_decode_response(SOUP_MESSAGE(msg))==0, NULL);
+
 	xmlstr = g_malloc0 (SOUP_MESSAGE (msg)->response.length + 1);
 	strncpy (xmlstr, SOUP_MESSAGE (msg)->response.body, SOUP_MESSAGE (msg)->response.length);
 
@@ -766,3 +789,4 @@
 
 	return soap_response;
 }
+


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]