]> 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)
committerGitLab <gitlab@git.dovecot.net>
Wed, 2 Nov 2016 11:42:18 +0000 (13:42 +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 bfa88bca9fb9349546c224cd1324959b7e7d9fe2..227d72ea9601404cd6fdfd711bc9208b7771acea 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 4f13542aeea24790a93fe4f7095b916237b12b0b..2d9022c4d0fa271c1a1d654f1644594138b6cc68 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 00a40bc0f7e2d018e3a765a7e5d06acbc2be2871..1ce5275645196d540d9528883809204bc2c7e007 100644 (file)
@@ -96,6 +96,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);
 
index 0ad2d3cd97af2dac62efca4a33d5a585c18384e9..0228503667d7d59f7f542a4f75b692d613b671e1 100644 (file)
@@ -24,6 +24,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;
 
        const struct ssl_iostream_settings *ssl;