Support TCP_DEFER_ACCEPT sockopts for listeners
authorMark Ellzey <mark.thomas@mandiant.com>
Tue, 14 Feb 2012 23:04:52 +0000 (18:04 -0500)
committerNick Mathewson <nickm@torproject.org>
Thu, 16 Feb 2012 02:19:28 +0000 (21:19 -0500)
A listening socket can be enabled with the sockopt
TCP_DEFER_ACCEPT. This informs the kernel to not call the user-land
accept() until real data has been written to the socket.

A new flag LEV_OPT_DEFERRED_ACCEPT has been introduced to
automatically set this option. Optionally
evutil_make_tcp_listen_socket_deferred() can be called manually.

(Tweaked slightly by nickm.)

configure.in
evutil.c
include/event2/listener.h
include/event2/util.h
listener.c

index 0bf5b5c6e69c0813b13cecd1113c2e2a61afd1e1..fce6ff52a9f198e3cbb25a41fd700fa145159584 100644 (file)
@@ -192,6 +192,7 @@ AC_CHECK_HEADERS([ \
   netdb.h \
   netinet/in.h \
   netinet/in6.h \
+  netinet/tcp.h \
   poll.h \
   port.h \
   stdarg.h \
index bc81a093cc9077d04e80301a6d174aa4362e6ac5..c340b54a18e3db19ae1afea093e002ebebbc213a 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -64,6 +64,9 @@
 #ifdef _EVENT_HAVE_NETINET_IN6_H
 #include <netinet/in6.h>
 #endif
+#ifdef _EVENT_HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
 #ifdef _EVENT_HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
@@ -364,6 +367,20 @@ evutil_make_listen_socket_reuseable(evutil_socket_t sock)
 #endif
 }
 
+int
+evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock)
+{
+#if defined(_EVENT_HAVE_NETINET_TCP_H) && defined(TCP_DEFER_ACCEPT)
+       int one = 1;
+
+       /* TCP_DEFER_ACCEPT tells the kernel to call defer accept() only after data
+        * has arrived and ready to read */ 
+       return setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, &one,
+               (ev_socklen_t)sizeof(one)); 
+#endif
+       return 0;
+}
+
 int
 evutil_make_socket_closeonexec(evutil_socket_t fd)
 {
index 14d1aec1a59f74b51a72b8870741228a94ba52c8..6c3bd775eb90b44c491723b400acc098f4390206 100644 (file)
@@ -72,6 +72,12 @@ typedef void (*evconnlistener_errorcb)(struct evconnlistener *, void *);
 /** Flag: Indicates that the listener should be created in disabled
  * state. Use evconnlistener_enable() to enable it later. */
 #define LEV_OPT_DISABLED               (1u<<5)
+/** Flag: Indicates that the listener should defer accept() until data is
+ * available, if possible.  Ignored on platforms that do not support this.
+ *
+ * This option can help performance for protocols where the client transmits
+ * immediately after connecting. */
+#define LEV_OPT_DEFERRED_ACCEPT                (1u<<6)
 
 /**
    Allocate a new evconnlistener object to listen for incoming TCP connections
index e5e0bc47959c91a5d74365dc43d99faa687c4ca5..d56e7446ada035297070cf72f49975888c7eb77d 100644 (file)
@@ -336,6 +336,18 @@ int evutil_make_socket_closeonexec(evutil_socket_t sock);
 int evutil_closesocket(evutil_socket_t sock);
 #define EVUTIL_CLOSESOCKET(s) evutil_closesocket(s)
 
+/** Do platform-specific operations, if possible, to make a tcp listener
+ *  socket defer accept()s until there is data to read.
+ *  
+ *  Not all platforms support this.  You don't want to do this for every
+ *  listener socket: only the ones that implement a protocol where the
+ *  client transmits before the server needs to respond.
+ *
+ *  @param sock The listening socket to to make deferred
+ *  @return 0 on success (whether the operation is supported or not),
+ *       -1 on failure
+*/ 
+int evutil_make_tcp_listen_socket_deferred(evutil_socket_t sock);
 
 #ifdef _WIN32
 /** Return the most recent socket error.  Not idempotent on all platforms. */
index a24ba0734b0f411f5fd34769162d75489dfc0d2d..3f6b44f957b70d9b83a5743f04f95dc6d532ffcc 100644 (file)
@@ -232,6 +232,10 @@ evconnlistener_new_bind(struct event_base *base, evconnlistener_cb cb,
                evutil_make_listen_socket_reuseable(fd);
        }
 
+       if (flags & LEV_OPT_DEFERRED_ACCEPT) {
+               evutil_make_tcp_listen_socket_deferred(fd);
+       }
+
        if (sa) {
                if (bind(fd, sa, socklen)<0) {
                        evutil_closesocket(fd);