test/jdk/java/net/httpclient/websocket/WebSocketTest.java
branchhttp-client-branch
changeset 56317 52a9f6b74e43
parent 56314 f92e7a8a189f
child 56318 2a96e88888b2
equal deleted inserted replaced
56316:c65662728983 56317:52a9f6b74e43
    22  */
    22  */
    23 
    23 
    24 /*
    24 /*
    25  * @test
    25  * @test
    26  * @build DummyWebSocketServer
    26  * @build DummyWebSocketServer
    27  * @run testng/othervm/timeout=600
    27  * @run testng/othervm
    28  *      -Djdk.internal.httpclient.websocket.debug=true
    28  *      -Djdk.internal.httpclient.websocket.debug=true
    29  *       WebSocketTest
    29  *       WebSocketTest
    30  */
    30  */
    31 import org.testng.annotations.DataProvider;
       
    32 import org.testng.annotations.Test;
       
    33 
    31 
    34 import java.io.IOException;
    32 import java.io.IOException;
    35 import java.net.http.WebSocket;
    33 import java.net.http.WebSocket;
    36 import java.nio.ByteBuffer;
    34 import java.nio.ByteBuffer;
    37 import java.nio.CharBuffer;
    35 import java.nio.CharBuffer;
    43 import java.util.concurrent.CompletableFuture;
    41 import java.util.concurrent.CompletableFuture;
    44 import java.util.concurrent.CompletionStage;
    42 import java.util.concurrent.CompletionStage;
    45 import java.util.concurrent.TimeUnit;
    43 import java.util.concurrent.TimeUnit;
    46 import java.util.concurrent.TimeoutException;
    44 import java.util.concurrent.TimeoutException;
    47 import java.util.stream.Collectors;
    45 import java.util.stream.Collectors;
    48 
    46 import org.testng.annotations.DataProvider;
       
    47 import org.testng.annotations.Test;
    49 import static java.net.http.HttpClient.newHttpClient;
    48 import static java.net.http.HttpClient.newHttpClient;
    50 import static java.net.http.WebSocket.NORMAL_CLOSURE;
    49 import static java.net.http.WebSocket.NORMAL_CLOSURE;
    51 import static org.testng.Assert.assertEquals;
    50 import static org.testng.Assert.assertEquals;
    52 import static org.testng.Assert.assertFalse;
    51 import static org.testng.Assert.assertFalse;
    53 import static org.testng.Assert.assertThrows;
    52 import static org.testng.Assert.assertThrows;
    54 import static org.testng.Assert.assertTrue;
    53 import static org.testng.Assert.assertTrue;
    55 import static org.testng.Assert.fail;
    54 import static org.testng.Assert.fail;
    56 
    55 
    57 public class WebSocketTest {
    56 public class WebSocketTest {
    58 
    57 
    59     private static final Class<NullPointerException> NPE = NullPointerException.class;
       
    60     private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
    58     private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
    61     private static final Class<IllegalStateException> ISE = IllegalStateException.class;
    59     private static final Class<IllegalStateException> ISE = IllegalStateException.class;
    62     private static final Class<IOException> IOE = IOException.class;
    60     private static final Class<IOException> IOE = IOException.class;
    63 
       
    64     /*
       
    65      * Examines WebSocket behaviour after a call to abort()
       
    66      */
       
    67     @Test
       
    68     public void immediateAbort() throws Exception {
       
    69         try (DummyWebSocketServer server = serverWithCannedData(0x81, 0x00, 0x88, 0x00)) {
       
    70             server.open();
       
    71             CompletableFuture<Void> messageReceived = new CompletableFuture<>();
       
    72             WebSocket.Listener listener = new WebSocket.Listener() {
       
    73 
       
    74                 @Override
       
    75                 public void onOpen(WebSocket webSocket) {
       
    76                     /* no initial request */
       
    77                 }
       
    78 
       
    79                 @Override
       
    80                 public CompletionStage<?> onText(WebSocket webSocket,
       
    81                                                  CharSequence message,
       
    82                                                  WebSocket.MessagePart part) {
       
    83                     messageReceived.complete(null);
       
    84                     return null;
       
    85                 }
       
    86 
       
    87                 @Override
       
    88                 public CompletionStage<?> onBinary(WebSocket webSocket,
       
    89                                                    ByteBuffer message,
       
    90                                                    WebSocket.MessagePart part) {
       
    91                     messageReceived.complete(null);
       
    92                     return null;
       
    93                 }
       
    94 
       
    95                 @Override
       
    96                 public CompletionStage<?> onPing(WebSocket webSocket,
       
    97                                                  ByteBuffer message) {
       
    98                     messageReceived.complete(null);
       
    99                     return null;
       
   100                 }
       
   101 
       
   102                 @Override
       
   103                 public CompletionStage<?> onPong(WebSocket webSocket,
       
   104                                                  ByteBuffer message) {
       
   105                     messageReceived.complete(null);
       
   106                     return null;
       
   107                 }
       
   108 
       
   109                 @Override
       
   110                 public CompletionStage<?> onClose(WebSocket webSocket,
       
   111                                                   int statusCode,
       
   112                                                   String reason) {
       
   113                     messageReceived.complete(null);
       
   114                     return null;
       
   115                 }
       
   116             };
       
   117 
       
   118             WebSocket ws = newHttpClient()
       
   119                     .newWebSocketBuilder()
       
   120                     .buildAsync(server.getURI(), listener)
       
   121                     .join();
       
   122             for (int i = 0; i < 3; i++) {
       
   123                 System.out.printf("iteration #%s%n", i);
       
   124                 // after the first abort() each consecutive one must be a no-op,
       
   125                 // moreover, query methods should continue to return consistent,
       
   126                 // permanent values
       
   127                 for (int j = 0; j < 3; j++) {
       
   128                     System.out.printf("abort #%s%n", j);
       
   129                     ws.abort();
       
   130                     assertTrue(ws.isInputClosed());
       
   131                     assertTrue(ws.isOutputClosed());
       
   132                     assertEquals(ws.getSubprotocol(), "");
       
   133                 }
       
   134                 // at this point valid requests MUST be a no-op:
       
   135                 for (int j = 0; j < 3; j++) {
       
   136                     System.out.printf("request #%s%n", j);
       
   137                     ws.request(1);
       
   138                     ws.request(2);
       
   139                     ws.request(8);
       
   140                     ws.request(Integer.MAX_VALUE);
       
   141                     ws.request(Long.MAX_VALUE);
       
   142                     // invalid requests MUST throw IAE:
       
   143                     assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
       
   144                     assertThrows(IAE, () -> ws.request(Long.MIN_VALUE));
       
   145                     assertThrows(IAE, () -> ws.request(-1));
       
   146                     assertThrows(IAE, () -> ws.request(0));
       
   147                 }
       
   148             }
       
   149             // even though there is a bunch of messages readily available on the
       
   150             // wire we shouldn't have received any of them as we aborted before
       
   151             // the first request
       
   152             try {
       
   153                 messageReceived.get(10, TimeUnit.SECONDS);
       
   154                 fail();
       
   155             } catch (TimeoutException expected) {
       
   156                 System.out.println("Finished waiting");
       
   157             }
       
   158             for (int i = 0; i < 3; i++) {
       
   159                 System.out.printf("send #%s%n", i);
       
   160                 assertFails(IOE, ws.sendText("text!", false));
       
   161                 assertFails(IOE, ws.sendText("text!", true));
       
   162                 assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), false));
       
   163                 assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), true));
       
   164                 assertFails(IOE, ws.sendPing(ByteBuffer.allocate(16)));
       
   165                 assertFails(IOE, ws.sendPong(ByteBuffer.allocate(16)));
       
   166                 assertFails(IOE, ws.sendClose(NORMAL_CLOSURE, "a reason"));
       
   167                 assertThrows(NPE, () -> ws.sendText(null, false));
       
   168                 assertThrows(NPE, () -> ws.sendText(null, true));
       
   169                 assertThrows(NPE, () -> ws.sendBinary(null, false));
       
   170                 assertThrows(NPE, () -> ws.sendBinary(null, true));
       
   171                 assertThrows(NPE, () -> ws.sendPing(null));
       
   172                 assertThrows(NPE, () -> ws.sendPong(null));
       
   173                 assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
       
   174             }
       
   175         }
       
   176     }
       
   177 
    61 
   178     /* shortcut */
    62     /* shortcut */
   179     private static void assertFails(Class<? extends Throwable> clazz,
    63     private static void assertFails(Class<? extends Throwable> clazz,
   180                                     CompletionStage<?> stage) {
    64                                     CompletionStage<?> stage) {
   181         Support.assertCompletesExceptionally(clazz, stage);
    65         Support.assertCompletesExceptionally(clazz, stage);
   197                 ByteBuffer closeMessage = ByteBuffer.wrap(copy);
    81                 ByteBuffer closeMessage = ByteBuffer.wrap(copy);
   198                 channel.write(closeMessage);
    82                 channel.write(closeMessage);
   199                 super.serve(channel);
    83                 super.serve(channel);
   200             }
    84             }
   201         };
    85         };
   202     }
       
   203 
       
   204     @Test
       
   205     public void sendMethodsThrowNPE() throws IOException {
       
   206         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   207             server.open();
       
   208             WebSocket ws = newHttpClient()
       
   209                     .newWebSocketBuilder()
       
   210                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   211                     .join();
       
   212 
       
   213             assertThrows(NPE, () -> ws.sendText(null, false));
       
   214             assertThrows(NPE, () -> ws.sendText(null, true));
       
   215             assertThrows(NPE, () -> ws.sendBinary(null, false));
       
   216             assertThrows(NPE, () -> ws.sendBinary(null, true));
       
   217             assertThrows(NPE, () -> ws.sendPing(null));
       
   218             assertThrows(NPE, () -> ws.sendPong(null));
       
   219             assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
       
   220 
       
   221             ws.abort();
       
   222 
       
   223             assertThrows(NPE, () -> ws.sendText(null, false));
       
   224             assertThrows(NPE, () -> ws.sendText(null, true));
       
   225             assertThrows(NPE, () -> ws.sendBinary(null, false));
       
   226             assertThrows(NPE, () -> ws.sendBinary(null, true));
       
   227             assertThrows(NPE, () -> ws.sendPing(null));
       
   228             assertThrows(NPE, () -> ws.sendPong(null));
       
   229             assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
       
   230         }
       
   231     }
       
   232 
       
   233     // TODO: request in onClose/onError
       
   234     // TODO: throw exception in onClose/onError
       
   235     // TODO: exception is thrown from request()
       
   236 
       
   237     @Test
       
   238     public void sendCloseCompleted() throws IOException {
       
   239         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   240             server.open();
       
   241             WebSocket ws = newHttpClient()
       
   242                     .newWebSocketBuilder()
       
   243                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   244                     .join();
       
   245             ws.sendClose(NORMAL_CLOSURE, "").join();
       
   246             assertTrue(ws.isOutputClosed());
       
   247             assertEquals(ws.getSubprotocol(), "");
       
   248             ws.request(1); // No exceptions must be thrown
       
   249         }
       
   250     }
       
   251 
       
   252     @Test
       
   253     public void sendClosePending() throws Exception {
       
   254         try (DummyWebSocketServer server = notReadingServer()) {
       
   255             server.open();
       
   256             WebSocket ws = newHttpClient()
       
   257                     .newWebSocketBuilder()
       
   258                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   259                     .join();
       
   260             try {
       
   261                 ByteBuffer data = ByteBuffer.allocate(65536);
       
   262                 for (int i = 0; ; i++) { // fill up the send buffer
       
   263                     System.out.printf("begin cycle #%s at %s%n",
       
   264                                       i, System.currentTimeMillis());
       
   265                     try {
       
   266                         ws.sendBinary(data, true).get(10, TimeUnit.SECONDS);
       
   267                         data.clear();
       
   268                     } catch (TimeoutException e) {
       
   269                         break;
       
   270                     } finally {
       
   271                         System.out.printf("end cycle #%s at %s%n",
       
   272                                           i, System.currentTimeMillis());
       
   273                     }
       
   274                 }
       
   275                 CompletableFuture<WebSocket> cf = ws.sendClose(NORMAL_CLOSURE, "");
       
   276                 // The output closes even if the Close message has not been sent
       
   277                 assertFalse(cf.isDone());
       
   278                 assertTrue(ws.isOutputClosed());
       
   279                 assertEquals(ws.getSubprotocol(), "");
       
   280             } finally {
       
   281                 ws.abort();
       
   282             }
       
   283         }
       
   284     }
    86     }
   285 
    87 
   286     /*
    88     /*
   287      * This server does not read from the wire, allowing its client to fill up
    89      * This server does not read from the wire, allowing its client to fill up
   288      * their send buffer. Used to test scenarios with outstanding send
    90      * their send buffer. Used to test scenarios with outstanding send
   299                 }
   101                 }
   300             }
   102             }
   301         };
   103         };
   302     }
   104     }
   303 
   105 
   304     @Test
       
   305     public void abortPendingSendBinary() throws Exception {
       
   306         try (DummyWebSocketServer server = notReadingServer()) {
       
   307             server.open();
       
   308             WebSocket ws = newHttpClient()
       
   309                     .newWebSocketBuilder()
       
   310                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   311                     .join();
       
   312             ByteBuffer data = ByteBuffer.allocate(65536);
       
   313             CompletableFuture<WebSocket> cf = null;
       
   314             for (int i = 0; ; i++) {  // fill up the send buffer
       
   315                 System.out.printf("begin cycle #%s at %s%n",
       
   316                                   i, System.currentTimeMillis());
       
   317                 try {
       
   318                     cf = ws.sendBinary(data, true);
       
   319                     cf.get(10, TimeUnit.SECONDS);
       
   320                     data.clear();
       
   321                 } catch (TimeoutException e) {
       
   322                     break;
       
   323                 } finally {
       
   324                     System.out.printf("end cycle #%s at %s%n",
       
   325                                       i, System.currentTimeMillis());
       
   326                 }
       
   327             }
       
   328             ws.abort();
       
   329             assertTrue(ws.isOutputClosed());
       
   330             assertTrue(ws.isInputClosed());
       
   331             assertFails(IOException.class, cf);
       
   332         }
       
   333     }
       
   334 
       
   335     @Test
       
   336     public void abortPendingSendText() throws Exception {
       
   337         try (DummyWebSocketServer server = notReadingServer()) {
       
   338             server.open();
       
   339             WebSocket ws = newHttpClient()
       
   340                     .newWebSocketBuilder()
       
   341                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   342                     .join();
       
   343             String data = stringWith2NBytes(32768);
       
   344             CompletableFuture<WebSocket> cf = null;
       
   345             for (int i = 0; ; i++) {  // fill up the send buffer
       
   346                 System.out.printf("begin cycle #%s at %s%n",
       
   347                                   i, System.currentTimeMillis());
       
   348                 try {
       
   349                     cf = ws.sendText(data, true);
       
   350                     cf.get(10, TimeUnit.SECONDS);
       
   351                 } catch (TimeoutException e) {
       
   352                     break;
       
   353                 } finally {
       
   354                     System.out.printf("end cycle #%s at %s%n",
       
   355                                       i, System.currentTimeMillis());
       
   356                 }
       
   357             }
       
   358             ws.abort();
       
   359             assertTrue(ws.isOutputClosed());
       
   360             assertTrue(ws.isInputClosed());
       
   361             assertFails(IOException.class, cf);
       
   362         }
       
   363     }
       
   364 
       
   365     private static String stringWith2NBytes(int n) {
   106     private static String stringWith2NBytes(int n) {
   366         // -- Russian Alphabet (33 characters, 2 bytes per char) --
   107         // -- Russian Alphabet (33 characters, 2 bytes per char) --
   367         char[] abc = {
   108         char[] abc = {
   368                 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
   109                 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
   369                 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
   110                 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
   377             sb.append(abc[j]);
   118             sb.append(abc[j]);
   378         }
   119         }
   379         String s = sb.toString();
   120         String s = sb.toString();
   380         assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n;
   121         assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n;
   381         return s;
   122         return s;
   382     }
       
   383 
       
   384     @Test
       
   385     public void sendCloseTimeout() throws Exception {
       
   386         try (DummyWebSocketServer server = notReadingServer()) {
       
   387             server.open();
       
   388             WebSocket ws = newHttpClient()
       
   389                     .newWebSocketBuilder()
       
   390                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   391                     .join();
       
   392             String data = stringWith2NBytes(32768);
       
   393             CompletableFuture<WebSocket> cf = null;
       
   394             for (int i = 0; ; i++) {  // fill up the send buffer
       
   395                 System.out.printf("begin cycle #%s at %s%n",
       
   396                                   i, System.currentTimeMillis());
       
   397                 try {
       
   398                     cf = ws.sendText(data, true);
       
   399                     cf.get(10, TimeUnit.SECONDS);
       
   400                 } catch (TimeoutException e) {
       
   401                     break;
       
   402                 } finally {
       
   403                     System.out.printf("end cycle #%s at %s%n",
       
   404                                       i, System.currentTimeMillis());
       
   405                 }
       
   406             }
       
   407             long before = System.currentTimeMillis();
       
   408             assertFails(IOException.class,
       
   409                         ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
       
   410             long after = System.currentTimeMillis();
       
   411             // default timeout should be 30 seconds
       
   412             long elapsed = after - before;
       
   413             System.out.printf("Elapsed %s ms%n", elapsed);
       
   414             assertTrue(elapsed >= 29_000, String.valueOf(elapsed));
       
   415             assertTrue(ws.isOutputClosed());
       
   416             assertTrue(ws.isInputClosed());
       
   417             assertFails(IOException.class, cf);
       
   418         }
       
   419     }
   123     }
   420 
   124 
   421     @Test
   125     @Test
   422     public void testIllegalArgument() throws IOException {
   126     public void testIllegalArgument() throws IOException {
   423         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
   127         try (DummyWebSocketServer server = new DummyWebSocketServer()) {