diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index c3a0e62553..f98f228160 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -26,7 +26,6 @@ #include #include #include -#include // SOL_TCP definition #include #include #include @@ -35,25 +34,18 @@ #include #include #include "io_netty_channel_epoll_Native.h" -#include "exception_helper.h" +#include "netty_unix_filedescriptor.h" +#include "netty_unix_socket.h" +#include "netty_unix_errors.h" // 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 #endif - -#ifndef _KERNEL_FASTOPEN -#define _KERNEL_FASTOPEN -// conditional define for TCP_FASTOPEN mostly on ubuntu +// TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile. #ifndef TCP_FASTOPEN -#define TCP_FASTOPEN 23 -#endif - -// conditional define for SOL_TCP mostly on ubuntu -#ifndef SOL_TCP -#define SOL_TCP 6 -#endif +#define TCP_FASTOPEN 23 #endif /** @@ -67,9 +59,7 @@ #define MAX_EPOLL_TIMEOUT_MSEC (35*60*1000) // optional -extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak)); extern int epoll_create1(int flags) __attribute__((weak)); -extern int pipe2(int pipefd[2], int flags) __attribute__((weak)); #ifdef IO_NETTY_SENDMMSG_NOT_FOUND extern int sendmmsg(int sockfd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags) __attribute__((weak)); @@ -83,11 +73,6 @@ struct mmsghdr { #endif // Those are initialized in the init(...) method and cached for performance reasons -jmethodID updatePosId = NULL; -jmethodID posId = NULL; -jmethodID limitId = NULL; -jfieldID posFieldId = NULL; -jfieldID limitFieldId = NULL; jfieldID fileChannelFieldId = NULL; jfieldID transferedFieldId = NULL; jfieldID fdFieldId = NULL; @@ -99,59 +84,7 @@ jfieldID packetPortFieldId = NULL; jfieldID packetMemoryAddressFieldId = NULL; jfieldID packetCountFieldId = NULL; -jmethodID inetSocketAddrMethodId = NULL; -jmethodID datagramSocketAddrMethodId = NULL; -jclass runtimeExceptionClass = NULL; -jclass channelExceptionClass = NULL; -jclass ioExceptionClass = NULL; -jclass closedChannelExceptionClass = NULL; -jmethodID closedChannelExceptionMethodId = NULL; -jclass inetSocketAddressClass = NULL; -jclass datagramSocketAddressClass = NULL; -jclass nativeDatagramPacketClass = NULL; -jclass netUtilClass = NULL; -jmethodID netUtilClassIpv4PreferredMethodId = NULL; - -static int socketType; -static const char* ip4prefix = "::ffff:"; - // util methods -void throwRuntimeException(JNIEnv* env, char* message) { - (*env)->ThrowNew(env, runtimeExceptionClass, message); -} - -void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { - char* allocatedMessage = exceptionMessage(message, errorNumber); - (*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage); - free(allocatedMessage); -} - -void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { - char* allocatedMessage = exceptionMessage(message, errorNumber); - (*env)->ThrowNew(env, channelExceptionClass, allocatedMessage); - free(allocatedMessage); -} - -void throwIOException(JNIEnv* env, char* message) { - (*env)->ThrowNew(env, ioExceptionClass, message); -} - -void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { - char* allocatedMessage = exceptionMessage(message, errorNumber); - (*env)->ThrowNew(env, ioExceptionClass, allocatedMessage); - free(allocatedMessage); -} - -void throwClosedChannelException(JNIEnv* env) { - jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); - (*env)->Throw(env, exception); -} - -void throwOutOfMemoryError(JNIEnv* env) { - jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); - (*env)->ThrowNew(env, exceptionClass, ""); -} - static int getSysctlValue(const char * property, int* returnValue) { int rc = -1; FILE *fd=fopen(property, "r"); @@ -166,23 +99,6 @@ static int getSysctlValue(const char * property, int* returnValue) { return rc; } -/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */ -char* exceptionMessage(char* msg, int error) { - if (error < 0) { - // some functions return negative values - // and it's hard to keep track of when to send -error and when not - // this will just take care when things are forgotten - // what would generate a proper error - error = error * -1; - } - //strerror is returning a constant, so no need to free anything coming from strerror - char* err = strerror(error); - char* result = malloc(strlen(msg) + strlen(err) + 1); - strcpy(result, msg); - strcat(result, err); - return result; -} - static inline jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags) { uint32_t events = flags; struct epoll_event ev = { @@ -192,224 +108,6 @@ static inline jint epollCtl(JNIEnv* env, jint efd, int op, jint fd, jint flags) return epoll_ctl(efd, op, fd, &ev); } - -static inline jint getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) { - int code; - code = getsockopt(fd, level, optname, optval, &optlen); - if (code == 0) { - return 0; - } - int err = errno; - throwChannelExceptionErrorNo(env, "getsockopt() failed: ", err); - return code; -} - -static inline int setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len) { - int rc = setsockopt(fd, level, optname, optval, len); - if (rc < 0) { - int err = errno; - throwChannelExceptionErrorNo(env, "setsockopt() failed: ", err); - } - return rc; -} - -jobject createInetSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr) { - char ipstr[INET6_ADDRSTRLEN]; - int port; - jstring ipString; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* s = (struct sockaddr_in*) addr; - port = ntohs(s->sin_port); - inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); - ipString = (*env)->NewStringUTF(env, ipstr); - } else { - struct sockaddr_in6* s = (struct sockaddr_in6*) addr; - port = ntohs(s->sin6_port); - inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); - if (strncasecmp(ipstr, ip4prefix, 7) == 0) { - // IPv4-mapped-on-IPv6. - // Cut of ::ffff: prefix to workaround performance issues when parsing these - // addresses in InetAddress.getByName(...). - // - // See https://github.com/netty/netty/issues/2867 - ipString = (*env)->NewStringUTF(env, &ipstr[7]); - } else { - ipString = (*env)->NewStringUTF(env, ipstr); - } - } - jobject socketAddr = (*env)->NewObject(env, inetSocketAddressClass, inetSocketAddrMethodId, ipString, port); - return socketAddr; -} - - -static inline int addressLength(const struct sockaddr_storage* addr) { - if (addr->ss_family == AF_INET) { - return 8; - } else { - struct sockaddr_in6* s = (struct sockaddr_in6*) addr; - if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00 - && s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00 - && s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) { - // IPv4-mapped-on-IPv6 - return 8; - } else { - return 24; - } - } -} - -static inline void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr, jbyteArray bArray, int offset, int len) { - int port; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* s = (struct sockaddr_in*) addr; - port = ntohs(s->sin_port); - - // Encode address and port into the array - unsigned char a[4]; - a[0] = port >> 24; - a[1] = port >> 16; - a[2] = port >> 8; - a[3] = port; - (*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &s->sin_addr.s_addr); - (*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a); - } else { - struct sockaddr_in6* s = (struct sockaddr_in6*) addr; - port = ntohs(s->sin6_port); - - if (len == 8) { - // IPv4-mapped-on-IPv6 - // Encode port into the array and write it into the jbyteArray - unsigned char a[4]; - a[0] = port >> 24; - a[1] = port >> 16; - a[2] = port >> 8; - a[3] = port; - - // we only need the last 4 bytes for mapped address - (*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &(s->sin6_addr.s6_addr[12])); - (*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a); - } else { - // Encode scopeid and port into the array - unsigned char a[8]; - a[0] = s->sin6_scope_id >> 24; - a[1] = s->sin6_scope_id >> 16; - a[2] = s->sin6_scope_id >> 8; - a[3] = s->sin6_scope_id; - a[4] = port >> 24; - a[5] = port >> 16; - a[6] = port >> 8; - a[7] = port; - - (*env)->SetByteArrayRegion(env, bArray, offset, 16, (jbyte*) &(s->sin6_addr.s6_addr)); - (*env)->SetByteArrayRegion(env, bArray, offset + 16, 8, (jbyte*) &a); - } - } -} - -static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) { - int len = addressLength(addr); - jbyteArray bArray = (*env)->NewByteArray(env, len); - - initInetSocketAddressArray(env, addr, bArray, 0, len); - return bArray; -} - -static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr, int len) { - char ipstr[INET6_ADDRSTRLEN]; - int port; - jstring ipString; - if (addr->ss_family == AF_INET) { - struct sockaddr_in* s = (struct sockaddr_in*) addr; - port = ntohs(s->sin_port); - inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); - ipString = (*env)->NewStringUTF(env, ipstr); - } else { - struct sockaddr_in6* s = (struct sockaddr_in6*) addr; - port = ntohs(s->sin6_port); - inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); - - if (strncasecmp(ipstr, ip4prefix, 7) == 0) { - // IPv4-mapped-on-IPv6. - // Cut of ::ffff: prefix to workaround performance issues when parsing these - // addresses in InetAddress.getByName(...). - // - // See https://github.com/netty/netty/issues/2867 - ipString = (*env)->NewStringUTF(env, &ipstr[7]); - } else { - ipString = (*env)->NewStringUTF(env, ipstr); - } - } - jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len); - return socketAddr; -} - -static int init_sockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr) { - uint16_t port = htons((uint16_t) jport); - // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like - // to not do a memory copy here. This is ok as we not do any blocking action here anyway. - // This is important as the VM may suspend GC for the time! - jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); - if (addressBytes == NULL) { - // No memory left ?!?!? - throwOutOfMemoryError(env); - } - if (socketType == AF_INET6) { - struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr; - ip6addr->sin6_family = AF_INET6; - ip6addr->sin6_port = port; - - if (scopeId != 0) { - ip6addr->sin6_scope_id = (uint32_t) scopeId; - } - memcpy(&(ip6addr->sin6_addr.s6_addr), addressBytes, 16); - } else { - struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr; - ipaddr->sin_family = AF_INET; - ipaddr->sin_port = port; - memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); - } - - (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); - return 0; -} - -static int socket_type(JNIEnv* env) { - jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId); - - if (ipv4Preferred) { - // User asked to use ipv4 explicitly. - return AF_INET; - } - int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0); - if (fd == -1) { - if (errno == EAFNOSUPPORT) { - return AF_INET; - } - return AF_INET6; - } else { - close(fd); - return AF_INET6; - } -} - -static int init_in_addr(JNIEnv* env, jbyteArray address, struct in_addr* addr) { - // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like - // to not do a memory copy here. This is ok as we not do any blocking action here anyway. - // This is important as the VM may suspend GC for the time! - jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); - if (addressBytes == NULL) { - // No memory left ?!?!? - throwOutOfMemoryError(env); - return -1; - } - if (socketType == AF_INET6) { - memcpy(addr, addressBytes, 16); - } else { - memcpy(addr, addressBytes + 12, 4); - } - (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); - return 0; -} // util methods end jint JNI_OnLoad(JavaVM* vm, void* reserved) { @@ -417,159 +115,15 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } else { - jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException"); - if (localRuntimeExceptionClass == NULL) { - // pending exception... + if (netty_unix_errors_JNI_OnLoad(env) == JNI_ERR) { return JNI_ERR; } - runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass); - if (runtimeExceptionClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); + if (netty_unix_filedescriptor_JNI_OnLoad(env) == JNI_ERR) { return JNI_ERR; } - - jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException"); - if (localChannelExceptionClass == NULL) { - // pending exception... + if (netty_unix_socket_JNI_OnLoad(env) == JNI_ERR) { return JNI_ERR; } - channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass); - if (channelExceptionClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - - jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" ); - if (localNetUtilClass == NULL) { - // pending exception... - return JNI_ERR; - } - netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass); - if (netUtilClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - - netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" ); - if (netUtilClassIpv4PreferredMethodId == NULL) { - // position method was not found.. something is wrong so bail out - throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()"); - return JNI_ERR; - } - // cache classes that are used within other jni methods for performance reasons - jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException"); - if (localClosedChannelExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass); - if (closedChannelExceptionClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "", "()V"); - if (closedChannelExceptionMethodId == NULL) { - throwRuntimeException(env, "failed to get method ID: ClosedChannelException.()"); - return JNI_ERR; - } - - jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException"); - if (localIoExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass); - if (ioExceptionClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - - jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress"); - if (localIoExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass); - if (inetSocketAddressClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - - jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/epoll/EpollDatagramChannel$DatagramSocketAddress"); - if (localDatagramSocketAddressClass == NULL) { - // pending exception... - return JNI_ERR; - } - datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass); - if (datagramSocketAddressClass == NULL) { - // out-of-memory! - throwOutOfMemoryError(env); - return JNI_ERR; - } - - void* mem = malloc(1); - if (mem == NULL) { - throwOutOfMemoryError(env); - return JNI_ERR; - } - jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); - if (directBuffer == NULL) { - free(mem); - - throwOutOfMemoryError(env); - return JNI_ERR; - } - - jclass cls = (*env)->GetObjectClass(env, directBuffer); - - // Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if - // it is not possible to obtain the position and limit using the fields directly. - posId = (*env)->GetMethodID(env, cls, "position", "()I"); - if (posId == NULL) { - free(mem); - - // position method was not found.. something is wrong so bail out - throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()"); - return JNI_ERR; - } - - limitId = (*env)->GetMethodID(env, cls, "limit", "()I"); - if (limitId == NULL) { - free(mem); - - // limit method was not found.. something is wrong so bail out - throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()"); - return JNI_ERR; - } - updatePosId = (*env)->GetMethodID(env, cls, "position", "(I)Ljava/nio/Buffer;"); - if (updatePosId == NULL) { - free(mem); - - // position method was not found.. something is wrong so bail out - throwRuntimeException(env, "failed to fet method ID: ByteBuffer.position(int)"); - return JNI_ERR; - } - // Try to get the ids of the position and limit fields. We later then check if we was able - // to find them and if so use them get the position and limit of the buffer. This is - // much faster then call back into java via (*env)->CallIntMethod(...). - posFieldId = (*env)->GetFieldID(env, cls, "position", "I"); - if (posFieldId == NULL) { - // this is ok as we can still use the method so just clear the exception - (*env)->ExceptionClear(env); - } - limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I"); - if (limitFieldId == NULL) { - // this is ok as we can still use the method so just clear the exception - (*env)->ExceptionClear(env); - } - - free(mem); jclass fileRegionCls = (*env)->FindClass(env, "io/netty/channel/DefaultFileRegion"); if (fileRegionCls == NULL) { @@ -578,12 +132,12 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;"); if (fileChannelFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file"); return JNI_ERR; } transferedFieldId = (*env)->GetFieldID(env, fileRegionCls, "transfered", "J"); if (transferedFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transfered"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transfered"); return JNI_ERR; } @@ -594,7 +148,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;"); if (fileDescriptorFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd"); return JNI_ERR; } @@ -605,22 +159,10 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { } fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I"); if (fdFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd"); return JNI_ERR; } - inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "", "(Ljava/lang/String;I)V"); - if (inetSocketAddrMethodId == NULL) { - throwRuntimeException(env, "failed to get method ID: InetSocketAddress.(String, int)"); - return JNI_ERR; - } - socketType = socket_type(env); - - datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", "(Ljava/lang/String;II)V"); - if (datagramSocketAddrMethodId == NULL) { - throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.(String, int, int)"); - return JNI_ERR; - } jclass nativeDatagramPacketCls = (*env)->FindClass(env, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket"); if (nativeDatagramPacketCls == NULL) { // pending exception... @@ -629,28 +171,28 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B"); if (packetAddrFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr"); return JNI_ERR; } packetScopeIdFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "scopeId", "I"); if (packetScopeIdFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId"); return JNI_ERR; } packetPortFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "port", "I"); if (packetPortFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port"); return JNI_ERR; } packetMemoryAddressFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "memoryAddress", "J"); if (packetMemoryAddressFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress"); return JNI_ERR; } packetCountFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "count", "I"); if (packetCountFieldId == NULL) { - throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count"); + netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count"); return JNI_ERR; } @@ -665,36 +207,18 @@ void JNI_OnUnload(JavaVM* vm, void* reserved) { return; } else { // delete global references so the GC can collect them - if (runtimeExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, runtimeExceptionClass); - } - if (channelExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, channelExceptionClass); - } - if (ioExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, ioExceptionClass); - } - if (closedChannelExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, closedChannelExceptionClass); - } - if (inetSocketAddressClass != NULL) { - (*env)->DeleteGlobalRef(env, inetSocketAddressClass); - } - if (datagramSocketAddressClass != NULL) { - (*env)->DeleteGlobalRef(env, datagramSocketAddressClass); - } - if (netUtilClass != NULL) { - (*env)->DeleteGlobalRef(env, netUtilClass); - } + netty_unix_errors_JNI_OnUnLoad(env); + netty_unix_filedescriptor_JNI_OnUnLoad(env); + netty_unix_socket_JNI_OnUnLoad(env); } } JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_eventFd(JNIEnv* env, jclass clazz) { - jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + jint eventFD = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); if (eventFD < 0) { int err = errno; - throwChannelExceptionErrorNo(env, "eventfd() failed: ", err); + netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd() failed: ", err); } return eventFD; } @@ -704,7 +228,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdWrite(JNIEnv* e if (eventFD < 0) { int err = errno; - throwChannelExceptionErrorNo(env, "eventfd_write() failed: ", err); + netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write() failed: ", err); } } @@ -713,7 +237,7 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_eventFdRead(JNIEnv* en if (eventfd_read(fd, &eventfd_t) != 0) { // something is serious wrong - throwRuntimeException(env, "eventfd_read() failed"); + netty_unix_errors_throwRuntimeException(env, "eventfd_read() failed"); } } @@ -728,9 +252,9 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (efd < 0) { int err = errno; if (epoll_create1) { - throwChannelExceptionErrorNo(env, "epoll_create1() failed: ", err); + netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create1() failed: ", err); } else { - throwChannelExceptionErrorNo(env, "epoll_create() failed: ", err); + netty_unix_errors_throwChannelExceptionErrorNo(env, "epoll_create() failed: ", err); } return efd; } @@ -738,7 +262,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCreate(JNIEnv* en if (fcntl(efd, F_SETFD, FD_CLOEXEC) < 0) { int err = errno; close(efd); - throwChannelExceptionErrorNo(env, "fcntl() failed: ", err); + netty_unix_errors_throwChannelExceptionErrorNo(env, "fcntl() failed: ", err); return err; } } @@ -792,91 +316,6 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_epollCtlDel0(JNIEnv* e return res; } -static inline jint _write(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { - ssize_t res; - int err; - do { - res = write(fd, buffer + pos, (size_t) (limit - pos)); - // keep on writing if it was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return (jint) res; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); - if (buffer == NULL) { - throwRuntimeException(env, "failed to get direct buffer address"); - return -1; - } - return _write(env, clazz, fd, buffer, pos, limit); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { - return _write(env, clazz, fd, (void*) address, pos, limit); -} - -static inline jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { - struct sockaddr_storage addr; - if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { - return -1; - } - - ssize_t res; - int err; - do { - res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_storage)); - // keep on writing if it was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return (jint) res; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendTo0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { - void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); - if (buffer == NULL) { - throwRuntimeException(env, "failed to get direct buffer address"); - return -1; - } - return _sendTo(env, fd, buffer, pos, limit, address, scopeId, port); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddress0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { - return _sendTo(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) { - struct sockaddr_storage addr; - - if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { - return -1; - } - - struct msghdr m; - m.msg_name = (void*) &addr; - m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage); - m.msg_iov = (struct iovec*) memoryAddress; - m.msg_iovlen = length; - - ssize_t res; - int err; - do { - res = sendmsg(fd, &m, 0); - // keep on writing if it was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return (jint) res; -} - JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg0(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len) { struct mmsghdr msg[len]; int i; @@ -891,7 +330,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg0(JNIEnv* env, jint scopeId = (*env)->GetIntField(env, packet, packetScopeIdFieldId); jint port = (*env)->GetIntField(env, packet, packetPortFieldId); - if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { + if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) { return -1; } @@ -915,290 +354,20 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_sendmmsg0(JNIEnv* env, return (jint) res; } -static inline jobject recvFrom0(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit) { - struct sockaddr_storage addr; - socklen_t addrlen = sizeof(addr); - ssize_t res; - int err; - - do { - res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, &addrlen); - // Keep on reading if we was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - if (err == EAGAIN || err == EWOULDBLOCK) { - // Nothing left to read - return NULL; - } - if (err == EBADF) { - throwClosedChannelException(env); - return NULL; - } - throwIOExceptionErrorNo(env, "recvfrom() failed: ", err); - return NULL; - } - - return createDatagramSocketAddress(env, &addr, res); -} - -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); - if (buffer == NULL) { - throwRuntimeException(env, "failed to get direct buffer address"); - return NULL; - } - - return recvFrom0(env, fd, buffer, pos, limit); -} - -JNIEXPORT jobject JNICALL Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { - return recvFrom0(env, fd, (void*) address, pos, limit); -} - -static inline jlong _writev(JNIEnv* env, jclass clazz, jint fd, struct iovec* iov, jint length) { - ssize_t res; - int err; - do { - res = writev(fd, iov, length); - // keep on writing if it was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return (jlong) res; -} - -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) { - struct iovec iov[length]; - int iovidx = 0; - int i; - int num = offset + length; - for (i = offset; i < num; i++) { - jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i); - jint pos; - // Get the current position using the (*env)->GetIntField if possible and fallback - // to slower (*env)->CallIntMethod(...) if needed - if (posFieldId == NULL) { - pos = (*env)->CallIntMethod(env, bufObj, posId, NULL); - } else { - pos = (*env)->GetIntField(env, bufObj, posFieldId); - } - jint limit; - // Get the current limit using the (*env)->GetIntField if possible and fallback - // to slower (*env)->CallIntMethod(...) if needed - if (limitFieldId == NULL) { - limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL); - } else { - limit = (*env)->GetIntField(env, bufObj, limitFieldId); - } - void* buffer = (*env)->GetDirectBufferAddress(env, bufObj); - if (buffer == NULL) { - throwRuntimeException(env, "failed to get direct buffer address"); - return -1; - } - iov[iovidx].iov_base = buffer + pos; - iov[iovidx].iov_len = (size_t) (limit - pos); - iovidx++; - - // Explicit delete local reference as otherwise the local references will only be released once the native method returns. - // Also there may be a lot of these and JNI specification only specify that 16 must be able to be created. - // - // See https://github.com/netty/netty/issues/2623 - (*env)->DeleteLocalRef(env, bufObj); - } - return _writev(env, clazz, fd, iov, length); -} - -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) { - struct iovec* iov = (struct iovec*) memoryAddress; - return _writev(env, clazz, fd, iov, length); -} - -static inline jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { - ssize_t res; - int err; - do { - res = read(fd, buffer + pos, (size_t) (limit - pos)); - // Keep on reading if we was interrupted - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return (jint) res; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { - void* buffer = (*env)->GetDirectBufferAddress(env, jbuffer); - if (buffer == NULL) { - throwRuntimeException(env, "failed to get direct buffer address"); - return -1; - } - return _read(env, clazz, fd, buffer, pos, limit); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { - return _read(env, clazz, fd, (void*) address, pos, limit); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd) { - if (close(fd) < 0) { - return -errno; - } - return 0; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) { - int mode; - if (read && write) { - mode = SHUT_RDWR; - } else if (read) { - mode = SHUT_RD; - } else if (write) { - mode = SHUT_WR; - } else { - return -EINVAL; - } - if (shutdown(fd, mode) < 0) { - return -errno; - } - return 0; -} - -static inline jint socket0(JNIEnv* env, jclass clazz, int type) { - int fd = socket(socketType, type | SOCK_NONBLOCK, 0); - if (fd == -1) { - return -errno; - } else if (socketType == AF_INET6) { - // Allow to listen /connect ipv4 and ipv6 - int optval = 0; - if (setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) { - // Something went wrong so close the fd and return here. setOption(...) itself throws the exception already. - close(fd); - return -1; - } - } - return fd; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz) { - return socket0(env, clazz, SOCK_DGRAM); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz) { - return socket0(env, clazz, SOCK_STREAM); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { - struct sockaddr_storage addr; - if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { - return -1; - } - - if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) { - return -errno; - } - return 0; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog) { - if (listen(fd, backlog) == -1) { - return -errno; - } - return 0; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { - struct sockaddr_storage addr; - if (init_sockaddr(env, address, scopeId, port, &addr) == -1) { - // A runtime exception was thrown - return -1; - } - - int res; - int err; - do { - res = connect(fd, (struct sockaddr*) &addr, sizeof(addr)); - } while (res == -1 && ((err = errno) == EINTR)); - - if (res < 0) { - return -err; - } - return 0; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd) { - // connect may be done - // return true if connection finished successfully - // return false if connection is still in progress - // throw exception if connection failed - int optval; - int res = getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)); - if (res != 0) { - // getOption failed - return -1; - } - if (optval == 0) { - // connect succeeded - return 0; - } - return -optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) { - jint socketFd; - int err; - struct sockaddr_storage addr; - socklen_t address_len = sizeof(addr); - - do { - if (accept4) { - socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC); - } else { - socketFd = accept(fd, (struct sockaddr*) &addr, &address_len); - } - } while (socketFd == -1 && ((err = errno) == EINTR)); - - if (socketFd == -1) { - return -err; - } - - int len = addressLength(&addr); - - // Fill in remote address details - (*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len); - initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len); - - if (accept4) { - return socketFd; - } else { - // accept4 was not present so need two more sys-calls ... - if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1) { - return -errno; - } - if (fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) { - return -errno; - } - } - return socketFd; -} - JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len) { jobject fileChannel = (*env)->GetObjectField(env, fileRegion, fileChannelFieldId); if (fileChannel == NULL) { - throwRuntimeException(env, "failed to get DefaultFileRegion.file"); + netty_unix_errors_throwRuntimeException(env, "failed to get DefaultFileRegion.file"); return -1; } jobject fileDescriptor = (*env)->GetObjectField(env, fileChannel, fileDescriptorFieldId); if (fileDescriptor == NULL) { - throwRuntimeException(env, "failed to get FileChannelImpl.fd"); + netty_unix_errors_throwRuntimeException(env, "failed to get FileChannelImpl.fd"); return -1; } jint srcFd = (*env)->GetIntField(env, fileDescriptor, fdFieldId); if (srcFd == -1) { - throwRuntimeException(env, "failed to get FileDescriptor.fd"); + netty_unix_errors_throwRuntimeException(env, "failed to get FileDescriptor.fd"); return -1; } ssize_t res; @@ -1218,107 +387,53 @@ JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env return res; } -JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd) { - socklen_t len; - struct sockaddr_storage addr; - - len = sizeof addr; - if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) { - return NULL; - } - return createInetSocketAddressArray(env, &addr); -} - -JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd) { - socklen_t len; - struct sockaddr_storage addr; - - len = sizeof addr; - if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) { - return NULL; - } - return createInetSocketAddressArray(env, &addr); -} - JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpFastopen(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_TCP, TCP_FASTOPEN, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_FASTOPEN, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)); -} - -JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) { - struct linger solinger; - if (optval < 0) { - solinger.l_onoff = 0; - solinger.l_linger = 0; - } else { - solinger.l_onoff = 1; - solinger.l_linger = optval; - } - setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)); } JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)); } JNIEXPORT void Java_io_netty_channel_epoll_Native_setTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)); } JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz, jint fd, jint optval) { - setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval)); + netty_unix_socket_setOption(env, fd, IPPROTO_IP, IP_FREEBIND, &optval, sizeof(optval)); } JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1326,39 +441,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) { - return -1; - } - return optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) { - int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) { - return -1; - } - return optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) { - int optval; - if (getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) { - return -1; - } - return optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) { - int optval; - if (getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) { - return -1; - } - return optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd) { - int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1366,27 +449,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)) == -1) { - return -1; - } - return optval; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd) { - struct linger optval; - if (getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) { - return -1; - } - if (optval.l_onoff == 0) { - return -1; - } else { - return optval.l_linger; - } -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd) { - int optval; - if (getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NOTSENT_LOWAT, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1394,7 +457,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1402,7 +465,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_BROADCAST, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1410,7 +473,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* en JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPIDLE, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1418,7 +481,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPINTVL, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1426,7 +489,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_KEEPCNT, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1434,7 +497,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpUserTimeout(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1442,7 +505,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTcpUserTimeout(JNIE JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd) { int optval; - if (getOption(env, fd, IPPROTO_TCP, IP_FREEBIND, &optval, sizeof(optval)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, IP_FREEBIND, &optval, sizeof(optval)) == -1) { return -1; } return optval; @@ -1450,7 +513,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* e JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array) { struct tcp_info tcp_info; - if (getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) { + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_INFO, &tcp_info, sizeof(tcp_info)) == -1) { return; } unsigned int cArray[32]; @@ -1498,7 +561,7 @@ JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_kernelVersion(JNIEn return (*env)->NewStringUTF(env, name.release); } int err = errno; - throwRuntimeExceptionErrorNo(env, "uname() failed: ", err); + netty_unix_errors_throwRuntimeExceptionErrorNo(env, "uname() failed: ", err); return NULL; } @@ -1526,105 +589,6 @@ JNIEXPORT jboolean JNICALL Java_io_netty_channel_epoll_Native_isSupportingTcpFas return JNI_FALSE; } -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoENOTCONN(JNIEnv* env, jclass clazz) { - return ENOTCONN; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz) { - return EBADF; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz) { - return EPIPE; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoECONNRESET(JNIEnv* env, jclass clazz) { - return ECONNRESET; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz) { - return EAGAIN; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) { - return EWOULDBLOCK; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz) { - return EINPROGRESS; -} - -JNIEXPORT jstring JNICALL Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint error) { - char* err = strerror(error); - return (*env)->NewStringUTF(env, err); -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_socketDomain(JNIEnv* env, jclass clazz) { - int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); - if (fd == -1) { - return -errno; - } - return fd; -} - -// macro to calculate the length of a sockaddr_un struct for a given path length. -// see sys/un.h#SUN_LEN, this is modified to allow nul bytes -#define _UNIX_ADDR_LENGTH(path_len) (uintptr_t) (((struct sockaddr_un *) 0)->sun_path) + path_len - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) { - struct sockaddr_un addr; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - - jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0); - jint socket_path_len = (*env)->GetArrayLength(env, socketPath); - if (socket_path_len > sizeof(addr.sun_path)) { - socket_path_len = sizeof(addr.sun_path); - } - memcpy(addr.sun_path, socket_path, socket_path_len); - - if (unlink(socket_path) == -1 && errno != ENOENT) { - return -errno; - } - - int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len)); - (*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0); - - if (res == -1) { - return -errno; - } - return res; -} - -JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) { - struct sockaddr_un addr; - jint socket_path_len; - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - - jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0); - socket_path_len = (*env)->GetArrayLength(env, socketPath); - if (socket_path_len > sizeof(addr.sun_path)) { - socket_path_len = sizeof(addr.sun_path); - } - memcpy(addr.sun_path, socket_path, socket_path_len); - - int res; - int err; - do { - res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len)); - } while (res == -1 && ((err = errno) == EINTR)); - - (*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0); - - if (res < 0) { - return -err; - } - return 0; -} - JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd) { int socketFd; struct msghdr descriptorMessage = { 0 }; @@ -1737,36 +701,6 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_offsetofEpollData(JNIE return offsetof(struct epoll_event, data); } -JNIEXPORT jlong JNICALL Java_io_netty_channel_epoll_Native_pipe0(JNIEnv* env, jclass clazz) { - int fd[2]; - if (pipe2) { - // we can just use pipe2 and so save extra syscalls; - if (pipe2(fd, O_NONBLOCK) != 0) { - return -errno; - } - } else { - if (pipe(fd) == 0) { - if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) { - int err = errno; - close(fd[0]); - close(fd[1]); - return -err; - } - if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) { - int err = errno; - close(fd[0]); - close(fd[1]); - return -err; - } - } else { - return -errno; - } - } - - // encode the fds into a long - return (((long) fd[0]) << 32) | (fd[1] & 0xffffffffL); -} - JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len) { ssize_t res; int err; @@ -1804,7 +738,7 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_tcpMd5SigMaxKeyLen(JNI JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpMd5Sig0(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jbyteArray key) { struct sockaddr_storage addr; - if (init_sockaddr(env, address, scopeId, 0, &addr) == -1) { + if (netty_unix_socket_initSockaddr(env, address, scopeId, 0, &addr) == -1) { return; } @@ -1835,6 +769,6 @@ JNIEXPORT void JNICALL Java_io_netty_channel_epoll_Native_setTcpMd5Sig0(JNIEnv* } if (setsockopt(fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) < 0) { - throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno); + netty_unix_errors_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno); } } diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h index d4900ec3d3..58c6899c6c 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h @@ -41,48 +41,16 @@ jint Java_io_netty_channel_epoll_Native_epollWait0(JNIEnv* env, jclass clazz, ji jint Java_io_netty_channel_epoll_Native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags); jint Java_io_netty_channel_epoll_Native_epollCtlMod0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags); jint Java_io_netty_channel_epoll_Native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd, jint fd); -jint Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); -jint Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); -jlong Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length); -jlong Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length); -jint Java_io_netty_channel_epoll_Native_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); -jint Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); -jint Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port); + jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len); - -jint Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); -jint Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); -jobject Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); -jobject Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); -jint Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write); -jint Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_socketDomain(JNIEnv* env, jclass clazz); - -jint Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); -jint Java_io_netty_channel_epoll_Native_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address); jint Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd); - -jint Java_io_netty_channel_epoll_Native_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog); -jint Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); -jint Java_io_netty_channel_epoll_Native_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address); -jint Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress); jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len); -jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd); -jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd); + void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setTcpFastopen(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval); -void Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval); void Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval); @@ -92,19 +60,14 @@ void Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz, jint Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd); -jint Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd); +void Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array); jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz); @@ -113,15 +76,6 @@ jlong Java_io_netty_channel_epoll_Native_ssizeMax(JNIEnv* env, jclass clazz); jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz); jboolean Java_io_netty_channel_epoll_Native_isSupportingTcpFastopen(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoENOTCONN(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoECONNRESET(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz); -jint Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz); -jstring Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint err); - jint Java_io_netty_channel_epoll_Native_epollin(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_epollout(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_epollrdhup(JNIEnv* env, jclass clazz); @@ -130,7 +84,6 @@ jint Java_io_netty_channel_epoll_Native_epollerr(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_sizeofEpollEvent(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_offsetofEpollData(JNIEnv* env, jclass clazz); -jlong Java_io_netty_channel_epoll_Native_pipe0(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len); jint Java_io_netty_channel_epoll_Native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz); diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.c b/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.c new file mode 100644 index 0000000000..30df59b11d --- /dev/null +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.c @@ -0,0 +1,191 @@ +/* + * Copyright 2015 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. + */ +#include +#include +#include +#include "netty_unix_errors.h" +#include "io_netty_channel_unix_Errors.h" + +static jclass runtimeExceptionClass = NULL; +static jclass channelExceptionClass = NULL; +static jclass ioExceptionClass = NULL; +static jclass closedChannelExceptionClass = NULL; +static jmethodID closedChannelExceptionMethodId = NULL; + +/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */ +static char* exceptionMessage(char* msg, int error) { + if (error < 0) { + // some functions return negative values + // and it's hard to keep track of when to send -error and when not + // this will just take care when things are forgotten + // what would generate a proper error + error = error * -1; + } + //strerror is returning a constant, so no need to free anything coming from strerror + char* err = strerror(error); + char* result = malloc(strlen(msg) + strlen(err) + 1); + strcpy(result, msg); + strcat(result, err); + return result; +} + +// Exported C methods +void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message) { + (*env)->ThrowNew(env, runtimeExceptionClass, message); +} + +void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage); + free(allocatedMessage); +} + +void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, channelExceptionClass, allocatedMessage); + free(allocatedMessage); +} + +void netty_unix_errors_throwIOException(JNIEnv* env, char* message) { + (*env)->ThrowNew(env, ioExceptionClass, message); +} + +void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { + char* allocatedMessage = exceptionMessage(message, errorNumber); + (*env)->ThrowNew(env, ioExceptionClass, allocatedMessage); + free(allocatedMessage); +} + +void netty_unix_errors_throwClosedChannelException(JNIEnv* env) { + jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); + (*env)->Throw(env, exception); +} + +void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) { + jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + (*env)->ThrowNew(env, exceptionClass, ""); +} + +jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) { + jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException"); + if (localRuntimeExceptionClass == NULL) { + // pending exception... + return JNI_ERR; + } + runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass); + if (runtimeExceptionClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + + jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException"); + if (localChannelExceptionClass == NULL) { + // pending exception... + return JNI_ERR; + } + channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass); + if (channelExceptionClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + + // cache classes that are used within other jni methods for performance reasons + jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException"); + if (localClosedChannelExceptionClass == NULL) { + // pending exception... + return JNI_ERR; + } + closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass); + if (closedChannelExceptionClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "", "()V"); + if (closedChannelExceptionMethodId == NULL) { + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ClosedChannelException.()"); + return JNI_ERR; + } + + jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException"); + if (localIoExceptionClass == NULL) { + // pending exception... + return JNI_ERR; + } + ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass); + if (ioExceptionClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + + return JNI_VERSION_1_6; +} + +void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { + // delete global references so the GC can collect them + if (runtimeExceptionClass != NULL) { + (*env)->DeleteGlobalRef(env, runtimeExceptionClass); + runtimeExceptionClass = NULL; + } + if (channelExceptionClass != NULL) { + (*env)->DeleteGlobalRef(env, channelExceptionClass); + channelExceptionClass = NULL; + } + if (ioExceptionClass != NULL) { + (*env)->DeleteGlobalRef(env, ioExceptionClass); + ioExceptionClass = NULL; + } + if (closedChannelExceptionClass != NULL) { + (*env)->DeleteGlobalRef(env, closedChannelExceptionClass); + closedChannelExceptionClass = NULL; + } +} + +// Exported JNI Methods +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz) { + return ENOTCONN; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz) { + return EBADF; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz) { + return EPIPE; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz) { + return ECONNRESET; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz) { + return EAGAIN; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) { + return EWOULDBLOCK; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) { + return EINPROGRESS; +} + +JNIEXPORT jstring JNICALL Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error) { + return (*env)->NewStringUTF(env, strerror(error)); +} diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.h b/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.h new file mode 100644 index 0000000000..4e92ba2e6d --- /dev/null +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 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. + */ +#ifndef IO_NETTY_ERROR_UNIX_H_ +#define IO_NETTY_ERROR_UNIX_H_ + +#include + +// Exported JNI methods +jint Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz); +jstring Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error); + +#endif /* IO_NETTY_ERROR_UNIX_H_ */ diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.c b/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.c index 91978b202d..103b293900 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.c @@ -13,12 +13,126 @@ * License for the specific language governing permissions and limitations * under the License. */ -#include -#include #include #include +#include +#include +#include + +#include "netty_unix_errors.h" +#include "netty_unix_filedescriptor.h" #include "io_netty_channel_unix_FileDescriptor.h" +static jmethodID posId = NULL; +static jmethodID limitId = NULL; +static jfieldID posFieldId = NULL; +static jfieldID limitFieldId = NULL; + +// Optional external methods +extern int pipe2(int pipefd[2], int flags) __attribute__((weak)); + +static jint _write(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { + ssize_t res; + int err; + do { + res = write(fd, buffer + pos, (size_t) (limit - pos)); + // keep on writing if it was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return (jint) res; +} + +static jlong _writev(JNIEnv* env, jclass clazz, jint fd, struct iovec* iov, jint length) { + ssize_t res; + int err; + do { + res = writev(fd, iov, length); + // keep on writing if it was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return (jlong) res; +} + +static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) { + ssize_t res; + int err; + do { + res = read(fd, buffer + pos, (size_t) (limit - pos)); + // Keep on reading if we was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return (jint) res; +} + +jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env) { + void* mem = malloc(1); + if (mem == NULL) { + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); + if (directBuffer == NULL) { + free(mem); + + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { + free(mem); + + netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address"); + return JNI_ERR; + } + + jclass cls = (*env)->GetObjectClass(env, directBuffer); + + // Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if + // it is not possible to obtain the position and limit using the fields directly. + posId = (*env)->GetMethodID(env, cls, "position", "()I"); + if (posId == NULL) { + free(mem); + + // position method was not found.. something is wrong so bail out + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()"); + return JNI_ERR; + } + + limitId = (*env)->GetMethodID(env, cls, "limit", "()I"); + if (limitId == NULL) { + free(mem); + + // limit method was not found.. something is wrong so bail out + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()"); + return JNI_ERR; + } + // Try to get the ids of the position and limit fields. We later then check if we was able + // to find them and if so use them get the position and limit of the buffer. This is + // much faster then call back into java via (*env)->CallIntMethod(...). + posFieldId = (*env)->GetFieldID(env, cls, "position", "I"); + if (posFieldId == NULL) { + // this is ok as we can still use the method so just clear the exception + (*env)->ExceptionClear(env); + } + limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I"); + if (limitFieldId == NULL) { + // this is ok as we can still use the method so just clear the exception + (*env)->ExceptionClear(env); + } + + free(mem); +} + +void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { } + JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd) { if (close(fd) < 0) { return -errno; @@ -27,7 +141,6 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* en } JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path) { - const char* f_path = (*env)->GetStringUTFChars(env, path, 0); int res = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0666); @@ -38,3 +151,95 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env } return res; } + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + // We check that GetDirectBufferAddress will not return NULL in OnLoad + return _write(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { + return _write(env, clazz, fd, (void*) address, pos, limit); +} + + +JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) { + struct iovec* iov = (struct iovec*) memoryAddress; + return _writev(env, clazz, fd, iov, length); +} + +JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) { + struct iovec iov[length]; + int iovidx = 0; + int i; + int num = offset + length; + for (i = offset; i < num; i++) { + jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i); + jint pos; + // Get the current position using the (*env)->GetIntField if possible and fallback + // to slower (*env)->CallIntMethod(...) if needed + if (posFieldId == NULL) { + pos = (*env)->CallIntMethod(env, bufObj, posId, NULL); + } else { + pos = (*env)->GetIntField(env, bufObj, posFieldId); + } + jint limit; + // Get the current limit using the (*env)->GetIntField if possible and fallback + // to slower (*env)->CallIntMethod(...) if needed + if (limitFieldId == NULL) { + limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL); + } else { + limit = (*env)->GetIntField(env, bufObj, limitFieldId); + } + void* buffer = (*env)->GetDirectBufferAddress(env, bufObj); + // We check that GetDirectBufferAddress will not return NULL in OnLoad + iov[iovidx].iov_base = buffer + pos; + iov[iovidx].iov_len = (size_t) (limit - pos); + iovidx++; + + // Explicit delete local reference as otherwise the local references will only be released once the native method returns. + // Also there may be a lot of these and JNI specification only specify that 16 must be able to be created. + // + // See https://github.com/netty/netty/issues/2623 + (*env)->DeleteLocalRef(env, bufObj); + } + return _writev(env, clazz, fd, iov, length); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + // We check that GetDirectBufferAddress will not return NULL in OnLoad + return _read(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { + return _read(env, clazz, fd, (void*) address, pos, limit); +} + +JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz) { + int fd[2]; + if (pipe2) { + // we can just use pipe2 and so save extra syscalls; + if (pipe2(fd, O_NONBLOCK) != 0) { + return -errno; + } + } else { + if (pipe(fd) == 0) { + if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) { + int err = errno; + close(fd[0]); + close(fd[1]); + return -err; + } + if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) { + int err = errno; + close(fd[0]); + close(fd[1]); + return -err; + } + } else { + return -errno; + } + } + + // encode the fds into a 64 bit value + return (((jlong) fd[0]) << 32) | fd[1]; +} diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.h b/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.h index 32ddc3125d..6de8ba3a57 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.h +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_FileDescriptor.h @@ -15,5 +15,16 @@ */ #include -int Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd); -int Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path); +// Exported JNI Methods +jint Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path); + +jlong Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz); + +jint Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); +jint Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); +jlong Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length); +jlong Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length); + +jint Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); +jint Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.c b/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.c new file mode 100644 index 0000000000..60d7c4bc75 --- /dev/null +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.c @@ -0,0 +1,715 @@ +/* + * Copyright 2015 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netty_unix_errors.h" +#include "netty_unix_socket.h" +#include "io_netty_channel_unix_Socket.h" + +static jclass datagramSocketAddressClass = NULL; +static jmethodID datagramSocketAddrMethodId = NULL; +static jmethodID inetSocketAddrMethodId = NULL; +static jclass inetSocketAddressClass = NULL; +static jclass netUtilClass = NULL; +static jmethodID netUtilClassIpv4PreferredMethodId = NULL; +static int socketType; +static const char* ip4prefix = "::ffff:"; + +// Optional external methods +extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak)); + +// macro to calculate the length of a sockaddr_un struct for a given path length. +// see sys/un.h#SUN_LEN, this is modified to allow nul bytes +#define _UNIX_ADDR_LENGTH(path_len) (uintptr_t) (((struct sockaddr_un *) 0)->sun_path) + path_len + +static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr, int len) { + char ipstr[INET6_ADDRSTRLEN]; + int port; + jstring ipString; + if (addr->ss_family == AF_INET) { + struct sockaddr_in* s = (struct sockaddr_in*) addr; + port = ntohs(s->sin_port); + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); + ipString = (*env)->NewStringUTF(env, ipstr); + } else { + struct sockaddr_in6* s = (struct sockaddr_in6*) addr; + port = ntohs(s->sin6_port); + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); + + if (strncasecmp(ipstr, ip4prefix, 7) == 0) { + // IPv4-mapped-on-IPv6. + // Cut of ::ffff: prefix to workaround performance issues when parsing these + // addresses in InetAddress.getByName(...). + // + // See https://github.com/netty/netty/issues/2867 + ipString = (*env)->NewStringUTF(env, &ipstr[7]); + } else { + ipString = (*env)->NewStringUTF(env, ipstr); + } + } + jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len); + return socketAddr; +} + +static int addressLength(const struct sockaddr_storage* addr) { + if (addr->ss_family == AF_INET) { + return 8; + } else { + struct sockaddr_in6* s = (struct sockaddr_in6*) addr; + if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00 + && s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00 + && s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) { + // IPv4-mapped-on-IPv6 + return 8; + } else { + return 24; + } + } +} + +static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr, jbyteArray bArray, int offset, int len) { + int port; + if (addr->ss_family == AF_INET) { + struct sockaddr_in* s = (struct sockaddr_in*) addr; + port = ntohs(s->sin_port); + + // Encode address and port into the array + unsigned char a[4]; + a[0] = port >> 24; + a[1] = port >> 16; + a[2] = port >> 8; + a[3] = port; + (*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &s->sin_addr.s_addr); + (*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a); + } else { + struct sockaddr_in6* s = (struct sockaddr_in6*) addr; + port = ntohs(s->sin6_port); + + if (len == 8) { + // IPv4-mapped-on-IPv6 + // Encode port into the array and write it into the jbyteArray + unsigned char a[4]; + a[0] = port >> 24; + a[1] = port >> 16; + a[2] = port >> 8; + a[3] = port; + + // we only need the last 4 bytes for mapped address + (*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &(s->sin6_addr.s6_addr[12])); + (*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a); + } else { + // Encode scopeid and port into the array + unsigned char a[8]; + a[0] = s->sin6_scope_id >> 24; + a[1] = s->sin6_scope_id >> 16; + a[2] = s->sin6_scope_id >> 8; + a[3] = s->sin6_scope_id; + a[4] = port >> 24; + a[5] = port >> 16; + a[6] = port >> 8; + a[7] = port; + + (*env)->SetByteArrayRegion(env, bArray, offset, 16, (jbyte*) &(s->sin6_addr.s6_addr)); + (*env)->SetByteArrayRegion(env, bArray, offset + 16, 8, (jbyte*) &a); + } + } +} + +static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) { + int len = addressLength(addr); + jbyteArray bArray = (*env)->NewByteArray(env, len); + + initInetSocketAddressArray(env, addr, bArray, 0, len); + return bArray; +} + +static jobject createInetSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr) { + char ipstr[INET6_ADDRSTRLEN]; + int port; + jstring ipString; + if (addr->ss_family == AF_INET) { + struct sockaddr_in* s = (struct sockaddr_in*) addr; + port = ntohs(s->sin_port); + inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr); + ipString = (*env)->NewStringUTF(env, ipstr); + } else { + struct sockaddr_in6* s = (struct sockaddr_in6*) addr; + port = ntohs(s->sin6_port); + inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr); + if (strncasecmp(ipstr, ip4prefix, 7) == 0) { + // IPv4-mapped-on-IPv6. + // Cut of ::ffff: prefix to workaround performance issues when parsing these + // addresses in InetAddress.getByName(...). + // + // See https://github.com/netty/netty/issues/2867 + ipString = (*env)->NewStringUTF(env, &ipstr[7]); + } else { + ipString = (*env)->NewStringUTF(env, ipstr); + } + } + jobject socketAddr = (*env)->NewObject(env, inetSocketAddressClass, inetSocketAddrMethodId, ipString, port); + return socketAddr; +} + +static int socket_type(JNIEnv* env) { + jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId); + + if (ipv4Preferred) { + // User asked to use ipv4 explicitly. + return AF_INET; + } + int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd == -1) { + if (errno == EAFNOSUPPORT) { + return AF_INET; + } + return AF_INET6; + } else { + close(fd); + return AF_INET6; + } +} + +static jint _socket(JNIEnv* env, jclass clazz, int type) { + int fd = socket(socketType, type | SOCK_NONBLOCK, 0); + if (fd == -1) { + return -errno; + } else if (socketType == AF_INET6) { + // Allow to listen /connect ipv4 and ipv6 + int optval = 0; + if (netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) { + // Something went wrong so close the fd and return here. setOption(...) itself throws the exception already. + close(fd); + return -1; + } + } + return fd; +} + +int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr) { + uint16_t port = htons((uint16_t) jport); + // Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like + // to not do a memory copy here. This is ok as we not do any blocking action here anyway. + // This is important as the VM may suspend GC for the time! + jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0); + if (addressBytes == NULL) { + // No memory left ?!?!? + netty_unix_errors_throwOutOfMemoryError(env); + return -1; + } + if (socketType == AF_INET6) { + struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr; + ip6addr->sin6_family = AF_INET6; + ip6addr->sin6_port = port; + + if (scopeId != 0) { + ip6addr->sin6_scope_id = (uint32_t) scopeId; + } + memcpy(&(ip6addr->sin6_addr.s6_addr), addressBytes, 16); + } else { + struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr; + ipaddr->sin_family = AF_INET; + ipaddr->sin_port = port; + memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4); + } + + (*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT); + return 0; +} + +static jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { + struct sockaddr_storage addr; + if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } + + ssize_t res; + int err; + do { + res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_storage)); + // keep on writing if it was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return (jint) res; +} + +static jobject _recvFrom(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit) { + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + ssize_t res; + int err; + + do { + res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, &addrlen); + // Keep on reading if we was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + if (err == EAGAIN || err == EWOULDBLOCK) { + // Nothing left to read + return NULL; + } + if (err == EBADF) { + netty_unix_errors_throwClosedChannelException(env); + return NULL; + } + netty_unix_errors_throwIOExceptionErrorNo(env, "recvfrom() failed: ", err); + return NULL; + } + + return createDatagramSocketAddress(env, &addr, res); +} + +int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) { + int rc = getsockopt(fd, level, optname, optval, &optlen); + if (rc < 0) { + netty_unix_errors_throwChannelExceptionErrorNo(env, "getsockopt() failed: ", errno); + } + return rc; +} + +int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len) { + int rc = setsockopt(fd, level, optname, optval, len); + if (rc < 0) { + netty_unix_errors_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno); + } + return rc; +} + +jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) { + jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/unix/DatagramSocketAddress"); + if (localDatagramSocketAddressClass == NULL) { + // pending exception... + return JNI_ERR; + } + datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass); + if (datagramSocketAddressClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", "(Ljava/lang/String;II)V"); + if (datagramSocketAddrMethodId == NULL) { + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.(String, int, int)"); + return JNI_ERR; + } + jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress"); + if (localInetSocketAddressClass == NULL) { + // pending exception... + return JNI_ERR; + } + inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass); + if (inetSocketAddressClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "", "(Ljava/lang/String;I)V"); + if (inetSocketAddrMethodId == NULL) { + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.(String, int)"); + return JNI_ERR; + } + jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" ); + if (localNetUtilClass == NULL) { + // pending exception... + return JNI_ERR; + } + netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass); + if (netUtilClass == NULL) { + // out-of-memory! + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" ); + if (netUtilClassIpv4PreferredMethodId == NULL) { + // position method was not found.. something is wrong so bail out + netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()"); + return JNI_ERR; + } + + void* mem = malloc(1); + if (mem == NULL) { + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); + if (directBuffer == NULL) { + free(mem); + + netty_unix_errors_throwOutOfMemoryError(env); + return JNI_ERR; + } + if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { + free(mem); + + netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address"); + return JNI_ERR; + } + free(mem); + + socketType = socket_type(env); +} + +void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { + if (datagramSocketAddressClass != NULL) { + (*env)->DeleteGlobalRef(env, datagramSocketAddressClass); + datagramSocketAddressClass = NULL; + } + if (inetSocketAddressClass != NULL) { + (*env)->DeleteGlobalRef(env, inetSocketAddressClass); + inetSocketAddressClass = NULL; + } + if (netUtilClass != NULL) { + (*env)->DeleteGlobalRef(env, netUtilClass); + netUtilClass = NULL; + } +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) { + int mode; + if (read && write) { + mode = SHUT_RDWR; + } else if (read) { + mode = SHUT_RD; + } else if (write) { + mode = SHUT_WR; + } else { + return -EINVAL; + } + if (shutdown(fd, mode) < 0) { + return -errno; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { + struct sockaddr_storage addr; + if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } + + if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) { + return -errno; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) { + if (listen(fd, backlog) == -1) { + return -errno; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) { + struct sockaddr_storage addr; + if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) { + // A runtime exception was thrown + return -1; + } + + int res; + int err; + do { + res = connect(fd, (struct sockaddr*) &addr, sizeof(addr)); + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return 0; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd) { + // connect may be done + // return true if connection finished successfully + // return false if connection is still in progress + // throw exception if connection failed + int optval; + int res = netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)); + if (res != 0) { + // getOption failed + return -1; + } + if (optval == 0) { + // connect succeeded + return 0; + } + return -optval; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) { + jint socketFd; + int err; + struct sockaddr_storage addr; + socklen_t address_len = sizeof(addr); + + do { + if (accept4) { + socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC); + } else { + socketFd = accept(fd, (struct sockaddr*) &addr, &address_len); + } + } while (socketFd == -1 && ((err = errno) == EINTR)); + + if (socketFd == -1) { + return -err; + } + + int len = addressLength(&addr); + + // Fill in remote address details + (*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len); + initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len); + + if (accept4) { + return socketFd; + } else { + // accept4 was not present so need two more sys-calls ... + if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1 || fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) { + return -errno; + } + } + return socketFd; +} + +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) { + return NULL; + } + return createInetSocketAddressArray(env, &addr); +} + +JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) { + return NULL; + } + return createInetSocketAddressArray(env, &addr); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz) { + return _socket(env, clazz, SOCK_DGRAM); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz) { + return _socket(env, clazz, SOCK_STREAM); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz) { + int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0); + if (fd == -1) { + return -errno; + } + return fd; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) { + // We check that GetDirectBufferAddress will not return NULL in OnLoad + return _sendTo(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit, address, scopeId, port); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) { + return _sendTo(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) { + struct sockaddr_storage addr; + + if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) { + return -1; + } + + struct msghdr m; + m.msg_name = (void*) &addr; + m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage); + m.msg_iov = (struct iovec*) memoryAddress; + m.msg_iovlen = length; + + ssize_t res; + int err; + do { + res = sendmsg(fd, &m, 0); + // keep on writing if it was interrupted + } while (res == -1 && ((err = errno) == EINTR)); + + if (res < 0) { + return -err; + } + return (jint) res; +} + +JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) { + // We check that GetDirectBufferAddress will not return NULL in OnLoad + return _recvFrom(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit); +} + +JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) { + return _recvFrom(env, fd, (void*) address, pos, limit); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) { + struct sockaddr_un addr; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0); + jint socket_path_len = (*env)->GetArrayLength(env, socketPath); + if (socket_path_len > sizeof(addr.sun_path)) { + socket_path_len = sizeof(addr.sun_path); + } + memcpy(addr.sun_path, socket_path, socket_path_len); + + if (unlink(socket_path) == -1 && errno != ENOENT) { + return -errno; + } + + int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len)); + (*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0); + + if (res == -1) { + return -errno; + } + return res; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) { + struct sockaddr_un addr; + jint socket_path_len; + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + + jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0); + socket_path_len = (*env)->GetArrayLength(env, socketPath); + if (socket_path_len > sizeof(addr.sun_path)) { + socket_path_len = sizeof(addr.sun_path); + } + memcpy(addr.sun_path, socket_path, socket_path_len); + + int res; + int err; + do { + res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len)); + } while (res == -1 && ((err = errno) == EINTR)); + + (*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0); + + if (res < 0) { + return -err; + } + return 0; +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)); +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)); +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)); +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)); +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) { + netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)); +} + +JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) { + struct linger solinger; + if (optval < 0) { + solinger.l_onoff = 0; + solinger.l_linger = 0; + } else { + solinger.l_onoff = 1; + solinger.l_linger = optval; + } + netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger)); +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd) { + struct linger optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) { + return -1; + } + if (optval.l_onoff == 0) { + return -1; + } else { + return optval.l_linger; + } +} + +JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd) { + int optval; + if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) { + return -1; + } + return optval; +} diff --git a/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.h b/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.h new file mode 100644 index 0000000000..af126bf690 --- /dev/null +++ b/transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.h @@ -0,0 +1,56 @@ +/* + * Copyright 2015 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. + */ +#include + +// Exported JNI Methods +jint Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write); + +jint Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz); +jint Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz); + +jint Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port); + +jobject Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit); +jobject Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit); + +jint Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address); +jint Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog); +jint Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress); + +jint Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port); +jint Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address); +jint Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd); + +jbyteArray Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd); +jbyteArray Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd); + +void Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval); +void Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval); +void Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval); +void Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval); +void Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval); +void Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval); + +jint Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd); diff --git a/transport-native-epoll/src/main/c/netty_unix_errors.h b/transport-native-epoll/src/main/c/netty_unix_errors.h new file mode 100644 index 0000000000..e23f383f0b --- /dev/null +++ b/transport-native-epoll/src/main/c/netty_unix_errors.h @@ -0,0 +1,33 @@ +/* + * Copyright 2015 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. + */ +#ifndef NETTY_UNIX_ERRORS_H_ +#define NETTY_UNIX_ERRORS_H_ + +#include + +void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message); +void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void netty_unix_errors_throwIOException(JNIEnv* env, char* message); +void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); +void netty_unix_errors_throwClosedChannelException(JNIEnv* env); +void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env); + +// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. +jint netty_unix_errors_JNI_OnLoad(JNIEnv* env); +void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env); + +#endif /* NETTY_UNIX_ERRORS_H_ */ diff --git a/transport-native-epoll/src/main/c/exception_helper.h b/transport-native-epoll/src/main/c/netty_unix_filedescriptor.h similarity index 57% rename from transport-native-epoll/src/main/c/exception_helper.h rename to transport-native-epoll/src/main/c/netty_unix_filedescriptor.h index f2f079876c..f5311168cb 100644 --- a/transport-native-epoll/src/main/c/exception_helper.h +++ b/transport-native-epoll/src/main/c/netty_unix_filedescriptor.h @@ -13,12 +13,13 @@ * License for the specific language governing permissions and limitations * under the License. */ +#ifndef NETTY_UNIX_FILEDESCRIPTOR_H_ +#define NETTY_UNIX_FILEDESCRIPTOR_H_ -void throwRuntimeException(JNIEnv* env, char* message); -void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); -void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); -void throwIOException(JNIEnv* env, char* message); -void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber); -void throwClosedChannelException(JNIEnv* env); -void throwOutOfMemoryError(JNIEnv* env); -char* exceptionMessage(char* msg, int error); +#include + +// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. +jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env); +void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env); + +#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */ diff --git a/transport-native-epoll/src/main/c/netty_unix_socket.h b/transport-native-epoll/src/main/c/netty_unix_socket.h new file mode 100644 index 0000000000..8a8c1ab652 --- /dev/null +++ b/transport-native-epoll/src/main/c/netty_unix_socket.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 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. + */ +#ifndef NETTY_UNIX_SOCKET_H_ +#define NETTY_UNIX_SOCKET_H_ + +#include +#include + +// External C methods +int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr); +int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen); +int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len); + +// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. +jint netty_unix_socket_JNI_OnLoad(JNIEnv* env); +void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env); + +#endif /* NETTY_UNIX_SOCKET_H_ */ diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java index 314a3d5b88..161f0bf1de 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java @@ -27,6 +27,7 @@ import io.netty.channel.EventLoop; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import io.netty.channel.unix.UnixChannel; import io.netty.util.ReferenceCountUtil; import io.netty.util.internal.OneTimeTask; @@ -36,32 +37,27 @@ import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.UnresolvedAddressException; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel { private static final ChannelMetadata METADATA = new ChannelMetadata(false); private final int readFlag; - private final FileDescriptor fileDescriptor; + private final Socket fileDescriptor; protected int flags = Native.EPOLLET; protected volatile boolean active; private volatile boolean inputShutdown; - AbstractEpollChannel(int fd, int flag) { + AbstractEpollChannel(Socket fd, int flag) { this(null, fd, flag, false); } - AbstractEpollChannel(Channel parent, int fd, int flag, boolean active) { - this(parent, new FileDescriptor(fd), flag, active); - } - - AbstractEpollChannel(Channel parent, FileDescriptor fd, int flag, boolean active) { + AbstractEpollChannel(Channel parent, Socket fd, int flag, boolean active) { super(parent); - if (fd == null) { - throw new NullPointerException("fd"); - } + fileDescriptor = checkNotNull(fd, "fd"); readFlag = flag; flags |= flag; this.active = active; - fileDescriptor = fd; } void setFlag(int flag) throws IOException { @@ -83,7 +79,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann } @Override - public final FileDescriptor fd() { + public final Socket fd() { return fileDescriptor; } @@ -109,7 +105,11 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann // deregister from epoll now and shutdown the socket. doDeregister(); if (active) { - shutdown(fd.intValue()); + try { + fd().shutdown(true, true); + } catch (IOException ignored) { + // The FD will be closed, so if shutdown fails there is nothing we can do. + } } } finally { // Ensure the file descriptor is closed in all cases. @@ -117,14 +117,6 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann } } - /** - * Called on {@link #doClose()} before the actual {@link FileDescriptor} is closed. - * This implementation does nothing. - */ - protected void shutdown(int fd) throws IOException { - // NOOP - } - @Override protected void doDisconnect() throws Exception { doClose(); @@ -252,11 +244,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann int localReadAmount; unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes()); if (byteBuf.hasMemoryAddress()) { - localReadAmount = Native.readAddress( - fileDescriptor.intValue(), byteBuf.memoryAddress(), writerIndex, byteBuf.capacity()); + localReadAmount = fileDescriptor.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity()); } else { ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes()); - localReadAmount = Native.read(fileDescriptor.intValue(), buf, buf.position(), buf.limit()); + localReadAmount = fileDescriptor.read(buf, buf.position(), buf.limit()); } if (localReadAmount > 0) { byteBuf.writerIndex(writerIndex + localReadAmount); @@ -272,8 +263,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann int readerIndex = buf.readerIndex(); int writerIndex = buf.writerIndex(); for (int i = writeSpinCount - 1; i >= 0; i--) { - int localFlushedAmount = Native.writeAddress( - fileDescriptor.intValue(), memoryAddress, readerIndex, writerIndex); + int localFlushedAmount = fileDescriptor.writeAddress(memoryAddress, readerIndex, writerIndex); if (localFlushedAmount > 0) { writtenBytes += localFlushedAmount; if (writtenBytes == readableBytes) { @@ -294,7 +284,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann for (int i = writeSpinCount - 1; i >= 0; i--) { int pos = nioBuf.position(); int limit = nioBuf.limit(); - int localFlushedAmount = Native.write(fileDescriptor.intValue(), nioBuf, pos, limit); + int localFlushedAmount = fileDescriptor.write(nioBuf, pos, limit); if (localFlushedAmount > 0) { nioBuf.position(pos + localFlushedAmount); writtenBytes += localFlushedAmount; @@ -381,7 +371,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann if (isOpen()) { if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) { try { - Native.shutdown(fd().intValue(), true, false); + fd().shutdown(true, false); clearEpollIn0(); pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); } catch (IOException e) { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java index d277a44158..db7c25b1f9 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java @@ -25,6 +25,7 @@ import io.netty.channel.EventLoop; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.ServerChannel; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import java.net.InetSocketAddress; import java.net.SocketAddress; @@ -32,12 +33,31 @@ import java.net.SocketAddress; public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel { private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16); + /** + * @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}. + */ protected AbstractEpollServerChannel(int fd) { - super(fd, Native.EPOLLIN); + this(new Socket(fd), false); } + /** + * @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}. + */ + @Deprecated protected AbstractEpollServerChannel(FileDescriptor fd) { - super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0); + this(new Socket(fd.intValue())); + } + + /** + * @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}. + */ + @Deprecated + protected AbstractEpollServerChannel(Socket fd) { + this(fd, fd.getSoError() == 0); + } + + protected AbstractEpollServerChannel(Socket fd, boolean active) { + super(null, fd, Native.EPOLLIN, active); } @Override @@ -109,7 +129,7 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im try { try { do { - int socketFd = Native.accept(fd().intValue(), acceptedAddress); + int socketFd = fd().accept(acceptedAddress); if (socketFd == -1) { // this means everything was handled for now break; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index fbcec8ff12..087d43614e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -15,7 +15,6 @@ */ package io.netty.channel.epoll; -import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; @@ -31,6 +30,7 @@ import io.netty.channel.DefaultFileRegion; import io.netty.channel.EventLoop; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.MpscLinkedQueueNode; import io.netty.util.internal.OneTimeTask; @@ -47,6 +47,9 @@ import java.util.Queue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import static io.netty.channel.unix.FileDescriptor.pipe; +import static io.netty.util.internal.ObjectUtil.checkNotNull; + public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { private static final String EXPECTED_TYPES = @@ -71,33 +74,53 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { private volatile boolean outputShutdown; // Lazy init these if we need to splice(...) - private int pipeIn = -1; - private int pipeOut = -1; + private FileDescriptor pipeIn; + private FileDescriptor pipeOut; + /** + * @deprecated Use {@link #AbstractEpollStreamChannel(Channel, Socket)}. + */ + @Deprecated protected AbstractEpollStreamChannel(Channel parent, int fd) { + this(parent, new Socket(fd)); + } + + /** + * @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}. + */ + @Deprecated + protected AbstractEpollStreamChannel(int fd) { + this(new Socket(fd)); + } + + /** + * @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}. + */ + @Deprecated + protected AbstractEpollStreamChannel(FileDescriptor fd) { + this(new Socket(fd.intValue())); + } + + /** + * @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}. + */ + @Deprecated + protected AbstractEpollStreamChannel(Socket fd) { + this(fd, fd.getSoError() == 0); + } + + protected AbstractEpollStreamChannel(Channel parent, Socket fd) { super(parent, fd, Native.EPOLLIN, true); // Add EPOLLRDHUP so we are notified once the remote peer close the connection. flags |= Native.EPOLLRDHUP; } - protected AbstractEpollStreamChannel(int fd) { - super(fd, Native.EPOLLIN); + protected AbstractEpollStreamChannel(Socket fd, boolean active) { + super(null, fd, Native.EPOLLIN, active); // Add EPOLLRDHUP so we are notified once the remote peer close the connection. flags |= Native.EPOLLRDHUP; } - protected AbstractEpollStreamChannel(FileDescriptor fd) { - super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0); - - // Add EPOLLRDHUP so we are notified once the remote peer close the connection. - flags |= Native.EPOLLRDHUP; - } - - @Override - protected void shutdown(int fd) throws IOException { - Native.shutdown(fd, true, true); - } - @Override protected AbstractEpollUnsafe newUnsafe() { return new EpollStreamUnsafe(); @@ -262,7 +285,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { int offset = 0; int end = offset + cnt; for (int i = writeSpinCount - 1; i >= 0; i--) { - long localWrittenBytes = Native.writevAddresses(fd().intValue(), array.memoryAddress(offset), cnt); + long localWrittenBytes = fd().writevAddresses(array.memoryAddress(offset), cnt); if (localWrittenBytes == 0) { break; } @@ -301,7 +324,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { int offset = 0; int end = offset + nioBufferCnt; for (int i = writeSpinCount - 1; i >= 0; i--) { - long localWrittenBytes = Native.writev(fd().intValue(), nioBuffers, offset, nioBufferCnt); + long localWrittenBytes = fd().writev(nioBuffers, offset, nioBufferCnt); if (localWrittenBytes == 0) { break; } @@ -511,7 +534,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { protected void shutdownOutput0(final ChannelPromise promise) { try { - Native.shutdown(fd().intValue(), false, true); + fd().shutdown(false, true); outputShutdown = true; promise.setSuccess(); } catch (Throwable cause) { @@ -558,12 +581,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { */ protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { if (localAddress != null) { - Native.bind(fd().intValue(), localAddress); + fd().bind(localAddress); } boolean success = false; try { - boolean connected = Native.connect(fd().intValue(), remoteAddress); + boolean connected = fd().connect(remoteAddress); if (!connected) { setFlag(Native.EPOLLOUT); } @@ -576,10 +599,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { } } - private void safeClosePipe(int pipe) { - if (pipe != -1) { + private void safeClosePipe(FileDescriptor fd) { + if (fd != null) { try { - Native.close(pipe); + fd.close(); } catch (IOException e) { if (logger.isWarnEnabled()) { logger.warn("Error while closing a pipe", e); @@ -735,7 +758,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { * Finish the connect */ private boolean doFinishConnect() throws Exception { - if (Native.finishConnect(fd().intValue())) { + if (fd().finishConnect()) { clearFlag(Native.EPOLLOUT); return true; } else { @@ -840,13 +863,13 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { abstract boolean spliceIn(RecvByteBufAllocator.Handle handle) throws IOException; - protected final int spliceIn(int pipeOut, RecvByteBufAllocator.Handle handle) throws IOException { + protected final int spliceIn(FileDescriptor pipeOut, RecvByteBufAllocator.Handle handle) throws IOException { // calculate the maximum amount of data we are allowed to splice int length = Math.min(handle.guess(), len); int splicedIn = 0; for (;;) { // Splicing until there is nothing left to splice. - int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut, -1, length); + int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut.intValue(), -1, length); if (localSplicedIn == 0) { break; } @@ -885,12 +908,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { // We create the pipe on the target channel as this will allow us to just handle pending writes // later in a correct fashion without get into any ordering issues when spliceTo(...) is called // on multiple Channels pointing to one target Channel. - int pipeOut = ch.pipeOut; - if (pipeOut == -1) { + FileDescriptor pipeOut = ch.pipeOut; + if (pipeOut == null) { // Create a new pipe as non was created before. - long fds = Native.pipe(); - ch.pipeIn = (int) (fds >> 32); - pipeOut = ch.pipeOut = (int) fds; + FileDescriptor[] pipe = pipe(); + ch.pipeIn = pipe[0]; + pipeOut = ch.pipeOut = pipe[1]; } int splicedIn = spliceIn(pipeOut, handle); @@ -946,7 +969,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { public boolean spliceOut() throws Exception { assert ch.eventLoop().inEventLoop(); try { - int splicedOut = Native.splice(ch.pipeIn, -1, ch.fd().intValue(), -1, len); + int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.fd().intValue(), -1, len); len -= splicedOut; if (len == 0) { if (autoRead) { @@ -991,35 +1014,34 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { return true; } - int pipeIn = -1; - int pipeOut = -1; try { - long fds = Native.pipe(); - pipeIn = (int) (fds >> 32); - pipeOut = (int) fds; - - int splicedIn = spliceIn(pipeOut, handle); - if (splicedIn > 0) { - // Integer.MAX_VALUE is a special value which will result in splice forever. - if (len != Integer.MAX_VALUE) { - len -= splicedIn; - } - do { - int splicedOut = Native.splice(pipeIn, -1, fd.intValue(), offset, splicedIn); - splicedIn -= splicedOut; - } while (splicedIn > 0); - if (len == 0) { - promise.setSuccess(); - return true; + FileDescriptor[] pipe = pipe(); + FileDescriptor pipeIn = pipe[0]; + FileDescriptor pipeOut = pipe[1]; + try { + int splicedIn = spliceIn(pipeOut, handle); + if (splicedIn > 0) { + // Integer.MAX_VALUE is a special value which will result in splice forever. + if (len != Integer.MAX_VALUE) { + len -= splicedIn; + } + do { + int splicedOut = Native.splice(pipeIn.intValue(), -1, fd.intValue(), offset, splicedIn); + splicedIn -= splicedOut; + } while (splicedIn > 0); + if (len == 0) { + promise.setSuccess(); + return true; + } } + return false; + } finally { + safeClosePipe(pipeIn); + safeClosePipe(pipeOut); } - return false; } catch (Throwable cause) { promise.setFailure(cause); return true; - } finally { - safeClosePipe(pipeIn); - safeClosePipe(pipeOut); } } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java index ccdfc58b64..d413957985 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Epoll.java @@ -15,6 +15,8 @@ */ package io.netty.channel.epoll; +import io.netty.channel.unix.FileDescriptor; + /** * Tells if {@code netty-transport-native-epoll} is supported. */ @@ -24,24 +26,24 @@ public final class Epoll { static { Throwable cause = null; - int epollFd = -1; - int eventFd = -1; + FileDescriptor epollFd = null; + FileDescriptor eventFd = null; try { - epollFd = Native.epollCreate(); - eventFd = Native.eventFd(); + epollFd = Native.newEpollCreate(); + eventFd = Native.newEventFd(); } catch (Throwable t) { cause = t; } finally { - if (epollFd != -1) { + if (epollFd != null) { try { - Native.close(epollFd); + epollFd.close(); } catch (Exception ignore) { // ignore } } - if (eventFd != -1) { + if (eventFd != null) { try { - Native.close(eventFd); + eventFd.close(); } catch (Exception ignore) { // ignore } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index a9c5e976cf..edda4e5e30 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -30,7 +30,9 @@ import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; +import io.netty.channel.unix.DatagramSocketAddress; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -45,6 +47,8 @@ import java.nio.channels.NotYetConnectedException; import java.util.ArrayList; import java.util.List; +import static io.netty.channel.unix.Socket.newSocketDgram; + /** * {@link DatagramChannel} implementation that uses linux EPOLL Edge-Triggered Mode for * maximal performance. @@ -64,20 +68,24 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements private final EpollDatagramChannelConfig config; public EpollDatagramChannel() { - super(Native.socketDgramFd(), Native.EPOLLIN); + super(newSocketDgram(), Native.EPOLLIN); config = new EpollDatagramChannelConfig(this); } /** - * Create a new {@link EpollDatagramChannel} from the given {@link FileDescriptor}. + * @deprecated Use {@link #EpollDatagramChannel(Socket)}. */ + @Deprecated public EpollDatagramChannel(FileDescriptor fd) { - super(null, fd, Native.EPOLLIN, true); - config = new EpollDatagramChannelConfig(this); + this(new Socket(fd.intValue())); + } + public EpollDatagramChannel(Socket fd) { + super(null, fd, Native.EPOLLIN, true); // As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local // address from it. This is needed as the FileDescriptor may be bound already. - local = Native.localAddress(fd.intValue()); + local = fd.localAddress(); + config = new EpollDatagramChannelConfig(this); } @Override @@ -276,9 +284,8 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements protected void doBind(SocketAddress localAddress) throws Exception { InetSocketAddress addr = (InetSocketAddress) localAddress; checkResolvable(addr); - int fd = fd().intValue(); - Native.bind(fd, addr); - local = Native.localAddress(fd); + fd().bind(addr); + local = fd().localAddress(); active = true; } @@ -372,18 +379,18 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements final int writtenBytes; if (data.hasMemoryAddress()) { long memoryAddress = data.memoryAddress(); - writtenBytes = Native.sendToAddress(fd().intValue(), memoryAddress, data.readerIndex(), data.writerIndex(), + writtenBytes = fd().sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(), remoteAddress.getAddress(), remoteAddress.getPort()); } else if (data instanceof CompositeByteBuf) { IovArray array = IovArrayThreadLocal.get((CompositeByteBuf) data); int cnt = array.count(); assert cnt != 0; - writtenBytes = Native.sendToAddresses(fd().intValue(), array.memoryAddress(0), + writtenBytes = fd().sendToAddresses(array.memoryAddress(0), cnt, remoteAddress.getAddress(), remoteAddress.getPort()); } else { ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes()); - writtenBytes = Native.sendTo(fd().intValue(), nioData, nioData.position(), nioData.limit(), + writtenBytes = fd().sendTo(nioData, nioData.position(), nioData.limit(), remoteAddress.getAddress(), remoteAddress.getPort()); } @@ -490,7 +497,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements checkResolvable(remoteAddress); EpollDatagramChannel.this.remote = remoteAddress; - EpollDatagramChannel.this.local = Native.localAddress(fd().intValue()); + EpollDatagramChannel.this.local = fd().localAddress(); success = true; // Regardless if the connection attempt was cancelled, channelActive() event should be triggered, @@ -543,12 +550,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements final DatagramSocketAddress remoteAddress; if (data.hasMemoryAddress()) { // has a memory address so use optimized call - remoteAddress = Native.recvFromAddress( - fd().intValue(), data.memoryAddress(), data.writerIndex(), data.capacity()); + remoteAddress = fd().recvFromAddress(data.memoryAddress(), data.writerIndex(), + data.capacity()); } else { ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes()); - remoteAddress = Native.recvFrom( - fd().intValue(), nioData, nioData.position(), nioData.limit()); + remoteAddress = fd().recvFrom(nioData, nioData.position(), nioData.limit()); } if (remoteAddress == null) { @@ -558,7 +564,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements } allocHandle.incMessagesRead(1); - allocHandle.lastBytesRead(remoteAddress.receivedAmount); + allocHandle.lastBytesRead(remoteAddress.receivedAmount()); data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead()); readPending = false; @@ -598,21 +604,4 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements } } } - - /** - * Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need - * to create more objects then needed. - */ - static final class DatagramSocketAddress extends InetSocketAddress { - - private static final long serialVersionUID = 1348596211215015739L; - - // holds the amount of received bytes - final int receivedAmount; - - DatagramSocketAddress(String addr, int port, int receivedAmount) { - super(addr, port); - this.receivedAmount = receivedAmount; - } - } } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java index d9f24696dc..a3c1b644d1 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannelConfig.java @@ -186,23 +186,23 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme @Override public int getSendBufferSize() { - return Native.getSendBufferSize(datagramChannel.fd().intValue()); + return datagramChannel.fd().getSendBufferSize(); } @Override public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) { - Native.setSendBufferSize(datagramChannel.fd().intValue(), sendBufferSize); + datagramChannel.fd().setSendBufferSize(sendBufferSize); return this; } @Override public int getReceiveBufferSize() { - return Native.getReceiveBufferSize(datagramChannel.fd().intValue()); + return datagramChannel.fd().getReceiveBufferSize(); } @Override public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) { - Native.setReceiveBufferSize(datagramChannel.fd().intValue(), receiveBufferSize); + datagramChannel.fd().setReceiveBufferSize(receiveBufferSize); return this; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java index 63c130dc03..e877d1b0a4 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java @@ -22,10 +22,12 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.DomainSocketChannel; import io.netty.channel.unix.FileDescriptor; -import io.netty.util.internal.OneTimeTask; +import io.netty.channel.unix.Socket; import java.net.SocketAddress; +import static io.netty.channel.unix.Socket.newSocketDomain; + public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel { private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this); @@ -33,22 +35,36 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i private volatile DomainSocketAddress remote; public EpollDomainSocketChannel() { - super(Native.socketDomainFd()); + super(newSocketDomain(), false); } + /** + * @deprecated Use {@link #EpollDomainSocketChannel(Channel, Socket)}. + */ + @Deprecated public EpollDomainSocketChannel(Channel parent, FileDescriptor fd) { - super(parent, fd.intValue()); + super(parent, new Socket(fd.intValue())); + } + + /** + * @deprecated Use {@link #EpollDomainSocketChannel(Socket, boolean)}. + *

+ * Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor} + */ + @Deprecated + public EpollDomainSocketChannel(FileDescriptor fd) { + super(fd); + } + + public EpollDomainSocketChannel(Channel parent, Socket fd) { + super(parent, fd); } /** * Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor} */ - public EpollDomainSocketChannel(FileDescriptor fd) { - super(fd); - } - - EpollDomainSocketChannel(Channel parent, int fd) { - super(parent, fd); + public EpollDomainSocketChannel(Socket fd, boolean active) { + super(fd, active); } @Override @@ -68,7 +84,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i @Override protected void doBind(SocketAddress localAddress) throws Exception { - Native.bind(fd().intValue(), localAddress); + fd().bind(localAddress); local = (DomainSocketAddress) localAddress; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index ad246c024a..028e1d1350 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -19,6 +19,7 @@ import io.netty.channel.EventLoop; import io.netty.channel.EventLoopGroup; import io.netty.channel.SingleThreadEventLoop; import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe; +import io.netty.channel.unix.FileDescriptor; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; import io.netty.util.internal.PlatformDependent; @@ -49,8 +50,8 @@ final class EpollEventLoop extends SingleThreadEventLoop { WAKEN_UP_UPDATER = updater; } - private final int epollFd; - private final int eventFd; + private final FileDescriptor epollFd; + private final FileDescriptor eventFd; private final IntObjectMap channels = new IntObjectHashMap(4096); private final boolean allowGrowing; private final EpollEventArray events; @@ -68,29 +69,29 @@ final class EpollEventLoop extends SingleThreadEventLoop { events = new EpollEventArray(maxEvents); } boolean success = false; - int epollFd = -1; - int eventFd = -1; + FileDescriptor epollFd = null; + FileDescriptor eventFd = null; try { - this.epollFd = epollFd = Native.epollCreate(); - this.eventFd = eventFd = Native.eventFd(); + this.epollFd = epollFd = Native.newEpollCreate(); + this.eventFd = eventFd = Native.newEventFd(); try { - Native.epollCtlAdd(epollFd, eventFd, Native.EPOLLIN); + Native.epollCtlAdd(epollFd.intValue(), eventFd.intValue(), Native.EPOLLIN); } catch (IOException e) { throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e); } success = true; } finally { if (!success) { - if (epollFd != -1) { + if (epollFd != null) { try { - Native.close(epollFd); + epollFd.close(); } catch (Exception e) { // ignore } } - if (eventFd != -1) { + if (eventFd != null) { try { - Native.close(eventFd); + eventFd.close(); } catch (Exception e) { // ignore } @@ -103,7 +104,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { protected void wakeup(boolean inEventLoop) { if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) { // write to the evfd which will then wake-up epoll_wait(...) - Native.eventFdWrite(eventFd, 1L); + Native.eventFdWrite(eventFd.intValue(), 1L); } } @@ -113,7 +114,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { void add(AbstractEpollChannel ch) throws IOException { assert inEventLoop(); int fd = ch.fd().intValue(); - Native.epollCtlAdd(epollFd, fd, ch.flags); + Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags); channels.put(fd, ch); } @@ -122,7 +123,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { */ void modify(AbstractEpollChannel ch) throws IOException { assert inEventLoop(); - Native.epollCtlMod(epollFd, ch.fd().intValue(), ch.flags); + Native.epollCtlMod(epollFd.intValue(), ch.fd().intValue(), ch.flags); } /** @@ -136,7 +137,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { if (channels.remove(fd) != null) { // Remove the epoll. This is only needed if it's still open as otherwise it will be automatically // removed once the file-descriptor is closed. - Native.epollCtlDel(epollFd, ch.fd().intValue()); + Native.epollCtlDel(epollFd.intValue(), ch.fd().intValue()); } } } @@ -173,7 +174,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L; if (timeoutMillis <= 0) { if (selectCnt == 0) { - int ready = Native.epollWait(epollFd, events, 0); + int ready = Native.epollWait(epollFd.intValue(), events, 0); if (ready > 0) { return ready; } @@ -181,7 +182,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { break; } - int selectedKeys = Native.epollWait(epollFd, events, (int) timeoutMillis); + int selectedKeys = Native.epollWait(epollFd.intValue(), events, (int) timeoutMillis); selectCnt ++; if (selectedKeys != 0 || oldWakenUp || wakenUp == 1 || hasTasks() || hasScheduledTasks()) { @@ -203,7 +204,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { int ready; if (hasTasks()) { // Non blocking just return what is ready directly without block - ready = Native.epollWait(epollFd, events, 0); + ready = Native.epollWait(epollFd.intValue(), events, 0); } else { ready = epollWait(oldWakenUp); @@ -236,7 +237,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { // (OK - no wake-up required). if (wakenUp == 1) { - Native.eventFdWrite(eventFd, 1L); + Native.eventFdWrite(eventFd.intValue(), 1L); } } @@ -284,7 +285,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { private void closeAll() { try { - Native.epollWait(epollFd, events, 0); + Native.epollWait(epollFd.intValue(), events, 0); } catch (IOException ignore) { // ignore on close } @@ -302,9 +303,9 @@ final class EpollEventLoop extends SingleThreadEventLoop { private void processReady(EpollEventArray events, int ready) { for (int i = 0; i < ready; i ++) { final int fd = events.fd(i); - if (fd == eventFd) { + if (fd == eventFd.intValue()) { // consume wakeup event - Native.eventFdRead(eventFd); + Native.eventFdRead(eventFd.intValue()); } else { final long ev = events.events(i); @@ -348,7 +349,7 @@ final class EpollEventLoop extends SingleThreadEventLoop { } else { // We received an event for an fd which we not use anymore. Remove it from the epoll_event set. try { - Native.epollCtlDel(epollFd, fd); + Native.epollCtlDel(epollFd.intValue(), fd); } catch (IOException ignore) { // This can happen but is nothing we need to worry about as we only try to delete // the fd from the epoll set as we not found it in our mappings. So this call to @@ -364,12 +365,12 @@ final class EpollEventLoop extends SingleThreadEventLoop { protected void cleanup() { try { try { - Native.close(epollFd); + epollFd.close(); } catch (IOException e) { logger.warn("Failed to close the epoll fd.", e); } try { - Native.close(eventFd); + eventFd.close(); } catch (IOException e) { logger.warn("Failed to close the event fd.", e); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java index 429bb3874f..7bcea9b0e3 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerChannelConfig.java @@ -16,7 +16,6 @@ package io.netty.channel.epoll; import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.MessageSizeEstimator; import io.netty.channel.RecvByteBufAllocator; @@ -96,11 +95,11 @@ public class EpollServerChannelConfig extends EpollChannelConfig { } public int getReceiveBufferSize() { - return Native.getReceiveBufferSize(channel.fd().intValue()); + return channel.fd().getReceiveBufferSize(); } public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) { - Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize); + channel.fd().setReceiveBufferSize(receiveBufferSize); return this; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java index 3850a56659..958d85a424 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java @@ -19,12 +19,15 @@ import io.netty.channel.Channel; import io.netty.channel.unix.DomainSocketAddress; import io.netty.channel.unix.FileDescriptor; import io.netty.channel.unix.ServerDomainSocketChannel; +import io.netty.channel.unix.Socket; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.io.File; import java.net.SocketAddress; +import static io.netty.channel.unix.Socket.newSocketDomain; + public final class EpollServerDomainSocketChannel extends AbstractEpollServerChannel implements ServerDomainSocketChannel { @@ -35,19 +38,31 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha private volatile DomainSocketAddress local; public EpollServerDomainSocketChannel() { - super(Native.socketDomainFd()); + super(newSocketDomain(), false); } /** + * @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}. * Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}. */ public EpollServerDomainSocketChannel(FileDescriptor fd) { super(fd); } + /** + * @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}. + */ + public EpollServerDomainSocketChannel(Socket fd) { + super(fd); + } + + public EpollServerDomainSocketChannel(Socket fd, boolean active) { + super(fd, active); + } + @Override protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception { - return new EpollDomainSocketChannel(this, fd); + return new EpollDomainSocketChannel(this, new Socket(fd)); } @Override @@ -57,9 +72,8 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha @Override protected void doBind(SocketAddress localAddress) throws Exception { - int fd = fd().intValue(); - Native.bind(fd, localAddress); - Native.listen(fd, config.getBacklog()); + fd().bind(localAddress); + fd().listen(config.getBacklog()); local = (DomainSocketAddress) localAddress; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java index b528899b21..a96a71c7ee 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java @@ -19,6 +19,7 @@ import io.netty.channel.Channel; import io.netty.channel.EventLoop; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -27,6 +28,9 @@ import java.util.Collection; import java.util.Collections; import java.util.Map; +import static io.netty.channel.unix.NativeInetAddress.address; +import static io.netty.channel.unix.Socket.newSocketStream; + /** * {@link ServerSocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for * maximal performance. @@ -38,20 +42,40 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i private volatile Collection tcpMd5SigAddresses = Collections.emptyList(); public EpollServerSocketChannel() { - super(Native.socketStreamFd()); + super(newSocketStream(), false); config = new EpollServerSocketChannelConfig(this); } /** + * @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}. * Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}. */ + @Deprecated public EpollServerSocketChannel(FileDescriptor fd) { - super(fd); - config = new EpollServerSocketChannelConfig(this); + // Must call this constructor to ensure this object's local address is configured correctly. + // The local address can only be obtained from a Socket object. + this(new Socket(fd.intValue())); + } + /** + * @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}. + * Creates a new {@link EpollServerSocketChannel} from an existing {@link Socket}. + */ + @Deprecated + public EpollServerSocketChannel(Socket fd) { + super(fd); // As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local // address from it. This is needed as the FileDescriptor may be bound already. - local = Native.localAddress(fd.intValue()); + local = fd.localAddress(); + config = new EpollServerSocketChannelConfig(this); + } + + public EpollServerSocketChannel(Socket fd, boolean active) { + super(fd, active); + // As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local + // address from it. This is needed as the FileDescriptor may be bound already. + local = fd.localAddress(); + config = new EpollServerSocketChannelConfig(this); } @Override @@ -63,13 +87,12 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i protected void doBind(SocketAddress localAddress) throws Exception { InetSocketAddress addr = (InetSocketAddress) localAddress; checkResolvable(addr); - int fd = fd().intValue(); - Native.bind(fd, addr); - local = Native.localAddress(fd); + fd().bind(addr); + local = fd().localAddress(); if (Native.IS_SUPPORTING_TCP_FASTOPEN && config.getTcpFastopen() > 0) { - Native.setTcpFastopen(fd, config.getTcpFastopen()); + Native.setTcpFastopen(fd().intValue(), config.getTcpFastopen()); } - Native.listen(fd, config.getBacklog()); + fd().listen(config.getBacklog()); active = true; } @@ -95,7 +118,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i @Override protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception { - return new EpollSocketChannel(this, fd, Native.address(address, offset, len)); + return new EpollSocketChannel(this, new Socket(fd), address(address, offset, len)); } Collection tcpMd5SigAddresses() { diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java index 2f16b0ab18..ffcf9d7049 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java @@ -22,6 +22,7 @@ import io.netty.channel.EventLoop; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.SocketChannel; import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.Socket; import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.internal.OneTimeTask; @@ -33,6 +34,8 @@ import java.util.Collections; import java.util.Map; import java.util.concurrent.Executor; +import static io.netty.channel.unix.Socket.newSocketStream; + /** * {@link SocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for * maximal performance. @@ -45,13 +48,13 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme private volatile InetSocketAddress remote; private volatile Collection tcpMd5SigAddresses = Collections.emptyList(); - EpollSocketChannel(Channel parent, int fd, InetSocketAddress remote) { + EpollSocketChannel(Channel parent, Socket fd, InetSocketAddress remote) { super(parent, fd); config = new EpollSocketChannelConfig(this); // Directly cache the remote and local addresses // See https://github.com/netty/netty/issues/2359 this.remote = remote; - local = Native.localAddress(fd); + local = fd.localAddress(); if (parent instanceof EpollServerSocketChannel) { tcpMd5SigAddresses = ((EpollServerSocketChannel) parent).tcpMd5SigAddresses(); @@ -59,21 +62,33 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme } public EpollSocketChannel() { - super(Native.socketStreamFd()); + super(newSocketStream(), false); + config = new EpollSocketChannelConfig(this); + } + + /** + * @deprecated Use {@link #EpollSocketChannel(Socket, boolean)}. + */ + @Deprecated + public EpollSocketChannel(FileDescriptor fd) { + super(fd); + // As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local + // address from it. This is needed as the FileDescriptor may be bound/connected already. + remote = fd().remoteAddress(); + local = fd().localAddress(); config = new EpollSocketChannelConfig(this); } /** * Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}. */ - public EpollSocketChannel(FileDescriptor fd) { - super(fd); - config = new EpollSocketChannelConfig(this); - + public EpollSocketChannel(Socket fd, boolean active) { + super(fd, active); // As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local // address from it. This is needed as the FileDescriptor may be bound/connected already. - remote = Native.remoteAddress(fd.intValue()); - local = Native.localAddress(fd.intValue()); + remote = fd.remoteAddress(); + local = fd.localAddress(); + config = new EpollSocketChannelConfig(this); } /** @@ -111,7 +126,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme protected SocketAddress remoteAddress0() { if (remote == null) { // Remote address not know, try to get it now. - InetSocketAddress address = Native.remoteAddress(fd().intValue()); + InetSocketAddress address = fd().remoteAddress(); if (address != null) { remote = address; } @@ -123,9 +138,8 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme @Override protected void doBind(SocketAddress local) throws Exception { InetSocketAddress localAddress = (InetSocketAddress) local; - int fd = fd().intValue(); - Native.bind(fd, localAddress); - this.local = Native.localAddress(fd); + fd().bind(localAddress); + this.local = fd().localAddress(); } @Override @@ -190,7 +204,6 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme checkResolvable((InetSocketAddress) localAddress); } checkResolvable((InetSocketAddress) remoteAddress); - int fd = fd().intValue(); boolean connected = super.doConnect(remoteAddress, localAddress); if (connected) { remote = (InetSocketAddress) remoteAddress; @@ -199,7 +212,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme // We always need to set the localAddress even if not connected yet // // See https://github.com/netty/netty/issues/3463 - local = Native.localAddress(fd); + local = fd().localAddress(); return connected; } 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 fc4aba215b..1e8cc66bc7 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 @@ -147,17 +147,17 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public int getReceiveBufferSize() { - return Native.getReceiveBufferSize(channel.fd().intValue()); + return channel.fd().getReceiveBufferSize(); } @Override public int getSendBufferSize() { - return Native.getSendBufferSize(channel.fd().intValue()); + return channel.fd().getSendBufferSize(); } @Override public int getSoLinger() { - return Native.getSoLinger(channel.fd().intValue()); + return channel.fd().getSoLinger(); } @Override @@ -167,7 +167,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public boolean isKeepAlive() { - return Native.isKeepAlive(channel.fd().intValue()) == 1; + return channel.fd().isKeepAlive(); } @Override @@ -177,14 +177,14 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public boolean isTcpNoDelay() { - return Native.isTcpNoDelay(channel.fd().intValue()) == 1; + return channel.fd().isTcpNoDelay(); } /** * Get the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details. */ public boolean isTcpCork() { - return Native.isTcpCork(channel.fd().intValue()) == 1; + return channel.fd().isTcpCork(); } /** @@ -225,7 +225,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) { - Native.setKeepAlive(channel.fd().intValue(), keepAlive ? 1 : 0); + channel.fd().setKeepAlive(keepAlive); return this; } @@ -237,7 +237,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) { - Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize); + channel.fd().setReceiveBufferSize(receiveBufferSize); return this; } @@ -249,19 +249,19 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement @Override public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) { - Native.setSendBufferSize(channel.fd().intValue(), sendBufferSize); + channel.fd().setSendBufferSize(sendBufferSize); return this; } @Override public EpollSocketChannelConfig setSoLinger(int soLinger) { - Native.setSoLinger(channel.fd().intValue(), soLinger); + channel.fd().setSoLinger(soLinger); return this; } @Override public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) { - Native.setTcpNoDelay(channel.fd().intValue(), tcpNoDelay ? 1 : 0); + channel.fd().setTcpNoDelay(tcpNoDelay); return this; } @@ -269,7 +269,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement * Set the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details. */ public EpollSocketChannelConfig setTcpCork(boolean tcpCork) { - Native.setTcpCork(channel.fd().intValue(), tcpCork ? 1 : 0); + channel.fd().setTcpCork(tcpCork); return this; } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index adde3f5400..ab2235547e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -15,34 +15,31 @@ */ package io.netty.channel.epoll; - -import io.netty.channel.ChannelException; import io.netty.channel.DefaultFileRegion; -import io.netty.channel.unix.DomainSocketAddress; -import io.netty.util.CharsetUtil; -import io.netty.util.internal.EmptyArrays; +import io.netty.channel.unix.Errors.NativeIoException; +import io.netty.channel.unix.FileDescriptor; +import io.netty.channel.unix.NativeInetAddress; import io.netty.util.internal.NativeLibraryLoader; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; import java.io.IOException; -import java.net.ConnectException; -import java.net.Inet6Address; import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; import java.util.Locale; +import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE; +import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE; +import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE; +import static io.netty.channel.unix.Errors.ioResult; +import static io.netty.channel.unix.Errors.newConnectionResetException; +import static io.netty.channel.unix.Errors.newIOException; + /** * Native helper methods * * Internal usage only! */ public final class Native { - static { String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim(); if (!name.startsWith("linux")) { @@ -65,95 +62,33 @@ public final class Native { public static final long SSIZE_MAX = ssizeMax(); public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen(); - private static final byte[] IPV4_MAPPED_IPV6_PREFIX = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff }; - - // As all our JNI methods return -errno on error we need to compare with the negative errno codes. - private static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN(); - private static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF(); - private static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE(); - private static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET(); - private static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN(); - private static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK(); - private static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS(); - - /** - * Holds the mappings for errno codes to String messages. - * This eliminates the need to call back into JNI to get the right String message on an exception - * and thus is faster. - * - * The array length of 1024 should be more then enough because errno.h only holds < 200 codes. - */ - private static final String[] ERRORS = new String[1024]; - - // Pre-instantiated exceptions which does not need any stacktrace and - // can be thrown multiple times for performance reasons. - private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION; - private static final IOException CONNECTION_RESET_EXCEPTION_WRITE; - private static final IOException CONNECTION_RESET_EXCEPTION_WRITEV; - private static final IOException CONNECTION_RESET_EXCEPTION_READ; - private static final IOException CONNECTION_RESET_EXCEPTION_SENDFILE; - private static final IOException CONNECTION_RESET_EXCEPTION_SENDTO; - private static final IOException CONNECTION_RESET_EXCEPTION_SENDMSG; - private static final IOException CONNECTION_RESET_EXCEPTION_SENDMMSG; - private static final IOException CONNECTION_RESET_EXCEPTION_SPLICE; + private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDFILE; + private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMMSG; + private static final NativeIoException CONNECTION_RESET_EXCEPTION_SPLICE; static { - for (int i = 0; i < ERRORS.length; i++) { - // This is ok as strerror returns 'Unknown error i' when the message is not known. - ERRORS[i] = strError(i); - } - - CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("syscall:read(...)", - ERRNO_ECONNRESET_NEGATIVE); - CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)", - ERRNO_EPIPE_NEGATIVE); - CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)", - ERRNO_EPIPE_NEGATIVE); CONNECTION_RESET_EXCEPTION_SENDFILE = newConnectionResetException("syscall:sendfile(...)", ERRNO_EPIPE_NEGATIVE); - CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)", - ERRNO_EPIPE_NEGATIVE); - CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)", - ERRNO_EPIPE_NEGATIVE); CONNECTION_RESET_EXCEPTION_SENDMMSG = newConnectionResetException("syscall:sendmmsg(...)", ERRNO_EPIPE_NEGATIVE); CONNECTION_RESET_EXCEPTION_SPLICE = newConnectionResetException("syscall:splice(...)", ERRNO_EPIPE_NEGATIVE); - CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); - CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); } - private static IOException newConnectionResetException(String method, int errnoNegative) { - IOException exception = newIOException(method, errnoNegative); - exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); - return exception; + public static FileDescriptor newEventFd() { + return new FileDescriptor(eventFd()); } - public static IOException newIOException(String method, int err) { - return new IOException(method + "() failed: " + ERRORS[-err]); - } - - private static int ioResult(String method, int err, IOException resetCause) throws IOException { - // network stack saturated... try again later - if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) { - return 0; - } - if (err == ERRNO_EPIPE_NEGATIVE || err == ERRNO_ECONNRESET_NEGATIVE) { - throw resetCause; - } - if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_NEGATIVE) { - throw CLOSED_CHANNEL_EXCEPTION; - } - // TODO: We could even go futher and use a pre-instanced IOException for the other error codes, but for - // all other errors it may be better to just include a stacktrace. - throw newIOException(method, err); - } - - public static native int eventFd(); + private static native int eventFd(); public static native void eventFdWrite(int fd, long value); public static native void eventFdRead(int fd); - public static native int epollCreate(); + + public static FileDescriptor newEpollCreate() { + return new FileDescriptor(epollCreate()); + } + + private static native int epollCreate(); + public static int epollWait(int efd, EpollEventArray events, int timeout) throws IOException { int ready = epollWait0(efd, events.memoryAddress(), events.length(), timeout); if (ready < 0) { @@ -187,26 +122,7 @@ public final class Native { } private static native int epollCtlDel0(int efd, final int fd); - private static native int errnoEBADF(); - private static native int errnoEPIPE(); - private static native int errnoECONNRESET(); - private static native int errnoENOTCONN(); - - private static native int errnoEAGAIN(); - private static native int errnoEWOULDBLOCK(); - private static native int errnoEINPROGRESS(); - private static native String strError(int err); - // File-descriptor operations - public static void close(int fd) throws IOException { - int res = close0(fd); - if (res < 0) { - throw newIOException("close", res); - } - } - - private static native int close0(int fd); - public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException { int res = splice0(fd, offIn, fdOut, offOut, len); if (res >= 0) { @@ -217,83 +133,6 @@ public final class Native { private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len); - public static long pipe() throws IOException { - long res = pipe0(); - if (res >= 0) { - return res; - } - throw newIOException("pipe", (int) res); - } - - private static native long pipe0(); - - public static int write(int fd, ByteBuffer buf, int pos, int limit) throws IOException { - int res = write0(fd, buf, pos, limit); - if (res >= 0) { - return res; - } - return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE); - } - - private static native int write0(int fd, ByteBuffer buf, int pos, int limit); - - public static int writeAddress(int fd, long address, int pos, int limit) throws IOException { - int res = writeAddress0(fd, address, pos, limit); - if (res >= 0) { - return res; - } - return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE); - } - - private static native int writeAddress0(int fd, long address, int pos, int limit); - - public static long writev(int fd, ByteBuffer[] buffers, int offset, int length) throws IOException { - long res = writev0(fd, buffers, offset, length); - if (res >= 0) { - return res; - } - return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); - } - - private static native long writev0(int fd, ByteBuffer[] buffers, int offset, int length); - - public static long writevAddresses(int fd, long memoryAddress, int length) - throws IOException { - long res = writevAddresses0(fd, memoryAddress, length); - if (res >= 0) { - return res; - } - return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); - } - - private static native long writevAddresses0(int fd, long memoryAddress, int length); - - public static int read(int fd, ByteBuffer buf, int pos, int limit) throws IOException { - int res = read0(fd, buf, pos, limit); - if (res > 0) { - return res; - } - if (res == 0) { - return -1; - } - return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ); - } - - private static native int read0(int fd, ByteBuffer buf, int pos, int limit); - - public static int readAddress(int fd, long address, int pos, int limit) throws IOException { - int res = readAddress0(fd, address, pos, limit); - if (res > 0) { - return res; - } - if (res == 0) { - return -1; - } - return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ); - } - - private static native int readAddress0(int fd, long address, int pos, int limit); - public static long sendfile( int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException { // Open the file-region as it may be created via the lazy constructor. This is needed as we directly access @@ -310,84 +149,6 @@ public final class Native { private static native long sendfile0( int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException; - public static int sendTo( - int fd, ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException { - // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected - // to be called frequently - byte[] address; - int scopeId; - if (addr instanceof Inet6Address) { - address = addr.getAddress(); - scopeId = ((Inet6Address) addr).getScopeId(); - } else { - // convert to ipv4 mapped ipv6 address; - scopeId = 0; - address = ipv4MappedIpv6Address(addr.getAddress()); - } - int res = sendTo0(fd, buf, pos, limit, address, scopeId, port); - if (res >= 0) { - return res; - } - return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO); - } - - private static native int sendTo0( - int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port); - - public static int sendToAddress( - int fd, long memoryAddress, int pos, int limit, InetAddress addr, int port) throws IOException { - // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected - // to be called frequently - byte[] address; - int scopeId; - if (addr instanceof Inet6Address) { - address = addr.getAddress(); - scopeId = ((Inet6Address) addr).getScopeId(); - } else { - // convert to ipv4 mapped ipv6 address; - scopeId = 0; - address = ipv4MappedIpv6Address(addr.getAddress()); - } - int res = sendToAddress0(fd, memoryAddress, pos, limit, address, scopeId, port); - if (res >= 0) { - return res; - } - return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO); - } - - private static native int sendToAddress0( - int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port); - - public static int sendToAddresses( - int fd, long memoryAddress, int length, InetAddress addr, int port) throws IOException { - // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected - // to be called frequently - byte[] address; - int scopeId; - if (addr instanceof Inet6Address) { - address = addr.getAddress(); - scopeId = ((Inet6Address) addr).getScopeId(); - } else { - // convert to ipv4 mapped ipv6 address; - scopeId = 0; - address = ipv4MappedIpv6Address(addr.getAddress()); - } - int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port); - if (res >= 0) { - return res; - } - return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG); - } - - private static native int sendToAddresses( - int fd, long memoryAddress, int length, byte[] address, int scopeId, int port); - - public static native EpollDatagramChannel.DatagramSocketAddress recvFrom( - int fd, ByteBuffer buf, int pos, int limit) throws IOException; - - public static native EpollDatagramChannel.DatagramSocketAddress recvFromAddress( - int fd, long memoryAddress, int pos, int limit) throws IOException; - public static int sendmmsg( int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException { int res = sendmmsg0(fd, msgs, offset, len); @@ -403,188 +164,6 @@ public final class Native { private static native boolean isSupportingSendmmsg(); private static native boolean isSupportingTcpFastopen(); - // socket operations - public static int socketStreamFd() { - int res = socketStream(); - if (res < 0) { - throw new ChannelException(newIOException("socketStreamFd", res)); - } - return res; - } - - public static int socketDgramFd() { - int res = socketDgram(); - if (res < 0) { - throw new ChannelException(newIOException("socketDgramFd", res)); - } - return res; - } - - public static int socketDomainFd() { - int res = socketDomain(); - if (res < 0) { - throw new ChannelException(newIOException("socketDomain", res)); - } - return res; - } - - private static native int socketStream(); - private static native int socketDgram(); - private static native int socketDomain(); - - public static void bind(int fd, SocketAddress socketAddress) throws IOException { - if (socketAddress instanceof InetSocketAddress) { - InetSocketAddress addr = (InetSocketAddress) socketAddress; - NativeInetAddress address = toNativeInetAddress(addr.getAddress()); - int res = bind(fd, address.address, address.scopeId, addr.getPort()); - if (res < 0) { - throw newIOException("bind", res); - } - } else if (socketAddress instanceof DomainSocketAddress) { - DomainSocketAddress addr = (DomainSocketAddress) socketAddress; - int res = bindDomainSocket(fd, addr.path().getBytes(CharsetUtil.UTF_8)); - if (res < 0) { - throw newIOException("bind", res); - } - } else { - throw new Error("Unexpected SocketAddress implementation " + socketAddress); - } - } - - private static native int bind(int fd, byte[] address, int scopeId, int port); - private static native int bindDomainSocket(int fd, byte[] path); - - public static void listen(int fd, int backlog) throws IOException { - int res = listen0(fd, backlog); - if (res < 0) { - throw newIOException("listen", res); - } - } - - private static native int listen0(int fd, int backlog); - - public static boolean connect(int fd, SocketAddress socketAddress) throws IOException { - int res; - if (socketAddress instanceof InetSocketAddress) { - InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; - NativeInetAddress address = toNativeInetAddress(inetSocketAddress.getAddress()); - res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort()); - } else if (socketAddress instanceof DomainSocketAddress) { - DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress; - res = connectDomainSocket(fd, unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8)); - } else { - throw new Error("Unexpected SocketAddress implementation " + socketAddress); - } - if (res < 0) { - if (res == ERRNO_EINPROGRESS_NEGATIVE) { - // connect not complete yet need to wait for EPOLLOUT event - return false; - } - throw newConnectException("connect", res); - } - return true; - } - - private static native int connect(int fd, byte[] address, int scopeId, int port); - private static native int connectDomainSocket(int fd, byte[] path); - - public static boolean finishConnect(int fd) throws IOException { - int res = finishConnect0(fd); - if (res < 0) { - if (res == ERRNO_EINPROGRESS_NEGATIVE) { - // connect still in progress - return false; - } - throw newConnectException("finishConnect", res); - } - return true; - } - - private static native int finishConnect0(int fd); - - private static ConnectException newConnectException(String method, int err) { - return new ConnectException(method + "() failed: " + ERRORS[-err]); - } - - public static InetSocketAddress remoteAddress(int fd) { - byte[] addr = remoteAddress0(fd); - // addr may be null if getpeername failed. - // See https://github.com/netty/netty/issues/3328 - if (addr == null) { - return null; - } - return address(addr, 0, addr.length); - } - - public static InetSocketAddress localAddress(int fd) { - byte[] addr = localAddress0(fd); - // addr may be null if getpeername failed. - // See https://github.com/netty/netty/issues/3328 - if (addr == null) { - return null; - } - return address(addr, 0, addr.length); - } - - static InetSocketAddress address(byte[] addr, int offset, int len) { - // The last 4 bytes are always the port - final int port = decodeInt(addr, offset + len - 4); - final InetAddress address; - - try { - switch (len) { - // 8 bytes: - // - 4 == ipaddress - // - 4 == port - case 8: - byte[] ipv4 = new byte[4]; - System.arraycopy(addr, offset, ipv4, 0, 4); - address = InetAddress.getByAddress(ipv4); - break; - - // 24 bytes: - // - 16 == ipaddress - // - 4 == scopeId - // - 4 == port - case 24: - byte[] ipv6 = new byte[16]; - System.arraycopy(addr, offset, ipv6, 0, 16); - int scopeId = decodeInt(addr, offset + len - 8); - address = Inet6Address.getByAddress(null, ipv6, scopeId); - break; - default: - throw new Error(); - } - return new InetSocketAddress(address, port); - } catch (UnknownHostException e) { - throw new Error("Should never happen", e); - } - } - - static int decodeInt(byte[] addr, int index) { - return (addr[index] & 0xff) << 24 | - (addr[index + 1] & 0xff) << 16 | - (addr[index + 2] & 0xff) << 8 | - addr[index + 3] & 0xff; - } - - private static native byte[] remoteAddress0(int fd); - private static native byte[] localAddress0(int fd); - - public static int accept(int fd, byte[] addr) throws IOException { - int res = accept0(fd, addr); - if (res >= 0) { - return res; - } - if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) { - // Everything consumed so just return -1 here. - return -1; - } - throw newIOException("accept", res); - } - - private static native int accept0(int fd, byte[] addr); - public static int recvFd(int fd) throws IOException { int res = recvFd0(fd); if (res > 0) { @@ -617,44 +196,22 @@ public final class Native { private static native int sendFd0(int socketFd, int fd); - public static void shutdown(int fd, boolean read, boolean write) throws IOException { - int res = shutdown0(fd, read, write); - if (res < 0) { - throw newIOException("shutdown", res); - } - } - - private static native int shutdown0(int fd, boolean read, boolean write); - // Socket option operations - public static native int getReceiveBufferSize(int fd); - public static native int getSendBufferSize(int fd); - public static native int isKeepAlive(int fd); public static native int isReuseAddress(int fd); public static native int isReusePort(int fd); - public static native int isTcpNoDelay(int fd); - public static native int isTcpCork(int fd); public static native int getTcpNotSentLowAt(int fd); - public static native int getSoLinger(int fd); public static native int getTrafficClass(int fd); public static native int isBroadcast(int fd); public static native int getTcpKeepIdle(int fd); public static native int getTcpKeepIntvl(int fd); public static native int getTcpKeepCnt(int fd); public static native int getTcpUserTimeout(int milliseconds); - public static native int getSoError(int fd); public static native int isIpFreeBind(int fd); - public static native void setKeepAlive(int fd, int keepAlive); - public static native void setReceiveBufferSize(int fd, int receiveBufferSize); public static native void setReuseAddress(int fd, int reuseAddress); public static native void setReusePort(int fd, int reuseAddress); - public static native void setSendBufferSize(int fd, int sendBufferSize); - public static native void setTcpNoDelay(int fd, int tcpNoDelay); - public static native void setTcpCork(int fd, int tcpCork); public static native void setTcpFastopen(int fd, int tcpFastopenBacklog); public static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt); - public static native void setSoLinger(int fd, int soLinger); public static native void setTrafficClass(int fd, int tcpNoDelay); public static native void setBroadcast(int fd, int broadcast); public static native void setTcpKeepIdle(int fd, int seconds); @@ -669,43 +226,12 @@ public final class Native { private static native void tcpInfo0(int fd, int[] array); public static void setTcpMd5Sig(int fd, InetAddress address, byte[] key) { - final NativeInetAddress a = toNativeInetAddress(address); - setTcpMd5Sig0(fd, a.address, a.scopeId, key); + final NativeInetAddress a = NativeInetAddress.newInstance(address); + setTcpMd5Sig0(fd, a.address(), a.scopeId(), key); } private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key); - private static NativeInetAddress toNativeInetAddress(InetAddress addr) { - byte[] bytes = addr.getAddress(); - if (addr instanceof Inet6Address) { - return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId()); - } else { - // convert to ipv4 mapped ipv6 address; - return new NativeInetAddress(ipv4MappedIpv6Address(bytes)); - } - } - - static byte[] ipv4MappedIpv6Address(byte[] ipv4) { - byte[] address = new byte[16]; - System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length); - System.arraycopy(ipv4, 0, address, 12, ipv4.length); - return address; - } - - private static class NativeInetAddress { - final byte[] address; - final int scopeId; - - NativeInetAddress(byte[] address, int scopeId) { - this.address = address; - this.scopeId = scopeId; - } - - NativeInetAddress(byte[] address) { - this(address, 0); - } - } - public static native String kernelVersion(); private static native int iovMax(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java index 2861e51032..33f4eb8c8e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeDatagramPacketArray.java @@ -15,6 +15,7 @@ */ package io.netty.channel.epoll; +import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.socket.DatagramPacket; @@ -148,7 +149,7 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr addr = address.getAddress(); scopeId = ((Inet6Address) address).getScopeId(); } else { - addr = Native.ipv4MappedIpv6Address(address.getAddress()); + addr = ipv4MappedIpv6Address(address.getAddress()); scopeId = 0; } port = recipient.getPort(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java new file mode 100644 index 0000000000..21d7215a49 --- /dev/null +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/DatagramSocketAddress.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 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.unix; + +import java.net.InetSocketAddress; + +/** + * Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need + * to create more objects then needed. + *

+ * Internal usage only! + */ +public final class DatagramSocketAddress extends InetSocketAddress { + private static final long serialVersionUID = 3094819287843178401L; + + // holds the amount of received bytes + private final int receivedAmount; + + DatagramSocketAddress(String addr, int port, int receivedAmount) { + super(addr, port); + this.receivedAmount = receivedAmount; + } + + public int receivedAmount() { + return receivedAmount; + } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/Errors.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/Errors.java new file mode 100644 index 0000000000..7bd7ed226a --- /dev/null +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/Errors.java @@ -0,0 +1,134 @@ +/* + * Copyright 2015 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.unix; + +import io.netty.util.internal.EmptyArrays; + +import java.io.IOException; +import java.net.ConnectException; +import java.nio.channels.ClosedChannelException; + +/** + * Internal usage only! + */ +public final class Errors { + // As all our JNI methods return -errno on error we need to compare with the negative errno codes. + public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN(); + public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF(); + public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE(); + public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET(); + public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN(); + public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK(); + public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS(); + + /** + * Holds the mappings for errno codes to String messages. + * This eliminates the need to call back into JNI to get the right String message on an exception + * and thus is faster. + * + * The array length of 512 should be more then enough because errno.h only holds < 200 codes. + */ + private static final String[] ERRORS = new String[512]; + + // Pre-instantiated exceptions which does not need any stacktrace and + // can be thrown multiple times for performance reasons. + static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION; + static final NativeIoException CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION; + static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITE; + static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITEV; + static final NativeIoException CONNECTION_RESET_EXCEPTION_READ; + static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDTO; + static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG; + + /** + * Internal usage only! + */ + public static final class NativeIoException extends IOException { + private static final long serialVersionUID = 8222160204268655526L; + private final int expectedErr; + public NativeIoException(String method, int expectedErr) { + super(method); + this.expectedErr = expectedErr; + } + + public int expectedErr() { + return expectedErr; + } + } + + static { + for (int i = 0; i < ERRORS.length; i++) { + // This is ok as strerror returns 'Unknown error i' when the message is not known. + ERRORS[i] = strError(i); + } + + CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("syscall:read(...)", + ERRNO_ECONNRESET_NEGATIVE); + CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)", + ERRNO_EPIPE_NEGATIVE); + CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)", + ERRNO_EPIPE_NEGATIVE); + CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)", + ERRNO_EPIPE_NEGATIVE); + CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)", + ERRNO_EPIPE_NEGATIVE); + CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION = newConnectionResetException("syscall:shutdown(...)", + ERRNO_ENOTCONN_NEGATIVE); + CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException(); + CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); + } + + static ConnectException newConnectException(String method, int err) { + return new ConnectException(method + "() failed: " + ERRORS[-err]); + } + + public static NativeIoException newConnectionResetException(String method, int errnoNegative) { + NativeIoException exception = newIOException(method, errnoNegative); + exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE); + return exception; + } + + public static NativeIoException newIOException(String method, int err) { + return new NativeIoException(method + "() failed: " + ERRORS[-err], err); + } + + public static int ioResult(String method, int err, NativeIoException resetCause) throws IOException { + // network stack saturated... try again later + if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) { + return 0; + } + if (err == resetCause.expectedErr()) { + throw resetCause; + } + if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_NEGATIVE) { + throw CLOSED_CHANNEL_EXCEPTION; + } + // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for + // all other errors it may be better to just include a stack trace. + throw newIOException(method, err); + } + + private static native int errnoEBADF(); + private static native int errnoEPIPE(); + private static native int errnoECONNRESET(); + private static native int errnoENOTCONN(); + private static native int errnoEAGAIN(); + private static native int errnoEWOULDBLOCK(); + private static native int errnoEINPROGRESS(); + private static native String strError(int err); + + private Errors() { } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/FileDescriptor.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/FileDescriptor.java index 29eb77d56c..285505c26f 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/unix/FileDescriptor.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/FileDescriptor.java @@ -15,11 +15,15 @@ */ package io.netty.channel.unix; -import io.netty.channel.epoll.Native; - import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; +import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_READ; +import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITE; +import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITEV; +import static io.netty.channel.unix.Errors.ioResult; +import static io.netty.channel.unix.Errors.newIOException; import static io.netty.util.internal.ObjectUtil.checkNotNull; /** @@ -27,7 +31,6 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull; * {@link FileDescriptor} for it. */ public class FileDescriptor { - private final int fd; private volatile boolean open = true; @@ -50,7 +53,10 @@ public class FileDescriptor { */ public void close() throws IOException { open = false; - close(fd); + int res = close(fd); + if (res < 0) { + throw newIOException("close", res); + } } /** @@ -60,6 +66,60 @@ public class FileDescriptor { return open; } + public final int write(ByteBuffer buf, int pos, int limit) throws IOException { + int res = write(fd, buf, pos, limit); + if (res >= 0) { + return res; + } + return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE); + } + + public final int writeAddress(long address, int pos, int limit) throws IOException { + int res = writeAddress(fd, address, pos, limit); + if (res >= 0) { + return res; + } + return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE); + } + + public final long writev(ByteBuffer[] buffers, int offset, int length) throws IOException { + long res = writev(fd, buffers, offset, length); + if (res >= 0) { + return res; + } + return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); + } + + public final long writevAddresses(long memoryAddress, int length) throws IOException { + long res = writevAddresses(fd, memoryAddress, length); + if (res >= 0) { + return res; + } + return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV); + } + + public final int read(ByteBuffer buf, int pos, int limit) throws IOException { + int res = read(fd, buf, pos, limit); + if (res > 0) { + return res; + } + if (res == 0) { + return -1; + } + return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ); + } + + public final int readAddress(long address, int pos, int limit) throws IOException { + int res = readAddress(fd, address, pos, limit); + if (res > 0) { + return res; + } + if (res == 0) { + return -1; + } + return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ); + } + @Override public String toString() { return "FileDescriptor{" + @@ -84,8 +144,6 @@ public class FileDescriptor { return fd; } - private static native int close(int fd); - /** * Open a new {@link FileDescriptor} for the given path. */ @@ -93,7 +151,7 @@ public class FileDescriptor { checkNotNull(path, "path"); int res = open(path); if (res < 0) { - throw Native.newIOException("open", res); + throw newIOException("open", res); } return new FileDescriptor(res); } @@ -105,5 +163,27 @@ public class FileDescriptor { return from(checkNotNull(file, "file").getPath()); } + /** + * @return [0] = read end, [1] = write end + */ + public static FileDescriptor[] pipe() throws IOException { + long res = newPipe(); + if (res < 0) { + throw newIOException("newPipe", (int) res); + } + return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)}; + } + private static native int open(String path); + private static native int close(int fd); + + private static native int write(int fd, ByteBuffer buf, int pos, int limit); + private static native int writeAddress(int fd, long address, int pos, int limit); + private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length); + private static native long writevAddresses(int fd, long memoryAddress, int length); + + private static native int read(int fd, ByteBuffer buf, int pos, int limit); + private static native int readAddress(int fd, long address, int pos, int limit); + + private static native long newPipe(); } diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/NativeInetAddress.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/NativeInetAddress.java new file mode 100644 index 0000000000..599f823122 --- /dev/null +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/NativeInetAddress.java @@ -0,0 +1,107 @@ +/* + * Copyright 2015 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.unix; + +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; + +/** + * Internal usage only! + */ +public final class NativeInetAddress { + private static final byte[] IPV4_MAPPED_IPV6_PREFIX = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff }; + final byte[] address; + final int scopeId; + + public static NativeInetAddress newInstance(InetAddress addr) { + byte[] bytes = addr.getAddress(); + if (addr instanceof Inet6Address) { + return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId()); + } else { + // convert to ipv4 mapped ipv6 address; + return new NativeInetAddress(ipv4MappedIpv6Address(bytes)); + } + } + + public NativeInetAddress(byte[] address, int scopeId) { + this.address = address; + this.scopeId = scopeId; + } + + public NativeInetAddress(byte[] address) { + this(address, 0); + } + + public byte[] address() { + return address; + } + + public int scopeId() { + return scopeId; + } + + public static byte[] ipv4MappedIpv6Address(byte[] ipv4) { + byte[] address = new byte[16]; + System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length); + System.arraycopy(ipv4, 0, address, 12, ipv4.length); + return address; + } + + public static InetSocketAddress address(byte[] addr, int offset, int len) { + // The last 4 bytes are always the port + final int port = decodeInt(addr, offset + len - 4); + final InetAddress address; + + try { + switch (len) { + // 8 bytes: + // - 4 == ipaddress + // - 4 == port + case 8: + byte[] ipv4 = new byte[4]; + System.arraycopy(addr, offset, ipv4, 0, 4); + address = InetAddress.getByAddress(ipv4); + break; + + // 24 bytes: + // - 16 == ipaddress + // - 4 == scopeId + // - 4 == port + case 24: + byte[] ipv6 = new byte[16]; + System.arraycopy(addr, offset, ipv6, 0, 16); + int scopeId = decodeInt(addr, offset + len - 8); + address = Inet6Address.getByAddress(null, ipv6, scopeId); + break; + default: + throw new Error(); + } + return new InetSocketAddress(address, port); + } catch (UnknownHostException e) { + throw new Error("Should never happen", e); + } + } + + static int decodeInt(byte[] addr, int index) { + return (addr[index] & 0xff) << 24 | + (addr[index + 1] & 0xff) << 16 | + (addr[index + 2] & 0xff) << 8 | + addr[index + 3] & 0xff; + } +} diff --git a/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java new file mode 100644 index 0000000000..d92709f886 --- /dev/null +++ b/transport-native-epoll/src/main/java/io/netty/channel/unix/Socket.java @@ -0,0 +1,359 @@ +/* + * Copyright 2015 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.unix; + +import io.netty.channel.ChannelException; +import io.netty.util.CharsetUtil; + +import java.io.IOException; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; + +import static io.netty.channel.unix.Errors.CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION; +import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDMSG; +import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDTO; +import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE; +import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE; +import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE; +import static io.netty.channel.unix.Errors.ioResult; +import static io.netty.channel.unix.Errors.newConnectException; +import static io.netty.channel.unix.Errors.newIOException; +import static io.netty.channel.unix.NativeInetAddress.address; +import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address; + +/** + * Provides a JNI bridge to native socket operations. + * Internal usage only! + */ +public final class Socket extends FileDescriptor { + private volatile boolean inputShutdown; + private volatile boolean outputShutdown; + + public Socket(int fd) { + super(fd); + } + + public void shutdown(boolean read, boolean write) throws IOException { + inputShutdown = read || inputShutdown; + outputShutdown = write || outputShutdown; + int res = shutdown(intValue(), read, write); + if (res < 0) { + ioResult("shutdown", res, CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION); + } + } + + public boolean isShutdown() { + return isInputShutdown() && isOutputShutdown(); + } + + public boolean isInputShutdown() { + return inputShutdown; + } + + public boolean isOutputShutdown() { + return outputShutdown; + } + + public int sendTo(ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException { + // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected + // to be called frequently + byte[] address; + int scopeId; + if (addr instanceof Inet6Address) { + address = addr.getAddress(); + scopeId = ((Inet6Address) addr).getScopeId(); + } else { + // convert to ipv4 mapped ipv6 address; + scopeId = 0; + address = ipv4MappedIpv6Address(addr.getAddress()); + } + int res = sendTo(intValue(), buf, pos, limit, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO); + } + + public int sendToAddress(long memoryAddress, int pos, int limit, InetAddress addr, int port) + throws IOException { + // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected + // to be called frequently + byte[] address; + int scopeId; + if (addr instanceof Inet6Address) { + address = addr.getAddress(); + scopeId = ((Inet6Address) addr).getScopeId(); + } else { + // convert to ipv4 mapped ipv6 address; + scopeId = 0; + address = ipv4MappedIpv6Address(addr.getAddress()); + } + int res = sendToAddress(intValue(), memoryAddress, pos, limit, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO); + } + + public int sendToAddresses(long memoryAddress, int length, InetAddress addr, int port) throws IOException { + // just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected + // to be called frequently + byte[] address; + int scopeId; + if (addr instanceof Inet6Address) { + address = addr.getAddress(); + scopeId = ((Inet6Address) addr).getScopeId(); + } else { + // convert to ipv4 mapped ipv6 address; + scopeId = 0; + address = ipv4MappedIpv6Address(addr.getAddress()); + } + int res = sendToAddresses(intValue(), memoryAddress, length, address, scopeId, port); + if (res >= 0) { + return res; + } + return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG); + } + + public DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException { + return recvFrom(intValue(), buf, pos, limit); + } + + public DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException { + return recvFromAddress(intValue(), memoryAddress, pos, limit); + } + + public boolean connect(SocketAddress socketAddress) throws IOException { + int res; + if (socketAddress instanceof InetSocketAddress) { + InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; + NativeInetAddress address = NativeInetAddress.newInstance(inetSocketAddress.getAddress()); + res = connect(intValue(), address.address, address.scopeId, inetSocketAddress.getPort()); + } else if (socketAddress instanceof DomainSocketAddress) { + DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress; + res = connectDomainSocket(intValue(), unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8)); + } else { + throw new Error("Unexpected SocketAddress implementation " + socketAddress); + } + if (res < 0) { + if (res == ERRNO_EINPROGRESS_NEGATIVE) { + // connect not complete yet need to wait for EPOLLOUT event + return false; + } + throw newConnectException("connect", res); + } + return true; + } + + public boolean finishConnect() throws IOException { + int res = finishConnect(intValue()); + if (res < 0) { + if (res == ERRNO_EINPROGRESS_NEGATIVE) { + // connect still in progress + return false; + } + throw newConnectException("finishConnect", res); + } + return true; + } + + public void bind(SocketAddress socketAddress) throws IOException { + if (socketAddress instanceof InetSocketAddress) { + InetSocketAddress addr = (InetSocketAddress) socketAddress; + NativeInetAddress address = NativeInetAddress.newInstance(addr.getAddress()); + int res = bind(intValue(), address.address, address.scopeId, addr.getPort()); + if (res < 0) { + throw newIOException("bind", res); + } + } else if (socketAddress instanceof DomainSocketAddress) { + DomainSocketAddress addr = (DomainSocketAddress) socketAddress; + int res = bindDomainSocket(intValue(), addr.path().getBytes(CharsetUtil.UTF_8)); + if (res < 0) { + throw newIOException("bind", res); + } + } else { + throw new Error("Unexpected SocketAddress implementation " + socketAddress); + } + } + + public void listen(int backlog) throws IOException { + int res = listen(intValue(), backlog); + if (res < 0) { + throw newIOException("listen", res); + } + } + + public int accept(byte[] addr) throws IOException { + int res = accept(intValue(), addr); + if (res >= 0) { + return res; + } + if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) { + // Everything consumed so just return -1 here. + return -1; + } + throw newIOException("accept", res); + } + + public InetSocketAddress remoteAddress() { + byte[] addr = remoteAddress(intValue()); + // addr may be null if getpeername failed. + // See https://github.com/netty/netty/issues/3328 + if (addr == null) { + return null; + } + return address(addr, 0, addr.length); + } + + public InetSocketAddress localAddress() { + byte[] addr = localAddress(intValue()); + // addr may be null if getpeername failed. + // See https://github.com/netty/netty/issues/3328 + if (addr == null) { + return null; + } + return address(addr, 0, addr.length); + } + + public int getReceiveBufferSize() { + return getReceiveBufferSize(intValue()); + } + + public int getSendBufferSize() { + return getSendBufferSize(intValue()); + } + + public boolean isKeepAlive() { + return isKeepAlive(intValue()) != 0; + } + + public boolean isTcpNoDelay() { + return isTcpNoDelay(intValue()) != 0; + } + + public boolean isTcpCork() { + return isTcpCork(intValue()) != 0; + } + + public int getSoLinger() { + return getSoLinger(intValue()); + } + + public int getSoError() { + return getSoError(intValue()); + } + + public void setKeepAlive(boolean keepAlive) { + setKeepAlive(intValue(), keepAlive ? 1 : 0); + } + + public void setReceiveBufferSize(int receiveBufferSize) { + setReceiveBufferSize(intValue(), receiveBufferSize); + } + + public void setSendBufferSize(int sendBufferSize) { + setSendBufferSize(intValue(), sendBufferSize); + } + + public void setTcpNoDelay(boolean tcpNoDelay) { + setTcpNoDelay(intValue(), tcpNoDelay ? 1 : 0); + } + + public void setTcpCork(boolean tcpCork) { + setTcpCork(intValue(), tcpCork ? 1 : 0); + } + + public void setSoLinger(int soLinger) { + setSoLinger(intValue(), soLinger); + } + + @Override + public String toString() { + return "Socket{" + + "fd=" + intValue() + + '}'; + } + + public static Socket newSocketStream() { + int res = newSocketStreamFd(); + if (res < 0) { + throw new ChannelException(newIOException("newSocketStream", res)); + } + return new Socket(res); + } + + public static Socket newSocketDgram() { + int res = newSocketDgramFd(); + if (res < 0) { + throw new ChannelException(newIOException("newSocketDgram", res)); + } + return new Socket(res); + } + + public static Socket newSocketDomain() { + int res = newSocketDomainFd(); + if (res < 0) { + throw new ChannelException(newIOException("newSocketDomain", res)); + } + return new Socket(res); + } + + private static native int shutdown(int fd, boolean read, boolean write); + private static native int connect(int fd, byte[] address, int scopeId, int port); + private static native int connectDomainSocket(int fd, byte[] path); + private static native int finishConnect(int fd); + private static native int bind(int fd, byte[] address, int scopeId, int port); + private static native int bindDomainSocket(int fd, byte[] path); + private static native int listen(int fd, int backlog); + private static native int accept(int fd, byte[] addr); + + private static native byte[] remoteAddress(int fd); + private static native byte[] localAddress(int fd); + + private static native int sendTo( + int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port); + private static native int sendToAddress( + int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port); + private static native int sendToAddresses( + int fd, long memoryAddress, int length, byte[] address, int scopeId, int port); + + private static native DatagramSocketAddress recvFrom( + int fd, ByteBuffer buf, int pos, int limit) throws IOException; + private static native DatagramSocketAddress recvFromAddress( + int fd, long memoryAddress, int pos, int limit) throws IOException; + + private static native int newSocketStreamFd(); + private static native int newSocketDgramFd(); + private static native int newSocketDomainFd(); + + private static native int getReceiveBufferSize(int fd); + private static native int getSendBufferSize(int fd); + private static native int isKeepAlive(int fd); + private static native int isTcpNoDelay(int fd); + private static native int isTcpCork(int fd); + private static native int getSoLinger(int fd); + private static native int getSoError(int fd); + + private static native void setKeepAlive(int fd, int keepAlive); + private static native void setReceiveBufferSize(int fd, int receiveBufferSize); + private static native void setSendBufferSize(int fd, int sendBufferSize); + 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); +} diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java index b29967ae96..109f388f07 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollSocketChannelTest.java @@ -145,7 +145,6 @@ public class EpollSocketChannelTest { Bootstrap b = new Bootstrap(); b.group(group); b.channel(channelClass); - b.option(ChannelOption.SO_KEEPALIVE, true); b.remoteAddress(serverChannel.localAddress()); b.handler(new MyInitializer()); clientChannel = b.connect().syncUninterruptibly().channel(); diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java index a28d785e00..770c0394db 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/NativeTest.java @@ -23,6 +23,8 @@ import java.net.Inet6Address; import java.net.InetSocketAddress; import java.nio.ByteBuffer; +import static io.netty.channel.unix.NativeInetAddress.address; + public class NativeTest { @Test @@ -32,7 +34,7 @@ public class NativeTest { ByteBuffer buffer = ByteBuffer.wrap(bytes); buffer.put(inetAddress.getAddress().getAddress()); buffer.putInt(inetAddress.getPort()); - Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length)); + Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length)); } @Test @@ -44,6 +46,6 @@ public class NativeTest { buffer.put(address.getAddress()); buffer.putInt(address.getScopeId()); buffer.putInt(inetAddress.getPort()); - Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length)); + Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length)); } }