libsoup r1243 - in trunk: . libsoup tests



Author: danw
Date: Thu Feb 19 18:55:49 2009
New Revision: 1243
URL: http://svn.gnome.org/viewvc/libsoup?rev=1243&view=rev

Log:
	Bug 572153 â SoupServer doesn't support SOUP_ENCODING_EOF

	* libsoup/soup-message-io.c (io_write): Various fixes to make
	SOUP_ENCODING_EOF work correctly when sending response
	bodies. (Previously, the code assumed that SoupServer responses
	would always be chunked or Content-Length-encoded.)

	* libsoup/soup-message-client-io.c (get_request_headers): when
	changing a request body from SOUP_ENCODING_NONE to
	SOUP_ENCODING_CONTENT_LENGTH, return the new encoding value to
	soup-message-io, not the old one.

	* libsoup/soup-message.c (set_property): when setting
	priv->server_side to TRUE, set the default encoding on the
	response headers to CONTENT_LENGTH. (Moved from SoupServer.)
	(soup_message_cleanup_response): If priv->server_side is TRUE,
	re-fix the response header encoding after clearing the headers.
	Otherwise the response headers revert to SOUP_ENCODING_EOF after
	sending a "100 Continue".
	(soup_message_is_keepalive): reorganize a little, fix a bug in the
	HTTP/1.0 case.

	* libsoup/soup-server.c (start_request): remove request encoding
	override from here.

	* tests/streaming-test.c: new test of SoupServer response
	streaming, testing chunked, content-length, and eof-terminated
	responses


Added:
   trunk/tests/streaming-test.c
Modified:
   trunk/ChangeLog
   trunk/libsoup/soup-message-client-io.c
   trunk/libsoup/soup-message-headers.c
   trunk/libsoup/soup-message-io.c
   trunk/libsoup/soup-message.c
   trunk/libsoup/soup-server.c
   trunk/tests/   (props changed)
   trunk/tests/Makefile.am

Modified: trunk/libsoup/soup-message-client-io.c
==============================================================================
--- trunk/libsoup/soup-message-client-io.c	(original)
+++ trunk/libsoup/soup-message-client-io.c	Thu Feb 19 18:55:49 2009
@@ -107,9 +107,11 @@
 		g_free (uri_host);
 
 	*encoding = soup_message_headers_get_encoding (req->request_headers);
-	if (*encoding != SOUP_ENCODING_CHUNKED &&
+	if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
+	     *encoding == SOUP_ENCODING_NONE) &&
 	    req->request_body->length > 0 &&
 	    !soup_message_headers_get_content_length (req->request_headers)) {
+		*encoding = SOUP_ENCODING_CONTENT_LENGTH;
 		soup_message_headers_set_content_length (req->request_headers,
 							 req->request_body->length);
 	}

Modified: trunk/libsoup/soup-message-headers.c
==============================================================================
--- trunk/libsoup/soup-message-headers.c	(original)
+++ trunk/libsoup/soup-message-headers.c	Thu Feb 19 18:55:49 2009
@@ -515,6 +515,13 @@
 			return hdrs->encoding;
 	}
 
+	/* Per RFC 2616 4.4, a response body that doesn't indicate its
+	 * encoding otherwise is terminated by connection close, and a
+	 * request that doesn't indicate otherwise has no body. Note
+	 * that SoupMessage calls soup_message_headers_set_encoding()
+	 * to override the response body default for our own
+	 * server-side messages.
+	 */
 	hdrs->encoding = (hdrs->type == SOUP_MESSAGE_HEADERS_RESPONSE) ?
 		SOUP_ENCODING_EOF : SOUP_ENCODING_NONE;
 	return hdrs->encoding;

Modified: trunk/libsoup/soup-message-io.c
==============================================================================
--- trunk/libsoup/soup-message-io.c	(original)
+++ trunk/libsoup/soup-message-io.c	Thu Feb 19 18:55:49 2009
@@ -453,7 +453,7 @@
 
 		g_string_truncate (io->write_buf, 0);
 
-		if (io->write_encoding != SOUP_ENCODING_CHUNKED) {
+		if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
 			SoupMessageHeaders *hdrs =
 				(io->mode == SOUP_MESSAGE_IO_CLIENT) ?
 				msg->request_headers : msg->response_headers;
@@ -516,7 +516,8 @@
 
 
 	case SOUP_MESSAGE_IO_STATE_BODY:
-		if (!io->write_length) {
+		if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF) {
+		wrote_body:
 			io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
 
 			SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
@@ -531,7 +532,8 @@
 				soup_message_io_pause (msg);
 				return;
 			}
-			if (io->write_chunk->length > io->write_length) {
+			if (io->write_chunk->length > io->write_length &&
+			    io->write_encoding != SOUP_ENCODING_EOF) {
 				/* App is trying to write more than it
 				 * claimed it would; we have to truncate.
 				 */
@@ -540,7 +542,9 @@
 								   0, io->write_length);
 				soup_buffer_free (io->write_chunk);
 				io->write_chunk = truncated;
-			}
+			} else if (io->write_encoding == SOUP_ENCODING_EOF &&
+				   !io->write_chunk->length)
+				goto wrote_body;
 		}
 
 		if (!write_data (msg, io->write_chunk->data,

Modified: trunk/libsoup/soup-message.c
==============================================================================
--- trunk/libsoup/soup-message.c	(original)
+++ trunk/libsoup/soup-message.c	Thu Feb 19 18:55:49 2009
@@ -499,6 +499,10 @@
 		break;
 	case PROP_SERVER_SIDE:
 		priv->server_side = g_value_get_boolean (value);
+		if (priv->server_side) {
+			soup_message_headers_set_encoding (msg->response_headers,
+							   SOUP_ENCODING_CONTENT_LENGTH);
+		}
 		break;
 	case PROP_STATUS_CODE:
 		soup_message_set_status (msg, g_value_get_uint (value));
@@ -1101,6 +1105,10 @@
 
 	soup_message_body_truncate (req->response_body);
 	soup_message_headers_clear (req->response_headers);
+	if (priv->server_side) {
+		soup_message_headers_set_encoding (req->response_headers,
+						   SOUP_ENCODING_CONTENT_LENGTH);
+	}
 
 	req->status_code = SOUP_STATUS_NONE;
 	if (req->reason_phrase) {
@@ -1237,6 +1245,10 @@
 	    msg->method == SOUP_METHOD_CONNECT)
 		return TRUE;
 
+	/* Not persistent if the server sent a terminate-by-EOF response */
+	if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
+		return FALSE;
+
 	if (SOUP_MESSAGE_GET_PRIVATE (msg)->http_version == SOUP_HTTP_1_0) {
 		/* Only persistent if the client requested keepalive
 		 * and the server agreed.
@@ -1244,11 +1256,9 @@
 
 		if (!c_conn || !s_conn)
 			return FALSE;
-		if (soup_header_contains (c_conn, "Keep-Alive") ||
-		    soup_header_contains (s_conn, "Keep-Alive"))
+		if (!soup_header_contains (c_conn, "Keep-Alive") ||
+		    !soup_header_contains (s_conn, "Keep-Alive"))
 			return FALSE;
-
-		return TRUE;
 	} else {
 		/* Normally persistent unless either side requested otherwise */
 		if (c_conn && soup_header_contains (c_conn, "close"))
@@ -1256,12 +1266,10 @@
 		if (s_conn && soup_header_contains (s_conn, "close"))
 			return FALSE;
 
-		/* But not if the server sent a terminate-by-EOF response */
-		if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_EOF)
-			return FALSE;
-
 		return TRUE;
 	}
+
+	return TRUE;
 }
 
 /**

Modified: trunk/libsoup/soup-server.c
==============================================================================
--- trunk/libsoup/soup-server.c	(original)
+++ trunk/libsoup/soup-server.c	Thu Feb 19 18:55:49 2009
@@ -810,8 +810,6 @@
 	msg = g_object_new (SOUP_TYPE_MESSAGE,
 			    SOUP_MESSAGE_SERVER_SIDE, TRUE,
 			    NULL);
-        soup_message_headers_set_encoding (msg->response_headers,
-                                           SOUP_ENCODING_CONTENT_LENGTH);
 	if (priv->server_header) {
 		soup_message_headers_append (msg->response_headers, "Server",
 					     priv->server_header);

Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am	(original)
+++ trunk/tests/Makefile.am	Thu Feb 19 18:55:49 2009
@@ -26,6 +26,7 @@
 	redirect-test	\
 	simple-httpd	\
 	simple-proxy	\
+	streaming-test	\
 	uri-parsing	\
 	$(CURL_TESTS)	\
 	$(APACHE_TESTS)	\
@@ -57,6 +58,7 @@
 simple_httpd_SOURCES = simple-httpd.c
 simple_proxy_SOURCES = simple-proxy.c
 ssl_test_SOURCES = ssl-test.c $(TEST_SRCS)
+streaming_test_SOURCES = streaming-test.c $(TEST_SRCS)
 uri_parsing_SOURCES = uri-parsing.c $(TEST_SRCS)
 xmlrpc_test_SOURCES = xmlrpc-test.c $(TEST_SRCS)
 xmlrpc_server_test_SOURCES = xmlrpc-server-test.c $(TEST_SRCS)
@@ -83,6 +85,7 @@
 	misc-test	\
 	ntlm-test	\
 	redirect-test	\
+	streaming-test	\
 	uri-parsing	\
 	$(APACHE_TESTS)	\
 	$(CURL_TESTS)	\

Added: trunk/tests/streaming-test.c
==============================================================================
--- (empty file)
+++ trunk/tests/streaming-test.c	Thu Feb 19 18:55:49 2009
@@ -0,0 +1,187 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include "test-utils.h"
+
+#define RESPONSE_CHUNK_SIZE 1024
+
+char *full_response, *full_response_md5;
+gsize full_response_length;
+
+static void
+get_full_response (void)
+{
+	GError *error = NULL;
+
+	if (!g_file_get_contents (SRCDIR "/index.txt",
+				  &full_response,
+				  &full_response_length,
+				  &error)) {
+		fprintf (stderr, "Could not read index file %s: %s\n",
+			 SRCDIR "/index.txt", error->message);
+		g_error_free (error);
+		exit (1);
+	}
+
+	full_response_md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+							 (guchar *)full_response,
+							 full_response_length);
+}
+
+static void
+write_next_chunk (SoupMessage *msg, gpointer user_data)
+{
+	gsize *offset = user_data;
+	gsize chunk_length;
+
+	chunk_length = MIN (RESPONSE_CHUNK_SIZE, full_response_length - *offset);
+	if (chunk_length > 0) {
+		debug_printf (2, "  writing chunk\n");
+		soup_message_body_append (msg->response_body,
+					  SOUP_MEMORY_STATIC,
+					  full_response + *offset,
+					  chunk_length);
+		*offset += chunk_length;
+	} else {
+		debug_printf (2, "  done\n");
+		/* This is only actually needed in the chunked and eof
+		 * cases, but it's harmless in the content-length
+		 * case.
+		 */
+		soup_message_body_complete (msg->response_body);
+	}
+}
+
+static void
+free_offset (SoupMessage *msg, gpointer offset)
+{
+	g_free (offset);
+}
+
+static void
+server_callback (SoupServer *server, SoupMessage *msg,
+		 const char *path, GHashTable *query,
+		 SoupClientContext *context, gpointer data)
+{
+	gsize *offset;
+
+	if (!strcmp (path, "/chunked")) {
+		soup_message_headers_set_encoding (msg->response_headers,
+						   SOUP_ENCODING_CHUNKED);
+	} else if (!strcmp (path, "/content-length")) {
+		soup_message_headers_set_encoding (msg->response_headers,
+						   SOUP_ENCODING_CONTENT_LENGTH);
+		soup_message_headers_set_content_length (msg->response_headers,
+							 full_response_length);
+	} else if (!strcmp (path, "/eof")) {
+		soup_message_headers_set_encoding (msg->response_headers,
+						   SOUP_ENCODING_EOF);
+	} else {
+		soup_message_set_status (msg, SOUP_STATUS_NOT_FOUND);
+		return;
+	}
+	soup_message_set_status (msg, SOUP_STATUS_OK);
+
+	offset = g_new0 (gsize, 1);
+	g_signal_connect (msg, "wrote_headers",
+			  G_CALLBACK (write_next_chunk), offset);
+	g_signal_connect (msg, "wrote_chunk",
+			  G_CALLBACK (write_next_chunk), offset);
+	g_signal_connect (msg, "finished",
+			  G_CALLBACK (free_offset), offset);
+}
+
+static void
+do_request (SoupSession *session, SoupURI *base_uri, char *path)
+{
+	SoupURI *uri;
+	SoupMessage *msg;
+	char *md5;
+
+	uri = soup_uri_new_with_base (base_uri, path);
+	msg = soup_message_new_from_uri ("GET", uri);
+	soup_uri_free (uri);
+
+	soup_session_send_message (session, msg);
+
+	if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) {
+		debug_printf (1, "  message failed: %d %s\n",
+			      msg->status_code, msg->reason_phrase);
+		errors++;
+	}
+
+	if (msg->response_body->length != full_response_length) {
+		debug_printf (1, "  received length mismatch: expected %d, got %d\n",
+			      (int)full_response_length, (int)msg->request_body->length);
+		errors++;
+	}
+
+	md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
+					   (guchar *)msg->response_body->data,
+					   msg->response_body->length);
+	if (strcmp (md5, full_response_md5) != 0) {
+		debug_printf (1, "  data mismatch: expected %s, got %s\n",
+			      full_response_md5, md5);
+		errors++;
+	}
+	g_free (md5);
+
+	g_object_unref (msg);
+}
+
+static void
+do_tests (SoupURI *base_uri)
+{
+	SoupSession *session;
+
+	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
+	debug_printf (1, "Chunked encoding\n");
+	do_request (session, base_uri, "chunked");
+	debug_printf (1, "\n");
+	debug_printf (1, "Content-Length encoding\n");
+	do_request (session, base_uri, "content-length");
+	debug_printf (1, "\n");
+	debug_printf (1, "EOF encoding\n");
+	do_request (session, base_uri, "eof");
+	soup_test_session_abort_unref (session);
+}
+
+int
+main (int argc, char **argv)
+{
+	GMainLoop *loop;
+	SoupServer *server;
+	guint port;
+	SoupURI *base_uri;
+
+	test_init (argc, argv, NULL);
+	get_full_response ();
+
+	server = soup_test_server_new (FALSE);
+	soup_server_add_handler (server, NULL,
+				 server_callback, NULL, NULL);
+	port = 	soup_server_get_port (server);
+
+	loop = g_main_loop_new (NULL, TRUE);
+
+	base_uri = soup_uri_new ("http://localhost";);
+	soup_uri_set_port (base_uri, port);
+	do_tests (base_uri);
+	soup_uri_free (base_uri);
+
+	g_main_loop_unref (loop);
+
+	test_cleanup ();
+	return errors != 0;
+}



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