bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Boschhttp_client_queue_fail(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_set_delay_timer(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_set_request_timer(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Queue object
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_find(struct http_client_host *host,
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch array_foreach_modifiable(&host->queues, queue_idx) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (http_client_peer_addr_cmp(&queue->addr, addr) == 0)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_create(struct http_client_host *host,
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch i_strdup_printf("raw://%s:%u", hostname, addr->a.tcp.port);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch i_strdup_printf("https://%s:%u", hostname, addr->a.tcp.port);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch queue->addr_name = i_strdup(addr->a.tcp.https_name);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch queue->addr.a.tcp.https_name = queue->addr_name;
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch i_strdup_printf("http://%s:%u", hostname, addr->a.tcp.port);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch queue->name = i_strdup_printf("unix:%s", addr->a.un.path);
2d1ad5742dd723b39c51bcf64c62a600237de8aeTimo Sirainen queue->event = event_create(queue->client->event);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Bosch i_array_init(&queue->queued_urgent_requests, 16);
8149ed57ae5abbb0c4ccfe88c1d7c58255dc85cfStephan Boschhttp_client_queue_get(struct http_client_host *host,
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschvoid http_client_queue_free(struct http_client_queue *queue)
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch /* currently only called when peer is freed, so there is no need to
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch unlink from the peer */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* unlink all peers */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch struct http_client_peer *peer = queue->cur_peer;
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch t_array_init(&peers, array_count(&queue->pending_peers));
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch array_copy(&peers.arr, 0, &queue->pending_peers.arr, 0,
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch http_client_peer_unlink_queue(*peer_idx, queue);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* abort all requests */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch (queue, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborted");
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* cancel timeouts */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Error handling
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Boschhttp_client_queue_fail_full(struct http_client_queue *queue,
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch unsigned int status, const char *error, bool queued_only)
0d30ec5bef193a1fdb4c166c264a2918df37033fStephan Bosch ARRAY_TYPE(http_client_request) *req_arr, treqs;
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch /* abort requests */
0d30ec5bef193a1fdb4c166c264a2918df37033fStephan Bosch array_copy(&treqs.arr, 0, &req_arr->arr, 0, array_count(req_arr));
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch i_assert(req->state >= HTTP_REQUEST_STATE_QUEUED);
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch http_client_request_error(&req, status, error);
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch /* all queues should be empty now... unless new requests were submitted
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch from the callback. this invariant captures it all: */
a8304588757ef197bf10461f668cb017cb75a68aStephan Bosch array_count(&queue->queued_urgent_requests)) ==
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Boschhttp_client_queue_fail(struct http_client_queue *queue,
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch http_client_queue_fail_full(queue, status, error, FALSE);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Connection management
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_is_last_connect_ip(struct http_client_queue *queue)
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch unsigned int ips_count = http_client_host_get_ips_count(host);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch i_assert(queue->ips_connect_start_idx < ips_count);
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch /* if a maximum connect attempts > 1 is set, enforce it directly */
501d0f9aa4e892c47dfe369774ef3c8961f753d2Stephan Bosch queue->connect_attempts >= set->max_connect_attempts)
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch /* otherwise, we'll always go through all the IPs. we don't necessarily
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch start connecting from the first IP, so we'll need to treat the IPs as
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch a ring buffer where we automatically wrap back to the first IP
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch when necessary. */
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch return (queue->ips_connect_idx + 1) % ips_count ==
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Boschhttp_client_queue_recover_from_lookup(struct http_client_queue *queue)
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch queue->ips_connect_idx = queue->ips_connect_start_idx = 0;
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch &queue->cur_peer->shared->addr.a.tcp.ip, &ip_idx)) {
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch /* continue with current peer */
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch queue->ips_connect_idx = queue->ips_connect_start_idx = ip_idx;
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch /* reset connect attempts */
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch queue->ips_connect_idx = queue->ips_connect_start_idx = 0;
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_soft_connect_timeout(struct http_client_queue *queue)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch const struct http_client_peer_addr *addr = &queue->addr;
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch unsigned int ips_count = http_client_host_get_ips_count(host);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch if (http_client_queue_is_last_connect_ip(queue)) {
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* no more IPs to try */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* if our our previous connection attempt takes longer than the
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch soft_connect_timeout, we start a connection attempt to the next IP in
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch https_name = http_client_peer_addr_get_https_name(addr);
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(queue->event, "Connection to %s%s is taking a long time; "
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch "starting parallel connection attempt to next IP",
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch http_client_peer_addr2str(addr), (https_name == NULL ? "" :
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* next IP */
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch queue->ips_connect_idx = (queue->ips_connect_idx + 1) % ips_count;
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* setup connection to new peer (can start new soft timeout) */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Boschhttp_client_queue_connection_attempt(struct http_client_queue *queue)
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch struct http_client_peer_addr *addr = &queue->addr;
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch /* check whether host IPs are still up-to-date */
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch if ((ret=http_client_host_refresh(host)) < 0) {
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch /* performing asynchronous lookup */
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch /* new lookup performed */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* update our peer address */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch const struct ip_addr *ip = http_client_host_get_ip(host,
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch ssl = http_client_peer_addr_get_https_name(addr);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch ssl = (ssl == NULL ? "" : t_strdup_printf(" (SSL=%s)", ssl));
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* already got a peer? */
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch i_assert(array_count(&queue->pending_peers) == 0);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* is it still the one we want? */
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch (addr, &queue->cur_peer->shared->addr) == 0) {
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* is it still connected? */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch if (http_client_peer_is_connected(queue->cur_peer)) {
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch "Using existing connection to %s%s "
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch "(%u requests pending)",
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch http_client_peer_addr2str(addr), ssl, num_requests);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* handle requests; */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch http_client_peer_trigger_request_handler(queue->cur_peer);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* peer is not relevant to this queue anymore */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch http_client_peer_unlink_queue(queue->cur_peer, queue);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch peer = http_client_peer_get(queue->client, addr);
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(queue->event, "Setting up connection to %s%s "
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch "(%u requests pending)", http_client_peer_addr2str(addr), ssl,
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* create provisional link between queue and peer */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* handle requests; creates new connections when needed/possible */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch http_client_peer_trigger_request_handler(peer);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch /* drop any pending peers */
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch array_foreach(&queue->pending_peers, peer_idx) {
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch http_client_peer_unlink_queue(*peer_idx, queue);
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch http_client_peer_trigger_request_handler(queue->cur_peer);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* not already connected, wait for connections */
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch /* we may be waiting for this peer already */
6a0e62561f965c86f98444c8ad48fad7c0d67bcbStephan Bosch array_foreach(&queue->pending_peers, peer_idx) {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(queue->event, "Started new connection to %s%s",
028a1c8de5166a81c2394131cac2406327febf52Timo Sirainen array_append(&queue->pending_peers, &peer, 1);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* start soft connect time-out (but only if we have another IP left) */
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch msecs = client->set.soft_connect_timeout_msecs;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch if (!http_client_queue_is_last_connect_ip(queue) && msecs > 0 &&
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch queue->to_connect = timeout_add_to(client->ioloop, msecs,
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch http_client_queue_soft_connect_timeout, queue);
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Boschvoid http_client_queue_connection_setup(struct http_client_queue *queue)
57d86b6fd46cb6d37bfc28f67ae4be80296ad35aStephan Bosch (void)http_client_queue_connection_attempt(queue);
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Boschhttp_client_queue_host_lookup_done(struct http_client_queue *queue)
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch http_client_queue_requests_pending(queue, NULL);
5791d02c3672f6a525f1fcf0f4f375b4be0ccf4bStephan Bosch struct http_client_queue *queue, const char *error)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_connection_success(struct http_client_queue *queue,
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch const struct http_client_peer_addr *addr = &peer->shared->addr;
1e63e30812158e6446d81cdbb2f45954794d4f8aStephan Bosch queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch /* we achieved at least one connection the the addr->ip */
97a8fde13ea33b09163c45d978a4949043f189c5Stephan Bosch &addr->a.tcp.ip, &queue->ips_connect_start_idx)) {
97a8fde13ea33b09163c45d978a4949043f189c5Stephan Bosch /* list of IPs changed during connect */
9d746c6785d17e421c3f3c74cf29d059ae2ab233Stephan Bosch /* reset attempt counter */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* stop soft connect time-out */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* drop all other attempts to the hport. note that we get here whenever
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch a connection is successfully created, so pending_peers array
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch may be empty. */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch array_foreach(&queue->pending_peers, peer_idx) {
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* don't drop any connections to the successfully
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch connected peer, even if some of the connections
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch are pending. they may be intended for urgent
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch /* unlink this queue from the peer; if this was the last/only queue, the
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch peer will be freed, closing all connections.
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch http_client_peer_unlink_queue(*peer_idx, queue);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_connection_failure(struct http_client_queue *queue,
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch struct http_client_peer *peer, const char *reason)
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch const struct http_client_peer_addr *addr = &peer->shared->addr;
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch const char *https_name = http_client_peer_addr_get_https_name(addr);
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch unsigned int ips_count = http_client_host_get_ips_count(host);
ede750711f27ca9d9037a7ab9f016411b57f1ad9Stephan Bosch "Failed to set up connection to %s%s: %s "
ee3c2fb2ebbf6fbbf12085f36102553ecbcb1397Timo Sirainen "(%u peers pending, %u requests pending)",
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch if (array_count(&queue->pending_peers) == 0) {
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch /* we're still doing the initial connections to this hport. if
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch we're also doing parallel connections with soft timeouts
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch (pending_peer_count>1), wait for them to finish
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch array_foreach(&queue->pending_peers, peer_idx) {
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch array_foreach_idx(&queue->pending_peers, peer_idx), 1);
b3df4be577af79d93f39e099e5e0b226ab7fd775Stephan Bosch "Waiting for remaining pending peers.");
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch /* one of the connections failed. if we're not using soft timeouts,
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch we need to try to connect to the next IP. if we are using soft
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch timeouts, we've already tried all of the IPs by now. */
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch if (queue->addr.type == HTTP_CLIENT_PEER_ADDR_UNIX) {
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch if (http_client_queue_is_last_connect_ip(queue)) {
e4e4b8544a492cf90bd7a93c9a26e8285fc7c00bStephan Bosch /* all IPs failed, but retry all of them again if we have more
e4e4b8544a492cf90bd7a93c9a26e8285fc7c00bStephan Bosch connect attempts left or on the next request. */
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch queue->ips_connect_idx = queue->ips_connect_start_idx =
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch if (queue->cur_peer == NULL && (set->max_connect_attempts == 0 ||
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch queue->connect_attempts >= set->max_connect_attempts)) {
e4e4b8544a492cf90bd7a93c9a26e8285fc7c00bStephan Bosch "Failed to set up any connection; failing all queued requests");
9284599e2d12b08170be81441bcfc53fa5b71a73Timo Sirainen timeval_diff_msecs(&ioloop_timeval, &queue->first_connect_time);
9284599e2d12b08170be81441bcfc53fa5b71a73Timo Sirainen reason = t_strdup_printf("%s (%u attempts in %u.%03u secs)",
e4e4b8544a492cf90bd7a93c9a26e8285fc7c00bStephan Bosch HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason);
23fe024e1dfc8eb5eaefc4e57a16b4257568f510Stephan Bosch queue->ips_connect_idx = (queue->ips_connect_idx + 1) % ips_count;
27a2e59eaa648fef2acb2c4b852567d22e016a2dStephan Bosch if (http_client_queue_connection_attempt(queue) != peer) {
c1423bdba971228a283653222ed0367f84ab6402Stephan Boschhttp_client_queue_peer_disconnected(struct http_client_queue *queue,
c1423bdba971228a283653222ed0367f84ab6402Stephan Bosch array_foreach(&queue->pending_peers, peer_idx) {
c1423bdba971228a283653222ed0367f84ab6402Stephan Bosch array_foreach_idx(&queue->pending_peers, peer_idx), 1);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Main request queue
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_drop_request(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch "Dropping request %s", http_client_request_label(req));
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* drop from queue */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch reqs = array_get_modifiable(&queue->queued_urgent_requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch array_delete(&queue->queued_urgent_requests, i, 1);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch reqs = array_get_modifiable(&queue->queued_requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* drop from delay queue */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch reqs = array_get_modifiable(&queue->delayed_requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (i == 0) {
49e66f665b8070be2bb8afb6b823bb9aefc838bcStephan Bosch http_client_queue_set_delay_timer(queue, reqs[1]->release_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* drop from main request list */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch reqs = array_get_modifiable(&queue->requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (i == 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (count > 1 && reqs[1]->timeout_time.tv_sec > 0)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_set_request_timer(queue, &reqs[1]->timeout_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_request_timeout(struct http_client_queue *queue)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch ARRAY_TYPE(http_client_request) failed_requests;
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(queue->event, "Timeout (now: %s.%03lu)",
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_timeval.tv_sec),
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch ((unsigned long)ioloop_timeval.tv_usec)/1000);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* collect failed requests */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
211c638d81d382517d196ad47565e0d85012c927klemens /* update timeout */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* abort all failed request */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch i_assert(count > 0); /* at least one request timed out */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch for (i = 0; i < count; i++) {
3dc38e4053b06b3f654d199a293a988b9c989347Stephan Bosch http_client_request_append_stats_text(req, str);
3dc38e4053b06b3f654d199a293a988b9c989347Stephan Bosch "Absolute timeout expired for request %s (%s)",
3dc38e4053b06b3f654d199a293a988b9c989347Stephan Bosch "Absolute request timeout expired (%s)",
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_set_request_timer(queue, &new_to);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_set_request_timer(struct http_client_queue *queue,
39018a601747b9d52a15ce2451e64e9515587944Timo Sirainen "Set request timeout to %s.%03lu (now: %s.%03lu)",
39018a601747b9d52a15ce2451e64e9515587944Timo Sirainen t_strflocaltime("%Y-%m-%d %H:%M:%S", time->tv_sec),
39018a601747b9d52a15ce2451e64e9515587944Timo Sirainen t_strflocaltime("%Y-%m-%d %H:%M:%S", ioloop_timeval.tv_sec),
39018a601747b9d52a15ce2451e64e9515587944Timo Sirainen ((unsigned long)ioloop_timeval.tv_usec)/1000);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* set timer */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Boschhttp_client_queue_request_timeout_cmp(struct http_client_request *const *req1,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* 0 means no timeout */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* sort by age */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if ((ret=timeval_cmp(&(*req1)->submit_time, &(*req2)->submit_time)) != 0)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch } else if ((*req2)->timeout_time.tv_sec == 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* sort by timeout */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch ((ret=timeval_cmp(&(*req1)->timeout_time, &(*req2)->timeout_time)) != 0) {
211c638d81d382517d196ad47565e0d85012c927klemens /* sort by minimum attempts for fairness */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch return ((*req2)->attempts - (*req1)->attempts);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschstatic void http_client_queue_submit_now(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* enqueue */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* no timeout; enqueue at end */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch } else if (timeval_diff_msecs(&req->timeout_time, &ioloop_timeval) <= 1) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* pretty much already timed out; don't bother */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* keep transmission queue sorted earliest timeout first */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch &req, http_client_queue_request_timeout_cmp, &insert_idx);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Delayed request queue
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschhttp_client_queue_delay_timeout(struct http_client_queue *queue)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch reqs = array_get(&queue->delayed_requests, &count);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch for (i = 0; i < count; i++) {
06d1b658d0f6840204bf1487b8bcad829d33b726Stephan Bosch if (timeval_cmp_margin(&reqs[i]->release_time,
06d1b658d0f6840204bf1487b8bcad829d33b726Stephan Bosch &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
36409af77b42dc1c18c0691970b2eb07785fbba4Timo Sirainen e_debug(queue->event, "Activated delayed request %s%s",
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch http_client_queue_set_delay_timer(queue, reqs[i]->release_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch array_delete(&queue->delayed_requests, 0, finished);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschhttp_client_queue_set_delay_timer(struct http_client_queue *queue,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch int usecs = timeval_diff_usecs(&time, &ioloop_timeval);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch /* round up to nearest microsecond */
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch /* set timer */
a68aaff537e2e30d782bb8b9d8782e1a10a17d1aStephan Bosch queue->to_delayed = timeout_add_to(client->ioloop, msecs,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschhttp_client_queue_delayed_cmp(struct http_client_request *const *req1,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch return timeval_cmp(&(*req1)->release_time, &(*req2)->release_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Request submission
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Boschvoid http_client_queue_submit_request(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_drop_request(req->queue, req);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* check delay vs timeout */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch if (req->release_time.tv_sec > 0 && req->timeout_time.tv_sec > 0 &&
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch &req->timeout_time, TIMEOUT_CMP_MARGIN_USECS) >= 0) {
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* release time is later than absolute timeout */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* timeout rightaway */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch "Delayed request %s%s already timed out",
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* add to main request list */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* no timeout; just append */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* keep main request list sorted earliest timeout first */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch (void)array_bsearch_insert_pos(&queue->requests,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch &req, http_client_queue_request_timeout_cmp, &insert_idx);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch array_insert(&queue->requests, insert_idx, &req, 1);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* now first in queue; update timer */
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch http_client_queue_set_request_timer(queue, &req->timeout_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch /* handle delay */
06d1b658d0f6840204bf1487b8bcad829d33b726Stephan Bosch &ioloop_timeval, TIMEOUT_CMP_MARGIN_USECS) > 0) {
a0923d7d097284e80be164c9d7a630f4b6c176e2Stephan Bosch "Delayed request %s%s submitted (time remaining: %d msecs)",
a0923d7d097284e80be164c9d7a630f4b6c176e2Stephan Bosch timeval_diff_msecs(&req->release_time, &ioloop_timeval));
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch (void)array_bsearch_insert_pos(&queue->delayed_requests,
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch &req, http_client_queue_delayed_cmp, &insert_idx);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch array_insert(&queue->delayed_requests, insert_idx, &req, 1);
93cc87bb22386e020cee1093b6bd59295e0b33f0Stephan Bosch http_client_queue_set_delay_timer(queue, req->release_time);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch * Request retrieval
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_claim_request(struct http_client_queue *queue,
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch const struct http_client_peer_addr *addr, bool no_urgent)
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch requests = array_get(&queue->queued_urgent_requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch requests = array_get(&queue->queued_requests, &count);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch array_delete(&queue->queued_urgent_requests, i, 1);
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch "Connection to peer %s claimed request %s %s",
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch http_client_peer_addr2str(addr), http_client_request_label(req),
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschhttp_client_queue_requests_pending(struct http_client_queue *queue,
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch unsigned int urg_count = array_count(&queue->queued_urgent_requests);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch return array_count(&queue->queued_requests) + urg_count;
c62f15cc134cc9701c391eec8e9ef92105aa6d33Stephan Boschhttp_client_queue_requests_active(struct http_client_queue *queue)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Boschvoid http_client_queue_switch_ioloop(struct http_client_queue *queue)
de96afeeaa5242cffe89f1966457e935806b5746Stephan Bosch queue->to_connect = io_loop_move_timeout(&queue->to_connect);
b66def5dadd3e7c250313a938d26ad113663f86bStephan Bosch queue->to_request = io_loop_move_timeout(&queue->to_request);