dovecot-2.2: lib-http: Added support for chunked input/output st...
dovecot at dovecot.org
dovecot at dovecot.org
Wed Dec 12 11:34:58 EET 2012
details: http://hg.dovecot.org/dovecot-2.2/rev/7a4d8cd0e079
changeset: 15460:7a4d8cd0e079
user: Stephan Bosch <stephan at rename-it.nl>
date: Wed Dec 12 11:34:09 2012 +0200
description:
lib-http: Added support for chunked input/output streams and some bugfixes.
diffstat:
src/lib-http/http-client-connection.c | 10 +-
src/lib-http/http-client-private.h | 4 +-
src/lib-http/http-client-request.c | 48 ++++++-
src/lib-http/http-response-parser.c | 1 -
src/lib-http/http-transfer-chunked.c | 202 ++++++++++++++++++++++++---
src/lib-http/http-transfer.h | 4 +
src/lib-http/test-http-client.c | 13 +-
src/lib-http/test-http-transfer.c | 243 ++++++++++++++++++++++++++++++++-
8 files changed, 478 insertions(+), 47 deletions(-)
diffs (truncated from 816 to 300 lines):
diff -r 4a110cf281aa -r 7a4d8cd0e079 src/lib-http/http-client-connection.c
--- a/src/lib-http/http-client-connection.c Tue Dec 11 08:08:12 2012 +0200
+++ b/src/lib-http/http-client-connection.c Wed Dec 12 11:34:09 2012 +0200
@@ -62,7 +62,7 @@
bool http_client_connection_is_ready(struct http_client_connection *conn)
{
return (conn->connected && !conn->output_locked &&
- array_count(&conn->request_wait_list) <
+ !conn->close_indicated && array_count(&conn->request_wait_list) <
conn->client->set.max_pipelined_requests);
}
@@ -428,6 +428,10 @@
return;
}
+ /* 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:
@@ -438,8 +442,6 @@
*/
if (req->payload_sync && response->status == 100) {
conn->payload_continue = TRUE;
- if (conn->to_response != NULL)
- timeout_remove(&conn->to_response);
http_client_connection_debug(conn,
"Got expected 100-continue response");
if (http_client_request_send_more(req) < 0) {
@@ -469,7 +471,7 @@
if (!aborted) {
if (response->status / 100 == 3) {
/* redirect */
- http_client_request_redirect(req, response->location);
+ http_client_request_redirect(req, response->status, response->location);
} else {
/* response for application */
if (!http_client_connection_return_response(conn, req, response))
diff -r 4a110cf281aa -r 7a4d8cd0e079 src/lib-http/http-client-private.h
--- a/src/lib-http/http-client-private.h Tue Dec 11 08:08:12 2012 +0200
+++ b/src/lib-http/http-client-private.h Wed Dec 12 11:34:09 2012 +0200
@@ -57,7 +57,7 @@
string_t *headers;
struct istream *input;
- uoff_t input_size;
+ uoff_t input_size, input_offset;
unsigned int attempts;
unsigned int redirects;
@@ -198,7 +198,7 @@
void http_client_request_error(struct http_client_request *req,
unsigned int status, const char *error);
void http_client_request_redirect(struct http_client_request *req,
- const char *location);
+ unsigned int status, const char *location);
void http_client_request_finish(struct http_client_request **_req);
struct connection_list *http_client_connection_list_init(void);
diff -r 4a110cf281aa -r 7a4d8cd0e079 src/lib-http/http-client-request.c
--- a/src/lib-http/http-client-request.c Tue Dec 11 08:08:12 2012 +0200
+++ b/src/lib-http/http-client-request.c Wed Dec 12 11:34:09 2012 +0200
@@ -137,6 +137,7 @@
req->input = input;
if (i_stream_get_size(input, TRUE, &req->input_size) <= 0)
i_unreached(); //FIXME
+ req->input_offset = input->v_offset;
/* prepare request payload sync using 100 Continue response from server */
if (req->input_size > 0 && sync) {
@@ -175,7 +176,6 @@
i_error("stream input size changed"); //FIXME
return -1;
}
- i_stream_unref(&req->input);
req->state = HTTP_REQUEST_STATE_WAITING;
conn->output_locked = FALSE;
http_client_request_debug(req, "Sent all payload");
@@ -313,7 +313,7 @@
}
void http_client_request_redirect(struct http_client_request *req,
- const char *location)
+ unsigned int status, const char *location)
{
struct http_url *url;
const char *error;
@@ -341,6 +341,18 @@
return;
}
+ /* rewind payload stream */
+ if (req->input != NULL && req->input_size > 0 && status != 303) {
+ if (req->input->v_offset != req->input_offset && !req->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);
+ }
+ }
+
newport = (url->have_port ? url->port : (url->have_ssl ? 443 : 80));
http_client_request_debug(req, "Redirecting to http://%s:%u%s",
@@ -354,6 +366,26 @@
req->target = p_strdup(req->pool, url->path);
req->ssl = url->have_ssl;
+ /* 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->input != NULL)
+ i_stream_unref(&req->input);
+ req->input_size = 0;
+ req->input_offset = 0;
+ }
+
/* resubmit */
req->client->pending_requests--;
req->state = HTTP_REQUEST_STATE_NEW;
@@ -364,6 +396,18 @@
{
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) {
+ 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);
+ }
+ }
+
req->conn = NULL;
req->peer = NULL;
req->state = HTTP_REQUEST_STATE_QUEUED;
diff -r 4a110cf281aa -r 7a4d8cd0e079 src/lib-http/http-response-parser.c
--- a/src/lib-http/http-response-parser.c Tue Dec 11 08:08:12 2012 +0200
+++ b/src/lib-http/http-response-parser.c Wed Dec 12 11:34:09 2012 +0200
@@ -476,7 +476,6 @@
/ transfer-extension ; [FIXME]
transfer-extension = token *( OWS ";" OWS transfer-parameter )
*/
- // FIXME: parse this directly when the header is parsed
http_parser_init(&hparser,
(const unsigned char *)parser->transfer_encoding,
strlen(parser->transfer_encoding));
diff -r 4a110cf281aa -r 7a4d8cd0e079 src/lib-http/http-transfer-chunked.c
--- a/src/lib-http/http-transfer-chunked.c Tue Dec 11 08:08:12 2012 +0200
+++ b/src/lib-http/http-transfer-chunked.c Wed Dec 12 11:34:09 2012 +0200
@@ -132,25 +132,25 @@
/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21;
Section 4.1:
- chunked-body = *chunk
- last-chunk
- trailer-part
- CRLF
+ chunked-body = *chunk
+ last-chunk
+ trailer-part
+ CRLF
- chunk = chunk-size [ chunk-ext ] CRLF
- chunk-data CRLF
- chunk-size = 1*HEXDIG
- last-chunk = 1*("0") [ chunk-ext ] CRLF
+ chunk = chunk-size [ chunk-ext ] CRLF
+ chunk-data CRLF
+ chunk-size = 1*HEXDIG
+ last-chunk = 1*("0") [ chunk-ext ] CRLF
- chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
- chunk-ext-name = token
- chunk-ext-val = token / quoted-str-nf
- chunk-data = 1*OCTET ; a sequence of chunk-size octets
- trailer-part = *( header-field CRLF )
+ chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
+ chunk-ext-name = token
+ chunk-ext-val = token / quoted-str-nf
+ chunk-data = 1*OCTET ; a sequence of chunk-size octets
+ trailer-part = *( header-field CRLF )
- quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE
- ; like quoted-string, but disallowing line folding
- qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
+ quoted-str-nf = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE
+ ; like quoted-string, but disallowing line folding
+ qdtext-nf = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
*/
@@ -202,7 +202,7 @@
if (*tcstream->cur != '"') {
tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN;
break;
- }
+ }
tcstream->cur++;
tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING;
if (tcstream->cur >= tcstream->end)
@@ -219,11 +219,11 @@
} else if ((ret=http_transfer_chunked_skip_qdtext(tcstream)) <= 0) {
if (ret < 0)
tcstream->error = "Invalid chunked extension value";
- return ret;
+ return ret;
} else if (*tcstream->cur == '\\') {
tcstream->cur++;
tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_ESCAPE;
- if (tcstream->cur >= tcstream->end)
+ if (tcstream->cur >= tcstream->end)
return 0;
break;
} else {
@@ -243,7 +243,7 @@
return -1;
}
tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_STRING;
- if (tcstream->cur >= tcstream->end)
+ if (tcstream->cur >= tcstream->end)
return 0;
break;
case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN:
@@ -258,7 +258,7 @@
tcstream->state = HTTP_CHUNKED_PARSE_STATE_LF;
if (*tcstream->cur == '\r') {
tcstream->cur++;
- if (tcstream->cur >= tcstream->end)
+ if (tcstream->cur >= tcstream->end)
return 0;
}
/* fall through */
@@ -281,7 +281,7 @@
tcstream->state = HTTP_CHUNKED_PARSE_STATE_DATA_LF;
if (*tcstream->cur == '\r') {
tcstream->cur++;
- if (tcstream->cur >= tcstream->end)
+ if (tcstream->cur >= tcstream->end)
return 0;
}
/* fall through */
@@ -307,6 +307,7 @@
static int http_transfer_chunked_parse_next(
struct http_transfer_chunked_istream *tcstream)
{
+ struct istream_private *stream = &tcstream->istream;
struct istream *input = tcstream->istream.parent;
size_t size;
int ret;
@@ -316,8 +317,10 @@
tcstream->cur = tcstream->begin;
tcstream->end = tcstream->cur + size;
- if ((ret=http_transfer_chunked_parse(tcstream)) < 0)
+ if ((ret=http_transfer_chunked_parse(tcstream)) < 0) {
+ stream->istream.stream_errno = EIO;
return -1;
+ }
i_stream_skip(input, tcstream->cur - tcstream->begin);
@@ -329,9 +332,23 @@
}
i_assert(ret != -2);
+
+ if (ret < 0) {
+ if ( stream->parent->eof && stream->parent->stream_errno == 0 ) {
+ /* unexpected EOF */
+ tcstream->error = "Unexpected end of payload";
+ stream->istream.stream_errno = EIO;
+ } else {
+ /* parent stream error */
+ tcstream->error = "Stream error";
+ stream->istream.stream_errno = stream->parent->stream_errno;
+ }
+ }
return ret;
}
More information about the dovecot-cvs
mailing list