]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MINOR] tcp: add support for the defer_accept bind option
authorWilly Tarreau <w@1wt.eu>
Tue, 13 Oct 2009 05:34:14 +0000 (07:34 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 13 Oct 2009 05:34:14 +0000 (07:34 +0200)
This can ensure that data is readily available on a socket when
we accept it, but a bug in the kernel ignores the timeout so the
socket can remain pending as long as the client does not talk.
Use with care.

doc/configuration.txt
include/types/protocols.h
src/cfgparse.c
src/proto_tcp.c

index c0131e8786408e6961371343627d3cb36f0b16e0..2a34d225d22595f55a21fb6e3ba72fd43da61d67 100644 (file)
@@ -1132,6 +1132,7 @@ bind [<address>]:<port> [, ...] mss <maxseg>
 bind [<address>]:<port> [, ...] transparent
 bind [<address>]:<port> [, ...] id <id>
 bind [<address>]:<port> [, ...] name <name>
+bind [<address>]:<port> [, ...] defer_accept
   Define one or several listening addresses and/or ports in a frontend.
   May be used in sections :   defaults | frontend | listen | backend
                                   no   |    yes   |   yes  |   no
@@ -1182,6 +1183,20 @@ bind [<address>]:<port> [, ...] name <name>
                   the specified port. This keyword is available only when
                   HAProxy is built with USE_LINUX_TPROXY=1.
 
+    defer_accept  is an optional keyword which is supported only on certain
+                  Linux kernels. It states that a connection will only be
+                  accepted once some data arrive on it, or at worst after the
+                  first retransmit. This should be used only on protocols for
+                  which the client talks first (eg: HTTP). It can slightly
+                  improve performance by ensuring that most of the request is
+                  already available when the connection is accepted. On the
+                  other hand, it will not be able to detect connections which
+                  don't talk. It is important to note that this option is
+                  broken in all kernels up to 2.6.31, as the connection is
+                  never accepted until the client talks. This can cause issues
+                  with front firewalls which would see an established
+                  connection while the proxy will only see it in SYN_RECV.
+
   It is possible to specify a list of address:port combinations delimited by
   commas. The frontend will then listen on all of these addresses. There is no
   fixed limit to the number of addresses and ports which can be listened on in
index a776cb339ae57c70b429cd69a6fa26c26c7085f4..66c742b0933f76b472e44ea0bbbadeda1438417d 100644 (file)
@@ -71,6 +71,7 @@
 #define LI_O_NOLINGER  0x0001  /* disable linger on this socket */
 #define LI_O_FOREIGN   0x0002  /* permit listening on foreing addresses */
 #define LI_O_NOQUICKACK        0x0004  /* disable quick ack of immediate data (linux) */
+#define LI_O_DEF_ACCEPT        0x0008  /* wait up to 1 second for data before accepting */
 
 /* The listener will be directly referenced by the fdtab[] which holds its
  * socket. The listener provides the protocol-specific accept() function to
index ace99313b27518029b0a24206ce9e0779f3b56d4..fd5c626e3deecc16cd0d5c21690ce4c2f1a0058e 100644 (file)
@@ -1143,6 +1143,24 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                                goto out;
 #endif
                        }
+
+                       if (!strcmp(args[cur_arg], "defer-accept")) { /* wait for some data for 1 second max before doing accept */
+#ifdef TCP_DEFER_ACCEPT
+                               struct listener *l;
+
+                               for (l = curproxy->listen; l != last_listen; l = l->next)
+                                       l->options |= LI_O_DEF_ACCEPT;
+
+                               cur_arg ++;
+                               continue;
+#else
+                               Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n",
+                                     file, linenum, args[0], args[cur_arg]);
+                               err_code |= ERR_ALERT | ERR_FATAL;
+                               goto out;
+#endif
+                       }
+
                        if (!strcmp(args[cur_arg], "transparent")) { /* transparently bind to these addresses */
 #ifdef CONFIG_HAP_LINUX_TPROXY
                                struct listener *l;
@@ -1212,7 +1230,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                                continue;
                        }
 
-                       Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'name', 'id', 'mss' and 'interface' options.\n",
+                       Alert("parsing [%s:%d] : '%s' only supports the 'transparent', 'defer-accept', 'name', 'id', 'mss' and 'interface' options.\n",
                              file, linenum, args[0]);
                        err_code |= ERR_ALERT | ERR_FATAL;
                        goto out;
index 7bb5d7e218fa527e34d71682aa644ee50151d71a..884016f5afc0ea4bd2137472110055017c4aa8a0 100644 (file)
@@ -519,6 +519,16 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen)
                        err |= ERR_WARN;
                }
        }
+#endif
+#if defined(TCP_DEFER_ACCEPT)
+       if (listener->options & LI_O_DEF_ACCEPT) {
+               /* defer accept by up to one second */
+               int accept_delay = 1;
+               if (setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &accept_delay, sizeof(accept_delay)) == -1) {
+                       msg = "cannot enable DEFER_ACCEPT";
+                       err |= ERR_WARN;
+               }
+       }
 #endif
        if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
                err |= ERR_RETRYABLE | ERR_ALERT;