--- old/make/mapfiles/libnet/mapfile-vers 2015-11-18 17:26:30.691896493 -0800 +++ new/make/mapfiles/libnet/mapfile-vers 2015-11-18 17:26:30.592896492 -0800 @@ -99,6 +99,9 @@ Java_sun_net_ExtendedOptionsImpl_setFlowOption; Java_sun_net_ExtendedOptionsImpl_getFlowOption; Java_sun_net_ExtendedOptionsImpl_flowSupported; + Java_sun_net_ExtendedOptionsImpl_setReusePortOption; + Java_sun_net_ExtendedOptionsImpl_getReusePortOption; + Java_sun_net_ExtendedOptionsImpl_reuseportSupported; NET_AllocSockaddr; NET_SockaddrToInetAddress; NET_SockaddrEqualsInetAddress; --- old/src/java.base/share/classes/java/net/MulticastSocket.java 2015-11-18 17:26:30.930896495 -0800 +++ new/src/java.base/share/classes/java/net/MulticastSocket.java 2015-11-18 17:26:30.830896494 -0800 @@ -27,6 +27,8 @@ import java.io.IOException; import java.util.Enumeration; +import jdk.net.Sockets; +import jdk.net.ExtendedSocketOptions; /** * The multicast datagram socket class is useful for sending @@ -166,7 +168,10 @@ // Enable SO_REUSEADDR before binding setReuseAddress(true); - + + // Enable SO_REUSEPORT before binding + Sockets.setOption(this, ExtendedSocketOptions.SO_REUSEPORT, true); + if (bindaddr != null) { try { bind(bindaddr); --- old/src/java.base/share/classes/jdk/net/ExtendedSocketOptions.java 2015-11-18 17:26:31.170896497 -0800 +++ new/src/java.base/share/classes/jdk/net/ExtendedSocketOptions.java 2015-11-18 17:26:31.071896496 -0800 @@ -59,4 +59,15 @@ */ public static final SocketOption SO_FLOW_SLA = new ExtSocketOption("SO_FLOW_SLA", SocketFlow.class); + + /** + * Sets SO_REUSEPORT for a socket. This option enables and disables + * the ability of having multiple sockets listen to the same address + * and port. It is supported in Linux with kernel 3.9+. + * Setting or getting this option requires + * {@code ("setOption.SO_REUSEPORT")} or {@code "getOption.SO_REUSEPORT"} + * respectively. + */ + public static final SocketOption SO_REUSEPORT = new + ExtSocketOption("SO_REUSEPORT", Boolean.class); } --- old/src/java.base/share/classes/jdk/net/Sockets.java 2015-11-18 17:26:31.406896499 -0800 +++ new/src/java.base/share/classes/jdk/net/Sockets.java 2015-11-18 17:26:31.308896498 -0800 @@ -254,6 +254,7 @@ private static void initOptionSets() { boolean flowsupported = ExtendedOptionsImpl.flowSupported(); + boolean reuseportsupported = ExtendedOptionsImpl.reuseportSupported(); // Socket @@ -268,6 +269,9 @@ if (flowsupported) { set.add(ExtendedSocketOptions.SO_FLOW_SLA); } + if (reuseportsupported) { + set.add(ExtendedSocketOptions.SO_REUSEPORT); + } set = Collections.unmodifiableSet(set); options.put(Socket.class, set); @@ -277,6 +281,9 @@ set.add(StandardSocketOptions.SO_RCVBUF); set.add(StandardSocketOptions.SO_REUSEADDR); set.add(StandardSocketOptions.IP_TOS); + if (reuseportsupported) { + set.add(ExtendedSocketOptions.SO_REUSEPORT); + } set = Collections.unmodifiableSet(set); options.put(ServerSocket.class, set); @@ -290,6 +297,9 @@ if (flowsupported) { set.add(ExtendedSocketOptions.SO_FLOW_SLA); } + if (reuseportsupported) { + set.add(ExtendedSocketOptions.SO_REUSEPORT); + } set = Collections.unmodifiableSet(set); options.put(DatagramSocket.class, set); @@ -306,6 +316,9 @@ if (flowsupported) { set.add(ExtendedSocketOptions.SO_FLOW_SLA); } + if (reuseportsupported) { + set.add(ExtendedSocketOptions.SO_REUSEPORT); + } set = Collections.unmodifiableSet(set); options.put(MulticastSocket.class, set); } --- old/src/java.base/share/classes/sun/net/ExtendedOptionsImpl.java 2015-11-18 17:26:31.641896502 -0800 +++ new/src/java.base/share/classes/sun/net/ExtendedOptionsImpl.java 2015-11-18 17:26:31.543896501 -0800 @@ -89,4 +89,13 @@ public static native void setFlowOption(FileDescriptor fd, SocketFlow f); public static native void getFlowOption(FileDescriptor fd, SocketFlow f); public static native boolean flowSupported(); + + /* + * Extension native implementations + * + * SO_REUSEPORT + */ + public static native void setReusePortOption(FileDescriptor fd, boolean on); + public static native Object getReusePortOption(FileDescriptor fd); + public static native boolean reuseportSupported(); } --- old/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java 2015-11-18 17:26:31.878896504 -0800 +++ new/src/java.base/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java 2015-11-18 17:26:31.779896503 -0800 @@ -512,6 +512,9 @@ if (ExtendedOptionsImpl.flowSupported()) { set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); } + if (ExtendedOptionsImpl.reuseportSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_REUSEPORT); + } return Collections.unmodifiableSet(set); } } --- old/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java 2015-11-18 17:26:32.118896506 -0800 +++ new/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java 2015-11-18 17:26:32.018896505 -0800 @@ -306,6 +306,9 @@ if (ExtendedOptionsImpl.flowSupported()) { set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); } + if (ExtendedOptionsImpl.reuseportSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_REUSEPORT); + } return Collections.unmodifiableSet(set); } } --- old/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java 2015-11-18 17:26:32.364896508 -0800 +++ new/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java 2015-11-18 17:26:32.263896507 -0800 @@ -242,6 +242,9 @@ if (ExtendedOptionsImpl.flowSupported()) { set.add(jdk.net.ExtendedSocketOptions.SO_FLOW_SLA); } + if (ExtendedOptionsImpl.reuseportSupported()) { + set.add(jdk.net.ExtendedSocketOptions.SO_REUSEPORT); + } return Collections.unmodifiableSet(set); } } --- old/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java 2015-11-18 17:26:32.609896511 -0800 +++ new/src/java.base/unix/classes/java/net/PlainDatagramSocketImpl.java 2015-11-18 17:26:32.509896510 -0800 @@ -44,30 +44,40 @@ } protected void setOption(SocketOption name, T value) throws IOException { - if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + if (!(name.equals(ExtendedSocketOptions.SO_FLOW_SLA) + || (name.equals(ExtendedSocketOptions.SO_REUSEPORT)))) { super.setOption(name, value); } else { if (isClosed()) { throw new SocketException("Socket closed"); } - checkSetOptionPermission(name); - checkValueType(value, SocketFlow.class); - setFlowOption(getFileDescriptor(), (SocketFlow)value); - } + if (name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + checkSetOptionPermission(name); + checkValueType(value, SocketFlow.class); + setFlowOption(getFileDescriptor(), (SocketFlow)value); + } else { + checkValueType(value, Boolean.class); + setReusePortOption(getFileDescriptor(), ((Boolean)value).booleanValue()); + } + } } @SuppressWarnings("unchecked") protected T getOption(SocketOption name) throws IOException { - if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + if (!(name.equals(ExtendedSocketOptions.SO_FLOW_SLA) + || (name.equals(ExtendedSocketOptions.SO_REUSEPORT)))) { return super.getOption(name); } if (isClosed()) { throw new SocketException("Socket closed"); } - checkGetOptionPermission(name); - SocketFlow flow = SocketFlow.create(); - getFlowOption(getFileDescriptor(), flow); - return (T)flow; + if (name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + checkGetOptionPermission(name); + SocketFlow flow = SocketFlow.create(); + getFlowOption(getFileDescriptor(), flow); + return (T)flow; + } + return (T)getReusePortOption(getFileDescriptor()); } protected Set> supportedOptions() { @@ -77,6 +87,9 @@ if (flowSupported()) { options.add(ExtendedSocketOptions.SO_FLOW_SLA); } + if (reuseportSupported()) { + options.add(ExtendedSocketOptions.SO_REUSEPORT); + } return options; } --- old/src/java.base/unix/classes/java/net/PlainSocketImpl.java 2015-11-18 17:26:32.869896513 -0800 +++ new/src/java.base/unix/classes/java/net/PlainSocketImpl.java 2015-11-18 17:26:32.768896512 -0800 @@ -58,30 +58,39 @@ } protected void setOption(SocketOption name, T value) throws IOException { - if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + if (!(name.equals(ExtendedSocketOptions.SO_FLOW_SLA) + || (name.equals(ExtendedSocketOptions.SO_REUSEPORT)))) { super.setOption(name, value); } else { if (isClosedOrPending()) { throw new SocketException("Socket closed"); } - checkSetOptionPermission(name); - checkValueType(value, SocketFlow.class); - setFlowOption(getFileDescriptor(), (SocketFlow)value); + if (name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + checkSetOptionPermission(name); + checkValueType(value, SocketFlow.class); + setFlowOption(getFileDescriptor(), (SocketFlow)value); + } else { + setReusePortOption(getFileDescriptor(), ((Boolean)value).booleanValue()); + } } } @SuppressWarnings("unchecked") protected T getOption(SocketOption name) throws IOException { - if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + if (!(name.equals(ExtendedSocketOptions.SO_FLOW_SLA) + || (name.equals(ExtendedSocketOptions.SO_REUSEPORT)))) { return super.getOption(name); } if (isClosedOrPending()) { throw new SocketException("Socket closed"); } - checkGetOptionPermission(name); - SocketFlow flow = SocketFlow.create(); - getFlowOption(getFileDescriptor(), flow); - return (T)flow; + if (name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) { + checkGetOptionPermission(name); + SocketFlow flow = SocketFlow.create(); + getFlowOption(getFileDescriptor(), flow); + return (T)flow; + } + return (T)getReusePortOption(getFileDescriptor()); } protected Set> supportedOptions() { @@ -91,6 +100,9 @@ if (getSocket() != null && flowSupported()) { options.add(ExtendedSocketOptions.SO_FLOW_SLA); } + if (getSocket() != null && reuseportSupported()) { + options.add(ExtendedSocketOptions.SO_REUSEPORT); + } return options; } --- old/src/java.base/unix/native/libnet/ExtendedOptionsImpl.c 2015-11-18 17:26:33.110896516 -0800 +++ new/src/java.base/unix/native/libnet/ExtendedOptionsImpl.c 2015-11-18 17:26:33.010896515 -0800 @@ -54,6 +54,7 @@ /* OS specific code is implemented in these three functions */ static jboolean flowSupported0() ; +static jboolean reuseportSupported0() ; /* * Class: sun_net_ExtendedOptionsImpl @@ -142,6 +143,25 @@ } /* + * Returns a java.lang.Boolean based on 'b' + */ +static jobject createBoolean(JNIEnv *env, int b) { + static jclass b_class; + static jmethodID b_ctrID; + + if (b_class == NULL) { + jclass c = (*env)->FindClass(env, "java/lang/Boolean"); + CHECK_NULL_RETURN(c, NULL); + b_ctrID = (*env)->GetMethodID(env, c, "", "(Z)V"); + CHECK_NULL_RETURN(b_ctrID, NULL); + b_class = (*env)->NewGlobalRef(env, c); + CHECK_NULL_RETURN(b_class, NULL); + } + + return( (*env)->NewObject(env, b_class, b_ctrID, (jboolean)(b!=0)) ); +} + +/* * Retrieve the int file-descriptor from a public socket type object. * Gets impl, then the FileDescriptor from the impl, and then the fd * from that. @@ -337,8 +357,131 @@ #endif /* __solaris__ */ + +#ifdef __linux__ +/* + * Class: sun_net_ExtendedOptionsImpl + * Method: setReusePortOption + * Signature: (Ljava/io/FileDescriptor;Ljava/lang/Object;)V + */ +JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) +{ + int fd = getFD(env, fileDesc); + int optval; + + if (fd < 0) { + NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); + return; + } else { + int rv; + optval = (on ? 1 : 0); + rv = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); + } else { + NET_ERROR(env, JNU_JAVANETPKG "SocketException", + "set option SO_REUSEPORT failed"); + } + return; + } + } +} +/* + * Class: sun_net_ExtendedOptionsImpl + * Method: getReusePortOption + * Signature: (Ljava/io/FileDescriptor;)Ljava/lang/Object; + */ +JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc) +{ + int fd = getFD(env, fileDesc); + + if (fd < 0) { + NET_ERROR(env, JNU_JAVANETPKG "SocketException", "socket closed"); + return JNI_FALSE; + } else { + int on; + socklen_t sz = sizeof(on); + + int rv = getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, &sz); + if (rv < 0) { + if (errno == ENOPROTOOPT) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); + } else { + NET_ERROR(env, JNU_JAVANETPKG "SocketException", + "set option SO_REUSEPORT failed"); + } + } + printf("getReusePortOption returns %d\n", on); + return createBoolean(env, on); + } +} + +static jboolean reuseportsupported; +static jboolean reuseportsupported_set = JNI_FALSE; + +static jboolean reuseportSupported0() +{ + /* Do a simple dummy call, and try to figure out from that */ + int one = 1; + int rv, s; + if (reuseportsupported_set) { + return reuseportsupported; + } + s = socket(PF_INET, SOCK_STREAM, 0); + if (s < 0) { + reuseportsupported = JNI_FALSE; + reuseportsupported_set = JNI_TRUE; + return JNI_FALSE; + } + rv = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (void *)&one, sizeof(one)); + if (rv != 0 && errno == ENOPROTOOPT) { + rv = JNI_FALSE; + } else { + rv = JNI_TRUE; + } + close(s); + reuseportsupported = rv; + reuseportsupported_set = JNI_TRUE; + return reuseportsupported; +} + +#else /* __linux__ */ + +/* Non Linux. Functionality is not supported. So, throw UnsupportedOpExc */ + +JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) +{ + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); +} + +JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc) +{ + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); + return JNI_FALSE; +} + +static jboolean reuseportSupported0() { + return JNI_FALSE; +} +#endif /* __linux__ */ + JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_flowSupported (JNIEnv *env, jclass UNUSED) { return flowSupported0(); } + +JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_reuseportSupported + (JNIEnv *env, jclass UNUSED) +{ + return reuseportSupported0(); +} --- old/src/java.base/unix/native/libnet/net_util_md.h 2015-11-18 17:26:33.350896518 -0800 +++ new/src/java.base/unix/native/libnet/net_util_md.h 2015-11-18 17:26:33.250896517 -0800 @@ -105,6 +105,10 @@ #endif #endif +#ifdef __linux__ +#define SO_REUSEPORT 15 +#endif + #ifdef __solaris__ int net_getParam(char *driver, char *param); --- old/src/java.base/windows/native/libnet/ExtendedOptionsImpl.c 2015-11-18 17:26:33.590896520 -0800 +++ new/src/java.base/windows/native/libnet/ExtendedOptionsImpl.c 2015-11-18 17:26:33.489896519 -0800 @@ -63,3 +63,24 @@ { return JNI_FALSE; } + +JNIEXPORT void JNICALL Java_sun_net_ExtendedOptionsImpl_setReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc, jboolean on) +{ + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); +} + +JNIEXPORT jobject JNICALL Java_sun_net_ExtendedOptionsImpl_getReusePortOption + (JNIEnv *env, jclass UNUSED, jobject fileDesc) +{ + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + "unsupported socket option"); + return JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_sun_net_ExtendedOptionsImpl_reuseportSupported + (JNIEnv *env, jclass UNUSED) +{ + return JNI_FALSE; +} --- old/test/jdk/net/Sockets/Test.java 2015-11-18 17:26:33.836896522 -0800 +++ new/test/jdk/net/Sockets/Test.java 2015-11-18 17:26:33.729896521 -0800 @@ -40,11 +40,34 @@ static boolean security; static boolean success; + static int testCount = 0; interface Runner { public void run() throws Exception; } + static void test(String msg) { + testCount++; + System.out.println("***************************************"); + System.out.println("Test " + testCount + ": " + msg); + } + + static void passed() { + System.out.println("Test passed."); + } + + static void failed() { + System.out.println("Test failed."); + } + + static void check(boolean pass) { + if (pass) { + passed(); + } else { + failed(); + } + } + public static void main(String[] args) throws Exception { // quick check to see if supportedOptions() working before @@ -69,59 +92,203 @@ .bandwidth(1000) .priority(SocketFlow.HIGH_PRIORITY); - ServerSocket ss = new ServerSocket(0); - int tcp_port = ss.getLocalPort(); - final InetAddress loop = InetAddress.getByName("127.0.0.1"); - final InetSocketAddress loopad = new InetSocketAddress(loop, tcp_port); - - DatagramSocket dg = new DatagramSocket(0); - final int udp_port = dg.getLocalPort(); + boolean flowsupported = true; + boolean reuseportsupported = true; // If option not available, end test + DatagramSocket dg = new DatagramSocket(0); Set> options = dg.supportedOptions(); if (!options.contains(ExtendedSocketOptions.SO_FLOW_SLA)) { System.out.println("SO_FLOW_SLA not supported"); - return; + flowsupported = false; + } + if (!options.contains(ExtendedSocketOptions.SO_REUSEPORT)) { + System.out.println("SO_REUSEPORT not supported"); + reuseportsupported = false; } - final Socket s = new Socket("127.0.0.1", tcp_port); - final SocketChannel sc = SocketChannel.open(); - sc.connect (new InetSocketAddress("127.0.0.1", tcp_port)); - - doTest(()->{ - Sockets.setOption(s, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); - doTest(()->{ - Sockets.getOption(s, ExtendedSocketOptions.SO_FLOW_SLA); - }); - doTest(()->{ - sc.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); - doTest(()->{ - sc.getOption(ExtendedSocketOptions.SO_FLOW_SLA); - }); - doTest(()->{ - DatagramSocket dg1 = new DatagramSocket(0); - dg1.connect(loop, udp_port); - Sockets.setOption(dg1, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); - doTest(()->{ - DatagramChannel dg2 = DatagramChannel.open(); - dg2.bind(new InetSocketAddress(loop, 0)); - dg2.connect(new InetSocketAddress(loop, udp_port)); - dg2.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); - doTest(()->{ - MulticastSocket mc1 = new MulticastSocket(0); - mc1.connect(loop, udp_port); - Sockets.setOption(mc1, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); - doTest(()->{ - AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); - Future f = asc.connect(loopad); - f.get(); - asc.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); - }); + if (flowsupported) { + ServerSocket ss = new ServerSocket(0); + int tcp_port = ss.getLocalPort(); + final InetAddress loop = InetAddress.getByName("127.0.0.1"); + final InetSocketAddress loopad = new InetSocketAddress(loop, tcp_port); + + final int udp_port = dg.getLocalPort(); + + final Socket s = new Socket("127.0.0.1", tcp_port); + final SocketChannel sc = SocketChannel.open(); + sc.connect (new InetSocketAddress("127.0.0.1", tcp_port)); + + doTest(()->{ + Sockets.setOption(s, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + doTest(()->{ + Sockets.getOption(s, ExtendedSocketOptions.SO_FLOW_SLA); + }); + doTest(()->{ + sc.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + doTest(()->{ + sc.getOption(ExtendedSocketOptions.SO_FLOW_SLA); + }); + doTest(()->{ + DatagramSocket dg1 = new DatagramSocket(0); + dg1.connect(loop, udp_port); + Sockets.setOption(dg1, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + doTest(()->{ + DatagramChannel dg2 = DatagramChannel.open(); + dg2.bind(new InetSocketAddress(loop, 0)); + dg2.connect(new InetSocketAddress(loop, udp_port)); + dg2.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + doTest(()->{ + MulticastSocket mc1 = new MulticastSocket(0); + mc1.connect(loop, udp_port); + Sockets.setOption(mc1, ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + doTest(()->{ + AsynchronousSocketChannel asc = AsynchronousSocketChannel.open(); + Future f = asc.connect(loopad); + f.get(); + asc.setOption(ExtendedSocketOptions.SO_FLOW_SLA, flowIn); + }); + } + if (reuseportsupported) { + Socket s1 = new Socket(); + Socket s2 = new Socket(); + test("Socket should be created with SO_REUSEPORT disabled"); + check(!Sockets.getOption(s1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("Socket.set ReusePort(true)"); + Sockets.setOption(s1, ExtendedSocketOptions.SO_REUSEPORT, true); + check(Sockets.getOption(s1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("Socket.set ReusePort(false)"); + Sockets.setOption(s1, ExtendedSocketOptions.SO_REUSEPORT, false); + check(!Sockets.getOption(s1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("Without setting SO_REUSEPORT, binding Socket to port already in use should throw a SocketException"); + s1.bind(new InetSocketAddress(0)); + try { + s2.bind( new InetSocketAddress(s1.getLocalPort())); + failed(); + } catch (SocketException e) { + passed(); + } + s1.close(); + s2.close(); + + test("By setting SO_REUSEPORT, binding Socket to port already in use should not throw a SocketException"); + s1 = new Socket(); + s2 = new Socket(); + Sockets.setOption(s1, ExtendedSocketOptions.SO_REUSEPORT, true); + Sockets.setOption(s2, ExtendedSocketOptions.SO_REUSEPORT, true); + s1.bind(new InetSocketAddress(0)); + try { + s2.bind( new InetSocketAddress(s1.getLocalPort()) ); + passed(); + } catch (SocketException e) { + failed(); + } + s1.close(); + s2.close(); + + ServerSocket ss1 = new ServerSocket(); + ServerSocket ss2 = new ServerSocket(); + + test("Server Socket should be created with SO_REUSEPORT disabled"); + check(!Sockets.getOption(ss1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("ServerSocket set ReusePort(true)"); + Sockets.setOption(ss1, ExtendedSocketOptions.SO_REUSEPORT, true); + check(Sockets.getOption(ss1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("ServerSocket set ReusePort(false)"); + Sockets.setOption(ss1, ExtendedSocketOptions.SO_REUSEPORT, false); + check(!Sockets.getOption(ss1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("Without setting SO_REUSEPORT, binding Server Socket to port already in use should throw a SocketException"); + ss1.bind(new InetSocketAddress(0)); + try { + ss2.bind( new InetSocketAddress(ss1.getLocalPort())); + failed(); + } catch (SocketException e) { + passed(); + } + ss1.close(); + ss2.close(); + + test("By setting SO_REUSEPORT, binding Server Socket to port already in use should not throw a SocketException"); + ss1 = new ServerSocket(); + ss2 = new ServerSocket(); + Sockets.setOption(ss1, ExtendedSocketOptions.SO_REUSEPORT, true); + Sockets.setOption(ss2, ExtendedSocketOptions.SO_REUSEPORT, true); + ss1.bind(new InetSocketAddress(0)); + try { + ss2.bind( new InetSocketAddress(ss1.getLocalPort()) ); + passed(); + } catch (SocketException e) { + failed(); + } + ss1.close(); + ss2.close(); + + DatagramSocket dg1 = new DatagramSocket(null); + DatagramSocket dg2 = new DatagramSocket(null); + + test("DatagramSocket should be created with SO_REUSEPORT disabled"); + check(!Sockets.getOption(dg1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("DatagramSocket.setReusePort(true)"); + Sockets.setOption(dg1, ExtendedSocketOptions.SO_REUSEPORT, true); + check(Sockets.getOption(dg1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("DatagramSocket.setReusePort(false)"); + Sockets.setOption(dg1, ExtendedSocketOptions.SO_REUSEPORT, false); + check(!Sockets.getOption(dg1, ExtendedSocketOptions.SO_REUSEPORT)); + + test("Without setting SO_REUSEPORT, binding Server Socket to port already in use should throw a SocketException"); + dg1.bind(new InetSocketAddress(0)); + try { + dg2.bind( new InetSocketAddress(dg1.getLocalPort())); + failed(); + } catch (SocketException e) { + passed(); + } + dg1.close(); + dg2.close(); + + test("By setting SO_REUSEPORT, binding Server Socket to port already in use should not throw a SocketException"); + dg1 = new DatagramSocket(null); + dg2 = new DatagramSocket(null); + Sockets.setOption(dg1, ExtendedSocketOptions.SO_REUSEPORT, true); + Sockets.setOption(dg2, ExtendedSocketOptions.SO_REUSEPORT, true); + dg1.bind(new InetSocketAddress(0)); + try { + dg2.bind( new InetSocketAddress(dg1.getLocalPort()) ); + passed(); + } catch (SocketException e) { + failed(); + } + dg1.close(); + dg2.close(); + + MulticastSocket mc1 = new MulticastSocket(); + + test("Check SO_REUSEPORT is enabled in MulticastSocket"); + check(Sockets.getOption(mc1, ExtendedSocketOptions.SO_REUSEPORT)); + mc1.close(); + + test("Check SO_REUSEPORT is not disabled by MulticastSocket.bind()"); + mc1 = new MulticastSocket(null); + + InetSocketAddress isa = new InetSocketAddress( + InetAddress.getLocalHost(), 0); + mc1.bind(isa); + check(Sockets.getOption(mc1, ExtendedSocketOptions.SO_REUSEPORT)); + mc1.close(); + } } static void doTest(Runner func) throws Exception {