< prev index next >

test/jdk/java/net/httpclient/websocket/WebSocketTest.java

Print this page


   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
  28  *       WebSocketTest
  29  */
  30 
  31 import org.testng.annotations.AfterTest;
  32 import org.testng.annotations.DataProvider;
  33 import org.testng.annotations.Test;
  34 
  35 import java.io.IOException;



  36 import java.net.http.WebSocket;

  37 import java.nio.ByteBuffer;
  38 import java.nio.charset.StandardCharsets;
  39 import java.util.ArrayList;

  40 import java.util.List;
  41 import java.util.concurrent.CompletableFuture;

  42 import java.util.concurrent.CompletionStage;
  43 import java.util.concurrent.TimeUnit;
  44 import java.util.concurrent.atomic.AtomicBoolean;

  45 import java.util.function.Supplier;
  46 import java.util.stream.Collectors;
  47 
  48 import static java.net.http.HttpClient.Builder.NO_PROXY;
  49 import static java.net.http.HttpClient.newBuilder;
  50 import static java.net.http.WebSocket.NORMAL_CLOSURE;

  51 import static org.testng.Assert.assertEquals;
  52 import static org.testng.Assert.assertThrows;

  53 
  54 public class WebSocketTest {
  55 
  56     private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
  57     private static final Class<IllegalStateException> ISE = IllegalStateException.class;
  58     private static final Class<IOException> IOE = IOException.class;
  59 
  60     /* shortcut */
  61     private static void assertFails(Class<? extends Throwable> clazz,
  62                                     CompletionStage<?> stage) {
  63         Support.assertCompletesExceptionally(clazz, stage);
  64     }
  65 
  66     private DummyWebSocketServer server;
  67     private WebSocket webSocket;
  68 
  69     @AfterTest
  70     public void cleanup() {


  71         server.close();

  72         webSocket.abort();
  73     }
  74 
  75     @Test
  76     public void illegalArgument() throws IOException {
  77         server = new DummyWebSocketServer();
  78         server.open();
  79         webSocket = newBuilder().proxy(NO_PROXY).build()
  80                 .newWebSocketBuilder()
  81                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
  82                 .join();
  83 
  84         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(126)));
  85         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(127)));
  86         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(128)));
  87         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(129)));
  88         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(256)));
  89 
  90         assertFails(IAE, webSocket.sendPong(ByteBuffer.allocate(126)));
  91         assertFails(IAE, webSocket.sendPong(ByteBuffer.allocate(127)));


 117         assertFails(IAE, webSocket.sendClose(1002, "a reason"));
 118         assertFails(IAE, webSocket.sendClose(1003, "a reason"));
 119         assertFails(IAE, webSocket.sendClose(1006, "a reason"));
 120         assertFails(IAE, webSocket.sendClose(1007, "a reason"));
 121         assertFails(IAE, webSocket.sendClose(1009, "a reason"));
 122         assertFails(IAE, webSocket.sendClose(1010, "a reason"));
 123         assertFails(IAE, webSocket.sendClose(1012, "a reason"));
 124         assertFails(IAE, webSocket.sendClose(1013, "a reason"));
 125         assertFails(IAE, webSocket.sendClose(1015, "a reason"));
 126         assertFails(IAE, webSocket.sendClose(5000, "a reason"));
 127         assertFails(IAE, webSocket.sendClose(32768, "a reason"));
 128         assertFails(IAE, webSocket.sendClose(65535, "a reason"));
 129         assertFails(IAE, webSocket.sendClose(65536, "a reason"));
 130         assertFails(IAE, webSocket.sendClose(Integer.MAX_VALUE, "a reason"));
 131         assertFails(IAE, webSocket.sendClose(Integer.MIN_VALUE, "a reason"));
 132 
 133         assertThrows(IAE, () -> webSocket.request(Integer.MIN_VALUE));
 134         assertThrows(IAE, () -> webSocket.request(Long.MIN_VALUE));
 135         assertThrows(IAE, () -> webSocket.request(-1));
 136         assertThrows(IAE, () -> webSocket.request(0));


 137     }
 138 
 139     @Test
 140     public void partialBinaryThenText() throws IOException {
 141         server = new DummyWebSocketServer();
 142         server.open();
 143         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 144                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 145                 .join();
 146         webSocket.sendBinary(ByteBuffer.allocate(16), false).join();
 147         assertFails(ISE, webSocket.sendText("text", false));
 148         assertFails(ISE, webSocket.sendText("text", true));
 149         // Pings & Pongs are fine
 150         webSocket.sendPing(ByteBuffer.allocate(125)).join();
 151         webSocket.sendPong(ByteBuffer.allocate(125)).join();

 152     }
 153 
 154     @Test
 155     public void partialTextThenBinary() throws IOException {
 156         server = new DummyWebSocketServer();
 157         server.open();
 158         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 159                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 160                 .join();
 161 
 162         webSocket.sendText("text", false).join();
 163         assertFails(ISE, webSocket.sendBinary(ByteBuffer.allocate(16), false));
 164         assertFails(ISE, webSocket.sendBinary(ByteBuffer.allocate(16), true));
 165         // Pings & Pongs are fine
 166         webSocket.sendPing(ByteBuffer.allocate(125)).join();
 167         webSocket.sendPong(ByteBuffer.allocate(125)).join();

 168     }
 169 
 170     @Test
 171     public void sendMethodsThrowIOE1() throws IOException {
 172         server = new DummyWebSocketServer();
 173         server.open();
 174         webSocket = newBuilder().proxy(NO_PROXY).build()
 175                 .newWebSocketBuilder()
 176                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 177                 .join();
 178 
 179         webSocket.sendClose(NORMAL_CLOSURE, "ok").join();
 180 
 181         assertFails(IOE, webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
 182 
 183         assertFails(IOE, webSocket.sendText("", true));
 184         assertFails(IOE, webSocket.sendText("", false));
 185         assertFails(IOE, webSocket.sendText("abc", true));
 186         assertFails(IOE, webSocket.sendText("abc", false));
 187         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), true));
 188         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), false));
 189         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), true));
 190         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), false));
 191 
 192         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(125)));
 193         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(124)));
 194         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(1)));
 195         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(0)));
 196 
 197         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(125)));
 198         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(124)));
 199         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(1)));
 200         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(0)));


 201     }
 202 
 203     @DataProvider(name = "sequence")
 204     public Object[][] data1() {
 205         int[] CLOSE = {
 206                 0x81, 0x00, // ""
 207                 0x82, 0x00, // []
 208                 0x89, 0x00, // <PING>
 209                 0x8a, 0x00, // <PONG>
 210                 0x88, 0x00, // <CLOSE>
 211         };
 212         int[] ERROR = {
 213                 0x81, 0x00, // ""
 214                 0x82, 0x00, // []
 215                 0x89, 0x00, // <PING>
 216                 0x8a, 0x00, // <PONG>
 217                 0x8b, 0x00, // 0xB control frame (causes an error)
 218         };
 219         return new Object[][]{
 220                 {CLOSE, 1},


 301                         () -> super.onClose(webSocket, statusCode, reason));
 302             }
 303 
 304             @Override
 305             public void onError(WebSocket webSocket, Throwable error) {
 306                 checkRunExclusively(() -> {
 307                     super.onError(webSocket, error);
 308                     return null;
 309                 });
 310             }
 311         };
 312 
 313         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 314                 .buildAsync(server.getURI(), listener)
 315                 .join();
 316 
 317 
 318         listener.invocations();
 319         violation.complete(null); // won't affect if completed exceptionally
 320         violation.join();


 321     }
 322 
 323     @Test
 324     public void sendMethodsThrowIOE2() throws Exception {
 325         server = Support.serverWithCannedData(0x88, 0x00);
 326         server.open();
 327         CompletableFuture<Void> onCloseCalled = new CompletableFuture<>();
 328         CompletableFuture<Void> canClose = new CompletableFuture<>();
 329 
 330         WebSocket.Listener listener = new WebSocket.Listener() {
 331             @Override
 332             public CompletionStage<?> onClose(WebSocket webSocket,
 333                                               int statusCode,
 334                                               String reason) {
 335                 System.out.printf("onClose(%s, '%s')%n", statusCode, reason);
 336                 onCloseCalled.complete(null);
 337                 return canClose;
 338             }
 339 
 340             @Override


 355         assertFails(IOE, webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
 356 
 357         assertFails(IOE, webSocket.sendText("", true));
 358         assertFails(IOE, webSocket.sendText("", false));
 359         assertFails(IOE, webSocket.sendText("abc", true));
 360         assertFails(IOE, webSocket.sendText("abc", false));
 361         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), true));
 362         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), false));
 363         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), true));
 364         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), false));
 365 
 366         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(125)));
 367         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(124)));
 368         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(1)));
 369         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(0)));
 370 
 371         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(125)));
 372         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(124)));
 373         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(1)));
 374         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(0)));


 375     }
 376 
 377     @Test
 378     public void simpleAggregatingBinaryMessages() throws IOException {




































 379         List<byte[]> expected = List.of("alpha", "beta", "gamma", "delta")
 380                 .stream()
 381                 .map(s -> s.getBytes(StandardCharsets.US_ASCII))
 382                 .collect(Collectors.toList());
 383         int[] binary = new int[]{
 384                 0x82, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // [alpha]
 385                 0x02, 0x02, 0x62, 0x65,                   // [be
 386                 0x80, 0x02, 0x74, 0x61,                   // ta]
 387                 0x02, 0x01, 0x67,                         // [g
 388                 0x00, 0x01, 0x61,                         // a
 389                 0x00, 0x00,                               //
 390                 0x00, 0x00,                               //
 391                 0x00, 0x01, 0x6d,                         // m
 392                 0x00, 0x01, 0x6d,                         // m
 393                 0x80, 0x01, 0x61,                         // a]
 394                 0x8a, 0x00,                               // <PONG>
 395                 0x02, 0x04, 0x64, 0x65, 0x6c, 0x74,       // [delt
 396                 0x00, 0x01, 0x61,                         // a
 397                 0x80, 0x00,                               // ]
 398                 0x88, 0x00                                // <CLOSE>
 399         };
 400         CompletableFuture<List<byte[]>> actual = new CompletableFuture<>();
 401 
 402         server = Support.serverWithCannedData(binary);
 403         server.open();
 404 
 405         WebSocket.Listener listener = new WebSocket.Listener() {
 406 
 407             List<byte[]> collectedBytes = new ArrayList<>();
 408             ByteBuffer buffer = ByteBuffer.allocate(1024);
 409 
 410             @Override
 411             public CompletionStage<?> onBinary(WebSocket webSocket,
 412                                                ByteBuffer message,
 413                                                boolean last) {
 414                 System.out.printf("onBinary(%s, %s)%n", message, last);
 415                 webSocket.request(1);
 416 
 417                 append(message);
 418                 if (last) {
 419                     buffer.flip();
 420                     byte[] bytes = new byte[buffer.remaining()];
 421                     buffer.get(bytes);
 422                     buffer.clear();
 423                     processWholeBinary(bytes);
 424                 }
 425                 return null;
 426             }
 427 
 428             private void append(ByteBuffer message) {
 429                 if (buffer.remaining() < message.remaining()) {
 430                     assert message.remaining() > 0;
 431                     int cap = (buffer.capacity() + message.remaining()) * 2;
 432                     ByteBuffer b = ByteBuffer.allocate(cap);
 433                     b.put(buffer.flip());
 434                     buffer = b;
 435                 }
 436                 buffer.put(message);
 437             }
 438 
 439             private void processWholeBinary(byte[] bytes) {
 440                 String stringBytes = new String(bytes, StandardCharsets.UTF_8);
 441                 System.out.println("processWholeBinary: " + stringBytes);
 442                 collectedBytes.add(bytes);
 443             }
 444 
 445             @Override
 446             public CompletionStage<?> onClose(WebSocket webSocket,
 447                                               int statusCode,
 448                                               String reason) {
 449                 actual.complete(collectedBytes);
 450                 return null;
 451             }
 452 
 453             @Override
 454             public void onError(WebSocket webSocket, Throwable error) {
 455                 actual.completeExceptionally(error);
 456             }
 457         };
 458 
 459         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()



 460                 .buildAsync(server.getURI(), listener)
 461                 .join();
 462 
 463         List<byte[]> a = actual.join();
 464         assertEquals(a, expected);
 465     }
 466 
 467     @Test
 468     public void simpleAggregatingTextMessages() throws IOException {
 469 





 470         List<String> expected = List.of("alpha", "beta", "gamma", "delta");
 471 
 472         int[] binary = new int[]{
 473                 0x81, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // "alpha"
 474                 0x01, 0x02, 0x62, 0x65,                   // "be
 475                 0x80, 0x02, 0x74, 0x61,                   // ta"
 476                 0x01, 0x01, 0x67,                         // "g
 477                 0x00, 0x01, 0x61,                         // a
 478                 0x00, 0x00,                               //
 479                 0x00, 0x00,                               //
 480                 0x00, 0x01, 0x6d,                         // m
 481                 0x00, 0x01, 0x6d,                         // m
 482                 0x80, 0x01, 0x61,                         // a"
 483                 0x8a, 0x00,                               // <PONG>
 484                 0x01, 0x04, 0x64, 0x65, 0x6c, 0x74,       // "delt
 485                 0x00, 0x01, 0x61,                         // a
 486                 0x80, 0x00,                               // "
 487                 0x88, 0x00                                // <CLOSE>
 488         };
 489         CompletableFuture<List<String>> actual = new CompletableFuture<>();
 490 
 491         server = Support.serverWithCannedData(binary);
 492         server.open();
 493 
 494         WebSocket.Listener listener = new WebSocket.Listener() {
 495 
 496             List<String> collectedStrings = new ArrayList<>();
 497             StringBuilder text = new StringBuilder();
 498 
 499             @Override
 500             public CompletionStage<?> onText(WebSocket webSocket,
 501                                              CharSequence message,
 502                                              boolean last) {
 503                 System.out.printf("onText(%s, %s)%n", message, last);
 504                 webSocket.request(1);
 505                 text.append(message);
 506                 if (last) {
 507                     String str = text.toString();
 508                     text.setLength(0);
 509                     processWholeText(str);
 510                 }
 511                 return null;


 513 
 514             private void processWholeText(String string) {
 515                 System.out.println(string);
 516                 collectedStrings.add(string);
 517             }
 518 
 519             @Override
 520             public CompletionStage<?> onClose(WebSocket webSocket,
 521                                               int statusCode,
 522                                               String reason) {
 523                 actual.complete(collectedStrings);
 524                 return null;
 525             }
 526 
 527             @Override
 528             public void onError(WebSocket webSocket, Throwable error) {
 529                 actual.completeExceptionally(error);
 530             }
 531         };
 532 
 533         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()



 534                 .buildAsync(server.getURI(), listener)
 535                 .join();
 536 
 537         List<String> a = actual.join();
 538         assertEquals(a, expected);


 539     }
 540 
 541     /*
 542      * Exercises the scenario where requests for more messages are made prior to
 543      * completing the returned CompletionStage instances.
 544      */
 545     @Test
 546     public void aggregatingTextMessages() throws IOException {
 547 


 548         List<String> expected = List.of("alpha", "beta", "gamma", "delta");
 549 
 550         int[] binary = new int[]{
 551                 0x81, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // "alpha"
 552                 0x01, 0x02, 0x62, 0x65,                   // "be
 553                 0x80, 0x02, 0x74, 0x61,                   // ta"
 554                 0x01, 0x01, 0x67,                         // "g
 555                 0x00, 0x01, 0x61,                         // a
 556                 0x00, 0x00,                               //
 557                 0x00, 0x00,                               //
 558                 0x00, 0x01, 0x6d,                         // m
 559                 0x00, 0x01, 0x6d,                         // m
 560                 0x80, 0x01, 0x61,                         // a"
 561                 0x8a, 0x00,                               // <PONG>
 562                 0x01, 0x04, 0x64, 0x65, 0x6c, 0x74,       // "delt
 563                 0x00, 0x01, 0x61,                         // a
 564                 0x80, 0x00,                               // "
 565                 0x88, 0x00                                // <CLOSE>
 566         };
 567         CompletableFuture<List<String>> actual = new CompletableFuture<>();
 568 
 569 
 570         server = Support.serverWithCannedData(binary);
 571         server.open();
 572 
 573         WebSocket.Listener listener = new WebSocket.Listener() {
 574 
 575             List<CharSequence> parts = new ArrayList<>();
 576             /*
 577              * A CompletableFuture which will complete once the current
 578              * message has been fully assembled. Until then the listener
 579              * returns this instance for every call.
 580              */
 581             CompletableFuture<?> currentCf = new CompletableFuture<>();
 582             List<String> collected = new ArrayList<>();
 583 
 584             @Override
 585             public CompletionStage<?> onText(WebSocket webSocket,
 586                                              CharSequence message,
 587                                              boolean last) {
 588                 parts.add(message);
 589                 if (!last) {
 590                     webSocket.request(1);


 606                 actual.complete(collected);
 607                 return null;
 608             }
 609 
 610             @Override
 611             public void onError(WebSocket webSocket, Throwable error) {
 612                 actual.completeExceptionally(error);
 613             }
 614 
 615             public void processWholeMessage(List<CharSequence> data,
 616                                             CompletableFuture<?> cf) {
 617                 StringBuilder b = new StringBuilder();
 618                 data.forEach(b::append);
 619                 String s = b.toString();
 620                 System.out.println(s);
 621                 cf.complete(null);
 622                 collected.add(s);
 623             }
 624         };
 625 
 626         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()



 627                 .buildAsync(server.getURI(), listener)
 628                 .join();
 629 
 630         List<String> a = actual.join();
 631         assertEquals(a, expected);

































































































 632     }
 633 }
   1 /*
   2  * Copyright (c) 2018, 2019, 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  * @bug 8217429
  27  * @build DummyWebSocketServer
  28  * @run testng/othervm
  29  *       WebSocketTest
  30  */
  31 
  32 import org.testng.annotations.AfterTest;
  33 import org.testng.annotations.DataProvider;
  34 import org.testng.annotations.Test;
  35 
  36 import java.io.IOException;
  37 import java.net.Authenticator;
  38 import java.net.PasswordAuthentication;
  39 import java.net.http.HttpResponse;
  40 import java.net.http.WebSocket;
  41 import java.net.http.WebSocketHandshakeException;
  42 import java.nio.ByteBuffer;
  43 import java.nio.charset.StandardCharsets;
  44 import java.util.ArrayList;
  45 import java.util.Base64;
  46 import java.util.List;
  47 import java.util.concurrent.CompletableFuture;
  48 import java.util.concurrent.CompletionException;
  49 import java.util.concurrent.CompletionStage;
  50 import java.util.concurrent.TimeUnit;
  51 import java.util.concurrent.atomic.AtomicBoolean;
  52 import java.util.function.Function;
  53 import java.util.function.Supplier;
  54 import java.util.stream.Collectors;
  55 
  56 import static java.net.http.HttpClient.Builder.NO_PROXY;
  57 import static java.net.http.HttpClient.newBuilder;
  58 import static java.net.http.WebSocket.NORMAL_CLOSURE;
  59 import static java.nio.charset.StandardCharsets.UTF_8;
  60 import static org.testng.Assert.assertEquals;
  61 import static org.testng.Assert.assertThrows;
  62 import static org.testng.Assert.fail;
  63 
  64 public class WebSocketTest {
  65 
  66     private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
  67     private static final Class<IllegalStateException> ISE = IllegalStateException.class;
  68     private static final Class<IOException> IOE = IOException.class;
  69 
  70     /* shortcut */
  71     private static void assertFails(Class<? extends Throwable> clazz,
  72                                     CompletionStage<?> stage) {
  73         Support.assertCompletesExceptionally(clazz, stage);
  74     }
  75 
  76     private DummyWebSocketServer server;
  77     private WebSocket webSocket;
  78 
  79     @AfterTest
  80     public void cleanup() {
  81         System.out.println("AFTER TEST");
  82         if (server != null)
  83             server.close();
  84         if (webSocket != null)
  85             webSocket.abort();
  86     }
  87 
  88     @Test
  89     public void illegalArgument() throws IOException {
  90         server = new DummyWebSocketServer();
  91         server.open();
  92         webSocket = newBuilder().proxy(NO_PROXY).build()
  93                 .newWebSocketBuilder()
  94                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
  95                 .join();
  96 
  97         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(126)));
  98         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(127)));
  99         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(128)));
 100         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(129)));
 101         assertFails(IAE, webSocket.sendPing(ByteBuffer.allocate(256)));
 102 
 103         assertFails(IAE, webSocket.sendPong(ByteBuffer.allocate(126)));
 104         assertFails(IAE, webSocket.sendPong(ByteBuffer.allocate(127)));


 130         assertFails(IAE, webSocket.sendClose(1002, "a reason"));
 131         assertFails(IAE, webSocket.sendClose(1003, "a reason"));
 132         assertFails(IAE, webSocket.sendClose(1006, "a reason"));
 133         assertFails(IAE, webSocket.sendClose(1007, "a reason"));
 134         assertFails(IAE, webSocket.sendClose(1009, "a reason"));
 135         assertFails(IAE, webSocket.sendClose(1010, "a reason"));
 136         assertFails(IAE, webSocket.sendClose(1012, "a reason"));
 137         assertFails(IAE, webSocket.sendClose(1013, "a reason"));
 138         assertFails(IAE, webSocket.sendClose(1015, "a reason"));
 139         assertFails(IAE, webSocket.sendClose(5000, "a reason"));
 140         assertFails(IAE, webSocket.sendClose(32768, "a reason"));
 141         assertFails(IAE, webSocket.sendClose(65535, "a reason"));
 142         assertFails(IAE, webSocket.sendClose(65536, "a reason"));
 143         assertFails(IAE, webSocket.sendClose(Integer.MAX_VALUE, "a reason"));
 144         assertFails(IAE, webSocket.sendClose(Integer.MIN_VALUE, "a reason"));
 145 
 146         assertThrows(IAE, () -> webSocket.request(Integer.MIN_VALUE));
 147         assertThrows(IAE, () -> webSocket.request(Long.MIN_VALUE));
 148         assertThrows(IAE, () -> webSocket.request(-1));
 149         assertThrows(IAE, () -> webSocket.request(0));
 150 
 151         server.close();
 152     }
 153 
 154     @Test
 155     public void partialBinaryThenText() throws IOException {
 156         server = new DummyWebSocketServer();
 157         server.open();
 158         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 159                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 160                 .join();
 161         webSocket.sendBinary(ByteBuffer.allocate(16), false).join();
 162         assertFails(ISE, webSocket.sendText("text", false));
 163         assertFails(ISE, webSocket.sendText("text", true));
 164         // Pings & Pongs are fine
 165         webSocket.sendPing(ByteBuffer.allocate(125)).join();
 166         webSocket.sendPong(ByteBuffer.allocate(125)).join();
 167         server.close();
 168     }
 169 
 170     @Test
 171     public void partialTextThenBinary() throws IOException {
 172         server = new DummyWebSocketServer();
 173         server.open();
 174         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 175                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 176                 .join();
 177 
 178         webSocket.sendText("text", false).join();
 179         assertFails(ISE, webSocket.sendBinary(ByteBuffer.allocate(16), false));
 180         assertFails(ISE, webSocket.sendBinary(ByteBuffer.allocate(16), true));
 181         // Pings & Pongs are fine
 182         webSocket.sendPing(ByteBuffer.allocate(125)).join();
 183         webSocket.sendPong(ByteBuffer.allocate(125)).join();
 184         server.close();
 185     }
 186 
 187     @Test
 188     public void sendMethodsThrowIOE1() throws IOException {
 189         server = new DummyWebSocketServer();
 190         server.open();
 191         webSocket = newBuilder().proxy(NO_PROXY).build()
 192                 .newWebSocketBuilder()
 193                 .buildAsync(server.getURI(), new WebSocket.Listener() { })
 194                 .join();
 195 
 196         webSocket.sendClose(NORMAL_CLOSURE, "ok").join();
 197 
 198         assertFails(IOE, webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
 199 
 200         assertFails(IOE, webSocket.sendText("", true));
 201         assertFails(IOE, webSocket.sendText("", false));
 202         assertFails(IOE, webSocket.sendText("abc", true));
 203         assertFails(IOE, webSocket.sendText("abc", false));
 204         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), true));
 205         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), false));
 206         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), true));
 207         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), false));
 208 
 209         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(125)));
 210         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(124)));
 211         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(1)));
 212         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(0)));
 213 
 214         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(125)));
 215         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(124)));
 216         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(1)));
 217         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(0)));
 218 
 219         server.close();
 220     }
 221 
 222     @DataProvider(name = "sequence")
 223     public Object[][] data1() {
 224         int[] CLOSE = {
 225                 0x81, 0x00, // ""
 226                 0x82, 0x00, // []
 227                 0x89, 0x00, // <PING>
 228                 0x8a, 0x00, // <PONG>
 229                 0x88, 0x00, // <CLOSE>
 230         };
 231         int[] ERROR = {
 232                 0x81, 0x00, // ""
 233                 0x82, 0x00, // []
 234                 0x89, 0x00, // <PING>
 235                 0x8a, 0x00, // <PONG>
 236                 0x8b, 0x00, // 0xB control frame (causes an error)
 237         };
 238         return new Object[][]{
 239                 {CLOSE, 1},


 320                         () -> super.onClose(webSocket, statusCode, reason));
 321             }
 322 
 323             @Override
 324             public void onError(WebSocket webSocket, Throwable error) {
 325                 checkRunExclusively(() -> {
 326                     super.onError(webSocket, error);
 327                     return null;
 328                 });
 329             }
 330         };
 331 
 332         webSocket = newBuilder().proxy(NO_PROXY).build().newWebSocketBuilder()
 333                 .buildAsync(server.getURI(), listener)
 334                 .join();
 335 
 336 
 337         listener.invocations();
 338         violation.complete(null); // won't affect if completed exceptionally
 339         violation.join();
 340 
 341         server.close();
 342     }
 343 
 344     @Test
 345     public void sendMethodsThrowIOE2() throws Exception {
 346         server = Support.serverWithCannedData(0x88, 0x00);
 347         server.open();
 348         CompletableFuture<Void> onCloseCalled = new CompletableFuture<>();
 349         CompletableFuture<Void> canClose = new CompletableFuture<>();
 350 
 351         WebSocket.Listener listener = new WebSocket.Listener() {
 352             @Override
 353             public CompletionStage<?> onClose(WebSocket webSocket,
 354                                               int statusCode,
 355                                               String reason) {
 356                 System.out.printf("onClose(%s, '%s')%n", statusCode, reason);
 357                 onCloseCalled.complete(null);
 358                 return canClose;
 359             }
 360 
 361             @Override


 376         assertFails(IOE, webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
 377 
 378         assertFails(IOE, webSocket.sendText("", true));
 379         assertFails(IOE, webSocket.sendText("", false));
 380         assertFails(IOE, webSocket.sendText("abc", true));
 381         assertFails(IOE, webSocket.sendText("abc", false));
 382         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), true));
 383         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(0), false));
 384         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), true));
 385         assertFails(IOE, webSocket.sendBinary(ByteBuffer.allocate(1), false));
 386 
 387         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(125)));
 388         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(124)));
 389         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(1)));
 390         assertFails(IOE, webSocket.sendPing(ByteBuffer.allocate(0)));
 391 
 392         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(125)));
 393         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(124)));
 394         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(1)));
 395         assertFails(IOE, webSocket.sendPong(ByteBuffer.allocate(0)));
 396 
 397         server.close();
 398     }
 399 
 400     // Used to verify a server requiring Authentication
 401     private static final String USERNAME = "chegar";
 402     private static final String PASSWORD = "a1b2c3";
 403 
 404     static class WSAuthenticator extends Authenticator {
 405         @Override
 406         protected PasswordAuthentication getPasswordAuthentication() {
 407             return new PasswordAuthentication(USERNAME, PASSWORD.toCharArray());
 408         }
 409     }
 410 
 411     static final Function<int[],DummyWebSocketServer> SERVER_WITH_CANNED_DATA =
 412         new Function<>() {
 413             @Override public DummyWebSocketServer apply(int[] data) {
 414                 return Support.serverWithCannedData(data); }
 415             @Override public String toString() { return "SERVER_WITH_CANNED_DATA"; }
 416         };
 417 
 418     static final Function<int[],DummyWebSocketServer> AUTH_SERVER_WITH_CANNED_DATA =
 419         new Function<>() {
 420             @Override public DummyWebSocketServer apply(int[] data) {
 421                 return Support.serverWithCannedDataAndAuthentication(USERNAME, PASSWORD, data); }
 422             @Override public String toString() { return "AUTH_SERVER_WITH_CANNED_DATA"; }
 423         };
 424 
 425     @DataProvider(name = "servers")
 426     public Object[][] servers() {
 427         return new Object[][] {
 428             { SERVER_WITH_CANNED_DATA },
 429             { AUTH_SERVER_WITH_CANNED_DATA },
 430         };
 431     }
 432 
 433     @Test(dataProvider = "servers")
 434     public void simpleAggregatingBinaryMessages
 435             (Function<int[],DummyWebSocketServer> serverSupplier)
 436         throws IOException
 437     {
 438         List<byte[]> expected = List.of("alpha", "beta", "gamma", "delta")
 439                 .stream()
 440                 .map(s -> s.getBytes(StandardCharsets.US_ASCII))
 441                 .collect(Collectors.toList());
 442         int[] binary = new int[]{
 443                 0x82, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // [alpha]
 444                 0x02, 0x02, 0x62, 0x65,                   // [be
 445                 0x80, 0x02, 0x74, 0x61,                   // ta]
 446                 0x02, 0x01, 0x67,                         // [g
 447                 0x00, 0x01, 0x61,                         // a
 448                 0x00, 0x00,                               //
 449                 0x00, 0x00,                               //
 450                 0x00, 0x01, 0x6d,                         // m
 451                 0x00, 0x01, 0x6d,                         // m
 452                 0x80, 0x01, 0x61,                         // a]
 453                 0x8a, 0x00,                               // <PONG>
 454                 0x02, 0x04, 0x64, 0x65, 0x6c, 0x74,       // [delt
 455                 0x00, 0x01, 0x61,                         // a
 456                 0x80, 0x00,                               // ]
 457                 0x88, 0x00                                // <CLOSE>
 458         };
 459         CompletableFuture<List<byte[]>> actual = new CompletableFuture<>();
 460 
 461         server = serverSupplier.apply(binary);
 462         server.open();
 463 
 464         WebSocket.Listener listener = new WebSocket.Listener() {
 465 
 466             List<byte[]> collectedBytes = new ArrayList<>();
 467             ByteBuffer buffer = ByteBuffer.allocate(1024);
 468 
 469             @Override
 470             public CompletionStage<?> onBinary(WebSocket webSocket,
 471                                                ByteBuffer message,
 472                                                boolean last) {
 473                 System.out.printf("onBinary(%s, %s)%n", message, last);
 474                 webSocket.request(1);
 475 
 476                 append(message);
 477                 if (last) {
 478                     buffer.flip();
 479                     byte[] bytes = new byte[buffer.remaining()];
 480                     buffer.get(bytes);
 481                     buffer.clear();
 482                     processWholeBinary(bytes);
 483                 }
 484                 return null;
 485             }
 486 
 487             private void append(ByteBuffer message) {
 488                 if (buffer.remaining() < message.remaining()) {
 489                     assert message.remaining() > 0;
 490                     int cap = (buffer.capacity() + message.remaining()) * 2;
 491                     ByteBuffer b = ByteBuffer.allocate(cap);
 492                     b.put(buffer.flip());
 493                     buffer = b;
 494                 }
 495                 buffer.put(message);
 496             }
 497 
 498             private void processWholeBinary(byte[] bytes) {
 499                 String stringBytes = new String(bytes, UTF_8);
 500                 System.out.println("processWholeBinary: " + stringBytes);
 501                 collectedBytes.add(bytes);
 502             }
 503 
 504             @Override
 505             public CompletionStage<?> onClose(WebSocket webSocket,
 506                                               int statusCode,
 507                                               String reason) {
 508                 actual.complete(collectedBytes);
 509                 return null;
 510             }
 511 
 512             @Override
 513             public void onError(WebSocket webSocket, Throwable error) {
 514                 actual.completeExceptionally(error);
 515             }
 516         };
 517 
 518         webSocket = newBuilder()
 519                 .proxy(NO_PROXY)
 520                 .authenticator(new WSAuthenticator())
 521                 .build().newWebSocketBuilder()
 522                 .buildAsync(server.getURI(), listener)
 523                 .join();
 524 
 525         List<byte[]> a = actual.join();
 526         assertEquals(a, expected);

 527 
 528         server.close();
 529     }
 530 
 531     @Test(dataProvider = "servers")
 532     public void simpleAggregatingTextMessages
 533             (Function<int[],DummyWebSocketServer> serverSupplier)
 534         throws IOException
 535     {
 536         List<String> expected = List.of("alpha", "beta", "gamma", "delta");
 537 
 538         int[] binary = new int[]{
 539                 0x81, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // "alpha"
 540                 0x01, 0x02, 0x62, 0x65,                   // "be
 541                 0x80, 0x02, 0x74, 0x61,                   // ta"
 542                 0x01, 0x01, 0x67,                         // "g
 543                 0x00, 0x01, 0x61,                         // a
 544                 0x00, 0x00,                               //
 545                 0x00, 0x00,                               //
 546                 0x00, 0x01, 0x6d,                         // m
 547                 0x00, 0x01, 0x6d,                         // m
 548                 0x80, 0x01, 0x61,                         // a"
 549                 0x8a, 0x00,                               // <PONG>
 550                 0x01, 0x04, 0x64, 0x65, 0x6c, 0x74,       // "delt
 551                 0x00, 0x01, 0x61,                         // a
 552                 0x80, 0x00,                               // "
 553                 0x88, 0x00                                // <CLOSE>
 554         };
 555         CompletableFuture<List<String>> actual = new CompletableFuture<>();
 556 
 557         server = serverSupplier.apply(binary);
 558         server.open();
 559 
 560         WebSocket.Listener listener = new WebSocket.Listener() {
 561 
 562             List<String> collectedStrings = new ArrayList<>();
 563             StringBuilder text = new StringBuilder();
 564 
 565             @Override
 566             public CompletionStage<?> onText(WebSocket webSocket,
 567                                              CharSequence message,
 568                                              boolean last) {
 569                 System.out.printf("onText(%s, %s)%n", message, last);
 570                 webSocket.request(1);
 571                 text.append(message);
 572                 if (last) {
 573                     String str = text.toString();
 574                     text.setLength(0);
 575                     processWholeText(str);
 576                 }
 577                 return null;


 579 
 580             private void processWholeText(String string) {
 581                 System.out.println(string);
 582                 collectedStrings.add(string);
 583             }
 584 
 585             @Override
 586             public CompletionStage<?> onClose(WebSocket webSocket,
 587                                               int statusCode,
 588                                               String reason) {
 589                 actual.complete(collectedStrings);
 590                 return null;
 591             }
 592 
 593             @Override
 594             public void onError(WebSocket webSocket, Throwable error) {
 595                 actual.completeExceptionally(error);
 596             }
 597         };
 598 
 599         webSocket = newBuilder()
 600                 .proxy(NO_PROXY)
 601                 .authenticator(new WSAuthenticator())
 602                 .build().newWebSocketBuilder()
 603                 .buildAsync(server.getURI(), listener)
 604                 .join();
 605 
 606         List<String> a = actual.join();
 607         assertEquals(a, expected);
 608 
 609         server.close();
 610     }
 611 
 612     /*
 613      * Exercises the scenario where requests for more messages are made prior to
 614      * completing the returned CompletionStage instances.
 615      */
 616     @Test(dataProvider = "servers")
 617     public void aggregatingTextMessages
 618         (Function<int[],DummyWebSocketServer> serverSupplier)
 619         throws IOException
 620     {
 621         List<String> expected = List.of("alpha", "beta", "gamma", "delta");
 622 
 623         int[] binary = new int[]{
 624                 0x81, 0x05, 0x61, 0x6c, 0x70, 0x68, 0x61, // "alpha"
 625                 0x01, 0x02, 0x62, 0x65,                   // "be
 626                 0x80, 0x02, 0x74, 0x61,                   // ta"
 627                 0x01, 0x01, 0x67,                         // "g
 628                 0x00, 0x01, 0x61,                         // a
 629                 0x00, 0x00,                               //
 630                 0x00, 0x00,                               //
 631                 0x00, 0x01, 0x6d,                         // m
 632                 0x00, 0x01, 0x6d,                         // m
 633                 0x80, 0x01, 0x61,                         // a"
 634                 0x8a, 0x00,                               // <PONG>
 635                 0x01, 0x04, 0x64, 0x65, 0x6c, 0x74,       // "delt
 636                 0x00, 0x01, 0x61,                         // a
 637                 0x80, 0x00,                               // "
 638                 0x88, 0x00                                // <CLOSE>
 639         };
 640         CompletableFuture<List<String>> actual = new CompletableFuture<>();
 641 
 642         server = serverSupplier.apply(binary);

 643         server.open();
 644 
 645         WebSocket.Listener listener = new WebSocket.Listener() {
 646 
 647             List<CharSequence> parts = new ArrayList<>();
 648             /*
 649              * A CompletableFuture which will complete once the current
 650              * message has been fully assembled. Until then the listener
 651              * returns this instance for every call.
 652              */
 653             CompletableFuture<?> currentCf = new CompletableFuture<>();
 654             List<String> collected = new ArrayList<>();
 655 
 656             @Override
 657             public CompletionStage<?> onText(WebSocket webSocket,
 658                                              CharSequence message,
 659                                              boolean last) {
 660                 parts.add(message);
 661                 if (!last) {
 662                     webSocket.request(1);


 678                 actual.complete(collected);
 679                 return null;
 680             }
 681 
 682             @Override
 683             public void onError(WebSocket webSocket, Throwable error) {
 684                 actual.completeExceptionally(error);
 685             }
 686 
 687             public void processWholeMessage(List<CharSequence> data,
 688                                             CompletableFuture<?> cf) {
 689                 StringBuilder b = new StringBuilder();
 690                 data.forEach(b::append);
 691                 String s = b.toString();
 692                 System.out.println(s);
 693                 cf.complete(null);
 694                 collected.add(s);
 695             }
 696         };
 697 
 698         webSocket = newBuilder()
 699                 .proxy(NO_PROXY)
 700                 .authenticator(new WSAuthenticator())
 701                 .build().newWebSocketBuilder()
 702                 .buildAsync(server.getURI(), listener)
 703                 .join();
 704 
 705         List<String> a = actual.join();
 706         assertEquals(a, expected);
 707 
 708         server.close();
 709     }
 710 
 711     // -- authentication specific tests
 712 
 713     /*
 714      * Ensures authentication succeeds when an Authenticator set on client builder.
 715      */
 716     @Test
 717     public void clientAuthenticate() throws IOException  {
 718         try (var server = new DummyWebSocketServer(USERNAME, PASSWORD)){
 719             server.open();
 720 
 721             var webSocket = newBuilder()
 722                     .proxy(NO_PROXY)
 723                     .authenticator(new WSAuthenticator())
 724                     .build()
 725                     .newWebSocketBuilder()
 726                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
 727                     .join();
 728         }
 729     }
 730 
 731     /*
 732      * Ensures authentication succeeds when an `Authorization` header is explicitly set.
 733      */
 734     @Test
 735     public void explicitAuthenticate() throws IOException  {
 736         try (var server = new DummyWebSocketServer(USERNAME, PASSWORD)) {
 737             server.open();
 738 
 739             String hv = "Basic " + Base64.getEncoder().encodeToString(
 740                     (USERNAME + ":" + PASSWORD).getBytes(UTF_8));
 741 
 742             var webSocket = newBuilder()
 743                     .proxy(NO_PROXY).build()
 744                     .newWebSocketBuilder()
 745                     .header("Authorization", hv)
 746                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
 747                     .join();
 748         }
 749     }
 750 
 751     /*
 752      * Ensures authentication does not succeed when no authenticator is present.
 753      */
 754     @Test
 755     public void failNoAuthenticator() throws IOException  {
 756         try (var server = new DummyWebSocketServer(USERNAME, PASSWORD)) {
 757             server.open();
 758 
 759             CompletableFuture<WebSocket> cf = newBuilder()
 760                     .proxy(NO_PROXY).build()
 761                     .newWebSocketBuilder()
 762                     .buildAsync(server.getURI(), new WebSocket.Listener() { });
 763 
 764             try {
 765                 var webSocket = cf.join();
 766                 fail("Expected exception not thrown");
 767             } catch (CompletionException expected) {
 768                 WebSocketHandshakeException e = (WebSocketHandshakeException)expected.getCause();
 769                 HttpResponse<?> response = e.getResponse();
 770                 assertEquals(response.statusCode(), 401);
 771             }
 772         }
 773     }
 774 
 775     /*
 776      * Ensures authentication does not succeed when the authenticator presents
 777      * unauthorized credentials.
 778      */
 779     @Test
 780     public void failBadCredentials() throws IOException  {
 781         try (var server = new DummyWebSocketServer(USERNAME, PASSWORD)) {
 782             server.open();
 783 
 784             Authenticator authenticator = new Authenticator() {
 785                 @Override protected PasswordAuthentication getPasswordAuthentication() {
 786                     return new PasswordAuthentication("BAD"+USERNAME, "".toCharArray());
 787                 }
 788             };
 789 
 790             CompletableFuture<WebSocket> cf = newBuilder()
 791                     .proxy(NO_PROXY)
 792                     .authenticator(authenticator)
 793                     .build()
 794                     .newWebSocketBuilder()
 795                     .buildAsync(server.getURI(), new WebSocket.Listener() { });
 796 
 797             try {
 798                 var webSocket = cf.join();
 799                 fail("Expected exception not thrown");
 800             } catch (CompletionException expected) {
 801                 System.out.println("caught expected exception:" + expected);
 802             }
 803         }
 804     }
 805 }
< prev index next >