Mercurial > dovecot > core-2.2
changeset 18001:cffa8349f167
lib-http: client: Fixed handling of requests aborted while still sending payload to server.
author | Stephan Bosch <stephan@rename-it.nl> |
---|---|
date | Sat, 25 Oct 2014 01:38:42 +0300 |
parents | 84b5e075c62c |
children | 4dc3c0cacd25 |
files | src/lib-http/http-client-connection.c src/lib-http/http-client-private.h |
diffstat | 2 files changed, 63 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/http-client-connection.c Sat Oct 25 01:38:42 2014 +0300 +++ b/src/lib-http/http-client-connection.c Sat Oct 25 01:38:42 2014 +0300 @@ -165,7 +165,7 @@ return FALSE; } - if (!conn->connected || conn->output_locked || + if (!conn->connected || conn->output_locked || conn->output_broken || conn->close_indicated || conn->tunneling || http_client_connection_count_pending(conn) >= conn->client->set.max_pipelined_requests) @@ -209,6 +209,9 @@ { unsigned int timeout, count; + i_assert(!conn->close_indicated); + i_assert(!conn->output_broken); + if (conn->connected && array_is_created(&conn->request_wait_list) && array_count(&conn->request_wait_list) == 0 && @@ -312,6 +315,8 @@ if (req == NULL) return 0; + i_assert(req->state == HTTP_REQUEST_STATE_QUEUED); + if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); @@ -328,7 +333,6 @@ if (conn->peer->no_payload_sync) req->payload_sync = FALSE; - i_assert(req->state == HTTP_REQUEST_STATE_QUEUED); array_append(&conn->request_wait_list, &req, 1); http_client_request_ref(req); @@ -552,11 +556,12 @@ struct http_client_connection *conn = (struct http_client_connection *)_conn; struct http_response response; - struct http_client_request *const *req_idx; + struct http_client_request *const *reqs; struct http_client_request *req = NULL; + enum http_response_payload_type payload_type; + unsigned int count; int finished = 0, ret; const char *error; - enum http_response_payload_type payload_type; i_assert(conn->incoming_payload == NULL); @@ -608,9 +613,9 @@ timeout_reset(conn->to_requests); /* get first waiting request */ - if (array_count(&conn->request_wait_list) > 0) { - req_idx = array_idx(&conn->request_wait_list, 0); - req = req_idx[0]; + reqs = array_get(&conn->request_wait_list, &count); + if (count > 0) { + req = reqs[0]; /* determine whether to expect a response payload */ payload_type = http_client_request_get_payload_type(req); @@ -619,6 +624,14 @@ payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED; } + /* drop connection with broken output if last possible input was + received */ + if (conn->output_broken && (count == 0 || + (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) { + http_client_connection_server_close(&conn); + return; + } + // FIXME: handle somehow if server replies before request->input is at EOF while ((ret=http_response_parse_next (conn->http_parser, payload_type, &response, &error)) > 0) { @@ -648,11 +661,21 @@ "Got 100-continue response after timeout"); continue; } + conn->peer->no_payload_sync = FALSE; conn->peer->seen_100_response = TRUE; conn->payload_continue = TRUE; + http_client_connection_debug(conn, "Got expected 100-continue response"); + + if (req->state == HTTP_REQUEST_STATE_ABORTED) { + http_client_connection_debug(conn, + "Request aborted before sending payload was complete."); + http_client_connection_close(&conn); + return; + } + if (http_client_request_send_more(req, &error) < 0) { http_client_connection_abort_temp_error(&conn, HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, @@ -660,11 +683,11 @@ } return; } else if (response.status / 100 == 1) { - /* ignore them for now */ + /* ignore other 1xx for now */ http_client_connection_debug(conn, "Got unexpected %u response; ignoring", response.status); continue; - } + } http_client_connection_debug(conn, "Got %u response for request %s (took %u ms + %u ms in queue)", @@ -736,9 +759,9 @@ } /* get next waiting request */ - if (array_count(&conn->request_wait_list) > 0) { - req_idx = array_idx(&conn->request_wait_list, 0); - req = req_idx[0]; + reqs = array_get(&conn->request_wait_list, &count); + if (count > 0) { + req = reqs[0]; /* determine whether to expect a response payload */ payload_type = http_client_request_get_payload_type(req); @@ -749,6 +772,14 @@ req = NULL; payload_type = HTTP_RESPONSE_PAYLOAD_TYPE_ALLOWED; } + + /* drop connection with broken output if last possible input was + received */ + if (conn->output_broken && (count == 0 || + (count == 1 && req->state == HTTP_REQUEST_STATE_ABORTED))) { + http_client_connection_server_close(&conn); + return; + } } if (ret <= 0 && @@ -783,8 +814,9 @@ int http_client_connection_output(struct http_client_connection *conn) { - struct http_client_request *const *req_idx, *req; + struct http_client_request *const *reqs; struct ostream *output = conn->conn.output; + unsigned int count; const char *error; int ret; @@ -802,14 +834,27 @@ return ret; } + i_assert(!conn->output_broken); + if (conn->ssl_iostream != NULL && !ssl_iostream_is_handshaked(conn->ssl_iostream)) return 1; - if (array_count(&conn->request_wait_list) > 0 && conn->output_locked) { - req_idx = array_idx(&conn->request_wait_list, - array_count(&conn->request_wait_list)-1); - req = req_idx[0]; + reqs = array_get(&conn->request_wait_list, &count); + if (count > 0 && conn->output_locked) { + struct http_client_request *req = reqs[count-1]; + + if (req->state == HTTP_REQUEST_STATE_ABORTED) { + http_client_connection_debug(conn, + "Request aborted before sending payload was complete."); + if (count == 1) { + http_client_connection_close(&conn); + } else { + o_stream_unset_flush_callback(output); + conn->output_broken = TRUE; + } + return 1; + } if (!req->payload_sync || conn->payload_continue) { if (http_client_request_send_more(req, &error) < 0) {
--- a/src/lib-http/http-client-private.h Sat Oct 25 01:38:42 2014 +0300 +++ b/src/lib-http/http-client-private.h Sat Oct 25 01:38:42 2014 +0300 @@ -147,6 +147,7 @@ unsigned int closing:1; unsigned int close_indicated:1; unsigned int output_locked:1; /* output is locked; no pipelining */ + unsigned int output_broken:1; /* output is broken; no more requests */ unsigned int payload_continue:1; /* received 100-continue for current request */ unsigned int in_req_callback:1; /* performin request callback (busy) */