dovecot-2.2: lib-http: Various fixes

dovecot at dovecot.org dovecot at dovecot.org
Mon Jan 7 12:24:34 EET 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/d1eaa348482f
changeset: 15579:d1eaa348482f
user:      Timo Sirainen <tss at iki.fi>
date:      Mon Jan 07 12:24:29 2013 +0200
description:
lib-http: Various fixes
Patch by Stephan Bosch.

diffstat:

 src/lib-http/http-client-connection.c |    7 +-
 src/lib-http/http-client-private.h    |    6 +-
 src/lib-http/http-client-request.c    |  115 ++++++++++++++++++++++++++-------
 src/lib-http/http-transfer-chunked.c  |   16 ++++
 src/lib-http/test-http-client.c       |   21 ++++++
 5 files changed, 135 insertions(+), 30 deletions(-)

diffs (truncated from 332 to 300 lines):

diff -r 13e74bd5ac8c -r d1eaa348482f src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c	Sat Jan 05 01:14:11 2013 +0200
+++ b/src/lib-http/http-client-connection.c	Mon Jan 07 12:24:29 2013 +0200
@@ -232,6 +232,7 @@
 	conn->payload_continue = FALSE;
 	if (conn->peer->no_payload_sync)
 		req->payload_sync = FALSE;
+
 	array_append(&conn->request_wait_list, &req, 1);
 	http_client_request_ref(req);
 
@@ -254,7 +255,7 @@
 	   period before sending the payload body.
 	 */
 	if (req->payload_sync) {
-		i_assert(req->input_size > 0);
+		i_assert(req->payload_size > 0);
 		i_assert(conn->to_response == NULL);
 		conn->to_response =	timeout_add(HTTP_CLIENT_CONTINUE_TIMEOUT_MSECS,
 			http_client_connection_continue_timeout, conn);
@@ -432,6 +433,10 @@
 		if (conn->to_response != NULL)
 			timeout_remove(&conn->to_response);
 
+		/* Got some response; cancel response timeout */
+		if (conn->to_response != NULL)
+			timeout_remove(&conn->to_response);
+
 		/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
 		     Section 7.2:
 
diff -r 13e74bd5ac8c -r d1eaa348482f src/lib-http/http-client-private.h
--- a/src/lib-http/http-client-private.h	Sat Jan 05 01:14:11 2013 +0200
+++ b/src/lib-http/http-client-private.h	Mon Jan 07 12:24:29 2013 +0200
@@ -56,8 +56,9 @@
 	struct http_client_connection *conn;
 
 	string_t *headers;
-	struct istream *input;
-	uoff_t input_size, input_offset;
+	struct istream *payload_input;
+	uoff_t payload_size, payload_offset;
+	struct ostream *payload_output;
 
 	unsigned int attempts;
 	unsigned int redirects;
@@ -68,6 +69,7 @@
 	enum http_request_state state;
 
 	unsigned int payload_sync:1;
+	unsigned int payload_chunked:1;
 	unsigned int ssl:1;
 	unsigned int urgent:1;
 };
diff -r 13e74bd5ac8c -r d1eaa348482f src/lib-http/http-client-request.c
--- a/src/lib-http/http-client-request.c	Sat Jan 05 01:14:11 2013 +0200
+++ b/src/lib-http/http-client-request.c	Mon Jan 07 12:24:29 2013 +0200
@@ -9,6 +9,7 @@
 #include "ostream.h"
 #include "http-url.h"
 #include "http-response-parser.h"
+#include "http-transfer.h"
 
 #include "http-client-private.h"
 
@@ -86,8 +87,10 @@
 	http_client_request_debug(req, "Destroy (requests left=%d)",
 		client->pending_requests);
 
-	if (req->input != NULL)
-		i_stream_unref(&req->input);
+	if (req->payload_input != NULL)
+		i_stream_unref(&req->payload_input);
+	if (req->payload_output != NULL)
+		o_stream_unref(&req->payload_output);
 	str_free(&req->headers);
 	pool_unref(&req->pool);
 	*_req = NULL;
@@ -131,18 +134,19 @@
 				     struct istream *input, bool sync)
 {
 	i_assert(req->state == HTTP_REQUEST_STATE_NEW);
-	i_assert(req->input == NULL);
+	i_assert(req->payload_input == NULL);
 
 	i_stream_ref(input);
-	req->input = input;
-	if (i_stream_get_size(input, TRUE, &req->input_size) <= 0)
-		i_unreached(); //FIXME	
-	req->input_offset = input->v_offset;
+	req->payload_input = input;
+	if (i_stream_get_size(input, TRUE, &req->payload_size) <= 0) {
+		req->payload_size = 0;
+		req->payload_chunked = TRUE;
+	}
+	req->payload_offset = input->v_offset;
 
 	/* prepare request payload sync using 100 Continue response from server */
-	if (req->input_size > 0 && sync) {
+	if ((req->payload_chunked || req->payload_size > 0) && sync)
 		req->payload_sync = TRUE;
-	}
 }
 
 void http_client_request_submit(struct http_client_request *req)
@@ -161,21 +165,24 @@
 int http_client_request_send_more(struct http_client_request *req)
 {
 	struct http_client_connection *conn = req->conn;
-	struct ostream *output = conn->conn.output;
+	struct ostream *output = req->payload_output;
 	int ret = 0;
 
-	i_assert(req->input != NULL);
+	i_assert(req->payload_input != NULL);
 
 	o_stream_set_max_buffer_size(output, 0);
-	if (o_stream_send_istream(output, req->input) < 0)
+	if (o_stream_send_istream(output, req->payload_input) < 0)
 		ret = -1;
 	o_stream_set_max_buffer_size(output, (size_t)-1);
 
-	if (!i_stream_have_bytes_left(req->input)) {
-		if (req->input->v_offset != req->input_size) {
+	if (!i_stream_have_bytes_left(req->payload_input)) {
+		if (!req->payload_chunked &&
+			req->payload_input->v_offset - req->payload_offset != req->payload_size) {
 			i_error("stream input size changed"); //FIXME
 			return -1;
 		}
+		o_stream_unref(&output);
+		req->payload_output = NULL;
 		req->state = HTTP_REQUEST_STATE_WAITING;
 		conn->output_locked = FALSE;
 		http_client_request_debug(req, "Sent all payload");
@@ -212,9 +219,15 @@
 	if (req->payload_sync) {
 		str_append(rtext, "Expect: 100-continue\r\n");
 	}
-	if (req->input_size != 0) {
+	if (req->payload_chunked) {
+		str_append(rtext, "Transfer-Encoding: chunked\r\n");
+		req->payload_output =
+			http_transfer_chunked_ostream_create(output);
+	} else if (req->payload_size != 0) {
 		str_printfa(rtext, "Content-Length: %"PRIuUOFF_T"\r\n",
-			    req->input_size);
+			    req->payload_size);
+		req->payload_output = output;
+		o_stream_ref(output);
 	}
 
 	iov[0].iov_base = str_data(rtext);
@@ -231,7 +244,7 @@
 
 	http_client_request_debug(req, "Sent");
 
-	if (ret >= 0 && req->input_size != 0) {
+	if (ret >= 0 && req->payload_output != NULL) {
 		if (!req->payload_sync) {
 			if (http_client_request_send_more(req) < 0)
 				ret = -1;
@@ -342,14 +355,28 @@
 	}
 
 	/* rewind payload stream */
-	if (req->input != NULL && req->input_size > 0 && status != 303) {
-		if (req->input->v_offset != req->input_offset && !req->input->seekable) {
+	if (req->payload_input != NULL && req->payload_size > 0 && status != 303) {
+		if (req->payload_input->v_offset != req->payload_offset &&
+			!req->payload_input->seekable) {
 			http_client_request_error(req,
 				HTTP_CLIENT_REQUEST_ERROR_ABORTED,
 				"Redirect failed: Cannot resend payload; stream is not seekable");
 			return;
 		} else {
-			i_stream_seek(req->input, req->input_offset);
+			i_stream_seek(req->payload_input, req->payload_offset);
+		}
+	}
+
+	/* rewind payload stream */
+	if (req->payload_input != NULL && req->payload_size > 0 && status != 303) {
+		if (req->payload_input->v_offset != req->payload_offset &&
+			!req->payload_input->seekable) {
+			http_client_request_error(req,
+				HTTP_CLIENT_REQUEST_ERROR_ABORTED,
+				"Redirect failed: Cannot resend payload; stream is not seekable");
+			return;
+		} else {
+			i_stream_seek(req->payload_input, req->payload_offset);
 		}
 	}
 
@@ -380,10 +407,30 @@
 		req->method = p_strdup(req->pool, "GET");
 
 		/* drop payload */
-		if (req->input != NULL)
-			i_stream_unref(&req->input);
-		req->input_size = 0;
-		req->input_offset = 0;
+		if (req->payload_input != NULL)
+			i_stream_unref(&req->payload_input);
+		req->payload_size = 0;
+		req->payload_offset = 0;
+	}
+
+	/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
+	      Section-7.4.4
+	
+	   -> A 303 `See Other' redirect status response is handled a bit differently.
+	   Basically, the response content is located elsewhere, but the original
+	   (POST) request is handled already.
+	 */
+	if (status == 303 && strcasecmp(req->method, "HEAD") != 0 &&
+		strcasecmp(req->method, "GET") != 0) {
+		// FIXME: should we provide the means to skip this step? The original
+		// request was already handled at this point.
+		req->method = p_strdup(req->pool, "GET");
+
+		/* drop payload */
+		if (req->payload_input != NULL)
+			i_stream_unref(&req->payload_input);
+		req->payload_size = 0;
+		req->payload_offset = 0;
 	}
 
 	/* resubmit */
@@ -397,14 +444,28 @@
 	http_client_request_debug(req, "Resubmitting request");
 
 	/* rewind payload stream */
-	if (req->input != NULL && req->input_size > 0) {
-		if (req->input->v_offset != req->input_offset && !req->input->seekable) {
+	if (req->payload_input != NULL && req->payload_size > 0) {
+		if (req->payload_input->v_offset != req->payload_offset &&
+			!req->payload_input->seekable) {
 			http_client_request_error(req,
 				HTTP_CLIENT_REQUEST_ERROR_ABORTED,
 				"Resubmission failed: Cannot resend payload; stream is not seekable");
 			return;
 		} else {
-			i_stream_seek(req->input, req->input_offset);
+			i_stream_seek(req->payload_input, req->payload_offset);
+		}
+	}
+
+	/* rewind payload stream */
+	if (req->payload_input != NULL && req->payload_size > 0) {
+		if (req->payload_input->v_offset != req->payload_offset &&
+			!req->payload_input->seekable) {
+			http_client_request_error(req,
+				HTTP_CLIENT_REQUEST_ERROR_ABORTED,
+				"Resubmission failed: Cannot resend payload; stream is not seekable");
+			return;
+		} else {
+			i_stream_seek(req->payload_input, req->payload_offset);
 		}
 	}
 
diff -r 13e74bd5ac8c -r d1eaa348482f src/lib-http/http-transfer-chunked.c
--- a/src/lib-http/http-transfer-chunked.c	Sat Jan 05 01:14:11 2013 +0200
+++ b/src/lib-http/http-transfer-chunked.c	Mon Jan 07 12:24:29 2013 +0200
@@ -475,6 +475,21 @@
 	return -1;
 }
 
+static void
+http_transfer_chunked_istream_destroy(struct iostream_private *stream)
+{
+	struct http_transfer_chunked_istream *tcstream =
+		(struct http_transfer_chunked_istream *)stream;
+
+	if (tcstream->header_parser != NULL)
+		http_header_parser_deinit(&tcstream->header_parser);
+
+	// FIXME: copied from istream.c; there's got to be a better way.
+	i_free(tcstream->istream.w_buffer);
+	if (tcstream->istream.parent != NULL)
+		i_stream_unref(&tcstream->istream.parent);
+}
+
 struct istream *
 http_transfer_chunked_istream_create(struct istream *input)
 {
@@ -485,6 +500,7 @@
 	tcstream->istream.max_buffer_size =
 		input->real_stream->max_buffer_size;
 
+	tcstream->istream.iostream.destroy = http_transfer_chunked_istream_destroy;
 	tcstream->istream.read = http_transfer_chunked_istream_read;
 
 	tcstream->istream.istream.readable_fd = FALSE;
diff -r 13e74bd5ac8c -r d1eaa348482f src/lib-http/test-http-client.c
--- a/src/lib-http/test-http-client.c	Sat Jan 05 01:14:11 2013 +0200
+++ b/src/lib-http/test-http-client.c	Mon Jan 07 12:24:29 2013 +0200
@@ -208,6 +208,17 @@
 
 	test_req = i_new(struct http_test_request, 1);


More information about the dovecot-cvs mailing list