Mercurial > dovecot > core-2.2
changeset 16483:07f72970bf61
lib-http: Added soft_connect_timeout_msecs setting to connect to multiple IPs in parallel.
Based on patch by Stephan Bosch.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 09 Jun 2013 02:46:50 +0300 |
parents | ac78c4c88ba9 |
children | 1f3f21081ee5 |
files | src/lib-http/http-client-connection.c src/lib-http/http-client-host.c src/lib-http/http-client-peer.c src/lib-http/http-client-private.h src/lib-http/http-client.c src/lib-http/http-client.h |
diffstat | 6 files changed, 177 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib-http/http-client-connection.c Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client-connection.c Sun Jun 09 02:46:50 2013 +0300 @@ -672,13 +672,13 @@ struct stat st; conn->connected = TRUE; - conn->peer->last_connect_failed = FALSE; - if (conn->to_connect != NULL && (conn->ssl_iostream == NULL || ssl_iostream_is_handshaked(conn->ssl_iostream))) timeout_remove(&conn->to_connect); + http_client_peer_connection_success(conn->peer); + if (conn->client->set.rawlog_dir != NULL && stat(conn->client->set.rawlog_dir, &st) == 0) { iostream_rawlog_create(conn->client->set.rawlog_dir,
--- a/src/lib-http/http-client-host.c Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client-host.c Sun Jun 09 02:46:50 2013 +0300 @@ -41,6 +41,9 @@ * Host:port */ +static void +http_client_host_port_connection_setup(struct http_client_host_port *hport); + static struct http_client_host_port * http_client_host_port_find(struct http_client_host *host, unsigned int port, const char *https_name) @@ -65,6 +68,7 @@ hport = http_client_host_port_find(host, port, https_name); if (hport == NULL) { hport = array_append_space(&host->ports); + hport->host = host; hport->port = port; hport->https_name = i_strdup(https_name); hport->ips_connect_idx = 0; @@ -110,16 +114,38 @@ } } -/* - * Host - */ +static void +http_client_host_port_soft_connect_timeout(struct http_client_host_port *hport) +{ + struct http_client_host *host = hport->host; + + if (hport->to_connect != NULL) + timeout_remove(&hport->to_connect); + + if (hport->ips_connect_idx + 1 >= host->ips_count) + return; + + /* if our our previous connection attempt takes longer than the + soft_connect_timeout we start a connection attempt to the next IP in + parallel */ + + http_client_host_debug(host, "Connection to %s:%u%s is taking a long time; " + "starting parallel connection attempt to next IP", + net_ip2addr(&host->ips[hport->ips_connect_idx]), hport->port, + hport->https_name == NULL ? "" : + t_strdup_printf(" (SSL=%s)", hport->https_name)); + + hport->ips_connect_idx++; + http_client_host_port_connection_setup(hport); +} static void -http_client_host_connection_setup(struct http_client_host *host, - struct http_client_host_port *hport) +http_client_host_port_connection_setup(struct http_client_host_port *hport) { + struct http_client_host *host = hport->host; struct http_client_peer *peer = NULL; struct http_client_peer_addr addr; + unsigned int msecs; addr.ip = host->ips[hport->ips_connect_idx]; addr.port = hport->port; @@ -131,6 +157,107 @@ peer = http_client_peer_get(host->client, &addr); http_client_peer_add_host(peer, host); + hport->pending_connection_count++; + + /* start soft connect time-out (but only if we have another IP left) */ + msecs = host->client->set.soft_connect_timeout_msecs; + if (host->ips_count - hport->ips_connect_idx > 1 && msecs > 0 && + hport->to_connect == NULL) { + hport->to_connect = + timeout_add(msecs, http_client_host_port_soft_connect_timeout, hport); + } +} + +static void +http_client_host_drop_pending_connections(struct http_client_host_port *hport) +{ + struct http_client_peer *peer; + struct http_client_connection *const *conns, *conn; + unsigned int i, count; + + for (peer = hport->host->client->peers_list; peer != NULL; peer = peer->next) { + if (!http_client_peer_have_host(peer, hport->host)) + continue; + + conns = array_get(&peer->conns, &count); + for (i = count; i > 0; i--) { + conn = conns[i-1]; + if (!conn->connected) { + i_assert(conn->refcount == 1); + /* avoid recreating the connection */ + peer->last_connect_failed = TRUE; + http_client_connection_unref(&conn); + } + } + } +} + +static void +http_client_host_port_connection_success(struct http_client_host_port *hport) +{ + /* we achieved at least one connection the the addr->ip */ + + /* stop soft connect time-out */ + if (hport->to_connect != NULL) + timeout_remove(&hport->to_connect); + + /* drop all other attempts. note that we get here whenever a connection + is successfully created, so pending_connection_count may be 0. */ + if (hport->pending_connection_count > 1) + http_client_host_drop_pending_connections(hport); + /* since this hport is now successfully connected, we won't be + getting any connection failures to it anymore. so we need + to reset the pending_connection_count count here. */ + hport->pending_connection_count = 0; +} + +static bool +http_client_host_port_connection_failure(struct http_client_host_port *hport, + const char *reason) +{ + struct http_client_host *host = hport->host; + + i_assert(hport->pending_connection_count > 0); + if (--hport->pending_connection_count > 0) + return TRUE; + + /* one of the connections failed. if we're not using soft timeouts, + we need to try to connect to the next IP. if we are using soft + timeouts, we've already tried all of the IPs by now. */ + if (hport->to_connect != NULL) + timeout_remove(&hport->to_connect); + + i_assert(hport->ips_connect_idx < host->ips_count); + if (++hport->ips_connect_idx == host->ips_count) { + /* all IPs failed, but retry all of them again on the + next request. */ + hport->ips_connect_idx = 0; + http_client_host_port_error(hport, + HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason); + return FALSE; + } + + http_client_host_port_connection_setup(hport); + return TRUE; +} + +/* + * Host + */ + +void http_client_host_connection_success(struct http_client_host *host, + const struct http_client_peer_addr *addr) +{ + struct http_client_host_port *hport; + + http_client_host_debug(host, "Successfully connected to %s:%u", + net_ip2addr(&addr->ip), addr->port); + + hport = http_client_host_port_find(host, addr->port, addr->https_name); + if (hport == NULL) + return; + + http_client_host_port_connection_success(hport); } void http_client_host_connection_failure(struct http_client_host *host, @@ -145,18 +272,11 @@ if (hport == NULL) return; - i_assert(hport->ips_connect_idx < host->ips_count); - if (++hport->ips_connect_idx == host->ips_count) { - /* all IPs failed, but retry all of them again on the - next request. */ - hport->ips_connect_idx = 0; - http_client_host_port_error(hport, - HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason); + if (!http_client_host_port_connection_failure(hport, reason)) { + /* failed definitively for currently queued requests */ if (host->client->ioloop != NULL) io_loop_stop(host->client->ioloop); - return; } - http_client_host_connection_setup(host, hport); } static void @@ -201,7 +321,7 @@ unsigned int count = array_count(&hport->request_queue); hport->ips_connect_idx = 0; if (count > 0) - http_client_host_connection_setup(host, hport); + http_client_host_port_connection_setup(hport); requests += count; } @@ -306,7 +426,7 @@ if (host->ips_count == 0) return; i_assert(hport->ips_connect_idx < host->ips_count); - http_client_host_connection_setup(host, hport); + http_client_host_port_connection_setup(hport); } struct http_client_request *
--- a/src/lib-http/http-client-peer.c Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client-peer.c Sun Jun 09 02:46:50 2013 +0300 @@ -76,7 +76,8 @@ } static unsigned int -http_client_peer_requests_pending(struct http_client_peer *peer, unsigned int *num_urgent_r) +http_client_peer_requests_pending(struct http_client_peer *peer, + unsigned int *num_urgent_r) { struct http_client_host *const *host; unsigned int num_requests = 0, num_urgent = 0, requests, urgent; @@ -232,20 +233,22 @@ return peer; } -void http_client_peer_add_host(struct http_client_peer *peer, - struct http_client_host *host) +bool http_client_peer_have_host(struct http_client_peer *peer, + struct http_client_host *host) { struct http_client_host *const *host_idx; - bool exists = FALSE; array_foreach(&peer->hosts, host_idx) { - if (*host_idx == host) { - exists = TRUE; - break; - } + if (*host_idx == host) + return TRUE; } + return FALSE; +} - if (!exists) +void http_client_peer_add_host(struct http_client_peer *peer, + struct http_client_host *host) +{ + if (!http_client_peer_have_host(peer, host)) array_append(&peer->hosts, &host, 1); http_client_peer_handle_requests(peer); } @@ -267,6 +270,17 @@ return NULL; } +void http_client_peer_connection_success(struct http_client_peer *peer) +{ + struct http_client_host *const *host; + + peer->last_connect_failed = FALSE; + + array_foreach(&peer->hosts, host) { + http_client_host_connection_success(*host, &peer->addr); + } +} + void http_client_peer_connection_failure(struct http_client_peer *peer, const char *reason) {
--- a/src/lib-http/http-client-private.h Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client-private.h Sun Jun 09 02:46:50 2013 +0300 @@ -71,15 +71,20 @@ }; struct http_client_host_port { + struct http_client_host *host; + unsigned int port; + char *https_name; /* current index in host->ips */ unsigned int ips_connect_idx; + /* number of connections trying to connect for this host+port */ + unsigned int pending_connection_count; /* requests pending in queue to be picked up by connections */ ARRAY_TYPE(http_client_request) request_queue; - char *https_name; + struct timeout *to_connect; }; struct http_client_host { @@ -228,12 +233,15 @@ http_client_peer_get(struct http_client *client, const struct http_client_peer_addr *addr); void http_client_peer_free(struct http_client_peer **_peer); +bool http_client_peer_have_host(struct http_client_peer *peer, + struct http_client_host *host); void http_client_peer_add_host(struct http_client_peer *peer, struct http_client_host *host); struct http_client_request * http_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent); void http_client_peer_handle_requests(struct http_client_peer *peer); +void http_client_peer_connection_success(struct http_client_peer *peer); void http_client_peer_connection_failure(struct http_client_peer *peer, const char *reason); void http_client_peer_connection_lost(struct http_client_peer *peer); @@ -247,6 +255,8 @@ struct http_client_request * http_client_host_claim_request(struct http_client_host *host, const struct http_client_peer_addr *addr, bool no_urgent); +void http_client_host_connection_success(struct http_client_host *host, + const struct http_client_peer_addr *addr); void http_client_host_connection_failure(struct http_client_host *host, const struct http_client_peer_addr *addr, const char *reason); unsigned int http_client_host_requests_pending(struct http_client_host *host,
--- a/src/lib-http/http-client.c Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client.c Sun Jun 09 02:46:50 2013 +0300 @@ -98,6 +98,7 @@ 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.soft_connect_timeout_msecs = set->soft_connect_timeout_msecs; client->set.debug = set->debug; client->conn_list = http_client_connection_list_init();
--- a/src/lib-http/http-client.h Sun Jun 09 02:08:24 2013 +0300 +++ b/src/lib-http/http-client.h Sun Jun 09 02:46:50 2013 +0300 @@ -63,6 +63,10 @@ /* max time to wait for connect() (and SSL handshake) to finish before retrying (default = request_timeout_msecs) */ unsigned int connect_timeout_msecs; + /* time to wait for connect() (and SSL handshake) to finish for the first + connection before trying the next IP in parallel + (default = 0; wait until current connection attempt finishes) */ + unsigned int soft_connect_timeout_msecs; bool debug; };