1 /*
   2  * Copyright (c) 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 RDMA SocketChannel.finishConnect
  27  * @requires (os.family == "linux")
  28  * @library .. /test/lib
  29  * @build jdk.test.lib.Utils TestServers
  30  * @build RsocketTest
  31  * @run main/othervm FinishConnect
  32  */
  33 
  34 import java.net.InetSocketAddress;
  35 import java.net.StandardProtocolFamily;
  36 import java.nio.ByteBuffer;
  37 import java.nio.CharBuffer;
  38 import java.nio.channels.Selector;
  39 import java.nio.channels.SelectionKey;
  40 import java.nio.channels.SocketChannel;
  41 import java.nio.channels.spi.SelectorProvider;
  42 import java.nio.charset.Charset;
  43 import java.util.Iterator;
  44 import java.util.Set;
  45 import jdk.net.RdmaSockets;
  46 
  47 import jtreg.SkippedException;
  48 
  49 public class FinishConnect {
  50 
  51     public static void main(String[] args) throws Exception {
  52         if (!RsocketTest.isRsocketAvailable())
  53             throw new SkippedException("rsocket is not available");
  54 
  55         try (TestServers.DayTimeServer dayTimeServer
  56                 = TestServers.DayTimeServer.startNewServer(100)) {
  57             test1(dayTimeServer, true, true);
  58             test1(dayTimeServer, true, false);
  59             test1(dayTimeServer, false, true);
  60             test1(dayTimeServer, false, false);
  61             test2(dayTimeServer);
  62         }
  63     }
  64 
  65     static void test1(TestServers.DayTimeServer daytimeServer,
  66                       boolean select,
  67                       boolean setBlocking)
  68         throws Exception
  69     {
  70         InetSocketAddress isa
  71             = new InetSocketAddress(daytimeServer.getAddress(),
  72                                     daytimeServer.getPort());
  73         SocketChannel sc = RdmaSockets.openSocketChannel(
  74             StandardProtocolFamily.INET);
  75         sc.configureBlocking(false);
  76         boolean connected = sc.connect(isa);
  77         int attempts = 0;
  78 
  79         try {
  80             sc.connect(isa);
  81             throw new RuntimeException("Allowed another connect call");
  82         } catch (IllegalStateException ise) {
  83             // Correct behavior
  84         }
  85 
  86         if (setBlocking)
  87             sc.configureBlocking(true);
  88 
  89         if (!connected && select && !setBlocking) {
  90             Selector selector = RdmaSockets.openSelector();
  91             sc.register(selector, SelectionKey.OP_CONNECT);
  92             while (!connected) {
  93                 int keysAdded = selector.select(100);
  94                 if (keysAdded > 0) {
  95                     Set readyKeys = selector.selectedKeys();
  96                     Iterator i = readyKeys.iterator();
  97                     while (i.hasNext()) {
  98                         SelectionKey sk = (SelectionKey)i.next();
  99                         SocketChannel nextReady =
 100                             (SocketChannel)sk.channel();
 101                         connected = sc.finishConnect();
 102                     }
 103                 }
 104             }
 105             selector.close();
 106         }
 107 
 108         while (!connected) {
 109             if (attempts++ > 30)
 110                 throw new RuntimeException("Failed to connect");
 111             Thread.sleep(100);
 112             connected = sc.finishConnect();
 113         }
 114 
 115         ByteBuffer bb = ByteBuffer.allocateDirect(100);
 116         int bytesRead = 0;
 117         int totalRead = 0;
 118         while (totalRead < 20) {
 119             bytesRead = sc.read(bb);
 120             if (bytesRead > 0)
 121                 totalRead += bytesRead;
 122             if (bytesRead < 0)
 123                 throw new RuntimeException("Message shorter than expected");
 124         }
 125         bb.position(bb.position() - 2);         // Drop CRLF
 126         bb.flip();
 127         CharBuffer cb = Charset.forName("US-ASCII").newDecoder().decode(bb);
 128         System.err.println(isa + " says: \"" + cb + "\"");
 129         sc.close();
 130     }
 131 
 132     static void test2(TestServers.DayTimeServer daytimeServer) throws Exception {
 133         InetSocketAddress isa
 134             = new InetSocketAddress(daytimeServer.getAddress(),
 135                                     daytimeServer.getPort());
 136         boolean done = false;
 137         int globalAttempts = 0;
 138         int connectSuccess = 0;
 139         while (!done) {
 140             // When using a local daytime server it is not always possible
 141             // to get a pending connection, as sc.connect(isa) may always
 142             // return true.
 143             // So we're going to throw the exception only if there was
 144             // at least 1 case where we did not manage to connect.
 145             if (globalAttempts++ > 50) {
 146                 if (globalAttempts == connectSuccess + 1) {
 147                     System.out.println("Can't fully test on "
 148                             + System.getProperty("os.name"));
 149                     break;
 150                 }
 151                 throw new RuntimeException("Failed to connect");
 152             }
 153             SocketChannel sc = RdmaSockets.openSocketChannel(
 154                 StandardProtocolFamily.INET);
 155             sc.configureBlocking(false);
 156             boolean connected = sc.connect(isa);
 157             int localAttempts = 0;
 158             while (!connected) {
 159                 if (localAttempts++ > 500)
 160                     throw new RuntimeException("Failed to connect");
 161                 connected = sc.finishConnect();
 162                 if (connected) {
 163                     done = true;
 164                     break;
 165                 }
 166                 Thread.sleep(10);
 167             }
 168             if (connected) {
 169                 connectSuccess++;
 170             }
 171             sc.close();
 172         }
 173     }
 174 
 175 }