changeset 21047:3a481a8f2ff2

lib-http: client: Implemented host name lookup TTL. 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.
author Stephan Bosch <stephan@dovecot.fi>
date Fri, 16 Sep 2016 20:22:17 +0200
parents 5b3dc5fef64c
children 4180c1dd0aa2
files 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
diffstat 5 files changed, 103 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-http/http-client-host.c	Thu Sep 15 22:49:54 2016 +0200
+++ b/src/lib-http/http-client-host.c	Fri Sep 16 20:22:17 2016 +0200
@@ -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 @@
 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 @@
 	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 @@
 	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 @@
 		(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 @@
 		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
--- a/src/lib-http/http-client-private.h	Thu Sep 15 22:49:54 2016 +0200
+++ b/src/lib-http/http-client-private.h	Fri Sep 16 20:22:17 2016 +0200
@@ -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 @@
 	/* 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_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_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
--- a/src/lib-http/http-client-queue.c	Thu Sep 15 22:49:54 2016 +0200
+++ b/src/lib-http/http-client-queue.c	Fri Sep 16 20:22:17 2016 +0200
@@ -216,6 +216,37 @@
 }
 
 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)
 {
 	struct http_client_host *host = queue->host;
@@ -258,10 +289,23 @@
 		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_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);
--- a/src/lib-http/http-client.c	Thu Sep 15 22:49:54 2016 +0200
+++ b/src/lib-http/http-client.c	Fri Sep 16 20:22:17 2016 +0200
@@ -95,6 +95,8 @@
 	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);
--- a/src/lib-http/http-client.h	Thu Sep 15 22:49:54 2016 +0200
+++ b/src/lib-http/http-client.h	Fri Sep 16 20:22:17 2016 +0200
@@ -22,6 +22,7 @@
 	   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;