1 /* 2 * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* @test 25 * @bug 8195160 26 * @summary Test socket-channel connection-state transitions 27 * @requires (os.family == "linux") 28 * @library .. /test/lib 29 * @build jdk.test.lib.Utils TestServers 30 * @build RsocketTest 31 * @run main/othervm -Djava.net.preferIPv4Stack=true ConnectState 32 */ 33 34 import java.io.*; 35 import java.net.*; 36 import java.nio.*; 37 import java.nio.channels.*; 38 import java.util.Arrays; 39 import java.util.Collection; 40 import java.util.Collections; 41 import java.util.HashSet; 42 import jdk.net.Sockets; 43 44 public class ConnectState { 45 46 static PrintStream log = System.err; 47 48 static InetSocketAddress remote; 49 50 final static int ST_UNCONNECTED = 0; 51 final static int ST_PENDING = 1; 52 final static int ST_CONNECTED = 2; 53 final static int ST_CLOSED = 3; 54 final static int ST_PENDING_OR_CONNECTED = 4; 55 // NO exceptions expected 56 final static Collection<Class<?>> NONE = Collections.emptySet(); 57 58 // make a set of expected exception. 59 static Collection<Class<?>> expectedExceptions(Class<?>... expected) { 60 final Collection<Class<?>> exceptions; 61 if (expected.length == 0) { 62 exceptions = NONE; 63 } else if (expected.length == 1) { 64 assert expected[0] != null; 65 exceptions = Collections.<Class<?>>singleton(expected[0]); 66 } else { 67 exceptions = new HashSet<>(Arrays.asList(expected)); 68 } 69 return exceptions; 70 } 71 72 static abstract class Test { 73 74 abstract String go(SocketChannel sc) throws Exception; 75 76 static void check(boolean test, String desc) throws Exception { 77 if (!test) 78 throw new Exception("Incorrect state: " + desc); 79 } 80 81 static void check(SocketChannel sc, int state) throws Exception { 82 switch (state) { 83 case ST_UNCONNECTED: 84 check(!sc.isConnected(), "!isConnected"); 85 check(!sc.isConnectionPending(), "!isConnectionPending"); 86 check(sc.isOpen(), "isOpen"); 87 break; 88 case ST_PENDING: 89 check(!sc.isConnected(), "!isConnected"); 90 check(sc.isConnectionPending(), "isConnectionPending"); 91 check(sc.isOpen(), "isOpen"); 92 break; 93 case ST_CONNECTED: 94 check(sc.isConnected(), "isConnected"); 95 check(!sc.isConnectionPending(), "!isConnectionPending"); 96 check(sc.isOpen(), "isOpen"); 97 break; 98 case ST_CLOSED: 99 check(sc.isConnected(), "isConnected"); 100 check(!sc.isConnectionPending(), "!isConnectionPending"); 101 check(sc.isOpen(), "isOpen"); 102 break; 103 case ST_PENDING_OR_CONNECTED: 104 check(sc.isConnected() || sc.isConnectionPending(), 105 "isConnected || isConnectionPending"); 106 check(sc.isOpen(), "isOpen"); 107 break; 108 } 109 } 110 111 Test(String name, Class<?> exception, int state) throws Exception { 112 this(name, expectedExceptions(exception), state); 113 } 114 115 // On some architecture we may need to accept several exceptions. 116 // For instance on Solaris, when using a server colocated on the 117 // machine we cannot guarantee that we will get a 118 // ConnectionPendingException when connecting twice on the same 119 // non-blocking socket. We may instead get a an 120 // AlreadyConnectedException, which is also valid: it simply means 121 // that the first connection has been immediately accepted. 122 Test(String name, Collection<Class<?>> exceptions, int state) 123 throws Exception { 124 SocketChannel sc = Sockets.openRdmaSocketChannel(); 125 String note; 126 try { 127 try { 128 note = go(sc); 129 } catch (Exception x) { 130 Class<?> expectedExceptionClass = null; 131 for (Class<?> exception : exceptions) { 132 if (exception.isInstance(x)) { 133 log.println(name + ": As expected: " 134 + x); 135 expectedExceptionClass = exception; 136 check(sc, state); 137 break; 138 } 139 } 140 if (expectedExceptionClass == null 141 && !exceptions.isEmpty()) { 142 // we had an exception, but it's not of the set of 143 // exceptions we expected. 144 throw new Exception(name 145 + ": Incorrect exception", 146 x); 147 } else if (exceptions.isEmpty()) { 148 // we didn't expect any exception 149 throw new Exception(name 150 + ": Unexpected exception", 151 x); 152 } 153 // if we reach here, we have our expected exception 154 assert expectedExceptionClass != null; 155 return; 156 } 157 if (!exceptions.isEmpty()) { 158 throw new Exception(name 159 + ": Expected exception not thrown: " 160 + exceptions.iterator().next()); 161 } 162 check(sc, state); 163 log.println(name + ": Returned normally" 164 + ((note != null) ? ": " + note : "")); 165 } finally { 166 if (sc.isOpen()) 167 sc.close(); 168 } 169 } 170 171 } 172 173 static void tests() throws Exception { 174 log.println(remote); 175 176 new Test("Read unconnected", NotYetConnectedException.class, 177 ST_UNCONNECTED) { 178 @Override 179 String go(SocketChannel sc) throws Exception { 180 ByteBuffer b = ByteBuffer.allocateDirect(1024); 181 sc.read(b); 182 return null; 183 }}; 184 185 new Test("Write unconnected", NotYetConnectedException.class, 186 ST_UNCONNECTED) { 187 @Override 188 String go(SocketChannel sc) throws Exception { 189 ByteBuffer b = ByteBuffer.allocateDirect(1024); 190 sc.write(b); 191 return null; 192 }}; 193 194 new Test("Simple connect", NONE, ST_CONNECTED) { 195 @Override 196 String go(SocketChannel sc) throws Exception { 197 sc.connect(remote); 198 return null; 199 }}; 200 201 new Test("Simple connect & finish", NONE, ST_CONNECTED) { 202 @Override 203 String go(SocketChannel sc) throws Exception { 204 sc.connect(remote); 205 if (!sc.finishConnect()) 206 throw new Exception("finishConnect returned false"); 207 return null; 208 }}; 209 210 new Test("Double connect", 211 AlreadyConnectedException.class, ST_CONNECTED) { 212 @Override 213 String go(SocketChannel sc) throws Exception { 214 sc.connect(remote); 215 sc.connect(remote); 216 return null; 217 }}; 218 219 new Test("Finish w/o start", 220 NoConnectionPendingException.class, ST_UNCONNECTED) { 221 @Override 222 String go(SocketChannel sc) throws Exception { 223 sc.finishConnect(); 224 return null; 225 }}; 226 227 // Note: using our local EchoServer rather than echo on a distant 228 // host - we see that Tries to finish = 0 (instead of ~ 18). 229 new Test("NB simple connect", NONE, ST_CONNECTED) { 230 @Override 231 String go(SocketChannel sc) throws Exception { 232 sc.configureBlocking(false); 233 sc.connect(remote); 234 int n = 0; 235 while (!sc.finishConnect()) { 236 Thread.sleep(10); 237 n++; 238 } 239 sc.finishConnect(); // Check redundant invocation 240 return ("Tries to finish = " + n); 241 }}; 242 243 // Note: using our local EchoServer rather than echo on a distant 244 // host - we cannot guarantee that this test will get a 245 // a ConnectionPendingException: it may get an 246 // AlreadyConnectedException, so we should allow for both. 247 new Test("NB double connect", 248 expectedExceptions(ConnectionPendingException.class, 249 AlreadyConnectedException.class), 250 ST_PENDING_OR_CONNECTED) { 251 @Override 252 String go(SocketChannel sc) throws Exception { 253 sc.configureBlocking(false); 254 sc.connect(remote); 255 sc.connect(remote); 256 return null; 257 }}; 258 259 new Test("NB finish w/o start", 260 NoConnectionPendingException.class, ST_UNCONNECTED) { 261 @Override 262 String go(SocketChannel sc) throws Exception { 263 sc.configureBlocking(false); 264 sc.finishConnect(); 265 return null; 266 }}; 267 268 new Test("NB connect, B finish", NONE, ST_CONNECTED) { 269 @Override 270 String go(SocketChannel sc) throws Exception { 271 sc.configureBlocking(false); 272 sc.connect(remote); 273 sc.configureBlocking(true); 274 sc.finishConnect(); 275 return null; 276 }}; 277 278 } 279 280 public static void main(String[] args) throws Exception { 281 if (!RsocketTest.isRsocketAvailable()) 282 return; 283 284 try (TestServers.EchoServer echoServer 285 = TestServers.EchoServer.startNewServer(500)) { 286 remote = new InetSocketAddress(echoServer.getAddress(), 287 echoServer.getPort()); 288 tests(); 289 } 290 } 291 292 }