Add support for TCP_DEFER_ACCEPT and TCP_QUICKACK
Motivation: When using the native transport have support for TCP_DEFER_ACCEPT or / and TCP_QUICKACK can be useful. Modifications: - Add support for TCP_DEFER_ACCEPT and TCP_QUICKACK - Ad unit tests Result: TCP_DEFER_ACCEPT and TCP_QUICKACK are supported now. Conflicts: transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
This commit is contained in:
parent
a47c03c3d7
commit
5317c7b648
@ -582,6 +582,14 @@ static void netty_unix_socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, ji
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
|
||||
}
|
||||
|
||||
static void netty_unix_socket_setTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static void netty_unix_socket_setTcpQuickAck(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
static jint netty_unix_socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) {
|
||||
@ -641,6 +649,22 @@ static jint netty_unix_socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_unix_socket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
static jint netty_unix_socket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
// JNI Registered Methods End
|
||||
|
||||
// JNI Method Registration Table Begin
|
||||
@ -669,13 +693,17 @@ static const JNINativeMethod fixed_method_table[] = {
|
||||
{ "setKeepAlive", "(II)V", (void *) netty_unix_socket_setKeepAlive },
|
||||
{ "setTcpCork", "(II)V", (void *) netty_unix_socket_setTcpCork },
|
||||
{ "setSoLinger", "(II)V", (void *) netty_unix_socket_setSoLinger },
|
||||
{ "setTcpDeferAccept", "(II)V", (void *) netty_unix_socket_setTcpDeferAccept },
|
||||
{ "setTcpQuickAck", "(II)V", (void *) netty_unix_socket_setTcpQuickAck },
|
||||
{ "isKeepAlive", "(I)I", (void *) netty_unix_socket_isKeepAlive },
|
||||
{ "isTcpNoDelay", "(I)I", (void *) netty_unix_socket_isTcpNoDelay },
|
||||
{ "getReceiveBufferSize", "(I)I", (void *) netty_unix_socket_getReceiveBufferSize },
|
||||
{ "getSendBufferSize", "(I)I", (void *) netty_unix_socket_getSendBufferSize },
|
||||
{ "isTcpCork", "(I)I", (void *) netty_unix_socket_isTcpCork },
|
||||
{ "getSoLinger", "(I)I", (void *) netty_unix_socket_getSoLinger },
|
||||
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError }
|
||||
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError },
|
||||
{ "getTcpDeferAccept", "(I)I", (void *) netty_unix_socket_getTcpDeferAccept },
|
||||
{ "isTcpQuickAck", "(I)I", (void *) netty_unix_socket_isTcpQuickAck }
|
||||
};
|
||||
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);
|
||||
|
||||
|
@ -32,6 +32,8 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
|
||||
public static final ChannelOption<Integer> TCP_USER_TIMEOUT = valueOf("TCP_USER_TIMEOUT");
|
||||
public static final ChannelOption<Boolean> IP_FREEBIND = valueOf("IP_FREEBIND");
|
||||
public static final ChannelOption<Integer> TCP_FASTOPEN = valueOf("TCP_FASTOPEN");
|
||||
public static final ChannelOption<Integer> TCP_DEFER_ACCEPT = valueOf("TCP_DEFER_ACCEPT");
|
||||
public static final ChannelOption<Integer> TCP_QUICKACK = valueOf("TCP_QUICKACK");
|
||||
|
||||
public static final ChannelOption<DomainSocketReadMode> DOMAIN_SOCKET_READ_MODE =
|
||||
valueOf("DOMAIN_SOCKET_READ_MODE");
|
||||
|
@ -38,7 +38,8 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
|
||||
@Override
|
||||
public Map<ChannelOption<?>, Object> getOptions() {
|
||||
return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND);
|
||||
return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND,
|
||||
EpollChannelOption.TCP_DEFER_ACCEPT);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -50,6 +51,9 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
if (option == EpollChannelOption.IP_FREEBIND) {
|
||||
return (T) Boolean.valueOf(isFreeBind());
|
||||
}
|
||||
if (option == EpollChannelOption.TCP_DEFER_ACCEPT) {
|
||||
return (T) Integer.valueOf(getTcpDeferAccept());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@ -65,6 +69,8 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value;
|
||||
setTcpMd5Sig(m);
|
||||
} else if (option == EpollChannelOption.TCP_DEFER_ACCEPT) {
|
||||
setTcpDeferAccept((Integer) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
@ -194,4 +200,19 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
|
||||
Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1: 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code TCP_DEFER_ACCEPT} option on the socket. See {@code man 7 tcp} for more details.
|
||||
*/
|
||||
public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) {
|
||||
channel.fd().setTcpDeferAccept(deferAccept);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a positive value if <a href="http://linux.die.net/man/7/tcp">TCP_DEFER_ACCEPT</a> is enabled.
|
||||
*/
|
||||
public int getTcpDeferAccept() {
|
||||
return channel.fd().getTcpDeferAccept();
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ 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_MD5SIG, EpollChannelOption.TCP_QUICKACK);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@ -99,6 +99,9 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
if (option == EpollChannelOption.TCP_USER_TIMEOUT) {
|
||||
return (T) Integer.valueOf(getTcpUserTimeout());
|
||||
}
|
||||
if (option == EpollChannelOption.TCP_QUICKACK) {
|
||||
return (T) Boolean.valueOf(isTcpQuickAck());
|
||||
}
|
||||
return super.getOption(option);
|
||||
}
|
||||
|
||||
@ -138,6 +141,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
@SuppressWarnings("unchecked")
|
||||
final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value;
|
||||
setTcpMd5Sig(m);
|
||||
} else if (option == EpollChannelOption.TCP_QUICKACK) {
|
||||
setTcpQuickAck((Boolean) value);
|
||||
} else {
|
||||
return super.setOption(option, value);
|
||||
}
|
||||
@ -323,7 +328,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Set the {@code TCP_MD5SIG} option on the socket. See {@code linux/tcp.h} for more details.
|
||||
* Keys can only be set on, not read to prevent a potential leak, as they are confidential.
|
||||
* Allowing them being read would mean anyone with access to the channel could get them.
|
||||
@ -333,6 +338,23 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@code TCP_QUICKACK} option on the socket. See <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a>
|
||||
* for more details.
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) {
|
||||
channel.fd().setTcpQuickAck(quickAck);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a> is enabled,
|
||||
* {@code false} otherwise.
|
||||
*/
|
||||
public boolean isTcpQuickAck() {
|
||||
return channel.fd().isTcpQuickAck();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowHalfClosure() {
|
||||
return allowHalfClosure;
|
||||
|
@ -263,6 +263,14 @@ public final class Socket extends FileDescriptor {
|
||||
return getSoLinger(intValue());
|
||||
}
|
||||
|
||||
public int getTcpDeferAccept() {
|
||||
return getTcpDeferAccept(intValue());
|
||||
}
|
||||
|
||||
public boolean isTcpQuickAck() {
|
||||
return isTcpQuickAck(intValue()) != 0;
|
||||
}
|
||||
|
||||
public int getSoError() {
|
||||
return getSoError(intValue());
|
||||
}
|
||||
@ -291,6 +299,14 @@ public final class Socket extends FileDescriptor {
|
||||
setSoLinger(intValue(), soLinger);
|
||||
}
|
||||
|
||||
public void setTcpDeferAccept(int deferAccept) {
|
||||
setTcpDeferAccept(intValue(), deferAccept);
|
||||
}
|
||||
|
||||
public void setTcpQuickAck(boolean quickAck) {
|
||||
setTcpQuickAck(intValue(), quickAck ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Socket{" +
|
||||
@ -357,6 +373,8 @@ public final class Socket extends FileDescriptor {
|
||||
private static native int isTcpCork(int fd);
|
||||
private static native int getSoLinger(int fd);
|
||||
private static native int getSoError(int fd);
|
||||
private static native int getTcpDeferAccept(int fd);
|
||||
private static native int isTcpQuickAck(int fd);
|
||||
|
||||
private static native void setKeepAlive(int fd, int keepAlive);
|
||||
private static native void setReceiveBufferSize(int fd, int receiveBufferSize);
|
||||
@ -364,4 +382,6 @@ public final class Socket extends FileDescriptor {
|
||||
private static native void setTcpNoDelay(int fd, int tcpNoDelay);
|
||||
private static native void setTcpCork(int fd, int tcpCork);
|
||||
private static native void setSoLinger(int fd, int soLinger);
|
||||
private static native void setTcpDeferAccept(int fd, int deferAccept);
|
||||
private static native void setTcpQuickAck(int fd, int quickAck);
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2016 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you under the Apache License,
|
||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class EpollServerSocketChannelConfigTest {
|
||||
|
||||
private static EventLoopGroup group;
|
||||
private static EpollServerSocketChannel ch;
|
||||
|
||||
@BeforeClass
|
||||
public static void before() {
|
||||
group = new EpollEventLoopGroup(1);
|
||||
ServerBootstrap bootstrap = new ServerBootstrap();
|
||||
ch = (EpollServerSocketChannel) bootstrap.group(group)
|
||||
.channel(EpollServerSocketChannel.class)
|
||||
.childHandler(new ChannelInboundHandlerAdapter())
|
||||
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
try {
|
||||
ch.close().syncUninterruptibly();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTcpDeferAccept() {
|
||||
ch.config().setTcpDeferAccept(0);
|
||||
assertEquals(0, ch.config().getTcpDeferAccept());
|
||||
ch.config().setTcpDeferAccept(10);
|
||||
// The returned value may be bigger then what we set.
|
||||
// See http://www.spinics.net/lists/netdev/msg117330.html
|
||||
assertTrue(10 <= ch.config().getTcpDeferAccept());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReusePort() {
|
||||
ch.config().setReusePort(false);
|
||||
assertFalse(ch.config().isReusePort());
|
||||
ch.config().setReusePort(true);
|
||||
assertTrue(ch.config().isReusePort());
|
||||
}
|
||||
}
|
@ -47,7 +47,11 @@ public class EpollSocketChannelConfigTest {
|
||||
|
||||
@AfterClass
|
||||
public static void after() {
|
||||
group.shutdownGracefully();
|
||||
try {
|
||||
ch.close().syncUninterruptibly();
|
||||
} finally {
|
||||
group.shutdownGracefully();
|
||||
}
|
||||
}
|
||||
|
||||
private long randLong(long min, long max) {
|
||||
@ -110,4 +114,12 @@ public class EpollSocketChannelConfigTest {
|
||||
ch.config().setTcpCork(true);
|
||||
assertTrue(ch.config().isTcpCork());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTcpQickAck() {
|
||||
ch.config().setTcpQuickAck(false);
|
||||
assertFalse(ch.config().isTcpQuickAck());
|
||||
ch.config().setTcpQuickAck(true);
|
||||
assertTrue(ch.config().isTcpQuickAck());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user