]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
login-proxy: If proxy destination is known to be down, fail immediately.
authorTimo Sirainen <tss@iki.fi>
Wed, 12 Aug 2009 22:11:15 +0000 (18:11 -0400)
committerTimo Sirainen <tss@iki.fi>
Wed, 12 Aug 2009 22:11:15 +0000 (18:11 -0400)
We'll use simple rules:

1. If connection to destination server failed more recently than it
succeeded AND there is currently at least one client trying to connect to
it, fail immediately without even trying to connect.

2. Whenever a connection to destination server fails because of timeout or
some connect failure AND last successful connection to server was before our
connect() started, update the "last failed" timestamp.

With these rules there are no unnecessary connect() attempts or waits to
servers that are down. When the server does come back up, it's noticed
immediately.

--HG--
branch : HEAD

src/login-common/Makefile.am
src/login-common/login-proxy.c
src/login-common/login-proxy.h
src/login-common/main.c

index 4f475856baf2a030a1dc712ade08b8f43acd1219..6440090f23c0502c4aff154261f2b32f88d2cac4 100644 (file)
@@ -11,6 +11,7 @@ liblogin_la_SOURCES = \
        client-common.c \
        client-common-auth.c \
        login-proxy.c \
+       login-proxy-state.c \
        login-settings.c \
        main.c \
        sasl-server.c \
@@ -24,6 +25,7 @@ liblogin_la_LIBADD = \
 noinst_HEADERS = \
        client-common.h \
        login-proxy.h \
+       login-proxy-state.h \
        login-settings.h \
        common.h \
        sasl-server.h \
index 6b224c1d2e10788f4ba051bf8bd758b22b35a898..9b9b864f83ef8c17787e18187f8c160163ba2f1c 100644 (file)
@@ -6,9 +6,11 @@
 #include "ostream.h"
 #include "llist.h"
 #include "str-sanitize.h"
+#include "time-util.h"
 #include "master-service.h"
 #include "client-common.h"
 #include "ssl-proxy.h"
+#include "login-proxy-state.h"
 #include "login-proxy.h"
 
 #define MAX_PROXY_INPUT_SIZE 4096
@@ -25,7 +27,9 @@ struct login_proxy {
        struct ip_addr ip;
        struct ssl_proxy *ssl_proxy;
 
+       struct timeval created;
        struct timeout *to;
+       struct login_proxy_record *state_rec;
 
        char *host, *user;
        unsigned int port;
@@ -38,6 +42,7 @@ struct login_proxy {
        unsigned int disconnecting:1;
 };
 
+static struct login_proxy_state *proxy_state;
 static struct login_proxy *login_proxies = NULL;
 
 static void server_input(struct login_proxy *proxy)
@@ -129,6 +134,19 @@ static void proxy_plain_connected(struct login_proxy *proxy)
                io_add(proxy->server_fd, IO_READ, proxy_prelogin_input, proxy);
 }
 
+static void proxy_fail_connect(struct login_proxy *proxy)
+{
+       if (timeval_cmp(&proxy->created, &proxy->state_rec->last_success) < 0) {
+               /* there was a successful connection done since we started
+                  connecting. perhaps this is just a temporary one-off
+                  failure. */
+       } else {
+               proxy->state_rec->last_failure = ioloop_timeval;
+       }
+       proxy->state_rec->num_waiting_connections--;
+       proxy->state_rec = NULL;
+}
+
 static void proxy_wait_connect(struct login_proxy *proxy)
 {
        int err;
@@ -137,9 +155,13 @@ static void proxy_wait_connect(struct login_proxy *proxy)
        if (err != 0) {
                i_error("proxy: connect(%s, %u) failed: %s",
                        proxy->host, proxy->port, strerror(err));
+               proxy_fail_connect(proxy);
                 login_proxy_free(&proxy);
                return;
        }
+       proxy->state_rec->last_success = ioloop_timeval;
+       proxy->state_rec->num_waiting_connections--;
+       proxy->state_rec = NULL;
 
        if (proxy->to != NULL)
                timeout_remove(&proxy->to);
@@ -159,6 +181,7 @@ static void proxy_wait_connect(struct login_proxy *proxy)
 static void proxy_connect_timeout(struct login_proxy *proxy)
 {
        i_error("proxy: connect(%s, %u) timed out", proxy->host, proxy->port);
+       proxy_fail_connect(proxy);
        login_proxy_free(&proxy);
 }
 
@@ -168,6 +191,7 @@ login_proxy_new(struct client *client, const struct login_proxy_settings *set,
                proxy_callback_t *callback, void *context)
 {
        struct login_proxy *proxy;
+       struct login_proxy_record *rec;
        struct ip_addr ip;
        int fd;
 
@@ -182,6 +206,13 @@ login_proxy_new(struct client *client, const struct login_proxy_settings *set,
                return NULL;
        }
 
+       rec = login_proxy_state_get(proxy_state, &ip);
+       if (timeval_cmp(&rec->last_failure, &rec->last_success) > 0 &&
+           rec->num_waiting_connections != 0) {
+               /* the server is down. fail immediately */
+              return NULL;
+       }
+
        fd = net_connect_ip(&ip, set->port, NULL);
        if (fd < 0) {
                i_error("proxy(%s): connect(%s, %u) failed: %m",
@@ -190,6 +221,7 @@ login_proxy_new(struct client *client, const struct login_proxy_settings *set,
        }
 
        proxy = i_new(struct login_proxy, 1);
+       proxy->created = ioloop_timeval;
        proxy->host = i_strdup(set->host);
        proxy->user = i_strdup(client->virtual_user);
        proxy->port = set->port;
@@ -208,6 +240,9 @@ login_proxy_new(struct client *client, const struct login_proxy_settings *set,
 
        proxy->ip = client->ip;
        proxy->client_fd = -1;
+
+       proxy->state_rec = rec;
+       rec->num_waiting_connections++;
        return proxy;
 }
 
@@ -225,6 +260,11 @@ void login_proxy_free(struct login_proxy **_proxy)
        if (proxy->to != NULL)
                timeout_remove(&proxy->to);
 
+       if (proxy->state_rec != NULL)
+               proxy->state_rec->num_waiting_connections--;
+       if (proxy->to != NULL)
+               timeout_remove(&proxy->to);
+
        if (proxy->server_io != NULL)
                io_remove(&proxy->server_io);
        if (proxy->server_input != NULL)
@@ -390,6 +430,11 @@ int login_proxy_starttls(struct login_proxy *proxy)
        return 0;
 }
 
+void login_proxy_init(void)
+{
+       proxy_state = login_proxy_state_init();
+}
+
 void login_proxy_deinit(void)
 {
        struct login_proxy *proxy;
@@ -398,4 +443,5 @@ void login_proxy_deinit(void)
                proxy = login_proxies;
                login_proxy_free(&proxy);
        }
+       login_proxy_state_deinit(&proxy_state);
 }
index 7d09fff8cdb1fcf7088fe4dffca0dee93d7a1ea2..f9b5a6e5880db569beaa83a55d0a68c91ce00275 100644 (file)
@@ -61,6 +61,7 @@ unsigned int login_proxy_get_port(const struct login_proxy *proxy) ATTR_PURE;
 enum login_proxy_ssl_flags
 login_proxy_get_ssl_flags(const struct login_proxy *proxy) ATTR_PURE;
 
+void login_proxy_init(void);
 void login_proxy_deinit(void);
 
 #endif
index 9f91313c76c530be277eb76305c721f9b5a7fa40..9137f484fb78024e139392c5546a6f72d8056f25 100644 (file)
@@ -129,6 +129,7 @@ static void main_init(void)
         auth_client_set_connect_notify(auth_client, auth_connect_notify, NULL);
 
        clients_init();
+       login_proxy_init();
        master_auth_init(master_service);
 }