test/jdk/java/net/httpclient/websocket/Abort.java
changeset 49765 ee6f7a61f3a5
child 58289 3a79d4cccbcb
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  * @build DummyWebSocketServer
       
    27  * @run testng/othervm
       
    28  *      -Djdk.internal.httpclient.websocket.debug=true
       
    29  *       Abort
       
    30  */
       
    31 
       
    32 import org.testng.annotations.AfterTest;
       
    33 import org.testng.annotations.Test;
       
    34 
       
    35 import java.io.IOException;
       
    36 import java.net.ProtocolException;
       
    37 import java.net.http.WebSocket;
       
    38 import java.nio.ByteBuffer;
       
    39 import java.util.Arrays;
       
    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.TimeoutException;
       
    45 
       
    46 import static java.net.http.HttpClient.newHttpClient;
       
    47 import static java.net.http.WebSocket.NORMAL_CLOSURE;
       
    48 import static org.testng.Assert.assertEquals;
       
    49 import static org.testng.Assert.assertThrows;
       
    50 import static org.testng.Assert.assertTrue;
       
    51 import static org.testng.Assert.fail;
       
    52 
       
    53 public class Abort {
       
    54 
       
    55     private static final Class<NullPointerException> NPE = NullPointerException.class;
       
    56     private static final Class<IllegalArgumentException> IAE = IllegalArgumentException.class;
       
    57     private static final Class<IOException> IOE = IOException.class;
       
    58 
       
    59     private DummyWebSocketServer server;
       
    60     private WebSocket webSocket;
       
    61 
       
    62     @AfterTest
       
    63     public void cleanup() {
       
    64         server.close();
       
    65         webSocket.abort();
       
    66     }
       
    67 
       
    68     @Test
       
    69     public void onOpenThenAbort() throws Exception {
       
    70         int[] bytes = new int[]{
       
    71                 0x88, 0x00, // opcode=close
       
    72         };
       
    73         server = Support.serverWithCannedData(bytes);
       
    74         server.open();
       
    75         // messages are available
       
    76         MockListener listener = new MockListener() {
       
    77             @Override
       
    78             protected void onOpen0(WebSocket webSocket) {
       
    79                 // unbounded request
       
    80                 webSocket.request(Long.MAX_VALUE);
       
    81                 webSocket.abort();
       
    82             }
       
    83         };
       
    84         webSocket = newHttpClient().newWebSocketBuilder()
       
    85                 .buildAsync(server.getURI(), listener)
       
    86                 .join();
       
    87         TimeUnit.SECONDS.sleep(5);
       
    88         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
    89         // no more invocations after onOpen as WebSocket was aborted
       
    90         assertEquals(inv, List.of(MockListener.Invocation.onOpen(webSocket)));
       
    91     }
       
    92 
       
    93     @Test
       
    94     public void onOpenThenOnTextThenAbort() throws Exception {
       
    95         int[] bytes = new int[]{
       
    96                 0x81, 0x00, // opcode=text, fin=true
       
    97                 0x88, 0x00, // opcode=close
       
    98         };
       
    99         server = Support.serverWithCannedData(bytes);
       
   100         server.open();
       
   101         MockListener listener = new MockListener() {
       
   102             @Override
       
   103             protected void onOpen0(WebSocket webSocket) {
       
   104                 // unbounded request
       
   105                 webSocket.request(Long.MAX_VALUE);
       
   106             }
       
   107 
       
   108             @Override
       
   109             protected CompletionStage<?> onText0(WebSocket webSocket,
       
   110                                                  CharSequence message,
       
   111                                                  boolean last) {
       
   112                 webSocket.abort();
       
   113                 return super.onText0(webSocket, message, last);
       
   114             }
       
   115         };
       
   116         webSocket = newHttpClient().newWebSocketBuilder()
       
   117                 .buildAsync(server.getURI(), listener)
       
   118                 .join();
       
   119         TimeUnit.SECONDS.sleep(5);
       
   120         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   121         // no more invocations after onOpen, onBinary as WebSocket was aborted
       
   122         List<MockListener.Invocation> expected = List.of(
       
   123                 MockListener.Invocation.onOpen(webSocket),
       
   124                 MockListener.Invocation.onText(webSocket, "", true));
       
   125         assertEquals(inv, expected);
       
   126     }
       
   127 
       
   128     @Test
       
   129     public void onOpenThenOnBinaryThenAbort() throws Exception {
       
   130         int[] bytes = new int[]{
       
   131                 0x82, 0x00, // opcode=binary, fin=true
       
   132                 0x88, 0x00, // opcode=close
       
   133         };
       
   134         server = Support.serverWithCannedData(bytes);
       
   135         server.open();
       
   136         MockListener listener = new MockListener() {
       
   137             @Override
       
   138             protected void onOpen0(WebSocket webSocket) {
       
   139                 // unbounded request
       
   140                 webSocket.request(Long.MAX_VALUE);
       
   141             }
       
   142 
       
   143             @Override
       
   144             protected CompletionStage<?> onBinary0(WebSocket webSocket,
       
   145                                                    ByteBuffer message,
       
   146                                                    boolean last) {
       
   147                 webSocket.abort();
       
   148                 return super.onBinary0(webSocket, message, last);
       
   149             }
       
   150         };
       
   151         webSocket = newHttpClient().newWebSocketBuilder()
       
   152                 .buildAsync(server.getURI(), listener)
       
   153                 .join();
       
   154         TimeUnit.SECONDS.sleep(5);
       
   155         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   156         // no more invocations after onOpen, onBinary as WebSocket was aborted
       
   157         List<MockListener.Invocation> expected = List.of(
       
   158                 MockListener.Invocation.onOpen(webSocket),
       
   159                 MockListener.Invocation.onBinary(webSocket, ByteBuffer.allocate(0), true));
       
   160         assertEquals(inv, expected);
       
   161     }
       
   162 
       
   163     @Test
       
   164     public void onOpenThenOnPingThenAbort() throws Exception {
       
   165         int[] bytes = {
       
   166                 0x89, 0x00, // opcode=ping
       
   167                 0x88, 0x00, // opcode=close
       
   168         };
       
   169         server = Support.serverWithCannedData(bytes);
       
   170         server.open();
       
   171         MockListener listener = new MockListener() {
       
   172             @Override
       
   173             protected void onOpen0(WebSocket webSocket) {
       
   174                 // unbounded request
       
   175                 webSocket.request(Long.MAX_VALUE);
       
   176             }
       
   177 
       
   178             @Override
       
   179             protected CompletionStage<?> onPing0(WebSocket webSocket,
       
   180                                                  ByteBuffer message) {
       
   181                 webSocket.abort();
       
   182                 return super.onPing0(webSocket, message);
       
   183             }
       
   184         };
       
   185         webSocket = newHttpClient().newWebSocketBuilder()
       
   186                 .buildAsync(server.getURI(), listener)
       
   187                 .join();
       
   188         TimeUnit.SECONDS.sleep(5);
       
   189         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   190         // no more invocations after onOpen, onPing as WebSocket was aborted
       
   191         List<MockListener.Invocation> expected = List.of(
       
   192                 MockListener.Invocation.onOpen(webSocket),
       
   193                 MockListener.Invocation.onPing(webSocket, ByteBuffer.allocate(0)));
       
   194         assertEquals(inv, expected);
       
   195     }
       
   196 
       
   197     @Test
       
   198     public void onOpenThenOnPongThenAbort() throws Exception {
       
   199         int[] bytes = {
       
   200                 0x8a, 0x00, // opcode=pong
       
   201                 0x88, 0x00, // opcode=close
       
   202         };
       
   203         server = Support.serverWithCannedData(bytes);
       
   204         server.open();
       
   205         MockListener listener = new MockListener() {
       
   206             @Override
       
   207             protected void onOpen0(WebSocket webSocket) {
       
   208                 // unbounded request
       
   209                 webSocket.request(Long.MAX_VALUE);
       
   210             }
       
   211 
       
   212             @Override
       
   213             protected CompletionStage<?> onPong0(WebSocket webSocket,
       
   214                                                  ByteBuffer message) {
       
   215                 webSocket.abort();
       
   216                 return super.onPong0(webSocket, message);
       
   217             }
       
   218         };
       
   219         webSocket = newHttpClient().newWebSocketBuilder()
       
   220                 .buildAsync(server.getURI(), listener)
       
   221                 .join();
       
   222         TimeUnit.SECONDS.sleep(5);
       
   223         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   224         // no more invocations after onOpen, onPong as WebSocket was aborted
       
   225         List<MockListener.Invocation> expected = List.of(
       
   226                 MockListener.Invocation.onOpen(webSocket),
       
   227                 MockListener.Invocation.onPong(webSocket, ByteBuffer.allocate(0)));
       
   228         assertEquals(inv, expected);
       
   229     }
       
   230 
       
   231     @Test
       
   232     public void onOpenThenOnCloseThenAbort() throws Exception {
       
   233         int[] bytes = {
       
   234                 0x88, 0x00, // opcode=close
       
   235                 0x8a, 0x00, // opcode=pong
       
   236         };
       
   237         server = Support.serverWithCannedData(bytes);
       
   238         server.open();
       
   239         MockListener listener = new MockListener() {
       
   240             @Override
       
   241             protected void onOpen0(WebSocket webSocket) {
       
   242                 // unbounded request
       
   243                 webSocket.request(Long.MAX_VALUE);
       
   244             }
       
   245 
       
   246             @Override
       
   247             protected CompletionStage<?> onClose0(WebSocket webSocket,
       
   248                                                   int statusCode,
       
   249                                                   String reason) {
       
   250                 webSocket.abort();
       
   251                 return super.onClose0(webSocket, statusCode, reason);
       
   252             }
       
   253         };
       
   254         webSocket = newHttpClient().newWebSocketBuilder()
       
   255                 .buildAsync(server.getURI(), listener)
       
   256                 .join();
       
   257         TimeUnit.SECONDS.sleep(5);
       
   258         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   259         // no more invocations after onOpen, onClose
       
   260         List<MockListener.Invocation> expected = List.of(
       
   261                 MockListener.Invocation.onOpen(webSocket),
       
   262                 MockListener.Invocation.onClose(webSocket, 1005, ""));
       
   263         assertEquals(inv, expected);
       
   264     }
       
   265 
       
   266     @Test
       
   267     public void onOpenThenOnErrorThenAbort() throws Exception {
       
   268         // A header of 128 bytes long Ping (which is a protocol error)
       
   269         int[] badPingHeader = new int[]{0x89, 0x7e, 0x00, 0x80};
       
   270         int[] closeMessage = new int[]{0x88, 0x00};
       
   271         int[] bytes = new int[badPingHeader.length + 128 + closeMessage.length];
       
   272         System.arraycopy(badPingHeader, 0, bytes, 0, badPingHeader.length);
       
   273         System.arraycopy(closeMessage, 0, bytes, badPingHeader.length + 128, closeMessage.length);
       
   274         server = Support.serverWithCannedData(bytes);
       
   275         server.open();
       
   276         MockListener listener = new MockListener() {
       
   277             @Override
       
   278             protected void onOpen0(WebSocket webSocket) {
       
   279                 // unbounded request
       
   280                 webSocket.request(Long.MAX_VALUE);
       
   281             }
       
   282 
       
   283             @Override
       
   284             protected void onError0(WebSocket webSocket, Throwable error) {
       
   285                 webSocket.abort();
       
   286                 super.onError0(webSocket, error);
       
   287             }
       
   288         };
       
   289         webSocket = newHttpClient().newWebSocketBuilder()
       
   290                 .buildAsync(server.getURI(), listener)
       
   291                 .join();
       
   292         TimeUnit.SECONDS.sleep(5);
       
   293         List<MockListener.Invocation> inv = listener.invocationsSoFar();
       
   294         // no more invocations after onOpen, onError
       
   295         List<MockListener.Invocation> expected = List.of(
       
   296                 MockListener.Invocation.onOpen(webSocket),
       
   297                 MockListener.Invocation.onError(webSocket, ProtocolException.class));
       
   298         System.out.println("actual invocations:" + Arrays.toString(inv.toArray()));
       
   299         assertEquals(inv, expected);
       
   300     }
       
   301 
       
   302     @Test
       
   303     public void immediateAbort() throws Exception {
       
   304         CompletableFuture<Void> messageReceived = new CompletableFuture<>();
       
   305         WebSocket.Listener listener = new WebSocket.Listener() {
       
   306 
       
   307             @Override
       
   308             public void onOpen(WebSocket webSocket) {
       
   309                 /* no initial request */
       
   310             }
       
   311 
       
   312             @Override
       
   313             public CompletionStage<?> onText(WebSocket webSocket,
       
   314                                              CharSequence message,
       
   315                                              boolean last) {
       
   316                 messageReceived.complete(null);
       
   317                 return null;
       
   318             }
       
   319 
       
   320             @Override
       
   321             public CompletionStage<?> onBinary(WebSocket webSocket,
       
   322                                                ByteBuffer message,
       
   323                                                boolean last) {
       
   324                 messageReceived.complete(null);
       
   325                 return null;
       
   326             }
       
   327 
       
   328             @Override
       
   329             public CompletionStage<?> onPing(WebSocket webSocket,
       
   330                                              ByteBuffer message) {
       
   331                 messageReceived.complete(null);
       
   332                 return null;
       
   333             }
       
   334 
       
   335             @Override
       
   336             public CompletionStage<?> onPong(WebSocket webSocket,
       
   337                                              ByteBuffer message) {
       
   338                 messageReceived.complete(null);
       
   339                 return null;
       
   340             }
       
   341 
       
   342             @Override
       
   343             public CompletionStage<?> onClose(WebSocket webSocket,
       
   344                                               int statusCode,
       
   345                                               String reason) {
       
   346                 messageReceived.complete(null);
       
   347                 return null;
       
   348             }
       
   349         };
       
   350 
       
   351         int[] bytes = new int[]{
       
   352                 0x82, 0x00, // opcode=binary, fin=true
       
   353                 0x88, 0x00, // opcode=close
       
   354         };
       
   355         server = Support.serverWithCannedData(bytes);
       
   356         server.open();
       
   357 
       
   358         WebSocket ws = newHttpClient()
       
   359                 .newWebSocketBuilder()
       
   360                 .buildAsync(server.getURI(), listener)
       
   361                 .join();
       
   362         for (int i = 0; i < 3; i++) {
       
   363             System.out.printf("iteration #%s%n", i);
       
   364             // after the first abort() each consecutive one must be a no-op,
       
   365             // moreover, query methods should continue to return consistent
       
   366             // values
       
   367             for (int j = 0; j < 3; j++) {
       
   368                 System.out.printf("abort #%s%n", j);
       
   369                 ws.abort();
       
   370                 assertTrue(ws.isInputClosed());
       
   371                 assertTrue(ws.isOutputClosed());
       
   372                 assertEquals(ws.getSubprotocol(), "");
       
   373             }
       
   374             // at this point valid requests MUST be a no-op:
       
   375             for (int j = 0; j < 3; j++) {
       
   376                 System.out.printf("request #%s%n", j);
       
   377                 ws.request(1);
       
   378                 ws.request(2);
       
   379                 ws.request(8);
       
   380                 ws.request(Integer.MAX_VALUE);
       
   381                 ws.request(Long.MAX_VALUE);
       
   382                 // invalid requests MUST throw IAE:
       
   383                 assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
       
   384                 assertThrows(IAE, () -> ws.request(Long.MIN_VALUE));
       
   385                 assertThrows(IAE, () -> ws.request(-1));
       
   386                 assertThrows(IAE, () -> ws.request(0));
       
   387             }
       
   388         }
       
   389         // even though there is a bunch of messages readily available on the
       
   390         // wire we shouldn't have received any of them as we aborted before
       
   391         // the first request
       
   392         try {
       
   393             messageReceived.get(5, TimeUnit.SECONDS);
       
   394             fail();
       
   395         } catch (TimeoutException expected) {
       
   396             System.out.println("Finished waiting");
       
   397         }
       
   398         for (int i = 0; i < 3; i++) {
       
   399             System.out.printf("send #%s%n", i);
       
   400             Support.assertFails(IOE, ws.sendText("text!", false));
       
   401             Support.assertFails(IOE, ws.sendText("text!", true));
       
   402             Support.assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), false));
       
   403             Support.assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), true));
       
   404             Support.assertFails(IOE, ws.sendPing(ByteBuffer.allocate(16)));
       
   405             Support.assertFails(IOE, ws.sendPong(ByteBuffer.allocate(16)));
       
   406             Support.assertFails(IOE, ws.sendClose(NORMAL_CLOSURE, "a reason"));
       
   407             assertThrows(NPE, () -> ws.sendText(null, false));
       
   408             assertThrows(NPE, () -> ws.sendText(null, true));
       
   409             assertThrows(NPE, () -> ws.sendBinary(null, false));
       
   410             assertThrows(NPE, () -> ws.sendBinary(null, true));
       
   411             assertThrows(NPE, () -> ws.sendPing(null));
       
   412             assertThrows(NPE, () -> ws.sendPong(null));
       
   413             assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
       
   414         }
       
   415     }
       
   416 }