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 }