[MINOR] tcp: add support for the defer_accept bind option

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.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index c0131e8..2a34d22 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1132,6 +1132,7 @@
 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 @@
                   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
diff --git a/include/types/protocols.h b/include/types/protocols.h
index a776cb3..66c742b 100644
--- a/include/types/protocols.h
+++ b/include/types/protocols.h
@@ -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
diff --git a/src/cfgparse.c b/src/cfgparse.c
index ace9931..fd5c626 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1143,6 +1143,24 @@
 				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 @@
 				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;
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 7bb5d7e..884016f 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -520,6 +520,16 @@
 		}
 	}
 #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;
 		msg = "cannot bind socket";