From 22215c2d7538f4367c93e2d8b6ec4722463ac757 Mon Sep 17 00:00:00 2001 From: Stephan Bosch Date: Sat, 4 Oct 2014 17:32:48 +0300 Subject: [PATCH] lib-http: Fixed detecting disconnection when ioloop is running only intermittently. This fix only applies to ioloops created and run by lib-http itself. --- src/lib-http/http-client-connection.c | 60 +++++++++++++++++++-------- src/lib-http/http-client-private.h | 1 + src/lib-http/http-client-request.c | 32 ++++++++------ 3 files changed, 63 insertions(+), 30 deletions(-) diff --git a/src/lib-http/http-client-connection.c b/src/lib-http/http-client-connection.c index 6089fc58b1..0c69ac54f1 100644 --- a/src/lib-http/http-client-connection.c +++ b/src/lib-http/http-client-connection.c @@ -58,23 +58,6 @@ http_client_connection_count_pending(struct http_client_connection *conn) return pending_count; } -bool http_client_connection_is_ready(struct http_client_connection *conn) -{ - if (conn->in_req_callback) { - /* this can happen when a nested ioloop is created inside request - callback. we currently don't reuse connections that are occupied - this way, but theoretically we could, although that would add - quite a bit of complexity. - */ - return FALSE; - } - - return (conn->connected && !conn->output_locked && - !conn->close_indicated && !conn->tunneling && - http_client_connection_count_pending(conn) < - conn->client->set.max_pipelined_requests); -} - bool http_client_connection_is_idle(struct http_client_connection *conn) { return (conn->to_idle != NULL); @@ -174,6 +157,48 @@ http_client_connection_abort_temp_error(struct http_client_connection **_conn, http_client_connection_unref(_conn); } +bool http_client_connection_is_ready(struct http_client_connection *conn) +{ + int ret; + + if (conn->in_req_callback) { + /* this can happen when a nested ioloop is created inside request + callback. we currently don't reuse connections that are occupied + this way, but theoretically we could, although that would add + quite a bit of complexity. + */ + return FALSE; + } + + if (!conn->connected || conn->output_locked || + conn->close_indicated || conn->tunneling || + http_client_connection_count_pending(conn) >= + conn->client->set.max_pipelined_requests) + return FALSE; + + if (conn->last_ioloop != NULL && conn->last_ioloop != current_ioloop) { + conn->last_ioloop = current_ioloop; + /* Active ioloop is different from what we saw earlier; + we may have missed a disconnection event on this connection. + Verify status by reading from connection. */ + if ((ret=i_stream_read(conn->conn.input)) < 0) { + int stream_errno = conn->conn.input->stream_errno; + + i_assert(ret != -2); + i_assert(conn->conn.input->stream_errno != 0 || conn->conn.input->eof); + http_client_connection_abort_temp_error(&conn, + HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST, + t_strdup_printf("Connection lost: read(%s) failed: %s", + i_stream_get_name(conn->conn.input), + stream_errno != 0 ? + i_stream_get_error(conn->conn.input) : + "EOF")); + return FALSE; + } + } + return TRUE; +} + static void http_client_connection_idle_timeout(struct http_client_connection *conn) { @@ -802,6 +827,7 @@ http_client_connection_ready(struct http_client_connection *conn) { /* connected */ conn->connected = TRUE; + conn->last_ioloop = current_ioloop; if (conn->to_connect != NULL && (conn->ssl_iostream == NULL || ssl_iostream_is_handshaked(conn->ssl_iostream))) diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index 75be4271cf..62b5174780 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -131,6 +131,7 @@ struct http_client_connection { struct http_client_request *pending_request; struct istream *incoming_payload; struct io *io_req_payload; + struct ioloop *last_ioloop; /* requests that have been sent, waiting for response */ ARRAY_TYPE(http_client_request) request_wait_list; diff --git a/src/lib-http/http-client-request.c b/src/lib-http/http-client-request.c index 7ba37a54e8..f33a445a22 100644 --- a/src/lib-http/http-client-request.c +++ b/src/lib-http/http-client-request.c @@ -792,21 +792,27 @@ static int http_client_request_send_real(struct http_client_request *req, o_stream_get_name(output), o_stream_get_error(output)); ret = -1; - } - - http_client_request_debug(req, "Sent header"); - - if (ret >= 0 && req->payload_output != NULL) { - if (!req->payload_sync) { - if (http_client_request_send_more(req, error_r) < 0) - ret = -1; + } else { + http_client_request_debug(req, "Sent header"); + + if (req->payload_output != NULL) { + if (!req->payload_sync) { + if (http_client_request_send_more(req, error_r) < 0) + ret = -1; + } else { + http_client_request_debug(req, "Waiting for 100-continue"); + conn->output_locked = TRUE; + } } else { - http_client_request_debug(req, "Waiting for 100-continue"); - conn->output_locked = TRUE; + req->state = HTTP_REQUEST_STATE_WAITING; + conn->output_locked = FALSE; + } + if (ret >= 0 && o_stream_flush(output) < 0) { + *error_r = t_strdup_printf("flush(%s) failed: %s", + o_stream_get_name(output), + o_stream_get_error(output)); + ret = -1; } - } else { - req->state = HTTP_REQUEST_STATE_WAITING; - conn->output_locked = FALSE; } o_stream_uncork(output); return ret; -- 2.47.3