Mercurial > dovecot > core-2.2
changeset 16451:a551409911f9
lib-http: Added connect and request timeout settings.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Wed, 05 Jun 2013 16:19:38 +0300 |
parents | 20b065a5299d |
children | 49e7a1c206f1 |
files | src/lib-http/http-client-connection.c src/lib-http/http-client-private.h src/lib-http/http-client.c src/lib-http/http-client.h |
diffstat | 4 files changed, 83 insertions(+), 3 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/http-client-connection.c Wed Jun 05 15:43:42 2013 +0300 +++ b/src/lib-http/http-client-connection.c Wed Jun 05 16:19:38 2013 +0300 @@ -8,6 +8,7 @@ #include "ioloop.h" #include "istream.h" #include "ostream.h" +#include "time-util.h" #include "iostream-rawlog.h" #include "iostream-ssl.h" #include "http-response-parser.h" @@ -202,6 +203,17 @@ } static void +http_client_connection_request_timeout(struct http_client_connection *conn) +{ + unsigned int msecs = conn->client->set.request_timeout_msecs; + + http_client_connection_abort_temp_error(&conn, + HTTP_CLIENT_REQUEST_ERROR_TIMED_OUT, t_strdup_printf( + "No response for request in %u.%03u secs", + msecs/1000, msecs%1000)); +} + +static void http_client_connection_continue_timeout(struct http_client_connection *conn) { struct http_client_request *const *req_idx; @@ -249,6 +261,11 @@ if (conn->to_idle != NULL) timeout_remove(&conn->to_idle); + if (conn->client->set.request_timeout_msecs > 0 && + conn->to_requests == NULL) { + conn->to_requests = timeout_add(conn->client->set.request_timeout_msecs, + http_client_connection_request_timeout, conn); + } req->conn = conn; conn->payload_continue = FALSE; if (conn->peer->no_payload_sync) @@ -293,14 +310,26 @@ struct http_client_connection *conn = (struct http_client_connection *)_conn; const char *error; + unsigned int msecs; conn->closing = TRUE; conn->connected = FALSE; switch (_conn->disconnect_reason) { case CONNECTION_DISCONNECT_CONNECT_TIMEOUT: - http_client_peer_connection_failure(conn->peer, t_strdup_printf( - "connect(%s) failed: Connection timed out", _conn->name)); + if (conn->connected_timestamp.tv_sec == 0) { + msecs = timeval_diff_msecs(&ioloop_timeval, + &conn->connect_start_timestamp); + http_client_peer_connection_failure(conn->peer, t_strdup_printf( + "connect(%s) failed: Connection timed out in %u.%03u secs", + _conn->name, msecs/1000, msecs%1000)); + } else { + msecs = timeval_diff_msecs(&ioloop_timeval, + &conn->connected_timestamp); + http_client_peer_connection_failure(conn->peer, t_strdup_printf( + "SSL handshaking to %s failed: Connection timed out in %u.%03u secs", + _conn->name, msecs/1000, msecs%1000)); + } break; case CONNECTION_DISCONNECT_CONN_CLOSED: /* retry pending requests if possible */ @@ -453,6 +482,8 @@ http_client_payload_finished(conn); finished++; } + if (conn->to_requests != NULL) + timeout_reset(conn->to_requests); /* get first waiting request */ if (array_count(&conn->request_wait_list) > 0) { @@ -560,6 +591,9 @@ req = req_idx[0]; no_payload = (strcmp(req->method, "HEAD") == 0); } else { + /* no more requests waiting for the connection */ + if (conn->to_requests != NULL) + timeout_remove(&conn->to_requests); req = NULL; no_payload = FALSE; } @@ -597,6 +631,9 @@ const char *error; int ret; + if (conn->to_requests != NULL) + timeout_reset(conn->to_requests); + if ((ret = o_stream_flush(output)) <= 0) { if (ret < 0) { http_client_connection_abort_temp_error(&conn, @@ -635,6 +672,8 @@ conn->connected = TRUE; conn->peer->last_connect_failed = FALSE; + if (conn->to_connect != NULL) + timeout_remove(&conn->to_connect); if (conn->client->set.rawlog_dir != NULL && stat(conn->client->set.rawlog_dir, &st) == 0) { @@ -718,6 +757,7 @@ http_client_peer_connection_failure(conn->peer, t_strdup_printf( "connect(%s) failed: %m", _conn->name)); } else { + conn->connected_timestamp = ioloop_timeval; http_client_connection_debug(conn, "Connected"); if (conn->peer->addr.https_name != NULL) { if (http_client_connection_ssl_init(conn, &error) < 0) { @@ -758,12 +798,32 @@ http_client_connection_unref(&conn); } +static void http_client_connect_timeout(struct http_client_connection *conn) +{ + conn->conn.disconnect_reason = CONNECTION_DISCONNECT_CONNECT_TIMEOUT; + http_client_connection_destroy(&conn->conn); +} + static void http_client_connection_connect(struct http_client_connection *conn) { + unsigned int msecs; + + conn->connect_start_timestamp = ioloop_timeval; if (connection_client_connect(&conn->conn) < 0) { conn->connect_errno = errno; conn->to_input = timeout_add_short(0, http_client_connection_delayed_connect_error, conn); + return; + } + + /* don't use connection.h timeout because we want this timeout + to include also the SSL handshake */ + msecs = conn->client->set.connect_timeout_msecs; + if (msecs == 0) + msecs = conn->client->set.request_timeout_msecs; + if (msecs > 0) { + conn->to_connect = + timeout_add(msecs, http_client_connect_timeout, conn); } } @@ -831,6 +891,10 @@ ssl_iostream_unref(&conn->ssl_iostream); connection_deinit(&conn->conn); + if (conn->to_requests != NULL) + timeout_remove(&conn->to_requests); + if (conn->to_connect != NULL) + timeout_remove(&conn->to_connect); if (conn->to_input != NULL) timeout_remove(&conn->to_input); if (conn->to_idle != NULL) @@ -855,6 +919,10 @@ void http_client_connection_switch_ioloop(struct http_client_connection *conn) { + if (conn->to_requests != NULL) + conn->to_requests = io_loop_move_timeout(&conn->to_requests); + if (conn->to_connect != NULL) + conn->to_requests = io_loop_move_timeout(&conn->to_connect); if (conn->to_input != NULL) conn->to_input = io_loop_move_timeout(&conn->to_input); if (conn->to_idle != NULL)
--- a/src/lib-http/http-client-private.h Wed Jun 05 15:43:42 2013 +0300 +++ b/src/lib-http/http-client-private.h Wed Jun 05 16:19:38 2013 +0300 @@ -134,10 +134,13 @@ unsigned int id; // DEBUG: identify parallel connections int connect_errno; + struct timeval connect_start_timestamp; + struct timeval connected_timestamp; struct ssl_iostream *ssl_iostream; struct http_response_parser *http_parser; - struct timeout *to_input, *to_idle, *to_response; + struct timeout *to_connect, *to_input, *to_idle, *to_response; + struct timeout *to_requests; struct http_client_request *pending_request; struct istream *incoming_payload;
--- a/src/lib-http/http-client.c Wed Jun 05 15:43:42 2013 +0300 +++ b/src/lib-http/http-client.c Wed Jun 05 16:19:38 2013 +0300 @@ -96,6 +96,8 @@ (set->max_pipelined_requests > 0 ? set->max_pipelined_requests : 1); client->set.max_attempts = set->max_attempts; client->set.max_redirects = set->max_redirects; + client->set.request_timeout_msecs = set->request_timeout_msecs; + client->set.connect_timeout_msecs = set->connect_timeout_msecs; client->set.debug = set->debug; client->conn_list = http_client_connection_list_init();
--- a/src/lib-http/http-client.h Wed Jun 05 15:43:42 2013 +0300 +++ b/src/lib-http/http-client.h Wed Jun 05 16:19:38 2013 +0300 @@ -57,6 +57,13 @@ /* maximum number of attempts for a request */ unsigned int max_attempts; + /* max time to wait for HTTP request to finish before retrying + (default = unlimited) */ + unsigned int request_timeout_msecs; + /* max time to wait for connect() (and SSL handshake) to finish before + retrying (default = request_timeout_msecs) */ + unsigned int connect_timeout_msecs; + bool debug; };