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  * @summary Test socket-channel connection-state transitions
26  * @library .. /test/lib
27  * @build jdk.test.lib.Utils TestServers
28  * @run main ConnectState
29  */
30 
31 import java.io.*;
32 import java.net.*;
33 import java.nio.*;
34 import java.nio.channels.*;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.HashSet;
39 
40 
41 public class ConnectState {
42 
43     static PrintStream log = System.err;
44 
45     static InetSocketAddress remote;
46 
47     final static int ST_UNCONNECTED = 0;
48     final static int ST_PENDING = 1;
49     final static int ST_CONNECTED = 2;
50     final static int ST_CLOSED = 3;
51     final static int ST_PENDING_OR_CONNECTED = 4;
52     // NO exceptions expected
53     final static Collection<Class<?>> NONE = Collections.emptySet();
54 
55     // make a set of expected exception.
expectedExceptions(Class<?>.... expected)56     static Collection<Class<?>> expectedExceptions(Class<?>... expected) {
57         final Collection<Class<?>> exceptions;
58         if (expected.length == 0) {
59             exceptions = NONE;
60         } else if (expected.length == 1) {
61             assert expected[0] != null;
62             exceptions = Collections.<Class<?>>singleton(expected[0]);
63         } else {
64             exceptions = new HashSet<>(Arrays.asList(expected));
65         }
66         return exceptions;
67     }
68 
69     static abstract class Test {
70 
go(SocketChannel sc)71         abstract String go(SocketChannel sc) throws Exception;
72 
check(boolean test, String desc)73         static void check(boolean test, String desc) throws Exception {
74             if (!test)
75                 throw new Exception("Incorrect state: " + desc);
76         }
77 
check(SocketChannel sc, int state)78         static void check(SocketChannel sc, int state) throws Exception {
79             switch (state) {
80             case ST_UNCONNECTED:
81                 check(!sc.isConnected(), "!isConnected");
82                 check(!sc.isConnectionPending(), "!isConnectionPending");
83                 check(sc.isOpen(), "isOpen");
84                 break;
85             case ST_PENDING:
86                 check(!sc.isConnected(), "!isConnected");
87                 check(sc.isConnectionPending(), "isConnectionPending");
88                 check(sc.isOpen(), "isOpen");
89                 break;
90             case ST_CONNECTED:
91                 check(sc.isConnected(), "isConnected");
92                 check(!sc.isConnectionPending(), "!isConnectionPending");
93                 check(sc.isOpen(), "isOpen");
94                 break;
95             case ST_CLOSED:
96                 check(sc.isConnected(), "isConnected");
97                 check(!sc.isConnectionPending(), "!isConnectionPending");
98                 check(sc.isOpen(), "isOpen");
99                 break;
100             case ST_PENDING_OR_CONNECTED:
101                 check(sc.isConnected() || sc.isConnectionPending(),
102                         "isConnected || isConnectionPending");
103                 check(sc.isOpen(), "isOpen");
104                 break;
105             }
106         }
107 
Test(String name, Class<?> exception, int state)108         Test(String name, Class<?> exception, int state) throws Exception {
109             this(name, expectedExceptions(exception), state);
110         }
111 
112         // On some architecture we may need to accept several exceptions.
113         // For instance on Solaris, when using a server colocated on the
114         // machine we cannot guarantee that we will get a
115         // ConnectionPendingException when connecting twice on the same
116         // non-blocking socket. We may instead get a an
117         // AlreadyConnectedException, which is also valid: it simply means
118         // that the first connection has been immediately accepted.
Test(String name, Collection<Class<?>> exceptions, int state)119         Test(String name, Collection<Class<?>> exceptions, int state)
120                 throws Exception {
121             SocketChannel sc = SocketChannel.open();
122             String note;
123             try {
124                 try {
125                     note = go(sc);
126                 } catch (Exception x) {
127                     Class<?> expectedExceptionClass = null;
128                     for (Class<?> exception : exceptions) {
129                         if (exception.isInstance(x)) {
130                             log.println(name + ": As expected: "
131                                         + x);
132                             expectedExceptionClass = exception;
133                             check(sc, state);
134                             break;
135                         }
136                     }
137                     if (expectedExceptionClass == null
138                             && !exceptions.isEmpty()) {
139                         // we had an exception, but it's not of the set of
140                         // exceptions we expected.
141                         throw new Exception(name
142                                                 + ": Incorrect exception",
143                                                 x);
144                     } else if (exceptions.isEmpty()) {
145                         // we didn't expect any exception
146                         throw new Exception(name
147                                             + ": Unexpected exception",
148                                             x);
149                     }
150                     // if we reach here, we have our expected exception
151                     assert expectedExceptionClass != null;
152                     return;
153                 }
154                 if (!exceptions.isEmpty()) {
155                     throw new Exception(name
156                                         + ": Expected exception not thrown: "
157                                         + exceptions.iterator().next());
158                 }
159                 check(sc, state);
160                 log.println(name + ": Returned normally"
161                             + ((note != null) ? ": " + note : ""));
162             } finally {
163                 if (sc.isOpen())
164                     sc.close();
165             }
166         }
167 
168     }
169 
tests()170     static void tests() throws Exception {
171         log.println(remote);
172 
173         new Test("Read unconnected", NotYetConnectedException.class,
174                  ST_UNCONNECTED) {
175                 @Override
176                 String go(SocketChannel sc) throws Exception {
177                     ByteBuffer b = ByteBuffer.allocateDirect(1024);
178                     sc.read(b);
179                     return null;
180                 }};
181 
182         new Test("Write unconnected", NotYetConnectedException.class,
183                  ST_UNCONNECTED) {
184                 @Override
185                 String go(SocketChannel sc) throws Exception {
186                     ByteBuffer b = ByteBuffer.allocateDirect(1024);
187                     sc.write(b);
188                     return null;
189                 }};
190 
191         new Test("Simple connect", NONE, ST_CONNECTED) {
192                 @Override
193                 String go(SocketChannel sc) throws Exception {
194                     sc.connect(remote);
195                     return null;
196                 }};
197 
198         new Test("Simple connect & finish", NONE, ST_CONNECTED) {
199                 @Override
200                 String go(SocketChannel sc) throws Exception {
201                     sc.connect(remote);
202                     if (!sc.finishConnect())
203                         throw new Exception("finishConnect returned false");
204                     return null;
205                 }};
206 
207         new Test("Double connect",
208                  AlreadyConnectedException.class, ST_CONNECTED) {
209                 @Override
210                 String go(SocketChannel sc) throws Exception {
211                     sc.connect(remote);
212                     sc.connect(remote);
213                     return null;
214                 }};
215 
216         new Test("Finish w/o start",
217                  NoConnectionPendingException.class, ST_UNCONNECTED) {
218                 @Override
219                 String go(SocketChannel sc) throws Exception {
220                     sc.finishConnect();
221                     return null;
222                 }};
223 
224         // Note: using our local EchoServer rather than echo on a distant
225         //       host - we see that Tries to finish = 0 (instead of ~ 18).
226         new Test("NB simple connect", NONE, ST_CONNECTED) {
227                 @Override
228                 String go(SocketChannel sc) throws Exception {
229                     sc.configureBlocking(false);
230                     sc.connect(remote);
231                     int n = 0;
232                     while (!sc.finishConnect()) {
233                         Thread.sleep(10);
234                         n++;
235                     }
236                     sc.finishConnect();         // Check redundant invocation
237                     return ("Tries to finish = " + n);
238                 }};
239 
240         // Note: using our local EchoServer rather than echo on a distant
241         //       host - we cannot guarantee that this test will get a
242         //       a ConnectionPendingException: it may get an
243         //       AlreadyConnectedException, so we should allow for both.
244         new Test("NB double connect",
245                  expectedExceptions(ConnectionPendingException.class,
246                                     AlreadyConnectedException.class),
247                  ST_PENDING_OR_CONNECTED) {
248                 @Override
249                 String go(SocketChannel sc) throws Exception {
250                     sc.configureBlocking(false);
251                     sc.connect(remote);
252                     sc.connect(remote);
253                     return null;
254                 }};
255 
256         new Test("NB finish w/o start",
257                  NoConnectionPendingException.class, ST_UNCONNECTED) {
258                 @Override
259                 String go(SocketChannel sc) throws Exception {
260                     sc.configureBlocking(false);
261                     sc.finishConnect();
262                     return null;
263                 }};
264 
265         new Test("NB connect, B finish", NONE, ST_CONNECTED) {
266                 @Override
267                 String go(SocketChannel sc) throws Exception {
268                     sc.configureBlocking(false);
269                     sc.connect(remote);
270                     sc.configureBlocking(true);
271                     sc.finishConnect();
272                     return null;
273                 }};
274 
275     }
276 
main(String[] args)277     public static void main(String[] args) throws Exception {
278         try (TestServers.EchoServer echoServer
279                 = TestServers.EchoServer.startNewServer(500)) {
280             remote = new InetSocketAddress(echoServer.getAddress(),
281                                            echoServer.getPort());
282             tests();
283         }
284     }
285 
286 }
287