diff --git a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c index 70211ebfcd..28d81faebc 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c @@ -39,6 +39,11 @@ #define TCP_FASTOPEN 23 #endif +// TCP_FASTOPEN_CONNECT is defined in linux 4.11. We define this here so older kernels can compile. +#ifndef TCP_FASTOPEN_CONNECT +#define TCP_FASTOPEN_CONNECT 30 +#endif + // TCP_NOTSENT_LOWAT is defined in linux 3.12. We define this here so older kernels can compile. #ifndef TCP_NOTSENT_LOWAT #define TCP_NOTSENT_LOWAT 25 @@ -68,6 +73,10 @@ static void netty_epoll_linuxsocket_setTcpFastOpen(JNIEnv* env, jclass clazz, ji netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN, &optval, sizeof(optval)); } +static void netty_epoll_linuxsocket_setTcpFastOpenConnect(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &optval, sizeof(optval)); +} + static void netty_epoll_linuxsocket_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) { netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)); } @@ -245,6 +254,14 @@ static jint netty_epoll_linuxsocket_isTcpQuickAck(JNIEnv* env, jclass clazz, jin return optval; } +static jint netty_epoll_linuxsocket_isTcpFastOpenConnect(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + static jint netty_epoll_linuxsocket_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd) { int optval; if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)) == -1) { @@ -275,6 +292,8 @@ static const JNINativeMethod fixed_method_table[] = { { "getTcpNotSentLowAt", "(I)I", (void *) netty_epoll_linuxsocket_getTcpNotSentLowAt }, { "isTcpQuickAck", "(I)I", (void *) netty_epoll_linuxsocket_isTcpQuickAck }, { "setTcpFastOpen", "(II)V", (void *) netty_epoll_linuxsocket_setTcpFastOpen }, + { "setTcpFastOpenConnect", "(II)V", (void *) netty_epoll_linuxsocket_setTcpFastOpenConnect }, + { "isTcpFastOpenConnect", "(I)I", (void *) netty_epoll_linuxsocket_isTcpFastOpenConnect }, { "setTcpKeepIdle", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIdle }, { "setTcpKeepIntvl", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepIntvl }, { "setTcpKeepCnt", "(II)V", (void *) netty_epoll_linuxsocket_setTcpKeepCnt }, diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java index b606c951a1..3ade823275 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java @@ -32,6 +32,8 @@ public final class EpollChannelOption extends UnixChannelOption { public static final ChannelOption IP_FREEBIND = valueOf("IP_FREEBIND"); public static final ChannelOption IP_TRANSPARENT = valueOf("IP_TRANSPARENT"); public static final ChannelOption TCP_FASTOPEN = valueOf(EpollChannelOption.class, "TCP_FASTOPEN"); + public static final ChannelOption TCP_FASTOPEN_CONNECT = + valueOf(EpollChannelOption.class, "TCP_FASTOPEN_CONNECT"); public static final ChannelOption TCP_DEFER_ACCEPT = ChannelOption.valueOf(EpollChannelOption.class, "TCP_DEFER_ACCEPT"); public static final ChannelOption TCP_QUICKACK = valueOf(EpollChannelOption.class, "TCP_QUICKACK"); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java index 98e5000ef0..b63af6c19f 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannelConfig.java @@ -60,7 +60,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS, ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT, EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL, - EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK, EpollChannelOption.IP_TRANSPARENT); + EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK, EpollChannelOption.IP_TRANSPARENT, + EpollChannelOption.TCP_FASTOPEN_CONNECT); } @SuppressWarnings("unchecked") @@ -114,6 +115,9 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement if (option == EpollChannelOption.IP_TRANSPARENT) { return (T) Boolean.valueOf(isIpTransparent()); } + if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { + return (T) Boolean.valueOf(isTcpFastOpenConnect()); + } return super.getOption(option); } @@ -157,6 +161,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement setTcpMd5Sig(m); } else if (option == EpollChannelOption.TCP_QUICKACK) { setTcpQuickAck((Boolean) value); + } else if (option == EpollChannelOption.TCP_FASTOPEN_CONNECT) { + setTcpFastOpenConnect((Boolean) value); } else { return super.setOption(option, value); } @@ -515,6 +521,32 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement } } + /** + * Set the {@code TCP_FASTOPEN_CONNECT} option on the socket. Requires Linux kernel 4.11 or later. + * See + * this commit + * for more details. + */ + public EpollSocketChannelConfig setTcpFastOpenConnect(boolean fastOpenConnect) { + try { + channel.socket.setTcpFastOpenConnect(fastOpenConnect); + return this; + } catch (IOException e) { + throw new ChannelException(e); + } + } + + /** + * Returns {@code true} if {@code TCP_FASTOPEN_CONNECT} is enabled, {@code false} otherwise. + */ + public boolean isTcpFastOpenConnect() { + try { + return channel.socket.isTcpFastOpenConnect(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + @Override public boolean isAllowHalfClosure() { return allowHalfClosure; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java index 4aa622e08a..96dce58658 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/LinuxSocket.java @@ -55,6 +55,14 @@ final class LinuxSocket extends Socket { setTcpFastOpen(intValue(), tcpFastopenBacklog); } + void setTcpFastOpenConnect(boolean tcpFastOpenConnect) throws IOException { + setTcpFastOpenConnect(intValue(), tcpFastOpenConnect ? 1 : 0); + } + + boolean isTcpFastOpenConnect() throws IOException { + return isTcpFastOpenConnect(intValue()) != 0; + } + void setTcpKeepIdle(int seconds) throws IOException { setTcpKeepIdle(intValue(), seconds); } @@ -156,12 +164,14 @@ final class LinuxSocket extends Socket { private static native int isIpTransparent(int fd) throws IOException; private static native void getTcpInfo(int fd, long[] array) throws IOException; private static native PeerCredentials getPeerCredentials(int fd) throws IOException; + private static native int isTcpFastOpenConnect(int fd) throws IOException; private static native void setTcpDeferAccept(int fd, int deferAccept) throws IOException; private static native void setTcpQuickAck(int fd, int quickAck) throws IOException; private static native void setTcpCork(int fd, int tcpCork) throws IOException; private static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException; private static native void setTcpFastOpen(int fd, int tcpFastopenBacklog) throws IOException; + private static native void setTcpFastOpenConnect(int fd, int tcpFastOpenConnect) throws IOException; private static native void setTcpKeepIdle(int fd, int seconds) throws IOException; private static native void setTcpKeepIntvl(int fd, int seconds) throws IOException; private static native void setTcpKeepCnt(int fd, int probes) throws IOException;