diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index ceda160a2eb..98f3414cd09 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "netty_kqueue_bsdsocket.h" @@ -259,14 +260,70 @@ static jshort netty_kqueue_native_noteDisconnected(JNIEnv* env, jclass clazz) { return NOTE_DISCONNECTED; } -static jint netty_kqueue_bsdsocket_connectResumeOnReadWrite(JNIEnv *env) { +static jint netty_kqueue_bsdsocket_connectResumeOnReadWrite(JNIEnv *env, jclass clazz) { return CONNECT_RESUME_ON_READ_WRITE; } -static jint netty_kqueue_bsdsocket_connectDataIdempotent(JNIEnv *env) { +static jint netty_kqueue_bsdsocket_connectDataIdempotent(JNIEnv *env, jclass clazz) { return CONNECT_DATA_IDEMPOTENT; } +static jint netty_kqueue_bsdsocket_fastOpenClient(JNIEnv *env, jclass clazz) { +#ifdef __APPLE__ + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if ((value & 2) == 2) { + return 1; + } + return 0; +#else + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen.client_enable", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if (value) { + return 1; + } + return 0; +#endif // __APPLE__ +} + +static jint netty_kqueue_bsdsocket_fastOpenServer(JNIEnv *env, jclass clazz) { +#ifdef __APPLE__ + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if ((value & 1) == 1) { + return 1; + } + return 0; +#else + unsigned int value = 0; + size_t len = sizeof(value); + if (sysctlbyname("net.inet.tcp.fastopen.server_enable", &value, &len, NULL, 0) != 0) { + int err = errno; + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "sysctlbyname failed", err); + return -err; + } + if (value) { + return 1; + } + return 0; +#endif // __APPLE__ +} + // JNI Method Registration Table Begin static const JNINativeMethod statically_referenced_fixed_method_table[] = { { "evfiltRead", "()S", (void *) netty_kqueue_native_evfiltRead }, @@ -284,7 +341,9 @@ static const JNINativeMethod statically_referenced_fixed_method_table[] = { { "noteConnReset", "()S", (void *) netty_kqueue_native_noteConnReset }, { "noteDisconnected", "()S", (void *) netty_kqueue_native_noteDisconnected }, { "connectResumeOnReadWrite", "()I", (void *) netty_kqueue_bsdsocket_connectResumeOnReadWrite }, - { "connectDataIdempotent", "()I", (void *) netty_kqueue_bsdsocket_connectDataIdempotent } + { "connectDataIdempotent", "()I", (void *) netty_kqueue_bsdsocket_connectDataIdempotent }, + { "fastOpenClient", "()I", (void *) netty_kqueue_bsdsocket_fastOpenClient }, + { "fastOpenServer", "()I", (void *) netty_kqueue_bsdsocket_fastOpenServer } }; static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]); static const JNINativeMethod fixed_method_table[] = { diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java index 83cb496251c..c5ba0bb05a3 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueue.java @@ -15,6 +15,7 @@ */ package io.netty.channel.kqueue; +import io.netty.channel.ChannelOption; import io.netty.channel.unix.FileDescriptor; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.UnstableApi; @@ -82,6 +83,26 @@ public static Throwable unavailabilityCause() { return UNAVAILABILITY_CAUSE; } + /** + * Returns {@code true} if the kqueue native transport is both {@linkplain #isAvailable() available} and supports + * {@linkplain ChannelOption#TCP_FASTOPEN_CONNECT client-side TCP FastOpen}. + * + * @return {@code true} if it's possible to use client-side TCP FastOpen via kqueue, otherwise {@code false}. + */ + public static boolean isTcpFastOpenClientSideAvailable() { + return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_CLIENT; + } + + /** + * Returns {@code true} if the kqueue native transport is both {@linkplain #isAvailable() available} and supports + * {@linkplain ChannelOption#TCP_FASTOPEN server-side TCP FastOpen}. + * + * @return {@code true} if it's possible to use server-side TCP FastOpen via kqueue, otherwise {@code false}. + */ + public static boolean isTcpFastOpenServerSideAvailable() { + return isAvailable() && Native.IS_SUPPORTING_TCP_FASTOPEN_SERVER; + } + private KQueue() { } } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java index e6926d65faf..e4a4a41ce8e 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods.java @@ -50,4 +50,8 @@ private KQueueStaticallyReferencedJniMethods() { } // Flags for connectx(2) static native int connectResumeOnReadWrite(); static native int connectDataIdempotent(); + + // Sysctl values. + static native int fastOpenClient(); + static native int fastOpenServer(); } diff --git a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java index 5f4c77fbc2b..20a09f49b14 100644 --- a/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java +++ b/transport-native-kqueue/src/main/java/io/netty/channel/kqueue/Native.java @@ -42,6 +42,8 @@ import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltSock; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltUser; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltWrite; +import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenClient; +import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenServer; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteConnReset; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteDisconnected; import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteReadClosed; @@ -110,6 +112,8 @@ public void run() { private static final int CONNECT_RESUME_ON_READ_WRITE = connectResumeOnReadWrite(); private static final int CONNECT_DATA_IDEMPOTENT = connectDataIdempotent(); static final int CONNECT_TCP_FASTOPEN = CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT; + static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT = isSupportingFastOpenClient(); + static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER = isSupportingFastOpenServer(); static FileDescriptor newKQueue() { return new FileDescriptor(kqueueCreate()); @@ -160,6 +164,24 @@ private static void loadNativeLibrary() { } } + private static boolean isSupportingFastOpenClient() { + try { + return fastOpenClient() == 1; + } catch (Exception e) { + logger.debug("Failed to probe fastOpenClient sysctl, assuming client-side TCP FastOpen cannot be used.", e); + } + return false; + } + + private static boolean isSupportingFastOpenServer() { + try { + return fastOpenServer() == 1; + } catch (Exception e) { + logger.debug("Failed to probe fastOpenServer sysctl, assuming server-side TCP FastOpen cannot be used.", e); + } + return false; + } + private Native() { // utility }