Mercurial > dovecot > core-2.2
view src/lib-http/http-client-host.c @ 22656:1789bf2a1e01
director: Make sure HOST-RESET-USERS isn't used with max_moving_users=0
The reset command would just hang in that case. doveadm would never have
sent this, so this is just an extra sanity check.
author | Timo Sirainen <timo.sirainen@dovecot.fi> |
---|---|
date | Sun, 05 Nov 2017 23:51:56 +0200 |
parents | 1b4a0735b158 |
children | cb108f786fb4 |
line wrap: on
line source
/* Copyright (c) 2013-2017 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "str.h" #include "hash.h" #include "array.h" #include "llist.h" #include "ioloop.h" #include "istream.h" #include "ostream.h" #include "time-util.h" #include "dns-lookup.h" #include "http-response-parser.h" #include "http-client-private.h" #define HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS 100 /* * Logging */ static inline void http_client_host_debug(struct http_client_host *host, const char *format, ...) ATTR_FORMAT(2, 3); static inline void http_client_host_debug(struct http_client_host *host, const char *format, ...) { va_list args; if (host->client->set.debug) { va_start(args, format); i_debug("http-client: host %s: %s", host->name, t_strdup_vprintf(format, args)); va_end(args); } } /* * Host */ static void http_client_host_lookup_failure(struct http_client_host *host, const char *error) { struct http_client_queue *const *queue_idx; error = t_strdup_printf("Failed to lookup host %s: %s", host->name, error); array_foreach_modifiable(&host->queues, queue_idx) { http_client_queue_host_lookup_failure(*queue_idx, error); } http_client_host_check_idle(host); } 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; } http_client_host_debug(host, "DNS lookup successful; got %d IPs", result->ips_count); i_assert(result->ips_count > 0); i_free(host->ips); 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); 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) { requests += http_client_queue_host_lookup_done(*queue_idx); } if (requests == 0 && host->client->ioloop != NULL) io_loop_stop(host->client->ioloop); } static void http_client_host_lookup (struct http_client_host *host) { struct http_client *client = host->client; struct dns_lookup_settings dns_set; struct ip_addr *ips; int ret; i_assert(!host->explicit_ip); i_assert(host->dns_lookup == NULL); if (client->set.dns_client != NULL) { http_client_host_debug(host, "Performing asynchronous DNS lookup"); (void)dns_client_lookup(client->set.dns_client, host->name, http_client_host_dns_callback, host, &host->dns_lookup); } else if (client->set.dns_client_socket_path != NULL) { http_client_host_debug(host, "Performing asynchronous DNS lookup"); i_zero(&dns_set); dns_set.dns_client_socket_path = client->set.dns_client_socket_path; if (client->set.connect_timeout_msecs > 0) dns_set.timeout_msecs = client->set.connect_timeout_msecs; else if (client->set.request_timeout_msecs > 0) dns_set.timeout_msecs = client->set.request_timeout_msecs; else { dns_set.timeout_msecs = HTTP_CLIENT_DEFAULT_DNS_LOOKUP_TIMEOUT_MSECS; } (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)); return; } http_client_host_debug(host, "DNS lookup successful; got %d IPs", ips_count); i_free(host->ips); host->ips_count = ips_count; host->ips = i_new(struct ip_addr, ips_count); memcpy(host->ips, ips, ips_count * sizeof(*ips)); 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; if (host->to_idle != NULL) 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 (struct http_client *client) { struct http_client_host *host; // FIXME: limit the maximum number of inactive cached hosts host = i_new(struct http_client_host, 1); host->client = client; i_array_init(&host->queues, 4); DLLIST_PREPEND(&client->hosts_list, host); return host; } struct http_client_host *http_client_host_get (struct http_client *client, const struct http_url *host_url) { struct http_client_host *host; if (host_url == NULL) { host = client->unix_host; if (host == NULL) { host = http_client_host_create(client); host->name = i_strdup("[unix]"); host->unix_local = TRUE; client->unix_host = host; http_client_host_debug(host, "Unix host created"); } } else { const char *hostname = host_url->host_name; struct ip_addr ip = host_url->host_ip; host = hash_table_lookup(client->hosts, hostname); if (host == NULL) { host = http_client_host_create(client); host->name = i_strdup(hostname); hostname = host->name; hash_table_insert(client->hosts, hostname, host); if (ip.family != 0 || net_addr2ip(host->name, &ip) == 0) { host->ips_count = 1; host->ips = i_new(struct ip_addr, host->ips_count); host->ips[0] = ip; host->explicit_ip = TRUE; } http_client_host_debug(host, "Host created"); } } return host; } void http_client_host_submit_request(struct http_client_host *host, struct http_client_request *req) { struct http_client_queue *queue; struct http_client_peer_addr addr; const char *error; req->host = host; http_client_request_get_peer_addr(req, &addr); if (http_client_peer_addr_is_https(&addr) && host->client->ssl_ctx == NULL) { if (http_client_init_ssl_ctx(host->client, &error) < 0) { http_client_request_error(&req, HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, error); return; } } /* add request to queue (grouped by tcp port) */ queue = http_client_queue_create(host, &addr); http_client_queue_submit_request(queue, req); /* cancel host idle timeout */ if (host->to_idle != NULL) timeout_remove(&host->to_idle); if (host->unix_local) { http_client_queue_connection_setup(queue); return; } /* start DNS lookup if necessary */ if (host->ips_count == 0 && host->dns_lookup == NULL) http_client_host_lookup(host); /* make a connection if we have an IP already */ if (host->ips_count > 0) http_client_queue_connection_setup(queue); } void http_client_host_free(struct http_client_host **_host) { struct http_client_host *host = *_host; struct http_client_queue *const *queue_idx; ARRAY_TYPE(http_client_queue) queues; const char *hostname = host->name; http_client_host_debug(host, "Host destroy"); if (host->to_idle != NULL) timeout_remove(&host->to_idle); DLLIST_REMOVE(&host->client->hosts_list, host); if (host == host->client->unix_host) host->client->unix_host = NULL; else hash_table_remove(host->client->hosts, hostname); if (host->dns_lookup != NULL) dns_lookup_abort(&host->dns_lookup); /* drop request queues */ t_array_init(&queues, array_count(&host->queues)); array_copy(&queues.arr, 0, &host->queues.arr, 0, array_count(&host->queues)); array_clear(&host->queues); array_foreach(&queues, queue_idx) { http_client_queue_free(*queue_idx); } array_free(&host->queues); i_free(host->ips); i_free(host->name); i_free(host); } static void http_client_host_idle_timeout(struct http_client_host *host) { http_client_host_debug(host, "Idle host timed out"); http_client_host_free(&host); } void http_client_host_check_idle(struct http_client_host *host) { struct http_client_queue *const *queue_idx; unsigned int requests = 0; int timeout = 0; if (host->to_idle != NULL) return; array_foreach(&host->queues, queue_idx) { requests += http_client_queue_requests_active(*queue_idx); } if (requests > 0) return; if (!host->unix_local && !host->explicit_ip && host->ips_timeout.tv_sec > 0) { timeout = timeval_diff_msecs (&host->ips_timeout, &ioloop_timeval); } if (timeout <= HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS) timeout = HTTP_CLIENT_HOST_MINIMUM_IDLE_TIMEOUT_MSECS; host->to_idle = timeout_add_short(timeout, http_client_host_idle_timeout, host); http_client_host_debug(host, "Host is idle (timeout = %u msecs)", timeout); } void http_client_host_switch_ioloop(struct http_client_host *host) { struct http_client_queue *const *queue_idx; if (host->dns_lookup != NULL && host->client->set.dns_client == NULL) dns_lookup_switch_ioloop(host->dns_lookup); array_foreach(&host->queues, queue_idx) http_client_queue_switch_ioloop(*queue_idx); if (host->to_idle != NULL) host->to_idle = io_loop_move_timeout(&host->to_idle); }