]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: client: Implemented host name lookup TTL.
authorStephan Bosch <stephan@dovecot.fi>
Fri, 16 Sep 2016 18:22:17 +0000 (20:22 +0200)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 9 Nov 2016 12:26:58 +0000 (14:26 +0200)
Host name lookups will now be performed again when the results have expired.
Without access to TTL information from DNS lookups, all lookups will use the same default TTL for now.

src/lib-http/http-client-host.c
src/lib-http/http-client-private.h
src/lib-http/http-client-queue.c
src/lib-http/http-client.c
src/lib-http/http-client.h

index f23ade0ce1e4a3fff2923ef1c53bd84d142be2d3..634300a5faaa533b33f77531ff7fcff6094f5271 100644 (file)
@@ -9,6 +9,7 @@
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
+#include "time-util.h"
 #include "dns-lookup.h"
 #include "http-response-parser.h"
 
@@ -59,12 +60,14 @@ static void
 http_client_host_dns_callback(const struct dns_lookup_result *result,
                              struct http_client_host *host)
 {
+       struct http_client *client = host->client;
        struct http_client_queue *const *queue_idx;
        unsigned int requests = 0;
 
        host->dns_lookup = NULL;
 
        if (result->ret != 0) {
+               /* lookup failed */
                http_client_host_lookup_failure(host, result->error);
                return;
        }
@@ -76,18 +79,13 @@ http_client_host_dns_callback(const struct dns_lookup_result *result,
        host->ips_count = result->ips_count;
        host->ips = i_new(struct ip_addr, host->ips_count);
        memcpy(host->ips, result->ips, sizeof(*host->ips) * host->ips_count);
-       
-       /* FIXME: make DNS result expire */
+
+       host->ips_timeout = ioloop_timeval;
+       timeval_add_msecs(&host->ips_timeout, client->set.dns_ttl_msecs);
 
        /* make connections to requested ports */
        array_foreach_modifiable(&host->queues, queue_idx) {
-               struct http_client_queue *queue = *queue_idx;
-               unsigned int reqs_pending = 
-                       http_client_queue_requests_pending(queue, NULL);
-               queue->ips_connect_idx = queue->ips_connect_start_idx = 0;
-               if (reqs_pending > 0)
-                       http_client_queue_connection_setup(queue);
-               requests += reqs_pending;
+               requests += http_client_queue_host_lookup_done(*queue_idx);
        }
 
        if (requests == 0 && host->client->ioloop != NULL)
@@ -100,7 +98,6 @@ static void http_client_host_lookup
        struct http_client *client = host->client;
        struct dns_lookup_settings dns_set;
        struct ip_addr *ips;
-       unsigned int ips_count;
        int ret;
 
        i_assert(!host->explicit_ip);
@@ -127,6 +124,8 @@ static void http_client_host_lookup
                (void)dns_lookup(host->name, &dns_set,
                                 http_client_host_dns_callback, host, &host->dns_lookup);
        } else {
+               unsigned int ips_count;
+
                ret = net_gethostbyname(host->name, &ips, &ips_count);
                if (ret != 0) {
                        http_client_host_lookup_failure(host, net_gethosterror(ret));
@@ -140,6 +139,34 @@ static void http_client_host_lookup
                host->ips = i_new(struct ip_addr, ips_count);
                memcpy(host->ips, ips, ips_count * sizeof(*ips));
        }
+
+       if (host->ips_count > 0) {
+               host->ips_timeout = ioloop_timeval;
+               timeval_add_msecs(&host->ips_timeout, client->set.dns_ttl_msecs);
+       }
+}
+
+int http_client_host_refresh(struct http_client_host *host)
+{
+       if (host->unix_local)
+               return 0;
+       if (host->explicit_ip)
+               return 0;
+
+       if (host->dns_lookup != NULL)
+               return -1;
+
+       if (host->ips_count > 0 &&
+               timeval_cmp(&host->ips_timeout, &ioloop_timeval) > 0)
+               return 0;
+
+       http_client_host_debug(host,
+               "IPs have expired; need to refresh DNS lookup");
+
+       http_client_host_lookup(host);
+       if (host->dns_lookup != NULL)
+               return -1;
+       return (host->ips_count > 0 ? 1 : -1);
 }
 
 static struct http_client_host *http_client_host_create
index 0cc9fd4958bf496da4f728558d7f2da168c532b9..8cc50294049ef06c86e4a46710c4642153176411 100644 (file)
@@ -18,6 +18,7 @@
 #define HTTP_CLIENT_DEFAULT_DNS_LOOKUP_TIMEOUT_MSECS (1000*10)
 #define HTTP_CLIENT_DEFAULT_BACKOFF_TIME_MSECS (100)
 #define HTTP_CLIENT_DEFAULT_BACKOFF_MAX_TIME_MSECS (1000*60)
+#define HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS (1000*60*30)
 
 /*
  * Types
@@ -261,6 +262,7 @@ struct http_client_host {
        /* the ip addresses DNS returned for this host */
        unsigned int ips_count;
        struct ip_addr *ips;
+       struct timeval ips_timeout;
 
        /* requests are managed on a per-port basis */
        ARRAY_TYPE(http_client_queue) queues;
@@ -478,6 +480,8 @@ void http_client_queue_free(struct http_client_queue *queue);
 void http_client_queue_fail(struct http_client_queue *queue,
        unsigned int status, const char *error);
 void http_client_queue_connection_setup(struct http_client_queue *queue);
+unsigned int
+http_client_queue_host_lookup_done(struct http_client_queue *queue);
 void http_client_queue_submit_request(struct http_client_queue *queue,
        struct http_client_request *req);
 void
@@ -524,6 +528,7 @@ void http_client_host_free(struct http_client_host **_host);
 void http_client_host_submit_request(struct http_client_host *host,
        struct http_client_request *req);
 void http_client_host_switch_ioloop(struct http_client_host *host);
+int http_client_host_refresh(struct http_client_host *host);
 
 /*
  * Client
index 8262dd57b4031718dfc7df659b936295189b9f5e..604d078e0b3a631071bf0222e6eaec15655c6b44 100644 (file)
@@ -215,6 +215,37 @@ http_client_queue_is_last_connect_ip(struct http_client_queue *queue)
                queue->ips_connect_start_idx;
 }
 
+static void
+http_client_queue_recover_from_lookup(struct http_client_queue *queue)
+{
+       struct http_client_host *host = queue->host;
+       struct http_client_peer_addr new_addr = queue->addr;
+       unsigned int i;
+
+       i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
+
+       if (queue->cur_peer == NULL) {
+               queue->ips_connect_idx = queue->ips_connect_start_idx = 0;
+               return;
+       }
+
+       /* try to find current peer amongst new IPs */
+       for (i = 0; i < host->ips_count; i++) {
+               new_addr.a.tcp.ip = host->ips[i];
+               if (http_client_peer_addr_cmp
+                       (&new_addr, &queue->cur_peer->addr) == 0)
+                       break;
+       }
+
+       if (i < host->ips_count) {
+               /* continue with current peer */
+               queue->ips_connect_idx = queue->ips_connect_start_idx = i;
+       } else {
+               /* reset connect attempts */
+               queue->ips_connect_idx = queue->ips_connect_start_idx = 0;
+       }
+}
+
 static void
 http_client_queue_soft_connect_timeout(struct http_client_queue *queue)
 {
@@ -258,10 +289,23 @@ http_client_queue_connection_attempt(struct http_client_queue *queue)
                array_count(&queue->queued_requests) +
                array_count(&queue->queued_urgent_requests);
        const char *ssl = "";
+       int ret;
 
        if (num_requests == 0)
                return NULL;
 
+       /* check whether host IPs are still up-to-date */
+       if ((ret=http_client_host_refresh(host)) < 0) {
+               /* performing asynchronous lookup */
+               if (queue->to_connect != NULL)
+                       timeout_remove(&queue->to_connect);
+               return NULL;
+       }
+       if (ret > 0) {
+               /* new lookup performed */
+               http_client_queue_recover_from_lookup(queue);
+       }
+
        /* update our peer address */
        if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
                i_assert(queue->ips_connect_idx < host->ips_count);
@@ -368,11 +412,24 @@ void http_client_queue_connection_setup(struct http_client_queue *queue)
        (void)http_client_queue_connection_attempt(queue);
 }
 
+
+unsigned int
+http_client_queue_host_lookup_done(struct http_client_queue *queue)
+{
+       unsigned int reqs_pending =
+               http_client_queue_requests_pending(queue, NULL);
+       http_client_queue_recover_from_lookup(queue);
+       if (reqs_pending > 0)
+               http_client_queue_connection_setup(queue);
+       return reqs_pending;
+}
+
 void
 http_client_queue_connection_success(struct http_client_queue *queue,
                                         const struct http_client_peer_addr *addr)
 {
-       if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
+       if (queue->host->dns_lookup == NULL &&
+               queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
                /* we achieved at least one connection the the addr->ip */
                queue->ips_connect_start_idx =
                        http_client_host_get_ip_idx(queue->host, &addr->a.tcp.ip);
index 14814f8047e2f2e2024866a61b9bd9a03c2b7b8c..13420fb9f33b53aadc4e8066391352418c45fab7 100644 (file)
@@ -95,6 +95,8 @@ struct http_client *http_client_init(const struct http_client_settings *set)
        client->set.dns_client = set->dns_client;
        client->set.dns_client_socket_path =
                p_strdup_empty(pool, set->dns_client_socket_path);
+       client->set.dns_ttl_msecs = (set->dns_ttl_msecs == 0 ?
+               HTTP_CLIENT_DEFAULT_DNS_TTL_MSECS : set->dns_ttl_msecs);
        client->set.user_agent = p_strdup_empty(pool, set->user_agent);
        client->set.rawlog_dir = p_strdup_empty(pool, set->rawlog_dir);
        client->set.ssl_ca_dir = p_strdup(pool, set->ssl_ca_dir);
index 6fdc21462703671024f5f9d5014182fa2ce2a1be..1f2dc61fa0a413644a181a76f33b67eb9259bbc9 100644 (file)
@@ -22,6 +22,7 @@ struct http_client_settings {
           c) Otherwise, blocking gethostbyname() lookups are used. */
        struct dns_client *dns_client;
        const char *dns_client_socket_path;
+       unsigned int dns_ttl_msecs;
 
        /* ssl configuration */
        const char *ssl_ca_dir, *ssl_ca_file, *ssl_ca;