# HG changeset patch # User prappo # Date 1516294334 0 # Node ID de352132c7e89e7ab3b01041ead1cdd69021f71f # Parent fa36a61f4cbf2b42b77e8742422e7cf113fec5ec http-client-branch: (WebSocket) a number of tests for exceptional completion diff -r fa36a61f4cbf -r de352132c7e8 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java Thu Jan 18 11:26:39 2018 +0000 +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/Utils.java Thu Jan 18 16:52:14 2018 +0000 @@ -110,7 +110,10 @@ if (!(x instanceof CompletionException) && !(x instanceof ExecutionException)) return x; final Throwable cause = x.getCause(); - return cause == null ? x : cause; + if (cause == null) { + throw new InternalError("Unexpected null cause", x); + } + return cause; } public static IOException getIOException(Throwable t) { diff -r fa36a61f4cbf -r de352132c7e8 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportImpl.java --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportImpl.java Thu Jan 18 11:26:39 2018 +0000 +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/TransportImpl.java Thu Jan 18 16:52:14 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,6 +41,7 @@ import java.util.function.Supplier; import static java.util.Objects.requireNonNull; +import static jdk.incubator.http.internal.common.MinimalFuture.failedFuture; import static jdk.incubator.http.internal.common.Pair.pair; public class TransportImpl implements Transport { @@ -154,7 +155,13 @@ public CompletableFuture sendText(CharSequence message, boolean isLast) { - return enqueue(new OutgoingMessage.Text(message, isLast)); + OutgoingMessage.Text m; + try { + m = new OutgoingMessage.Text(message, isLast); + } catch (IllegalArgumentException e) { + return failedFuture(e); + } + return enqueue(m); } public CompletableFuture sendBinary(ByteBuffer message, @@ -163,15 +170,33 @@ } public CompletableFuture sendPing(ByteBuffer message) { - return enqueue(new OutgoingMessage.Ping(message)); + OutgoingMessage.Ping m; + try { + m = new OutgoingMessage.Ping(message); + } catch (IllegalArgumentException e) { + return failedFuture(e); + } + return enqueue(m); } public CompletableFuture sendPong(ByteBuffer message) { - return enqueue(new OutgoingMessage.Pong(message)); + OutgoingMessage.Pong m; + try { + m = new OutgoingMessage.Pong(message); + } catch (IllegalArgumentException e) { + return failedFuture(e); + } + return enqueue(m); } public CompletableFuture sendClose(int statusCode, String reason) { - return enqueue(new OutgoingMessage.Close(statusCode, reason)); + OutgoingMessage.Close m; + try { + m = new OutgoingMessage.Close(statusCode, reason); + } catch (IllegalArgumentException e) { + return failedFuture(e); + } + return enqueue(m); } private CompletableFuture enqueue(OutgoingMessage m) { diff -r fa36a61f4cbf -r de352132c7e8 src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java --- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java Thu Jan 18 11:26:39 2018 +0000 +++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java Thu Jan 18 16:52:14 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,6 @@ import java.nio.ByteBuffer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -202,13 +201,14 @@ // a second sendClose may prematurely close the channel outputClosed = true; return transport.sendClose(statusCode, reason) - .whenComplete((r, error) -> { + .whenComplete((result, error) -> { try { transport.closeOutput(); } catch (IOException e) { Log.logError(e); } - if (error instanceof TimeoutException) { + Throwable cause = Utils.getCompletionCause(error); + if (cause instanceof TimeoutException) { try { transport.closeInput(); } catch (IOException e) { diff -r fa36a61f4cbf -r de352132c7e8 test/jdk/java/net/httpclient/websocket/Exceptionally.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/jdk/java/net/httpclient/websocket/Exceptionally.java Thu Jan 18 16:52:14 2018 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @build DummyWebSocketServer + * @run testng/othervm -Djdk.httpclient.HttpClient.log=trace Exceptionally + */ + +import jdk.incubator.http.WebSocket; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; + +import static jdk.incubator.http.HttpClient.newHttpClient; +import static jdk.incubator.http.WebSocket.NORMAL_CLOSURE; +import static org.testng.Assert.assertThrows; + +public class Exceptionally { + + static final Class NPE = NullPointerException.class; + + @Test + public void testNull() throws IOException { + try (DummyWebSocketServer server = new DummyWebSocketServer()) { + server.open(); + WebSocket ws = newHttpClient() + .newWebSocketBuilder() + .buildAsync(server.getURI(), new WebSocket.Listener() { }) + .join(); + + assertThrows(NPE, () -> ws.sendPing(null)); + assertThrows(NPE, () -> ws.sendPong(null)); + assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null)); + + // ... add more NPE scenarios + } + } + + private static String stringWithNBytes(int n) { + StringBuilder sb = new StringBuilder(n); + for (int i = 0; i < n; i++) + sb.append("A"); + return sb.toString(); + } + + @Test + public void testIllegalArgument() throws IOException { + try (DummyWebSocketServer server = new DummyWebSocketServer()) { + server.open(); + WebSocket ws = newHttpClient() + .newWebSocketBuilder() + .buildAsync(server.getURI(), new WebSocket.Listener() { }) + .join(); + + assertIAE(ws.sendPing(ByteBuffer.allocate(126))); + assertIAE(ws.sendPing(ByteBuffer.allocate(127))); + assertIAE(ws.sendPing(ByteBuffer.allocate(128))); + assertIAE(ws.sendPing(ByteBuffer.allocate(150))); + + assertIAE(ws.sendPong(ByteBuffer.allocate(126))); + assertIAE(ws.sendPong(ByteBuffer.allocate(127))); + assertIAE(ws.sendPong(ByteBuffer.allocate(128))); + assertIAE(ws.sendPong(ByteBuffer.allocate(150))); + + assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124))); + assertIAE(ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(150))); + assertIAE(ws.sendClose(NORMAL_CLOSURE - 1, "a reason")); + + // ... add more CF complete exceptionally scenarios + } + } + + @Test + public void testIllegalState() throws IOException { + try (DummyWebSocketServer server = new DummyWebSocketServer()) { + server.open(); + WebSocket ws = newHttpClient() + .newWebSocketBuilder() + .buildAsync(server.getURI(), new WebSocket.Listener() { }) + .join(); + + ws.sendClose(NORMAL_CLOSURE, "normal close").join(); + + assertISE(ws.sendPing(ByteBuffer.allocate(125))); + assertISE(ws.sendPing(ByteBuffer.allocate(124))); + assertISE(ws.sendPing(ByteBuffer.allocate( 1))); + assertISE(ws.sendPing(ByteBuffer.allocate( 0))); + + assertISE(ws.sendPong(ByteBuffer.allocate(125))); + assertISE(ws.sendPong(ByteBuffer.allocate(124))); + assertISE(ws.sendPong(ByteBuffer.allocate( 1))); + assertISE(ws.sendPong(ByteBuffer.allocate( 0))); + + // ... add more CF complete exceptionally scenarios + } + } + + private void assertIAE(CompletableFuture stage) { + assertExceptionally(IllegalArgumentException.class, stage); + } + + private void assertISE(CompletableFuture stage) { + assertExceptionally(IllegalStateException.class, stage); + } + + private void assertExceptionally(Class clazz, + CompletableFuture stage) { + stage.handle((result, error) -> { + if (error instanceof CompletionException) { + Throwable cause = error.getCause(); + if (cause == null) { + throw new AssertionError("Unexpected null cause: " + error); + } + assertException(clazz, cause); + } else { + assertException(clazz, error); + } + return null; + }).join(); + } + + private void assertException(Class clazz, Throwable t) { + if (t == null) { + throw new AssertionError("Expected " + clazz + ", caught nothing"); + } + if (!clazz.isInstance(t)) { + throw new AssertionError("Expected " + clazz + ", caught " + t); + } + } + + // ... more API assertions ??? +} diff -r fa36a61f4cbf -r de352132c7e8 test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java --- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java Thu Jan 18 11:26:39 2018 +0000 +++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/TestSupport.java Thu Jan 18 16:52:14 2018 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -314,7 +314,7 @@ CompletionStage stage) { CompletableFuture cf = CompletableFuture.completedFuture(null).thenCompose(x -> stage); - return assertThrows(t -> clazz == t.getCause().getClass(), cf::get); + return assertThrows(t -> clazz.isInstance(t.getCause()), cf::get); } interface ThrowingProcedure {