test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java
changeset 49765 ee6f7a61f3a5
child 49944 4690a2871b44
child 56451 9585061fdb04
equal deleted inserted replaced
49707:f7fd051519ac 49765:ee6f7a61f3a5
       
     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  * @bug 8159053
       
    27  *
       
    28  *
       
    29  * @run testng/othervm
       
    30  *      -Djdk.internal.httpclient.websocket.debug=true
       
    31  *      -Djdk.internal.httpclient.debug=true
       
    32  *      -Djdk.httpclient.websocket.writeBufferSize=1024
       
    33  *      -Djdk.httpclient.websocket.intermediateBufferSize=2048 WebSocketExtendedTest
       
    34  */
       
    35 
       
    36 import org.testng.annotations.DataProvider;
       
    37 import org.testng.annotations.Test;
       
    38 
       
    39 import java.io.IOException;
       
    40 import java.net.http.WebSocket;
       
    41 import java.nio.ByteBuffer;
       
    42 import java.nio.charset.StandardCharsets;
       
    43 import java.util.ArrayList;
       
    44 import java.util.List;
       
    45 import java.util.Random;
       
    46 
       
    47 import static java.net.http.HttpClient.newHttpClient;
       
    48 import static org.testng.Assert.assertEquals;
       
    49 import static org.testng.Assert.assertTrue;
       
    50 
       
    51 
       
    52 /*
       
    53  * This battery of tests exercises sending data (Text/Binary) messages with
       
    54  * possible fragmentation.
       
    55  */
       
    56 public class WebSocketExtendedTest {
       
    57 // * run testng/othervm
       
    58 // *      -Djdk.httpclient.websocket.writeBufferSize=16
       
    59 // *      -Djdk.httpclient.sendBufferSize=32 WebSocketTextTest
       
    60 
       
    61     private final static Random random;
       
    62     static {
       
    63         long seed = System.currentTimeMillis();
       
    64         System.out.println("seed=" + seed);
       
    65         random = new Random(seed);
       
    66     }
       
    67 
       
    68     // FIXME ensure subsequent (sendText/Binary, false) only CONTINUATIONs
       
    69 
       
    70     @Test(dataProvider = "binary")
       
    71     public void binary(ByteBuffer expected) throws IOException, InterruptedException {
       
    72         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
    73             server.open();
       
    74             WebSocket ws = newHttpClient()
       
    75                     .newWebSocketBuilder()
       
    76                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
    77                     .join();
       
    78             ws.sendBinary(expected.duplicate(), true).join();
       
    79             ws.abort();
       
    80             ByteBuffer data = server.read();
       
    81             List<Frame> frames = readFrames(data);
       
    82             assertEquals(frames.size(), 1);
       
    83             Frame f = frames.get(0);
       
    84             assertTrue(f.last);
       
    85             assertEquals(f.opcode, Frame.Opcode.BINARY);
       
    86             assertEquals(f.data, expected);
       
    87         }
       
    88     }
       
    89 
       
    90     private static List<Frame> readFrames(ByteBuffer src) {
       
    91         List<Frame> frames = new ArrayList<>();
       
    92         Frame.Consumer consumer = new Frame.Consumer() {
       
    93 
       
    94             ByteBuffer data;
       
    95             Frame.Opcode opcode;
       
    96             Frame.Masker masker = new Frame.Masker();
       
    97             boolean last;
       
    98 
       
    99             @Override
       
   100             public void fin(boolean value) {
       
   101                 last = value;
       
   102             }
       
   103 
       
   104             @Override
       
   105             public void rsv1(boolean value) {
       
   106                 if (value) {
       
   107                     throw new AssertionError();
       
   108                 }
       
   109             }
       
   110 
       
   111             @Override
       
   112             public void rsv2(boolean value) {
       
   113                 if (value) {
       
   114                     throw new AssertionError();
       
   115                 }
       
   116             }
       
   117 
       
   118             @Override
       
   119             public void rsv3(boolean value) {
       
   120                 if (value) {
       
   121                     throw new AssertionError();
       
   122                 }
       
   123             }
       
   124 
       
   125             @Override
       
   126             public void opcode(Frame.Opcode value) {
       
   127                 opcode = value;
       
   128             }
       
   129 
       
   130             @Override
       
   131             public void mask(boolean value) {
       
   132                 if (!value) { // Frames from the client MUST be masked
       
   133                     throw new AssertionError();
       
   134                 }
       
   135             }
       
   136 
       
   137             @Override
       
   138             public void payloadLen(long value) {
       
   139                 data = ByteBuffer.allocate((int) value);
       
   140             }
       
   141 
       
   142             @Override
       
   143             public void maskingKey(int value) {
       
   144                 masker.mask(value);
       
   145             }
       
   146 
       
   147             @Override
       
   148             public void payloadData(ByteBuffer data) {
       
   149                 masker.transferMasking(data, this.data);
       
   150             }
       
   151 
       
   152             @Override
       
   153             public void endFrame() {
       
   154                 frames.add(new Frame(opcode, this.data.flip(), last));
       
   155             }
       
   156         };
       
   157 
       
   158         Frame.Reader r = new Frame.Reader();
       
   159         while (src.hasRemaining()) {
       
   160             r.readFrame(src, consumer);
       
   161         }
       
   162         return frames;
       
   163     }
       
   164 
       
   165     @Test(dataProvider = "pingPong")
       
   166     public void ping(ByteBuffer expected) throws Exception {
       
   167         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   168             server.open();
       
   169             WebSocket ws = newHttpClient()
       
   170                     .newWebSocketBuilder()
       
   171                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   172                     .join();
       
   173             ws.sendPing(expected.duplicate()).join();
       
   174             ws.abort();
       
   175             ByteBuffer data = server.read();
       
   176             List<Frame> frames = readFrames(data);
       
   177             assertEquals(frames.size(), 1);
       
   178             Frame f = frames.get(0);
       
   179             assertEquals(f.opcode, Frame.Opcode.PING);
       
   180             ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
       
   181             actual.put(f.data);
       
   182             actual.flip();
       
   183             assertEquals(actual, expected);
       
   184         }
       
   185     }
       
   186 
       
   187     @Test(dataProvider = "pingPong")
       
   188     public void pong(ByteBuffer expected) throws Exception {
       
   189         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   190             server.open();
       
   191             WebSocket ws = newHttpClient()
       
   192                     .newWebSocketBuilder()
       
   193                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   194                     .join();
       
   195             ws.sendPong(expected.duplicate()).join();
       
   196             ws.abort();
       
   197             ByteBuffer data = server.read();
       
   198             List<Frame> frames = readFrames(data);
       
   199             assertEquals(frames.size(), 1);
       
   200             Frame f = frames.get(0);
       
   201             assertEquals(f.opcode, Frame.Opcode.PONG);
       
   202             ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
       
   203             actual.put(f.data);
       
   204             actual.flip();
       
   205             assertEquals(actual, expected);
       
   206         }
       
   207     }
       
   208 
       
   209     @Test(dataProvider = "close")
       
   210     public void close(int statusCode, String reason) throws Exception {
       
   211         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   212             server.open();
       
   213             WebSocket ws = newHttpClient()
       
   214                     .newWebSocketBuilder()
       
   215                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   216                     .join();
       
   217             ws.sendClose(statusCode, reason).join();
       
   218             ws.abort();
       
   219             ByteBuffer data = server.read();
       
   220             List<Frame> frames = readFrames(data);
       
   221             assertEquals(frames.size(), 1);
       
   222             Frame f = frames.get(0);
       
   223             assertEquals(f.opcode, Frame.Opcode.CLOSE);
       
   224             ByteBuffer actual = ByteBuffer.allocate(Frame.MAX_CONTROL_FRAME_PAYLOAD_SIZE);
       
   225             actual.put(f.data);
       
   226             actual.flip();
       
   227             assertEquals(actual.getChar(), statusCode);
       
   228             assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), reason);
       
   229         }
       
   230     }
       
   231 
       
   232     @Test(dataProvider = "text")
       
   233     public void text(String expected) throws Exception {
       
   234         try (DummyWebSocketServer server = new DummyWebSocketServer()) {
       
   235             server.open();
       
   236             WebSocket ws = newHttpClient()
       
   237                     .newWebSocketBuilder()
       
   238                     .buildAsync(server.getURI(), new WebSocket.Listener() { })
       
   239                     .join();
       
   240             ws.sendText(expected, true).join();
       
   241             ws.abort();
       
   242             ByteBuffer data = server.read();
       
   243             List<Frame> frames = readFrames(data);
       
   244 
       
   245             int maxBytes = (int) StandardCharsets.UTF_8.newEncoder().maxBytesPerChar() * expected.length();
       
   246             ByteBuffer actual = ByteBuffer.allocate(maxBytes);
       
   247             frames.stream().forEachOrdered(f -> actual.put(f.data));
       
   248             actual.flip();
       
   249             assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), expected);
       
   250         }
       
   251     }
       
   252 
       
   253     @DataProvider(name = "pingPong")
       
   254     public Object[][] pingPongSizes() {
       
   255         return new Object[][]{
       
   256                 {bytes(  0)},
       
   257                 {bytes(  1)},
       
   258                 {bytes( 63)},
       
   259                 {bytes(125)},
       
   260         };
       
   261     }
       
   262 
       
   263     @DataProvider(name = "close")
       
   264     public Object[][] closeArguments() {
       
   265         return new Object[][]{
       
   266                 {WebSocket.NORMAL_CLOSURE, utf8String( 0)},
       
   267                 {WebSocket.NORMAL_CLOSURE, utf8String( 1)},
       
   268                 // 123 / 3 = max reason bytes / max bytes per char
       
   269                 {WebSocket.NORMAL_CLOSURE, utf8String(41)},
       
   270         };
       
   271     }
       
   272 
       
   273     private static String utf8String(int n) {
       
   274         char[] abc = {
       
   275                 // -- English Alphabet (26 characters, 1 byte per char) --
       
   276                 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
       
   277                 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
       
   278                 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
       
   279                 0x0059, 0x005A,
       
   280                 // -- Russian Alphabet (33 characters, 2 bytes per char) --
       
   281                 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
       
   282                 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
       
   283                 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426,
       
   284                 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
       
   285                 0x042F,
       
   286                 // -- Hiragana base characters (46 characters, 3 bytes per char) --
       
   287                 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
       
   288                 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
       
   289                 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
       
   290                 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
       
   291                 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
       
   292                 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093,
       
   293         };
       
   294 
       
   295         assert new String(abc).getBytes(StandardCharsets.UTF_8).length > abc.length;
       
   296 
       
   297         StringBuilder str = new StringBuilder(n);
       
   298         random.ints(0, abc.length).limit(n).forEach(i -> str.append(abc[i]));
       
   299         return str.toString();
       
   300     }
       
   301 
       
   302     @DataProvider(name = "text")
       
   303     public Object[][] texts() {
       
   304         return new Object[][]{
       
   305                 {utf8String(   0)},
       
   306                 {utf8String(1024)},
       
   307         };
       
   308     }
       
   309 
       
   310     @DataProvider(name = "binary")
       
   311     public Object[][] binary() {
       
   312         return new Object[][]{
       
   313                 {bytes(   0)},
       
   314                 {bytes(1024)},
       
   315         };
       
   316     }
       
   317 
       
   318     private static ByteBuffer bytes(int n) {
       
   319         byte[] array = new byte[n];
       
   320         random.nextBytes(array);
       
   321         return ByteBuffer.wrap(array);
       
   322     }
       
   323 }