http-client-peer.c revision 94d1b08c9e20d637db568a3eab3dfc2b9e96e62a
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Copyright (c) 2013-2014 Dovecot authors, see the included COPYING file */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic inline void
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenyhttp_client_peer_debug(struct http_client_peer *peer,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic inline void
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekhttp_client_peer_debug(struct http_client_peer *peer,
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek const char *format, ...)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek http_client_peer_label(peer), t_strdup_vprintf(format, args));
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek * Peer address
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek return net_ip_hash(&peer->ip) + peer->port + 1;
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek (peer->https_name == NULL ? 0 : str_hash(peer->https_name));
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek if ((ret=net_ip_cmp(&peer1->ip, &peer2->ip)) != 0)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek return null_strcmp(peer1->https_name, peer2->https_name);
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekhttp_client_peer_do_connect(struct http_client_peer *peer,
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek unsigned int count)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek unsigned int i;
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek for (i = 0; i < count; i++) {
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekhttp_client_peer_connect_backoff(struct http_client_peer *peer)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek "Backoff timer expired");
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekhttp_client_peer_start_backoff_timer(struct http_client_peer *peer)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek timeval_diff_msecs(&ioloop_timeval, &peer->last_failure);
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek if (backoff_time_spent < (int)peer->backoff_time_msecs) {
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek "Starting backoff timer for %d msecs",
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek peer->backoff_time_msecs - backoff_time_spent);
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek ((unsigned int)(peer->backoff_time_msecs - backoff_time_spent),
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek "Backoff time already exceeded by %d msecs",
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek backoff_time_spent - peer->backoff_time_msecs);
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekhttp_client_peer_connect(struct http_client_peer *peer, unsigned int count)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek if (http_client_peer_start_backoff_timer(peer))
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozekbool http_client_peer_is_connected(struct http_client_peer *peer)
50936fc7230a9b3f01e285e72c4182013542f53eJakub Hrozek struct http_client_connection *const *conn_idx;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenyhttp_client_peer_disconnect(struct http_client_peer *peer)
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* make a copy of the connection array; freed connections modify it */
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek array_copy(&conns.arr, 0, &peer->conns.arr, 0, array_count(&peer->conns));
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bosestatic void http_client_peer_check_idle(struct http_client_peer *peer)
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic unsigned int
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenyhttp_client_peer_requests_pending(struct http_client_peer *peer,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny unsigned int *num_urgent_r)
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce unsigned int num_requests = 0, num_urgent = 0, requests, urgent;
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny requests = http_client_queue_requests_pending(*queue, &urgent);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorcehttp_client_peer_handle_requests_real(struct http_client_peer *peer)
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny unsigned int num_pending, num_urgent, new_connections, working_conn_count;
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce /* FIXME: limit the number of requests handled in one run to prevent
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce I/O starvation. */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov /* disconnect if we're not linked to any queue anymore */
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce /* don't do anything unless we have pending requests */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny num_pending = http_client_peer_requests_pending(peer, &num_urgent);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny t_array_init(&conns_avail, array_count(&peer->conns));
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek /* gather connection statistics */
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose if (http_client_connection_is_ready(*conn_idx)) {
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek /* compile sorted availability list */
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek pending_requests = http_client_connection_count_pending(*conn_idx);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce array_foreach_modifiable(&conns_avail, conn_avail_idx) {
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce if (conn_avail_idx->pending_requests > pending_requests) {
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce insert_idx = array_foreach_idx(&conns_avail, conn_avail_idx);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce conn_avail = array_insert_space(&conns_avail, insert_idx);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce /* count the number of connecting and closing connections */
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce working_conn_count = array_count(&peer->conns) - closing;
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce /* use idle connections right away */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov "Using %u idle connections to handle %u requests "
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov "(%u total connections ready)",
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce array_foreach_modifiable(&conns_avail, conn_avail_idx) {
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce if (num_pending == 0 || conn_avail_idx->pending_requests > 0)
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose /* no longer available (probably connection error/closed) */
a3c8390d19593b1e5277d95bfb4ab206d4785150Nikolai Kondrashov /* update statistics */
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek /* don't continue unless we have more pending requests */
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek num_pending = http_client_peer_requests_pending(peer, &num_urgent);
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose /* determine how many new connections we can set up */
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose if (peer->last_failure.tv_sec > 0 && working_conn_count > 0 &&
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose /* don't create new connections until the existing ones have
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose finished connecting successfully. */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose if (working_conn_count - connecting + num_urgent >=
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* only create connections for urgent requests */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose new_connections = (num_urgent > connecting ? num_urgent - connecting : 0);
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* there are already enough connections being made */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* no connections succeeded so far, don't hammer the server with more
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose than one connection attempt unless its urgent */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose (num_urgent > connecting ? num_urgent - connecting : 0);
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose peer->client->set.max_parallel_connections - working_conn_count) {
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* create maximum allowed connections */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose peer->client->set.max_parallel_connections - working_conn_count;
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* create as many connections as we need */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* create connections */
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek "Creating %u new connections to handle requests "
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek "(already %u usable, connecting to %u, closing %u)",
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek new_connections, working_conn_count - connecting,
ea224c3813a537639778f91ac762732b3c289603Jakub Hrozek http_client_peer_connect(peer, new_connections);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce /* cannot create new connections for normal request; attempt pipelining */
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek unsigned int pipeline_level = 0, total_handled = 0, handled;
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce "Will not pipeline until peer has shown support");
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* fill pipelines */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* fill smallest pipelines first,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny until all pipelines are filled to the same level */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny array_foreach_modifiable(&conns_avail, conn_avail_idx) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny pipeline_level = conn_avail_idx->pending_requests;
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce } else if (conn_avail_idx->pending_requests > pipeline_level) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny pipeline_level = conn_avail_idx->pending_requests;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny break; /* restart from least busy connection */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* pipeline it */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (http_client_connection_next_request(conn_avail_idx->conn) <= 0) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* connection now unavailable */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* successfully pipelined */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny } while (num_pending > num_urgent && handled > 0);
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce "Pipelined %u requests (filled pipelines up to %u requests)",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* still waiting for connections to finish */
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce "No request handled; waiting for new connections");
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zelenystatic void http_client_peer_handle_requests(struct http_client_peer *peer)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zelenyvoid http_client_peer_trigger_request_handler(struct http_client_peer *peer)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* trigger request handling through timeout */
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce timeout_add_short(0, http_client_peer_handle_requests, peer);
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorcehttp_client_peer_create(struct http_client *client,
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce i_assert(addr->https_name == NULL || client->ssl_ctx != NULL);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny (client->peers, (const struct http_client_peer_addr *)&peer->addr, peer);
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorcevoid http_client_peer_free(struct http_client_peer **_peer)
17195241500e46272018d7897d6e87249870caf2Pavel Reichl (peer->client->peers, (const struct http_client_peer_addr *)&peer->addr);
17195241500e46272018d7897d6e87249870caf2Pavel Reichl DLLIST_REMOVE(&peer->client->peers_list, peer);
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bosebool http_client_peer_have_queue(struct http_client_peer *peer,
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bosevoid http_client_peer_link_queue(struct http_client_peer *peer,
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bosevoid http_client_peer_unlink_queue(struct http_client_peer *peer,
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose if (http_client_peer_start_backoff_timer(peer)) {
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* will disconnect any pending connections */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose /* drop peer immediately */
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bosehttp_client_peer_claim_request(struct http_client_peer *peer, bool no_urgent)
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bosevoid http_client_peer_connection_success(struct http_client_peer *peer)
9f734d4c122e37cc3080974342ed9586d05d5f83Sumit Bose "Successfully connected (connections=%u)",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny peer->last_failure.tv_sec = peer->last_failure.tv_usec = 0;
17195241500e46272018d7897d6e87249870caf2Pavel Reichl http_client_queue_connection_success(*queue, &peer->addr);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zelenyvoid http_client_peer_connection_failure(struct http_client_peer *peer,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny const char *reason)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny const struct http_client_settings *set = &peer->client->set;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny unsigned int pending;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* count number of pending connections */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny pending = http_client_peer_pending_connections(peer);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "Failed to make connection "
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "(connections=%u, connecting=%u)",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* manage backoff timer only when this was the only attempt */
7fe69bb6ec70bce439c6b975a9a0044c98ff502bSimo Sorce peer->backoff_time_msecs = set->connect_backoff_time_msecs;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (peer->backoff_time_msecs > set->connect_backoff_max_time_msecs)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny peer->backoff_time_msecs = set->connect_backoff_max_time_msecs;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* if there are other connections attempting to connect, wait
c03b28a38b14fdb59f74864ae4dc56affe256508Simo Sorce for them before failing the requests. remember that we had
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny trouble with connecting so in future we don't try to create
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny more than one connection until connects work again. */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* this was the only/last connection and connecting to it
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny failed. a second connect will probably also fail, so just
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny try another IP for the hosts(s) or abort all requests if this
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce was the only/last option. */
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce http_client_queue_connection_failure(*queue, &peer->addr, reason);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zelenyvoid http_client_peer_connection_lost(struct http_client_peer *peer)
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce unsigned int num_urgent;
c03b28a38b14fdb59f74864ae4dc56affe256508Simo Sorce /* we get here when an already connected connection fails. if the
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny connect itself fails, http_client_peer_connection_failure() is
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny called instead. */
17195241500e46272018d7897d6e87249870caf2Pavel Reichl http_client_peer_debug(peer, "Lost a connection (%d connections left)",
17195241500e46272018d7897d6e87249870caf2Pavel Reichl /* if there are pending requests for this peer, create a new connection
17195241500e46272018d7897d6e87249870caf2Pavel Reichl for them. */
17195241500e46272018d7897d6e87249870caf2Pavel Reichl http_client_peer_trigger_request_handler(peer);
17195241500e46272018d7897d6e87249870caf2Pavel Reichl http_client_peer_requests_pending(peer, &num_urgent) == 0)
17195241500e46272018d7897d6e87249870caf2Pavel Reichlhttp_client_peer_idle_connections(struct http_client_peer *peer)
17195241500e46272018d7897d6e87249870caf2Pavel Reichl struct http_client_connection *const *conn_idx;
17195241500e46272018d7897d6e87249870caf2Pavel Reichl unsigned int idle = 0;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* find idle connections */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zelenyhttp_client_peer_pending_connections(struct http_client_peer *peer)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny unsigned int pending = 0;
3b0e0352d8076909608d04750d3ea6b0d9ba33f6Jakub Hrozek /* find idle connections */
3b0e0352d8076909608d04750d3ea6b0d9ba33f6Jakub Hrozek if (!(*conn_idx)->closing && !(*conn_idx)->connected)