test/jdk/java/net/httpclient/websocket/Exceptionally.java
branchhttp-client-branch
changeset 56052 da2aa3c773c9
parent 56051 c4867f1e7e5a
child 56053 8588095e95b0
equal deleted inserted replaced
56051:c4867f1e7e5a 56052:da2aa3c773c9
     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 /*
       
    25  * @test
       
    26  * @build DummyWebSocketServer
       
    27  * @run testng/othervm -Djdk.httpclient.HttpClient.log=trace Exceptionally
       
    28  */
       
    29 
       
    30 import jdk.incubator.http.WebSocket;
       
    31 import org.testng.annotations.Test;
       
    32 
       
    33 import java.io.IOException;
       
    34 import java.nio.ByteBuffer;
       
    35 import java.nio.CharBuffer;
       
    36 import java.nio.channels.SocketChannel;
       
    37 import java.nio.charset.StandardCharsets;
       
    38 import java.util.concurrent.CompletableFuture;
       
    39 import java.util.concurrent.CompletionException;
       
    40 import java.util.concurrent.CompletionStage;
       
    41 import java.util.concurrent.TimeUnit;
       
    42 import java.util.concurrent.TimeoutException;
       
    43 
       
    44 import static jdk.incubator.http.HttpClient.newHttpClient;
       
    45 import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE;
       
    46 import static org.testng.Assert.assertEquals;
       
    47 import static org.testng.Assert.assertFalse;
       
    48 import static org.testng.Assert.assertThrows;
       
    49 import static org.testng.Assert.assertTrue;
       
    50 
       
    51 public class Exceptionally {
       
    52 
       
    53     private static final Class<NullPointerException> NPE
       
    54             = NullPointerException.class;
       
    55     private static final Class<IllegalArgumentException> IAE
       
    56             = IllegalArgumentException.class;
       
    57     private static final Class<IllegalStateException> ISE
       
    58             = IllegalStateException.class;
       
    59 
       
    60     @Test
       
    61     public void testNull() throws IOException {
       
    62         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
    63             server.open();
       
    64             WebSocket ws = newHttpClient()
       
    65                     .newWebSocketBuilder()
       
    66                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
    67                     .join();
       
    68 
       
    69             assertThrows(NPE, () -> ws.sendText(null, false));
       
    70             assertThrows(NPE, () -> ws.sendText(null, true));
       
    71             assertThrows(NPE, () -> ws.sendBinary(null, false));
       
    72             assertThrows(NPE, () -> ws.sendBinary(null, true));
       
    73             assertThrows(NPE, () -> ws.sendPing(null));
       
    74             assertThrows(NPE, () -> ws.sendPong(null));
       
    75             assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
       
    76         }
       
    77     }
       
    78 
       
    79     @Test
       
    80     public void testSendClose1() throws IOException {
       
    81         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
    82             server.open();
       
    83             WebSocket ws = newHttpClient()
       
    84                     .newWebSocketBuilder()
       
    85                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
    86                     .join();
       
    87             ws.sendClose(NORMAL_CLOSURE, "").join();
       
    88             assertTrue(ws.isOutputClosed());
       
    89             assertFalse(ws.isInputClosed());
       
    90             assertEquals(ws.getSubprotocol(), "");
       
    91             ws.request(1); // No exceptions must be thrown
       
    92         }
       
    93     }
       
    94 
       
    95     @Test
       
    96     public void testSendClose2() throws Exception {
       
    97         try (DummyWebSocketServer server = notReadingServer()) {
       
    98             server.open();
       
    99             WebSocket ws = newHttpClient()
       
   100                     .newWebSocketBuilder()
       
   101                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   102                     .join();
       
   103             ByteBuffer data = ByteBuffer.allocate(65536);
       
   104             for (int i = 0; ; i++) {
       
   105                 System.out.println("cycle #" + i);
       
   106                 try {
       
   107                     ws.sendBinary(data, true).get(10, TimeUnit.SECONDS);
       
   108                     data.clear();
       
   109                 } catch (TimeoutException e) {
       
   110                     break;
       
   111                 }
       
   112             }
       
   113             CompletableFuture<WebSocket> cf = ws.sendClose(NORMAL_CLOSURE, "");
       
   114             assertTrue(ws.isOutputClosed());
       
   115             assertFalse(ws.isInputClosed());
       
   116             assertEquals(ws.getSubprotocol(), "");
       
   117             // The output closes regardless of whether or not the Close message
       
   118             // has been sent
       
   119             assertFalse(cf.isDone());
       
   120         }
       
   121     }
       
   122 
       
   123     /*
       
   124      * This server does not read from the wire, allowing its client to fill up
       
   125      * their send buffer. Used to test scenarios with outstanding send
       
   126      * operations.
       
   127      */
       
   128     private static DummyWebSocketServer notReadingServer() {
       
   129         return new DummyWebSocketServer() {
       
   130             @Override
       
   131             protected void serve(SocketChannel channel) throws IOException {
       
   132                 try {
       
   133                     Thread.sleep(Long.MAX_VALUE);
       
   134                 } catch (InterruptedException e) {
       
   135                     throw new IOException(e);
       
   136                 }
       
   137             }
       
   138         };
       
   139     }
       
   140 
       
   141     @Test
       
   142     public void testIllegalArgument() throws IOException {
       
   143         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   144             server.open();
       
   145             WebSocket ws = newHttpClient()
       
   146                     .newWebSocketBuilder()
       
   147                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   148                     .join();
       
   149 
       
   150             assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(126)));
       
   151             assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(127)));
       
   152             assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(128)));
       
   153             assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(129)));
       
   154             assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(256)));
       
   155 
       
   156             assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(126)));
       
   157             assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(127)));
       
   158             assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(128)));
       
   159             assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(129)));
       
   160             assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(256)));
       
   161 
       
   162             assertCompletesExceptionally(IAE, ws.sendText(incompleteString(), true));
       
   163             assertCompletesExceptionally(IAE, ws.sendText(incompleteString(), false));
       
   164             assertCompletesExceptionally(IAE, ws.sendText(malformedString(), true));
       
   165             assertCompletesExceptionally(IAE, ws.sendText(malformedString(), false));
       
   166 
       
   167             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124)));
       
   168             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(125)));
       
   169             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(128)));
       
   170             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(256)));
       
   171             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(257)));
       
   172             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWith2NBytes((123 / 2) + 1)));
       
   173             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, malformedString()));
       
   174             assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, incompleteString()));
       
   175 
       
   176             assertCompletesExceptionally(IAE, ws.sendClose(-2, "a reason"));
       
   177             assertCompletesExceptionally(IAE, ws.sendClose(-1, "a reason"));
       
   178             assertCompletesExceptionally(IAE, ws.sendClose(0, "a reason"));
       
   179             assertCompletesExceptionally(IAE, ws.sendClose(1, "a reason"));
       
   180             assertCompletesExceptionally(IAE, ws.sendClose(500, "a reason"));
       
   181             assertCompletesExceptionally(IAE, ws.sendClose(998, "a reason"));
       
   182             assertCompletesExceptionally(IAE, ws.sendClose(999, "a reason"));
       
   183             assertCompletesExceptionally(IAE, ws.sendClose(1002, "a reason"));
       
   184             assertCompletesExceptionally(IAE, ws.sendClose(1003, "a reason"));
       
   185             assertCompletesExceptionally(IAE, ws.sendClose(1006, "a reason"));
       
   186             assertCompletesExceptionally(IAE, ws.sendClose(1007, "a reason"));
       
   187             assertCompletesExceptionally(IAE, ws.sendClose(1009, "a reason"));
       
   188             assertCompletesExceptionally(IAE, ws.sendClose(1010, "a reason"));
       
   189             assertCompletesExceptionally(IAE, ws.sendClose(1012, "a reason"));
       
   190             assertCompletesExceptionally(IAE, ws.sendClose(1013, "a reason"));
       
   191             assertCompletesExceptionally(IAE, ws.sendClose(1015, "a reason"));
       
   192             assertCompletesExceptionally(IAE, ws.sendClose(5000, "a reason"));
       
   193             assertCompletesExceptionally(IAE, ws.sendClose(32768, "a reason"));
       
   194             assertCompletesExceptionally(IAE, ws.sendClose(65535, "a reason"));
       
   195             assertCompletesExceptionally(IAE, ws.sendClose(65536, "a reason"));
       
   196             assertCompletesExceptionally(IAE, ws.sendClose(Integer.MAX_VALUE, "a reason"));
       
   197             assertCompletesExceptionally(IAE, ws.sendClose(Integer.MIN_VALUE, "a reason"));
       
   198 
       
   199             assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
       
   200             assertThrows(IAE, () -> ws.request(-1));
       
   201             assertThrows(IAE, () -> ws.request(0));
       
   202         }
       
   203     }
       
   204 
       
   205     private static String malformedString() {
       
   206         return new String(new char[]{0xDC00, 0xD800});
       
   207     }
       
   208 
       
   209     private static String incompleteString() {
       
   210         return new String(new char[]{0xD800});
       
   211     }
       
   212 
       
   213     private static String stringWithNBytes(int n) {
       
   214         StringBuilder sb = new StringBuilder(n);
       
   215         for (int i = 0; i < n; i++) {
       
   216             sb.append("A");
       
   217         }
       
   218         return sb.toString();
       
   219     }
       
   220 
       
   221     private static String stringWith2NBytes(int n) {
       
   222         // Russian alphabet repeated cyclically
       
   223         char FIRST = '\u0410';
       
   224         char LAST = '\u042F';
       
   225         StringBuilder sb = new StringBuilder(n);
       
   226         char c = FIRST;
       
   227         for (int i = 0; i < n; i++) {
       
   228             if (++c > LAST) {
       
   229                 c = FIRST;
       
   230             }
       
   231             sb.append(c);
       
   232         }
       
   233         String s = sb.toString();
       
   234         assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n;
       
   235         return s;
       
   236     }
       
   237 
       
   238     private static void assertCompletesExceptionally(Class<? extends Throwable> clazz,
       
   239                                                      CompletableFuture<?> stage) {
       
   240         stage.handle((result, error) -> {
       
   241             if (error instanceof CompletionException) {
       
   242                 Throwable cause = error.getCause();
       
   243                 if (cause == null) {
       
   244                     throw new AssertionError("Unexpected null cause: " + error);
       
   245                 }
       
   246                 assertException(clazz, cause);
       
   247             } else {
       
   248                 assertException(clazz, error);
       
   249             }
       
   250             return null;
       
   251         }).join();
       
   252     }
       
   253 
       
   254     private static void assertException(Class<? extends Throwable> clazz,
       
   255                                         Throwable t) {
       
   256         if (t == null) {
       
   257             throw new AssertionError("Expected " + clazz + ", caught nothing");
       
   258         }
       
   259         if (!clazz.isInstance(t)) {
       
   260             throw new AssertionError("Expected " + clazz + ", caught " + t);
       
   261         }
       
   262     }
       
   263 
       
   264     @Test
       
   265     public void testIllegalStateOutstanding1() throws Exception {
       
   266         try (DummyWebSocketServer server = notReadingServer()) {
       
   267             server.open();
       
   268             WebSocket ws = newHttpClient()
       
   269                     .newWebSocketBuilder()
       
   270                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   271                     .join();
       
   272 
       
   273             ByteBuffer data = ByteBuffer.allocate(65536);
       
   274             for (int i = 0; ; i++) {
       
   275                 System.out.println("cycle #" + i);
       
   276                 try {
       
   277                     ws.sendBinary(data, true).get(10, TimeUnit.SECONDS);
       
   278                     data.clear();
       
   279                 } catch (TimeoutException e) {
       
   280                     break;
       
   281                 }
       
   282             }
       
   283             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
       
   284             assertCompletesExceptionally(ISE, ws.sendText("", true));
       
   285         }
       
   286     }
       
   287 
       
   288     @Test
       
   289     public void testIllegalStateOutstanding2() throws Exception {
       
   290         try (DummyWebSocketServer server = notReadingServer()) {
       
   291             server.open();
       
   292             WebSocket ws = newHttpClient()
       
   293                     .newWebSocketBuilder()
       
   294                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   295                     .join();
       
   296 
       
   297             CharBuffer data = CharBuffer.allocate(65536);
       
   298             for (int i = 0; ; i++) {
       
   299                 System.out.println("cycle #" + i);
       
   300                 try {
       
   301                     ws.sendText(data, true).get(10, TimeUnit.SECONDS);
       
   302                     data.clear();
       
   303                 } catch (TimeoutException e) {
       
   304                     break;
       
   305                 }
       
   306             }
       
   307             assertCompletesExceptionally(ISE, ws.sendText("", true));
       
   308             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
       
   309         }
       
   310     }
       
   311 
       
   312     @Test
       
   313     public void testIllegalStateIntermixed1() throws IOException {
       
   314         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   315             server.open();
       
   316             WebSocket ws = newHttpClient()
       
   317                     .newWebSocketBuilder()
       
   318                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   319                     .join();
       
   320 
       
   321             ws.sendBinary(ByteBuffer.allocate(16), false).join();
       
   322             assertCompletesExceptionally(ISE, ws.sendText("text", false));
       
   323             assertCompletesExceptionally(ISE, ws.sendText("text", true));
       
   324         }
       
   325     }
       
   326 
       
   327     @Test
       
   328     public void testIllegalStateIntermixed2() throws IOException {
       
   329         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   330             server.open();
       
   331             WebSocket ws = newHttpClient()
       
   332                     .newWebSocketBuilder()
       
   333                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   334                     .join();
       
   335 
       
   336             ws.sendText("text", false).join();
       
   337             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(16), false));
       
   338             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(16), true));
       
   339         }
       
   340     }
       
   341 
       
   342     @Test
       
   343     public void testIllegalStateSendClose() throws IOException {
       
   344         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   345             server.open();
       
   346             WebSocket ws = newHttpClient()
       
   347                     .newWebSocketBuilder()
       
   348                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   349                     .join();
       
   350 
       
   351             ws.sendClose(NORMAL_CLOSURE, "normal close").join();
       
   352 
       
   353             assertCompletesExceptionally(ISE, ws.sendText("", true));
       
   354             assertCompletesExceptionally(ISE, ws.sendText("", false));
       
   355             assertCompletesExceptionally(ISE, ws.sendText("abc", true));
       
   356             assertCompletesExceptionally(ISE, ws.sendText("abc", false));
       
   357             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
       
   358             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), false));
       
   359             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(1), true));
       
   360             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(1), false));
       
   361 
       
   362             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(125)));
       
   363             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(124)));
       
   364             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(1)));
       
   365             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(0)));
       
   366 
       
   367             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(125)));
       
   368             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(124)));
       
   369             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(1)));
       
   370             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(0)));
       
   371         }
       
   372     }
       
   373 
       
   374     @Test
       
   375     public void testIllegalStateOnClose() throws Exception {
       
   376         DummyWebSocketServer server = new DummyWebSocketServer() {
       
   377             @Override
       
   378             protected void serve(SocketChannel channel) throws IOException {
       
   379                 ByteBuffer closeMessage = ByteBuffer.wrap(new byte[]{(byte) 0x88, 0x00});
       
   380                 int wrote = channel.write(closeMessage);
       
   381                 System.out.println("Wrote bytes: " + wrote);
       
   382                 super.serve(channel);
       
   383             }
       
   384         };
       
   385         try (server) {
       
   386             server.open();
       
   387             CompletableFuture<Void> onCloseCalled = new CompletableFuture<>();
       
   388             CompletableFuture<Void> canClose = new CompletableFuture<>();
       
   389 
       
   390             WebSocket ws = newHttpClient()
       
   391                     .newWebSocketBuilder()
       
   392                     .buildAsync(server.getURI(), new WebSocket.Listener() {
       
   393                         @Override
       
   394                         public CompletionStage<?> onClose(WebSocket webSocket,
       
   395                                                           int statusCode,
       
   396                                                           String reason) {
       
   397                             System.out.println("onClose(" + statusCode + ")");
       
   398                             onCloseCalled.complete(null);
       
   399                             return canClose;
       
   400                         }
       
   401 
       
   402                         @Override
       
   403                         public void onError(WebSocket webSocket, Throwable error) {
       
   404                             System.out.println("onError(" + error + ")");
       
   405                             error.printStackTrace();
       
   406                         }
       
   407                     })
       
   408                     .join();
       
   409 
       
   410             onCloseCalled.join();      // Wait for onClose to be called
       
   411             TimeUnit.SECONDS.sleep(5); // Give canClose some time to reach the WebSocket
       
   412             canClose.complete(null);   // Signal to the WebSocket it can close the output
       
   413 
       
   414             assertCompletesExceptionally(ISE, ws.sendText("", true));
       
   415             assertCompletesExceptionally(ISE, ws.sendText("", false));
       
   416             assertCompletesExceptionally(ISE, ws.sendText("abc", true));
       
   417             assertCompletesExceptionally(ISE, ws.sendText("abc", false));
       
   418             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
       
   419             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), false));
       
   420             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(1), true));
       
   421             assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(1), false));
       
   422 
       
   423             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(125)));
       
   424             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(124)));
       
   425             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(1)));
       
   426             assertCompletesExceptionally(ISE, ws.sendPing(ByteBuffer.allocate(0)));
       
   427 
       
   428             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(125)));
       
   429             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(124)));
       
   430             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(1)));
       
   431             assertCompletesExceptionally(ISE, ws.sendPong(ByteBuffer.allocate(0)));
       
   432         }
       
   433     }
       
   434 }