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