--- a/src/java.net.http/share/classes/jdk/internal/net/http/websocket/MessageEncoder.java Wed Mar 14 16:01:41 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/websocket/MessageEncoder.java Fri Mar 16 01:28:51 2018 +0000
@@ -81,7 +81,7 @@
private boolean previousFin = true;
/* Was the previous frame TEXT or a CONTINUATION thereof? */
private boolean previousText;
- private boolean closed; // TODO: too late, need to check it before accepting otherwise the queue might blow up
+ private boolean closed;
/*
* How many bytes of the current message have been already encoded.
@@ -129,8 +129,8 @@
public boolean encodeText(CharBuffer src, boolean last, ByteBuffer dst)
throws IOException
{
- debug.log(Level.DEBUG, "encodeText src.remaining()=%s, %s, %s",
- src.remaining(), last, dst);
+ debug.log(Level.DEBUG, "encode text src=[pos=%s lim=%s cap=%s] last=%s dst=%s",
+ src.position(), src.limit(), src.capacity(), last, dst);
if (closed) {
throw new IOException("Output closed");
}
@@ -207,7 +207,7 @@
public boolean encodeBinary(ByteBuffer src, boolean last, ByteBuffer dst)
throws IOException
{
- debug.log(Level.DEBUG, "encodeBinary %s, %s, %s",
+ debug.log(Level.DEBUG, "encode binary src=%s last=%s dst=%s",
src, last, dst);
if (closed) {
throw new IOException("Output closed");
@@ -245,10 +245,10 @@
public boolean encodePing(ByteBuffer src, ByteBuffer dst)
throws IOException
{
+ debug.log(Level.DEBUG, "encode ping src=%s dst=%s", src, dst);
if (closed) {
throw new IOException("Output closed");
}
- debug.log(Level.DEBUG, "encodePing %s, %s", src, dst);
if (!started) {
expectedLen = src.remaining();
if (expectedLen > Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH) {
@@ -271,11 +271,11 @@
public boolean encodePong(ByteBuffer src, ByteBuffer dst)
throws IOException
{
+ debug.log(Level.DEBUG, "encode pong src=%s dst=%s",
+ src, dst);
if (closed) {
throw new IOException("Output closed");
}
- debug.log(Level.DEBUG, "encodePong %s, %s",
- src, dst);
if (!started) {
expectedLen = src.remaining();
if (expectedLen > Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH) {
@@ -298,13 +298,14 @@
public boolean encodeClose(int statusCode, CharBuffer reason, ByteBuffer dst)
throws IOException
{
- debug.log(Level.DEBUG, "encodeClose %s, reason.length=%s, %s",
- statusCode, reason.length(), dst);
+ debug.log(Level.DEBUG, "encode close statusCode=%s reason=[pos=%s lim=%s cap=%s] dst=%s",
+ statusCode, reason.position(), reason.limit(), reason.capacity(), dst);
if (closed) {
throw new IOException("Output closed");
}
if (!started) {
- debug.log(Level.DEBUG, "reason size %s", reason.remaining());
+ debug.log(Level.DEBUG, "reason [pos=%s lim=%s cap=%s]",
+ reason.position(), reason.limit(), reason.capacity());
intermediateBuffer.position(0).limit(Frame.MAX_CONTROL_FRAME_PAYLOAD_LENGTH);
intermediateBuffer.putChar((char) statusCode);
CoderResult r = charsetEncoder.reset().encode(reason, intermediateBuffer, true);
@@ -329,8 +330,7 @@
setupHeader(Opcode.CLOSE, true, intermediateBuffer.remaining());
started = true;
closed = true;
- debug.log(Level.DEBUG, "intermediateBuffer=%s",
- intermediateBuffer);
+ debug.log(Level.DEBUG, "intermediateBuffer=%s", intermediateBuffer);
}
if (!putAvailable(headerBuffer, dst)) {
return false;
--- a/src/java.net.http/share/classes/jdk/internal/net/http/websocket/TransportImpl.java Wed Mar 14 16:01:41 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/websocket/TransportImpl.java Fri Mar 16 01:28:51 2018 +0000
@@ -96,7 +96,7 @@
private ByteBuffer createWriteBuffer() {
String name = "jdk.httpclient.websocket.writeBufferSize";
int capacity = Utils.getIntegerNetProperty(name, 16384);
- debug.log(Level.DEBUG, "write buffer capacity %s%n", capacity);
+ debug.log(Level.DEBUG, "write buffer capacity %s", capacity);
// TODO (optimization?): allocateDirect if SSL?
return ByteBuffer.allocate(capacity);
@@ -122,7 +122,7 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = counter.incrementAndGet();
- debug.log(Level.DEBUG, "enter send text %s message.length()=%s last=%s",
+ debug.log(Level.DEBUG, "enter send text %s message.length=%s last=%s",
id, message.length(), isLast);
}
// TODO (optimization?):
@@ -153,7 +153,7 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = counter.incrementAndGet();
- debug.log(Level.DEBUG, "enter send binary %s message.remaining()=%s last=%s",
+ debug.log(Level.DEBUG, "enter send binary %s message.remaining=%s last=%s",
id, message.remaining(), isLast);
}
MinimalFuture<T> f = new MinimalFuture<>();
@@ -174,7 +174,7 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = counter.incrementAndGet();
- debug.log(Level.DEBUG, "enter send ping %s message.remaining()=%s",
+ debug.log(Level.DEBUG, "enter send ping %s message.remaining=%s",
id, message.remaining());
}
MinimalFuture<T> f = new MinimalFuture<>();
@@ -195,7 +195,7 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = counter.incrementAndGet();
- debug.log(Level.DEBUG, "enter send pong %s message.remaining()=%s",
+ debug.log(Level.DEBUG, "enter send pong %s message.remaining=%s",
id, message.remaining());
}
MinimalFuture<T> f = new MinimalFuture<>();
@@ -238,7 +238,7 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = counter.incrementAndGet();
- debug.log(Level.DEBUG, "enter send close %s statusCode=%s, reason.length()=%s",
+ debug.log(Level.DEBUG, "enter send close %s statusCode=%s reason.length=%s",
id, statusCode, reason.length());
}
MinimalFuture<T> f = new MinimalFuture<>();
@@ -489,10 +489,10 @@
while (!queue.isEmpty()) {
try {
if (dst.hasRemaining()) {
- debug.log(Level.DEBUG, "%s bytes in buffer",
- dst.remaining());
- // The previous part of the binary representation of the message
- // hasn't been fully written
+ debug.log(Level.DEBUG, "%s bytes remaining in buffer %s",
+ dst.remaining(), dst);
+ // The previous part of the binary representation of the
+ // message hasn't been fully written
if (!tryCompleteWrite()) {
break;
}
@@ -527,7 +527,7 @@
}
private boolean tryCompleteWrite() throws IOException {
- debug.log(Level.DEBUG, "enter writing");
+ debug.log(Level.DEBUG, "enter writing");
boolean finished = false;
loop:
while (true) {
@@ -564,8 +564,7 @@
@SuppressWarnings("unchecked")
private void removeAndComplete(Throwable error) {
- debug.log(Level.DEBUG, "removeAndComplete error=%s",
- (Object) error);
+ debug.log(Level.DEBUG, "removeAndComplete error=%s", (Object) error);
queue.remove();
if (error != null) {
try {
@@ -597,7 +596,7 @@
while (!receiveScheduler.isStopped()) {
if (data.hasRemaining()) {
debug.log(Level.DEBUG, "remaining bytes received %s",
- data.remaining());
+ data.remaining());
if (!demand.isFulfilled()) {
try {
int oldPos = data.position();
--- a/src/java.net.http/share/classes/jdk/internal/net/http/websocket/WebSocketImpl.java Wed Mar 14 16:01:41 2018 +0000
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/websocket/WebSocketImpl.java Fri Mar 16 01:28:51 2018 +0000
@@ -34,12 +34,17 @@
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.lang.System.Logger.Level;
import java.lang.ref.Reference;
-import java.lang.System.Logger.Level;
import java.net.ProtocolException;
import java.net.URI;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@@ -74,7 +79,7 @@
private static final boolean DEBUG = Utils.DEBUG_WS;
private static final System.Logger debug =
- Utils.getWebSocketLogger("[Websocket]"::toString, DEBUG);
+ Utils.getWebSocketLogger("[WebSocket]"::toString, DEBUG);
private final AtomicLong sendCounter = new AtomicLong();
private final AtomicLong receiveCounter = new AtomicLong();
@@ -94,7 +99,7 @@
private final MinimalFuture<WebSocket> DONE = MinimalFuture.completedFuture(this);
private final long closeTimeout;
private volatile boolean inputClosed;
- private volatile boolean outputClosed;
+ private final AtomicBoolean outputClosed = new AtomicBoolean();
private final AtomicReference<State> state = new AtomicReference<>(OPEN);
@@ -176,8 +181,7 @@
v = defaultValue;
}
}
- debug.log(Level.DEBUG, "%s=%s, using value %s",
- property, value, v);
+ debug.log(Level.DEBUG, "%s=%s, using value %s", property, value, v);
return v;
}
@@ -190,8 +194,8 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = sendCounter.incrementAndGet();
- debug.log(Level.DEBUG, "%s send text: payload length=%s last=%s",
- id, message.length(), isLast);
+ debug.log(Level.DEBUG, "enter send text %s payload length=%s last=%s",
+ id, message.length(), isLast);
}
CompletableFuture<WebSocket> result;
if (!outstandingSend.compareAndSet(false, true)) {
@@ -200,9 +204,7 @@
result = transport.sendText(message, isLast, this,
(r, e) -> outstandingSend.set(false));
}
- debug.log(Level.DEBUG,
- "%s send text: returned %s",
- id, result);
+ debug.log(Level.DEBUG, "exit send text %s returned %s", id, result);
return replaceNull(result);
}
@@ -214,9 +216,8 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = sendCounter.incrementAndGet();
- debug.log(Level.DEBUG,
- "%s send binary: payload=%s last=%s",
- id, message, isLast);
+ debug.log(Level.DEBUG, "enter send binary %s payload=%s last=%s",
+ id, message, isLast);
}
CompletableFuture<WebSocket> result;
if (!outstandingSend.compareAndSet(false, true)) {
@@ -225,8 +226,7 @@
result = transport.sendBinary(message, isLast, this,
(r, e) -> outstandingSend.set(false));
}
- debug.log(Level.DEBUG,
- "%s send binary: returned %s", id, result);
+ debug.log(Level.DEBUG, "exit send binary %s returned %s", id, result);
return replaceNull(result);
}
@@ -246,12 +246,11 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = sendCounter.incrementAndGet();
- debug.log(Level.DEBUG, "%s send ping: payload=%s",
- id, message);
+ debug.log(Level.DEBUG, "enter send ping %s payload=%s", id, message);
}
CompletableFuture<WebSocket> result = transport.sendPing(message, this,
(r, e) -> { });
- debug.log(Level.DEBUG, "%s send ping: returned %s", id, result);
+ debug.log(Level.DEBUG, "exit send ping %s returned %s", id, result);
return replaceNull(result);
}
@@ -261,13 +260,11 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = sendCounter.incrementAndGet();
- debug.log(Level.DEBUG, "%s send pong: payload=%s",
- id, message);
+ debug.log(Level.DEBUG, "enter send pong %s payload=%s", id, message);
}
CompletableFuture<WebSocket> result = transport.sendPong(message, this,
(r, e) -> { });
- debug.log(Level.DEBUG, "%s send pong: returned %s",
- id, result);
+ debug.log(Level.DEBUG, "exit send pong %s returned %s", id, result);
return replaceNull(result);
}
@@ -279,24 +276,50 @@
if (debug.isLoggable(Level.DEBUG)) {
id = sendCounter.incrementAndGet();
debug.log(Level.DEBUG,
- "%s send close: statusCode=%s, reason.length=%s",
- id, statusCode, reason);
+ "enter send close %s statusCode=%s reason.length=%s",
+ id, statusCode, reason.length());
}
CompletableFuture<WebSocket> result;
+ // Close message is the only type of message whose validity is checked
+ // in the corresponding send method. This is made in order to close the
+ // output in place. Otherwise the number of Close messages in queue
+ // would not be bounded.
if (!isLegalToSendFromClient(statusCode)) {
result = failedFuture(new IllegalArgumentException("statusCode"));
+ } else if (!isLegalReason(reason)) {
+ result = failedFuture(new IllegalArgumentException("reason"));
+ } else if (!outputClosed.compareAndSet(false, true)){
+ result = failedFuture(new IOException("Output closed"));
} else {
- // check outputClosed
result = sendClose0(statusCode, reason);
}
- debug.log(Level.DEBUG, "%s send close: returned %s",
- id, result);
+ debug.log(Level.DEBUG, "exit send close %s returned %s", id, result);
return replaceNull(result);
}
+ private static boolean isLegalReason(String reason) {
+ if (reason.length() > 123) { // quick check
+ return false;
+ }
+ CharsetEncoder encoder = StandardCharsets.UTF_8.newEncoder()
+ .onMalformedInput(CodingErrorAction.REPORT)
+ .onUnmappableCharacter(CodingErrorAction.REPORT);
+ ByteBuffer bytes;
+ try {
+ bytes = encoder.encode(CharBuffer.wrap(reason));
+ } catch (CharacterCodingException ignored) {
+ return false;
+ }
+ return bytes.remaining() <= 123;
+ }
+
+ /*
+ * The implementation uses this method internally to send Close messages
+ * with codes that are not allowed to be sent through the API.
+ */
private CompletableFuture<WebSocket> sendClose0(int statusCode,
String reason) {
- outputClosed = true;
+ // TODO: timeout on onClose receiving
CompletableFuture<WebSocket> cf
= transport.sendClose(statusCode, reason, this, (r, e) -> { });
CompletableFuture<WebSocket> closeOrTimeout
@@ -317,19 +340,21 @@
} else {
debug.log(Level.DEBUG, "send close completed with error", e);
}
-
if (e == null) {
+ try {
+ transport.closeOutput();
+ } catch (IOException ignored) { }
return completedFuture(webSocket);
}
Throwable cause = Utils.getCompletionCause(e);
if (cause instanceof IllegalArgumentException) {
return failedFuture(cause);
}
- try {
- transport.closeOutput();
- } catch (IOException ignored) { }
-
if (cause instanceof TimeoutException) {
+ outputClosed.set(true);
+ try {
+ transport.closeOutput();
+ } catch (IOException ignored) { }
inputClosed = true;
try {
transport.closeInput();
@@ -355,7 +380,7 @@
@Override
public boolean isOutputClosed() {
- return outputClosed;
+ return outputClosed.get();
}
@Override
@@ -367,7 +392,7 @@
public void abort() {
debug.log(Level.DEBUG, "abort");
inputClosed = true;
- outputClosed = true;
+ outputClosed.set(true);
receiveScheduler.stop();
close();
}
@@ -443,8 +468,8 @@
}
break loop;
case WAITING:
- // For debugging spurious signalling: when there was a
- // signal, but apparently nothing has changed
+ // For debugging spurious signalling: when there was
+ // a signal, but apparently nothing has changed
break loop;
default:
throw new InternalError(String.valueOf(s));
@@ -464,22 +489,14 @@
if (err instanceof FailWebSocketException) {
int code1 = ((FailWebSocketException) err).getStatusCode();
err = new ProtocolException().initCause(err);
- debug.log(Level.DEBUG,
- "failing %s with error=%s statusCode=%s",
+ debug.log(Level.DEBUG, "failing %s with error=%s statusCode=%s",
WebSocketImpl.this, err, code1);
- sendClose0(code1, "") // TODO handle errors from here
- .whenComplete(
- (r, e) -> {
- if (e != null) {
- Log.logError(e);
- }
- });
+ sendCloseSilently(code1);
}
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = receiveCounter.incrementAndGet();
- debug.log(Level.DEBUG, "enter onError %s error=%s",
- id, err);
+ debug.log(Level.DEBUG, "enter onError %s error=%s", id, err);
}
try {
listener.onError(WebSocketImpl.this, err);
@@ -496,15 +513,14 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = receiveCounter.incrementAndGet();
- debug.log(Level.DEBUG, "enter onClose %s statusCode=%s reason.length=%s",
- id, statusCode, reason.length());
+ debug.log(Level.DEBUG,
+ "enter onClose %s statusCode=%s reason.length=%s",
+ id, statusCode, reason.length());
}
try {
cs = listener.onClose(WebSocketImpl.this, statusCode, reason);
} finally {
- debug.log(Level.DEBUG,
- "exit onClose %s returned %s",
- id, cs);
+ debug.log(Level.DEBUG, "exit onClose %s returned %s", id, cs);
}
if (cs == null) {
cs = DONE;
@@ -512,22 +528,17 @@
int code;
if (statusCode == NO_STATUS_CODE || statusCode == CLOSED_ABNORMALLY) {
code = NORMAL_CLOSURE;
- debug.log(Level.DEBUG,
- "using statusCode %s instead of %s",
- statusCode, code);
+ debug.log(Level.DEBUG, "using statusCode %s instead of %s",
+ statusCode, code);
} else {
code = statusCode;
}
- cs.whenComplete((r, e) -> { // TODO log
- sendClose0(code, "") // TODO handle errors from here
- .whenComplete((r1, e1) -> {
- if (e1 != null) {
- debug.log(Level.DEBUG,
- "processClose completed with errors",
- e1);
- }
- });
+ cs.whenComplete((r, e) -> {
+ debug.log(Level.DEBUG,
+ "CompletionStage returned by onClose completed result=%s error=%s",
+ r, e);
+ sendCloseSilently(code);
});
}
@@ -536,15 +547,13 @@
if (debug.isLoggable(Level.DEBUG)) {
id = receiveCounter.incrementAndGet();
debug.log(Level.DEBUG, "enter onPong %s payload=%s",
- id, binaryData);
+ id, binaryData);
}
CompletionStage<?> cs = null;
try {
cs = listener.onPong(WebSocketImpl.this, binaryData);
} finally {
- debug.log(Level.DEBUG,
- "exit onPong %s returned %s",
- id, cs);
+ debug.log(Level.DEBUG, "exit onPong %s returned %s", id, cs);
}
}
@@ -571,16 +580,13 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = receiveCounter.incrementAndGet();
- debug.log(Level.DEBUG, "enter onPing %s payload=%s",
- id, slice);
+ debug.log(Level.DEBUG, "enter onPing %s payload=%s", id, slice);
}
CompletionStage<?> cs = null;
try {
cs = listener.onPing(WebSocketImpl.this, slice);
} finally {
- debug.log(Level.DEBUG,
- "exit onPing %s returned %s",
- id, cs);
+ debug.log(Level.DEBUG, "exit onPing %s returned %s", id, cs);
}
}
@@ -588,17 +594,14 @@
long id = 0;
if (debug.isLoggable(Level.DEBUG)) {
id = receiveCounter.incrementAndGet();
- debug.log(Level.DEBUG,
- "enter onBinary %s payload=%s part=%s",
+ debug.log(Level.DEBUG, "enter onBinary %s payload=%s part=%s",
id, binaryData, part);
}
CompletionStage<?> cs = null;
try {
cs = listener.onBinary(WebSocketImpl.this, binaryData, part);
} finally {
- debug.log(Level.DEBUG,
- "exit onBinary %s returned %s",
- id, cs);
+ debug.log(Level.DEBUG, "exit onBinary %s returned %s", id, cs);
}
}
@@ -614,8 +617,7 @@
try {
cs = listener.onText(WebSocketImpl.this, text, part);
} finally {
- debug.log(Level.DEBUG,
- "exit onText %s returned %s", id, cs);
+ debug.log(Level.DEBUG, "exit onText %s returned %s", id, cs);
}
}
@@ -633,6 +635,15 @@
}
}
+ private void sendCloseSilently(int statusCode) {
+ sendClose0(statusCode, "").whenComplete((r, e) -> {
+ if (e != null) {
+ debug.log(Level.DEBUG, "automatic closure completed with error",
+ (Object) e);
+ }
+ });
+ }
+
private ByteBuffer clearAutomaticPong() {
ByteBuffer data;
do {
@@ -646,6 +657,7 @@
return data;
}
+ // bound pings
private boolean trySwapAutomaticPong(ByteBuffer copy) {
ByteBuffer message;
boolean swapped;
@@ -666,8 +678,8 @@
break;
}
}
- debug.log(Level.DEBUG, "swapped automatic pong from %s to %s%n",
- message, copy);
+ debug.log(Level.DEBUG, "swapped automatic pong from %s to %s",
+ message, copy);
return swapped;
}
@@ -677,9 +689,9 @@
}
private void signalError(Throwable error) {
- debug.log(Level.DEBUG, "signalError %s", error);
+ debug.log(Level.DEBUG, "signalError %s", (Object) error);
inputClosed = true;
- outputClosed = true;
+ outputClosed.set(true);
if (!this.error.compareAndSet(null, error) || !trySetState(ERROR)) {
Log.logError(error);
} else {
@@ -711,9 +723,7 @@
e = second;
}
if (e != null) {
- debug.log(Level.DEBUG,
- "unexpected exception in close: ",
- e);
+ debug.log(Level.DEBUG, "exception in close", e);
}
}
}
@@ -726,14 +736,13 @@
this.reason = reason;
boolean managed = trySetState(CLOSE);
debug.log(Level.DEBUG,
- "signalClose statusCode=%s, reason.length()=%s: %s",
+ "signalClose statusCode=%s reason.length=%s: %s",
statusCode, reason.length(), managed);
if (managed) {
try {
transport.closeInput();
} catch (Throwable t) {
- debug.log(Level.DEBUG,
- "unexpected exception closing input", t);
+ debug.log(Level.DEBUG, "exception closing input", (Object) t);
}
}
}
@@ -801,8 +810,7 @@
break;
}
}
- debug.log(Level.DEBUG,
- "set state %s (previous %s) %s",
+ debug.log(Level.DEBUG, "set state %s (previous %s) %s",
newState, currentState, success);
return success;
}
@@ -818,8 +826,7 @@
// from IDLE to WAITING: the state has changed to terminal
throw new InternalError();
}
- debug.log(Level.DEBUG,
- "change state from %s to %s %s",
+ debug.log(Level.DEBUG, "change state from %s to %s %s",
expectedState, newState, success);
return success;
}
--- a/test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java Wed Mar 14 16:01:41 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-/*
- * 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
- * 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
- * @bug 8159053
- * @modules java.net.http/jdk.internal.net.http.websocket:open
- * @run testng/othervm
- * --add-reads java.net.http=ALL-UNNAMED
- * java.net.http/jdk.internal.net.http.websocket.BuildingWebSocketTest
- */
-public final class BuildingWebSocketDriver { }
--- a/test/jdk/java/net/httpclient/websocket/MockListener.java Wed Mar 14 16:01:41 2018 +0000
+++ b/test/jdk/java/net/httpclient/websocket/MockListener.java Fri Mar 16 01:28:51 2018 +0000
@@ -29,13 +29,16 @@
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
public class MockListener implements WebSocket.Listener {
private final long bufferSize;
private long count;
- private final List<Invocation> invocations = new ArrayList<>();
+ private final List<Invocation> invocations = new ArrayList<>(); // better sync
private final CompletableFuture<?> lastCall = new CompletableFuture<>();
private final Predicate<? super Invocation> collectUntil;
@@ -170,6 +173,13 @@
}
}
+ public List<Invocation> invocations(long timeout, TimeUnit unit)
+ throws InterruptedException, ExecutionException, TimeoutException
+ {
+ lastCall.get(timeout, unit);
+ return new ArrayList<>(invocations);
+ }
+
public List<Invocation> invocations() {
lastCall.join();
return new ArrayList<>(invocations);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/websocket/Support.java Fri Mar 16 01:28:51 2018 +0000
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.CompletionStage;
+
+import static org.testng.Assert.assertThrows;
+
+public class Support {
+
+ private Support() { }
+
+ public static void assertCompletesExceptionally(Class<? extends Throwable> clazz,
+ CompletionStage<?> stage) {
+ CompletableFuture<?> cf =
+ CompletableFuture.completedFuture(null).thenCompose(x -> stage);
+ assertThrows(clazz, () -> {
+ try {
+ cf.join();
+ } catch (CompletionException e) {
+ throw e.getCause();
+ }
+ });
+ }
+
+ public static ByteBuffer fullCopy(ByteBuffer src) {
+ ByteBuffer copy = ByteBuffer.allocate(src.capacity());
+ int p = src.position();
+ int l = src.limit();
+ src.clear();
+ copy.put(src).position(p).limit(l);
+ src.position(p).limit(l);
+ return copy;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java Fri Mar 16 01:28:51 2018 +0000
@@ -0,0 +1,234 @@
+/*
+ * 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
+ * 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.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.WebSocket;
+import java.time.Duration;
+import java.util.List;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.testng.Assert.assertThrows;
+
+/*
+ * @test
+ * @bug 8159053
+ * @run testng/othervm WebSocketBuilderTest
+ */
+
+/*
+ * In some places in this test a new String is created out of a string literal.
+ * The idea is to make sure the code under test relies on something better than
+ * the reference equality ( == ) for string equality checks.
+ */
+public final class WebSocketBuilderTest {
+
+ private final static URI VALID_URI = URI.create("ws://websocket.example.com");
+
+ @Test
+ public void nullArguments() {
+ HttpClient c = HttpClient.newHttpClient();
+
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .buildAsync(null, listener()));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .buildAsync(VALID_URI, null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .buildAsync(null, null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .header(null, "value"));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .header("name", null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .header(null, null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols(null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols(null, "sub2.example.com"));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", (String) null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", (String[]) null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", "sub2.example.com", null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", null, "sub3.example.com"));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
+ .connectTimeout(null));
+ }
+
+ @Test(dataProvider = "badURIs")
+ void illegalURI(URI uri) {
+ WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder();
+ assertFails(IllegalArgumentException.class,
+ b.buildAsync(uri, listener()));
+ }
+
+ @Test
+ public void illegalHeaders() {
+ List<String> headers =
+ List.of("Sec-WebSocket-Accept",
+ "Sec-WebSocket-Extensions",
+ "Sec-WebSocket-Key",
+ "Sec-WebSocket-Protocol",
+ "Sec-WebSocket-Version")
+ .stream()
+ .flatMap(s -> Stream.of(s, new String(s))) // a string and a copy of it
+ .collect(Collectors.toList());
+
+ Function<String, CompletionStage<?>> f =
+ header -> HttpClient.newHttpClient()
+ .newWebSocketBuilder()
+ .header(header, "value")
+ .buildAsync(VALID_URI, listener());
+
+ headers.forEach(h -> assertFails(IllegalArgumentException.class, f.apply(h)));
+ }
+
+ // TODO: test for bad syntax headers
+ // TODO: test for overwrites (subprotocols) and additions (headers)
+
+ @Test(dataProvider = "badSubprotocols")
+ public void illegalSubprotocolsSyntax(String s) {
+ WebSocket.Builder b = HttpClient.newHttpClient()
+ .newWebSocketBuilder()
+ .subprotocols(s);
+ assertFails(IllegalArgumentException.class,
+ b.buildAsync(VALID_URI, listener()));
+ }
+
+ @Test(dataProvider = "duplicatingSubprotocols")
+ public void illegalSubprotocolsDuplicates(String mostPreferred,
+ String[] lesserPreferred) {
+ WebSocket.Builder b = HttpClient.newHttpClient()
+ .newWebSocketBuilder()
+ .subprotocols(mostPreferred, lesserPreferred);
+ assertFails(IllegalArgumentException.class,
+ b.buildAsync(VALID_URI, listener()));
+ }
+
+ @Test(dataProvider = "badConnectTimeouts")
+ public void illegalConnectTimeout(Duration d) {
+ WebSocket.Builder b = HttpClient.newHttpClient()
+ .newWebSocketBuilder()
+ .connectTimeout(d);
+ assertFails(IllegalArgumentException.class,
+ b.buildAsync(VALID_URI, listener()));
+ }
+
+ @DataProvider
+ public Object[][] badURIs() {
+ return new Object[][]{
+ {URI.create("http://example.com")},
+ {URI.create("ftp://example.com")},
+ {URI.create("wss://websocket.example.com/hello#fragment")},
+ {URI.create("ws://websocket.example.com/hello#fragment")},
+ };
+ }
+
+ @DataProvider
+ public Object[][] badConnectTimeouts() {
+ return new Object[][]{
+ {Duration.ofDays(0)},
+ {Duration.ofDays(-1)},
+ {Duration.ofHours(0)},
+ {Duration.ofHours(-1)},
+ {Duration.ofMinutes(0)},
+ {Duration.ofMinutes(-1)},
+ {Duration.ofSeconds(0)},
+ {Duration.ofSeconds(-1)},
+ {Duration.ofMillis(0)},
+ {Duration.ofMillis(-1)},
+ {Duration.ofNanos(0)},
+ {Duration.ofNanos(-1)},
+ {Duration.ZERO},
+ };
+ }
+
+ // https://tools.ietf.org/html/rfc7230#section-3.2.6
+ // https://tools.ietf.org/html/rfc20
+ @DataProvider
+ public static Object[][] badSubprotocols() {
+ return new Object[][]{
+ {""},
+ {new String("")},
+ {"round-brackets("},
+ {"round-brackets)"},
+ {"comma,"},
+ {"slash/"},
+ {"colon:"},
+ {"semicolon;"},
+ {"angle-brackets<"},
+ {"angle-brackets>"},
+ {"equals="},
+ {"question-mark?"},
+ {"at@"},
+ {"brackets["},
+ {"backslash\\"},
+ {"brackets]"},
+ {"curly-brackets{"},
+ {"curly-brackets}"},
+ {"space "},
+ {"non-printable-character " + Character.toString((char) 31)},
+ {"non-printable-character " + Character.toString((char) 127)},
+ };
+ }
+
+ @DataProvider
+ public static Object[][] duplicatingSubprotocols() {
+ return new Object[][]{
+ {"a.b.c", new String[]{"a.b.c"}},
+ {"a.b.c", new String[]{"x.y.z", "p.q.r", "x.y.z"}},
+ {"a.b.c", new String[]{new String("a.b.c")}},
+ };
+ }
+
+ private static WebSocket.Listener listener() {
+ return new WebSocket.Listener() { };
+ }
+
+ /* shortcut */
+ public static void assertFails(Class<? extends Throwable> clazz,
+ CompletionStage<?> stage) {
+ Support.assertCompletesExceptionally(clazz, stage);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java Fri Mar 16 01:28:51 2018 +0000
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ */
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.net.http.WebSocket;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static java.net.http.HttpClient.newHttpClient;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/*
+ * @test
+ * @bug 8159053
+ *
+ *
+ * @run testng/othervm
+ * -Djdk.internal.httpclient.websocket.debug=true
+ * -Djdk.internal.httpclient.debug=true
+ * -Djdk.httpclient.websocket.writeBufferSize=1024
+ * -Djdk.httpclient.websocket.intermediateBufferSize=2048 WebSocketExtendedTest
+ */
+
+/*
+ * This battery of tests exercises sending data (Text/Binary) messages with
+ * possible fragmentation.
+ */
+public class WebSocketExtendedTest {
+// * run testng/othervm
+// * -Djdk.httpclient.websocket.writeBufferSize=16
+// * -Djdk.httpclient.sendBufferSize=32 WebSocketTextTest
+
+ private final static Random random;
+ static {
+ long seed = System.currentTimeMillis();
+ System.out.println("seed=" + seed);
+ random = new Random(seed);
+ }
+
+ // FIXME ensure subsequent (sendText/Binary, false) only CONTINUATIONs
+
+ @Test(dataProvider = "binary")
+ public void binary(ByteBuffer expected) throws IOException, InterruptedException {
+ try (DummyWebSocketServer server = new DummyWebSocketServer()) {
+ server.open();
+ WebSocket ws = newHttpClient()
+ .newWebSocketBuilder()
+ .buildAsync(server.getURI(), new WebSocket.Listener() { })
+ .join();
+ ws.sendBinary(expected.duplicate(), true).join();
+ ws.abort();
+ ByteBuffer data = server.read();
+ List<Frame> frames = readFrames(data);
+ assertEquals(frames.size(), 1);
+ Frame f = frames.get(0);
+ assertTrue(f.last);
+ assertEquals(f.opcode, Frame.Opcode.BINARY);
+ assertEquals(f.data, expected);
+ }
+ }
+
+ private static List<Frame> readFrames(ByteBuffer src) {
+ List<Frame> frames = new ArrayList<>();
+ Frame.Consumer consumer = new Frame.Consumer() {
+
+ ByteBuffer data;
+ Frame.Opcode opcode;
+ Frame.Masker masker = new Frame.Masker();
+ boolean last;
+
+ @Override
+ public void fin(boolean value) {
+ last = value;
+ }
+
+ @Override
+ public void rsv1(boolean value) {
+ if (value) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void rsv2(boolean value) {
+ if (value) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void rsv3(boolean value) {
+ if (value) {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void opcode(Frame.Opcode value) {
+ opcode = value;
+ }
+
+ @Override
+ public void mask(boolean value) {
+ if (!value) { // Frames from the client MUST be masked
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public void payloadLen(long value) {
+ data = ByteBuffer.allocate((int) value);
+ }
+
+ @Override
+ public void maskingKey(int value) {
+ masker.mask(value);
+ }
+
+ @Override
+ public void payloadData(ByteBuffer data) {
+ masker.transferMasking(data, this.data);
+ }
+
+ @Override
+ public void endFrame() {
+ frames.add(new Frame(opcode, this.data.flip(), last));
+ }
+ };
+
+ Frame.Reader r = new Frame.Reader();
+ while (src.hasRemaining()) {
+ r.readFrame(src, consumer);
+ }
+ return frames;
+ }
+
+ @Test(dataProvider = "pingPong")
+ public void ping(ByteBuffer expected) throws Exception {
+ try (DummyWebSocketServer server = new DummyWebSocketServer()) {
+ server.open();
+ WebSocket ws = newHttpClient()
+ .newWebSocketBuilder()
+ .buildAsync(server.getURI(), new WebSocket.Listener() { })
+ .join();
+ ws.sendPing(expected.duplicate()).join();
+ ws.abort();
+ ByteBuffer data = server.read();
+ List<Frame> frames = readFrames(data);
+ assertEquals(frames.size(), 1);
+ Frame f = frames.get(0);
+ assertEquals(f.opcode, Frame.Opcode.PING);
+ ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
+ actual.put(f.data);
+ actual.flip();
+ assertEquals(actual, expected);
+ }
+ }
+
+ @Test(dataProvider = "pingPong")
+ public void pong(ByteBuffer expected) throws Exception {
+ try (DummyWebSocketServer server = new DummyWebSocketServer()) {
+ server.open();
+ WebSocket ws = newHttpClient()
+ .newWebSocketBuilder()
+ .buildAsync(server.getURI(), new WebSocket.Listener() { })
+ .join();
+ ws.sendPong(expected.duplicate()).join();
+ ws.abort();
+ ByteBuffer data = server.read();
+ List<Frame> frames = readFrames(data);
+ assertEquals(frames.size(), 1);
+ Frame f = frames.get(0);
+ assertEquals(f.opcode, Frame.Opcode.PONG);
+ ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
+ actual.put(f.data);
+ actual.flip();
+ assertEquals(actual, expected);
+ }
+ }
+
+ @Test(dataProvider = "close")
+ public void close(int statusCode, String reason) throws Exception {
+ try (DummyWebSocketServer server = new DummyWebSocketServer()) {
+ server.open();
+ WebSocket ws = newHttpClient()
+ .newWebSocketBuilder()
+ .buildAsync(server.getURI(), new WebSocket.Listener() { })
+ .join();
+ ws.sendClose(statusCode, reason).join();
+ ws.abort();
+ ByteBuffer data = server.read();
+ List<Frame> frames = readFrames(data);
+ assertEquals(frames.size(), 1);
+ Frame f = frames.get(0);
+ assertEquals(f.opcode, Frame.Opcode.CLOSE);
+ ByteBuffer actual = ByteBuffer.allocate(Frame.MAX_CONTROL_FRAME_PAYLOAD_SIZE);
+ actual.put(f.data);
+ actual.flip();
+ assertEquals(actual.getChar(), statusCode);
+ assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), reason);
+ }
+ }
+
+ @Test(dataProvider = "text")
+ public void text(String expected) throws Exception {
+ try (DummyWebSocketServer server = new DummyWebSocketServer()) {
+ server.open();
+ WebSocket ws = newHttpClient()
+ .newWebSocketBuilder()
+ .buildAsync(server.getURI(), new WebSocket.Listener() { })
+ .join();
+ ws.sendText(expected, true).join();
+ ws.abort();
+ ByteBuffer data = server.read();
+ List<Frame> frames = readFrames(data);
+
+ int maxBytes = (int) StandardCharsets.UTF_8.newEncoder().maxBytesPerChar() * expected.length();
+ ByteBuffer actual = ByteBuffer.allocate(maxBytes);
+ frames.stream().forEachOrdered(f -> actual.put(f.data));
+ actual.flip();
+ assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), expected);
+ }
+ }
+
+ @DataProvider(name = "pingPong")
+ public Object[][] pingPongSizes() {
+ return new Object[][]{
+ {bytes( 0)},
+ {bytes( 1)},
+ {bytes( 63)},
+ {bytes(125)},
+ };
+ }
+
+ @DataProvider(name = "close")
+ public Object[][] closeArguments() {
+ return new Object[][]{
+ {WebSocket.NORMAL_CLOSURE, utf8String( 0)},
+ {WebSocket.NORMAL_CLOSURE, utf8String( 1)},
+ // 123 / 3 = max reason bytes / max bytes per char
+ {WebSocket.NORMAL_CLOSURE, utf8String(41)},
+ };
+ }
+
+ private static String utf8String(int n) {
+ char[] abc = {
+ // -- English Alphabet (26 characters, 1 byte per char) --
+ 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
+ 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
+ 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
+ 0x0059, 0x005A,
+ // -- Russian Alphabet (33 characters, 2 bytes per char) --
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
+ 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
+ 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426,
+ 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
+ 0x042F,
+ // -- Hiragana base characters (46 characters, 3 bytes per char) --
+ 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
+ 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
+ 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
+ 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
+ 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
+ 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093,
+ };
+
+ assert new String(abc).getBytes(StandardCharsets.UTF_8).length > abc.length;
+
+ StringBuilder str = new StringBuilder(n);
+ random.ints(0, abc.length).limit(n).forEach(i -> str.append(abc[i]));
+ return str.toString();
+ }
+
+ @DataProvider(name = "text")
+ public Object[][] texts() {
+ return new Object[][]{
+ {utf8String( 0)},
+ {utf8String(1024)},
+ };
+ }
+
+ @DataProvider(name = "binary")
+ public Object[][] binary() {
+ return new Object[][]{
+ {bytes( 0)},
+ {bytes(1024)},
+ };
+ }
+
+ private static ByteBuffer bytes(int n) {
+ byte[] array = new byte[n];
+ random.nextBytes(array);
+ return ByteBuffer.wrap(array);
+ }
+}
--- a/test/jdk/java/net/httpclient/websocket/WebSocketTest.java Wed Mar 14 16:01:41 2018 +0000
+++ b/test/jdk/java/net/httpclient/websocket/WebSocketTest.java Fri Mar 16 01:28:51 2018 +0000
@@ -24,9 +24,8 @@
/*
* @test
* @build DummyWebSocketServer
- * @run testng/othervm
+ * @run testng/othervm/timeout=600
* -Djdk.internal.httpclient.websocket.debug=true
- * -Djdk.internal.httpclient.debug=true
* WebSocketTest
*/
import org.testng.annotations.DataProvider;
@@ -42,7 +41,6 @@
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -63,79 +61,92 @@
private static final Class<IllegalStateException> ISE = IllegalStateException.class;
private static final Class<IOException> IOE = IOException.class;
+ /*
+ * Examines WebSocket behaviour after a call to abort()
+ */
@Test
public void immediateAbort() throws Exception {
try (DummyWebSocketServer server = serverWithCannedData(0x81, 0x00, 0x88, 0x00)) {
server.open();
CompletableFuture<Void> messageReceived = new CompletableFuture<>();
+ WebSocket.Listener listener = new WebSocket.Listener() {
+
+ @Override
+ public void onOpen(WebSocket webSocket) {
+ /* no initial request */
+ }
+
+ @Override
+ public CompletionStage<?> onText(WebSocket webSocket,
+ CharSequence message,
+ WebSocket.MessagePart part) {
+ messageReceived.complete(null);
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> onBinary(WebSocket webSocket,
+ ByteBuffer message,
+ WebSocket.MessagePart part) {
+ messageReceived.complete(null);
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> onPing(WebSocket webSocket,
+ ByteBuffer message) {
+ messageReceived.complete(null);
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> onPong(WebSocket webSocket,
+ ByteBuffer message) {
+ messageReceived.complete(null);
+ return null;
+ }
+
+ @Override
+ public CompletionStage<?> onClose(WebSocket webSocket,
+ int statusCode,
+ String reason) {
+ messageReceived.complete(null);
+ return null;
+ }
+ };
+
WebSocket ws = newHttpClient()
.newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() {
-
- @Override
- public void onOpen(WebSocket webSocket) {
- /* no initial request */
- }
-
- @Override
- public CompletionStage<?> onText(WebSocket webSocket,
- CharSequence message,
- WebSocket.MessagePart part) {
- messageReceived.complete(null);
- return null;
- }
-
- @Override
- public CompletionStage<?> onBinary(WebSocket webSocket,
- ByteBuffer message,
- WebSocket.MessagePart part) {
- messageReceived.complete(null);
- return null;
- }
-
- @Override
- public CompletionStage<?> onPing(WebSocket webSocket,
- ByteBuffer message) {
- messageReceived.complete(null);
- return null;
- }
-
- @Override
- public CompletionStage<?> onPong(WebSocket webSocket,
- ByteBuffer message) {
- messageReceived.complete(null);
- return null;
- }
-
- @Override
- public CompletionStage<?> onClose(WebSocket webSocket,
- int statusCode,
- String reason) {
- messageReceived.complete(null);
- return null;
- }
- })
+ .buildAsync(server.getURI(), listener)
.join();
-
- ws.abort();
- // Each consecutive abort MUST be a no-op:
- ws.abort();
- assertTrue(ws.isInputClosed());
- assertTrue(ws.isOutputClosed());
- assertEquals(ws.getSubprotocol(), "");
- ws.abort();
- assertTrue(ws.isInputClosed());
- assertTrue(ws.isOutputClosed());
- assertEquals(ws.getSubprotocol(), "");
- // At this point request MUST be a no-op:
- ws.request(1);
- ws.request(Long.MAX_VALUE);
- // Throws IAE regardless of whether WebSocket is closed or not:
- assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
- assertThrows(IAE, () -> ws.request(Long.MIN_VALUE));
- assertThrows(IAE, () -> ws.request(-1));
- assertThrows(IAE, () -> ws.request(0));
- // Even though there is a bunch of messages readily available on the
+ for (int i = 0; i < 3; i++) {
+ System.out.printf("iteration #%s%n", i);
+ // after the first abort() each consecutive one must be a no-op,
+ // moreover, query methods should continue to return consistent,
+ // permanent values
+ for (int j = 0; j < 3; j++) {
+ System.out.printf("abort #%s%n", j);
+ ws.abort();
+ assertTrue(ws.isInputClosed());
+ assertTrue(ws.isOutputClosed());
+ assertEquals(ws.getSubprotocol(), "");
+ }
+ // at this point valid requests MUST be a no-op:
+ for (int j = 0; j < 3; j++) {
+ System.out.printf("request #%s%n", j);
+ ws.request(1);
+ ws.request(2);
+ ws.request(8);
+ ws.request(Integer.MAX_VALUE);
+ ws.request(Long.MAX_VALUE);
+ // invalid requests MUST throw IAE:
+ assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
+ assertThrows(IAE, () -> ws.request(Long.MIN_VALUE));
+ assertThrows(IAE, () -> ws.request(-1));
+ assertThrows(IAE, () -> ws.request(0));
+ }
+ }
+ // even though there is a bunch of messages readily available on the
// wire we shouldn't have received any of them as we aborted before
// the first request
try {
@@ -144,17 +155,32 @@
} catch (TimeoutException expected) {
System.out.println("Finished waiting");
}
- assertCompletesExceptionally(IOE, ws.sendText("text!", false));
- assertCompletesExceptionally(IOE, ws.sendText("text!", true));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(16), false));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(16), true));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(16)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(16)));
- // Checked last because it changes the state of WebSocket
- assertCompletesExceptionally(IOE, ws.sendClose(NORMAL_CLOSURE, "a reason"));
+ for (int i = 0; i < 3; i++) {
+ System.out.printf("send #%s%n", i);
+ assertFails(IOE, ws.sendText("text!", false));
+ assertFails(IOE, ws.sendText("text!", true));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), false));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(16), true));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(16)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(16)));
+ assertFails(IOE, ws.sendClose(NORMAL_CLOSURE, "a reason"));
+ assertThrows(NPE, () -> ws.sendText(null, false));
+ assertThrows(NPE, () -> ws.sendText(null, true));
+ assertThrows(NPE, () -> ws.sendBinary(null, false));
+ assertThrows(NPE, () -> ws.sendBinary(null, true));
+ assertThrows(NPE, () -> ws.sendPing(null));
+ assertThrows(NPE, () -> ws.sendPong(null));
+ assertThrows(NPE, () -> ws.sendClose(NORMAL_CLOSURE, null));
+ }
}
}
+ /* shortcut */
+ private static void assertFails(Class<? extends Throwable> clazz,
+ CompletionStage<?> stage) {
+ Support.assertCompletesExceptionally(clazz, stage);
+ }
+
private static DummyWebSocketServer serverWithCannedData(int... data) {
byte[] copy = new byte[data.length];
for (int i = 0; i < data.length; i++) {
@@ -175,32 +201,6 @@
};
}
- private static void assertCompletesExceptionally(Class<? extends Throwable> 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 static void assertException(Class<? extends Throwable> clazz,
- Throwable t) {
- if (t == null) {
- throw new AssertionError("Expected " + clazz + ", caught nothing");
- }
- if (!clazz.isInstance(t)) {
- throw new AssertionError("Expected " + clazz + ", caught " + t);
- }
- }
-
@Test
public void sendMethodsThrowNPE() throws IOException {
try (DummyWebSocketServer server = new DummyWebSocketServer()) {
@@ -328,7 +328,7 @@
ws.abort();
assertTrue(ws.isOutputClosed());
assertTrue(ws.isInputClosed());
- assertCompletesExceptionally(IOException.class, cf);
+ assertFails(IOException.class, cf);
}
}
@@ -358,10 +358,29 @@
ws.abort();
assertTrue(ws.isOutputClosed());
assertTrue(ws.isInputClosed());
- assertCompletesExceptionally(IOException.class, cf);
+ assertFails(IOException.class, cf);
}
}
+ private static String stringWith2NBytes(int n) {
+ // -- Russian Alphabet (33 characters, 2 bytes per char) --
+ char[] abc = {
+ 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
+ 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
+ 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426,
+ 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
+ 0x042F,
+ };
+ // repeat cyclically
+ StringBuilder sb = new StringBuilder(n);
+ for (int i = 0, j = 0; i < n; i++, j = (j + 1) % abc.length) {
+ sb.append(abc[j]);
+ }
+ String s = sb.toString();
+ assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n;
+ return s;
+ }
+
@Test
public void sendCloseTimeout() throws Exception {
try (DummyWebSocketServer server = notReadingServer()) {
@@ -386,8 +405,8 @@
}
}
long before = System.currentTimeMillis();
- assertCompletesExceptionally(IOException.class,
- ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
+ assertFails(IOException.class,
+ ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
long after = System.currentTimeMillis();
// default timeout should be 30 seconds
long elapsed = after - before;
@@ -395,29 +414,10 @@
assertTrue(elapsed >= 29_000, String.valueOf(elapsed));
assertTrue(ws.isOutputClosed());
assertTrue(ws.isInputClosed());
- assertCompletesExceptionally(IOException.class, cf);
+ assertFails(IOException.class, cf);
}
}
- private static String stringWith2NBytes(int n) {
- // -- Russian Alphabet (33 characters, 2 bytes per char) --
- char[] abc = {
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
- 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
- 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426,
- 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
- 0x042F,
- };
- // repeat cyclically
- StringBuilder sb = new StringBuilder(n);
- for (int i = 0, j = 0; i < n; i++, j = (j + 1) % abc.length) {
- sb.append(abc[j]);
- }
- String s = sb.toString();
- assert s.length() == n && s.getBytes(StandardCharsets.UTF_8).length == 2 * n;
- return s;
- }
-
@Test
public void testIllegalArgument() throws IOException {
try (DummyWebSocketServer server = new DummyWebSocketServer()) {
@@ -427,54 +427,54 @@
.buildAsync(server.getURI(), new WebSocket.Listener() { })
.join();
- assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(126)));
- assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(127)));
- assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(128)));
- assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(129)));
- assertCompletesExceptionally(IAE, ws.sendPing(ByteBuffer.allocate(256)));
+ assertFails(IAE, ws.sendPing(ByteBuffer.allocate(126)));
+ assertFails(IAE, ws.sendPing(ByteBuffer.allocate(127)));
+ assertFails(IAE, ws.sendPing(ByteBuffer.allocate(128)));
+ assertFails(IAE, ws.sendPing(ByteBuffer.allocate(129)));
+ assertFails(IAE, ws.sendPing(ByteBuffer.allocate(256)));
- assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(126)));
- assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(127)));
- assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(128)));
- assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(129)));
- assertCompletesExceptionally(IAE, ws.sendPong(ByteBuffer.allocate(256)));
+ assertFails(IAE, ws.sendPong(ByteBuffer.allocate(126)));
+ assertFails(IAE, ws.sendPong(ByteBuffer.allocate(127)));
+ assertFails(IAE, ws.sendPong(ByteBuffer.allocate(128)));
+ assertFails(IAE, ws.sendPong(ByteBuffer.allocate(129)));
+ assertFails(IAE, ws.sendPong(ByteBuffer.allocate(256)));
- assertCompletesExceptionally(IOE, ws.sendText(incompleteString(), true));
- assertCompletesExceptionally(IOE, ws.sendText(incompleteString(), false));
- assertCompletesExceptionally(IOE, ws.sendText(malformedString(), true));
- assertCompletesExceptionally(IOE, ws.sendText(malformedString(), false));
+ assertFails(IOE, ws.sendText(incompleteString(), true));
+ assertFails(IOE, ws.sendText(incompleteString(), false));
+ assertFails(IOE, ws.sendText(malformedString(), true));
+ assertFails(IOE, ws.sendText(malformedString(), false));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(125)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(128)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(256)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(257)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, stringWith2NBytes((123 / 2) + 1)));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, malformedString()));
- assertCompletesExceptionally(IAE, ws.sendClose(NORMAL_CLOSURE, incompleteString()));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(124)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(125)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(128)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(256)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWithNBytes(257)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, stringWith2NBytes((123 / 2) + 1)));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, malformedString()));
+ assertFails(IAE, ws.sendClose(NORMAL_CLOSURE, incompleteString()));
- assertCompletesExceptionally(IAE, ws.sendClose(-2, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(-1, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(0, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(500, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(998, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(999, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1002, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1003, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1006, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1007, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1009, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1010, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1012, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1013, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(1015, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(5000, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(32768, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(65535, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(65536, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(Integer.MAX_VALUE, "a reason"));
- assertCompletesExceptionally(IAE, ws.sendClose(Integer.MIN_VALUE, "a reason"));
+ assertFails(IAE, ws.sendClose(-2, "a reason"));
+ assertFails(IAE, ws.sendClose(-1, "a reason"));
+ assertFails(IAE, ws.sendClose(0, "a reason"));
+ assertFails(IAE, ws.sendClose(1, "a reason"));
+ assertFails(IAE, ws.sendClose(500, "a reason"));
+ assertFails(IAE, ws.sendClose(998, "a reason"));
+ assertFails(IAE, ws.sendClose(999, "a reason"));
+ assertFails(IAE, ws.sendClose(1002, "a reason"));
+ assertFails(IAE, ws.sendClose(1003, "a reason"));
+ assertFails(IAE, ws.sendClose(1006, "a reason"));
+ assertFails(IAE, ws.sendClose(1007, "a reason"));
+ assertFails(IAE, ws.sendClose(1009, "a reason"));
+ assertFails(IAE, ws.sendClose(1010, "a reason"));
+ assertFails(IAE, ws.sendClose(1012, "a reason"));
+ assertFails(IAE, ws.sendClose(1013, "a reason"));
+ assertFails(IAE, ws.sendClose(1015, "a reason"));
+ assertFails(IAE, ws.sendClose(5000, "a reason"));
+ assertFails(IAE, ws.sendClose(32768, "a reason"));
+ assertFails(IAE, ws.sendClose(65535, "a reason"));
+ assertFails(IAE, ws.sendClose(65536, "a reason"));
+ assertFails(IAE, ws.sendClose(Integer.MAX_VALUE, "a reason"));
+ assertFails(IAE, ws.sendClose(Integer.MIN_VALUE, "a reason"));
assertThrows(IAE, () -> ws.request(Integer.MIN_VALUE));
assertThrows(IAE, () -> ws.request(Long.MIN_VALUE));
@@ -516,8 +516,8 @@
break;
}
}
- assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
- assertCompletesExceptionally(ISE, ws.sendText("", true));
+ assertFails(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
+ assertFails(ISE, ws.sendText("", true));
}
}
@@ -544,8 +544,8 @@
i, System.currentTimeMillis());
}
}
- assertCompletesExceptionally(ISE, ws.sendText("", true));
- assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
+ assertFails(ISE, ws.sendText("", true));
+ assertFails(ISE, ws.sendBinary(ByteBuffer.allocate(0), true));
}
}
@@ -559,8 +559,8 @@
.join();
ws.sendBinary(ByteBuffer.allocate(16), false).join();
- assertCompletesExceptionally(ISE, ws.sendText("text", false));
- assertCompletesExceptionally(ISE, ws.sendText("text", true));
+ assertFails(ISE, ws.sendText("text", false));
+ assertFails(ISE, ws.sendText("text", true));
}
}
@@ -574,8 +574,8 @@
.join();
ws.sendText("text", false).join();
- assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(16), false));
- assertCompletesExceptionally(ISE, ws.sendBinary(ByteBuffer.allocate(16), true));
+ assertFails(ISE, ws.sendBinary(ByteBuffer.allocate(16), false));
+ assertFails(ISE, ws.sendBinary(ByteBuffer.allocate(16), true));
}
}
@@ -590,26 +590,26 @@
ws.sendClose(NORMAL_CLOSURE, "ok").join();
- assertCompletesExceptionally(IOE, ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
+ assertFails(IOE, ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
- assertCompletesExceptionally(IOE, ws.sendText("", true));
- assertCompletesExceptionally(IOE, ws.sendText("", false));
- assertCompletesExceptionally(IOE, ws.sendText("abc", true));
- assertCompletesExceptionally(IOE, ws.sendText("abc", false));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(0), true));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(0), false));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(1), true));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(1), false));
+ assertFails(IOE, ws.sendText("", true));
+ assertFails(IOE, ws.sendText("", false));
+ assertFails(IOE, ws.sendText("abc", true));
+ assertFails(IOE, ws.sendText("abc", false));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(0), true));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(0), false));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(1), true));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(1), false));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(125)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(124)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(1)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(0)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(125)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(124)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(1)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(0)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(125)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(124)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(1)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(0)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(125)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(124)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(1)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(0)));
}
}
@@ -644,26 +644,26 @@
canClose.complete(null); // Signal to the WebSocket it can close the output
TimeUnit.SECONDS.sleep(5); // Give canClose some time to reach the WebSocket
- assertCompletesExceptionally(IOE, ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
+ assertFails(IOE, ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok"));
- assertCompletesExceptionally(IOE, ws.sendText("", true));
- assertCompletesExceptionally(IOE, ws.sendText("", false));
- assertCompletesExceptionally(IOE, ws.sendText("abc", true));
- assertCompletesExceptionally(IOE, ws.sendText("abc", false));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(0), true));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(0), false));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(1), true));
- assertCompletesExceptionally(IOE, ws.sendBinary(ByteBuffer.allocate(1), false));
+ assertFails(IOE, ws.sendText("", true));
+ assertFails(IOE, ws.sendText("", false));
+ assertFails(IOE, ws.sendText("abc", true));
+ assertFails(IOE, ws.sendText("abc", false));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(0), true));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(0), false));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(1), true));
+ assertFails(IOE, ws.sendBinary(ByteBuffer.allocate(1), false));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(125)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(124)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(1)));
- assertCompletesExceptionally(IOE, ws.sendPing(ByteBuffer.allocate(0)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(125)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(124)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(1)));
+ assertFails(IOE, ws.sendPing(ByteBuffer.allocate(0)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(125)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(124)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(1)));
- assertCompletesExceptionally(IOE, ws.sendPong(ByteBuffer.allocate(0)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(125)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(124)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(1)));
+ assertFails(IOE, ws.sendPong(ByteBuffer.allocate(0)));
}
}
@@ -963,8 +963,12 @@
}
}
+ /*
+ * The server sends Pong messages. The WebSocket replies to messages automatically.
+ * According to RFC 6455 The WebSocket is free
+ */
@Test(dataProvider = "nPings")
- public void swappingPongs(int nPings) throws Exception {
+ public void automaticPongs(int nPings) throws Exception {
// big enough to not bother with resize
ByteBuffer buffer = ByteBuffer.allocate(16384);
Frame.HeaderWriter w = new Frame.HeaderWriter();
@@ -992,6 +996,67 @@
.join();
List<MockListener.Invocation> inv = listener.invocations();
assertEquals(inv.size(), nPings + 2); // onOpen + onClose + n*onPing
+
+ ByteBuffer data = server.read();
+ Frame.Reader reader = new Frame.Reader();
+
+ Frame.Consumer consumer = new Frame.Consumer() {
+
+ ByteBuffer number = ByteBuffer.allocate(4);
+ Frame.Masker masker = new Frame.Masker();
+ int i = -1;
+ boolean closed;
+
+ @Override
+ public void fin(boolean value) { assertTrue(value); }
+ @Override
+ public void rsv1(boolean value) { assertFalse(value); }
+ @Override
+ public void rsv2(boolean value) { assertFalse(value); }
+ @Override
+ public void rsv3(boolean value) { assertFalse(value); }
+ @Override
+ public void opcode(Frame.Opcode value) {
+ if (value == Frame.Opcode.CLOSE) {
+ closed = true;
+ return;
+ }
+ assertEquals(value, Frame.Opcode.PONG);
+ }
+ @Override
+ public void mask(boolean value) { assertTrue(value); }
+ @Override
+ public void payloadLen(long value) {
+ if (!closed)
+ assertEquals(value, 4);
+ }
+ @Override
+ public void maskingKey(int value) {
+ masker.mask(value);
+ }
+
+ @Override
+ public void payloadData(ByteBuffer src) {
+ masker.transferMasking(src, number);
+ if (closed) {
+ return;
+ }
+ number.flip();
+ int n = number.getInt();
+ System.out.printf("pong number=%s%n", n);
+ number.clear();
+ if (i >= n) {
+ fail(String.format("i=%s, n=%s", i, n));
+ }
+ i = n;
+ }
+
+ @Override
+ public void endFrame() { }
+ };
+ while (data.hasRemaining()) {
+ reader.readFrame(data, consumer);
+ }
}
}
--- a/test/jdk/java/net/httpclient/websocket/WebSocketTextTest.java Wed Mar 14 16:01:41 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,320 +0,0 @@
-/*
- * 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.
- */
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import java.io.IOException;
-import java.net.http.WebSocket;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-
-import static java.net.http.HttpClient.newHttpClient;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-/*
- * @test
- * @bug 8159053
- *
- *
- * @run testng/othervm
- * -Djdk.internal.httpclient.websocket.debug=true
- * -Djdk.internal.httpclient.debug=true
- * -Djdk.httpclient.websocket.writeBufferSize=1024
- * -Djdk.httpclient.websocket.intermediateBufferSize=2048 WebSocketTextTest
- */
-public class WebSocketTextTest {
-
- private final static Random random;
- static {
- long seed = System.currentTimeMillis();
- System.out.println("seed=" + seed);
- random = new Random(seed);
- }
-
-// * @run testng/othervm
-// * -Djdk.httpclient.websocket.writeBufferSize=16
-// * -Djdk.httpclient.sendBufferSize=32 WebSocketTextTest
-
-
-
- // FIXME ensure subsequent (sendText/Binary, false) only CONTINUATIONs
-
- @Test(dataProvider = "binary")
- public void binary(ByteBuffer expected) throws IOException, InterruptedException {
- try (DummyWebSocketServer server = new DummyWebSocketServer()) {
- server.open();
- WebSocket ws = newHttpClient()
- .newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() { })
- .join();
- ws.sendBinary(expected.duplicate(), true).join();
- ws.abort();
- ByteBuffer data = server.read();
- List<Frame> frames = readFrames(data);
- assertEquals(frames.size(), 1);
- Frame f = frames.get(0);
- assertTrue(f.last);
- assertEquals(f.opcode, Frame.Opcode.BINARY);
- assertEquals(f.data, expected);
- }
- }
-
- private static List<Frame> readFrames(ByteBuffer src) {
- List<Frame> frames = new ArrayList<>();
- Frame.Consumer consumer = new Frame.Consumer() {
-
- ByteBuffer data;
- Frame.Opcode opcode;
- Frame.Masker masker = new Frame.Masker();
- boolean last;
-
- @Override
- public void fin(boolean value) {
- last = value;
- }
-
- @Override
- public void rsv1(boolean value) {
- if (value) {
- throw new AssertionError();
- }
- }
-
- @Override
- public void rsv2(boolean value) {
- if (value) {
- throw new AssertionError();
- }
- }
-
- @Override
- public void rsv3(boolean value) {
- if (value) {
- throw new AssertionError();
- }
- }
-
- @Override
- public void opcode(Frame.Opcode value) {
- opcode = value;
- }
-
- @Override
- public void mask(boolean value) {
- if (!value) { // Frames from the client MUST be masked
- throw new AssertionError();
- }
- }
-
- @Override
- public void payloadLen(long value) {
- data = ByteBuffer.allocate((int) value);
- }
-
- @Override
- public void maskingKey(int value) {
- masker.mask(value);
- }
-
- @Override
- public void payloadData(ByteBuffer data) {
- masker.transferMasking(data, this.data);
- }
-
- @Override
- public void endFrame() {
- frames.add(new Frame(opcode, this.data.flip(), last));
- }
- };
-
- Frame.Reader r = new Frame.Reader();
- while (src.hasRemaining()) {
- r.readFrame(src, consumer);
- }
- return frames;
- }
-
- @Test(dataProvider = "pingPong")
- public void ping(ByteBuffer expected) throws Exception {
- try (DummyWebSocketServer server = new DummyWebSocketServer()) {
- server.open();
- WebSocket ws = newHttpClient()
- .newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() { })
- .join();
- ws.sendPing(expected.duplicate()).join();
- ws.abort();
- ByteBuffer data = server.read();
- List<Frame> frames = readFrames(data);
- assertEquals(frames.size(), 1);
- Frame f = frames.get(0);
- assertEquals(f.opcode, Frame.Opcode.PING);
- ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
- actual.put(f.data);
- actual.flip();
- assertEquals(actual, expected);
- }
- }
-
- @Test(dataProvider = "pingPong")
- public void pong(ByteBuffer expected) throws Exception {
- try (DummyWebSocketServer server = new DummyWebSocketServer()) {
- server.open();
- WebSocket ws = newHttpClient()
- .newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() { })
- .join();
- ws.sendPong(expected.duplicate()).join();
- ws.abort();
- ByteBuffer data = server.read();
- List<Frame> frames = readFrames(data);
- assertEquals(frames.size(), 1);
- Frame f = frames.get(0);
- assertEquals(f.opcode, Frame.Opcode.PONG);
- ByteBuffer actual = ByteBuffer.allocate(expected.remaining());
- actual.put(f.data);
- actual.flip();
- assertEquals(actual, expected);
- }
- }
-
- @Test(dataProvider = "close")
- public void close(int statusCode, String reason) throws Exception {
- try (DummyWebSocketServer server = new DummyWebSocketServer()) {
- server.open();
- WebSocket ws = newHttpClient()
- .newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() { })
- .join();
- ws.sendClose(statusCode, reason).join();
- ws.abort();
- ByteBuffer data = server.read();
- List<Frame> frames = readFrames(data);
- assertEquals(frames.size(), 1);
- Frame f = frames.get(0);
- assertEquals(f.opcode, Frame.Opcode.CLOSE);
- ByteBuffer actual = ByteBuffer.allocate(Frame.MAX_CONTROL_FRAME_PAYLOAD_SIZE);
- actual.put(f.data);
- actual.flip();
- assertEquals(actual.getChar(), statusCode);
- assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), reason);
- }
- }
-
- @Test(dataProvider = "text")
- public void text(String expected) throws Exception {
- try (DummyWebSocketServer server = new DummyWebSocketServer()) {
- server.open();
- WebSocket ws = newHttpClient()
- .newWebSocketBuilder()
- .buildAsync(server.getURI(), new WebSocket.Listener() { })
- .join();
- ws.sendText(expected, true).join();
- ws.abort();
- ByteBuffer data = server.read();
- List<Frame> frames = readFrames(data);
-
- int maxBytes = (int) StandardCharsets.UTF_8.newEncoder().maxBytesPerChar() * expected.length();
- ByteBuffer actual = ByteBuffer.allocate(maxBytes);
- frames.stream().forEachOrdered(f -> actual.put(f.data));
- actual.flip();
- assertEquals(StandardCharsets.UTF_8.decode(actual).toString(), expected);
- }
- }
-
- @DataProvider(name = "pingPong")
- public Object[][] pingPongSizes() {
- return new Object[][]{
- {bytes( 0)},
- {bytes( 1)},
- {bytes( 63)},
- {bytes(125)},
- };
- }
-
- @DataProvider(name = "close")
- public Object[][] closeArguments() {
- return new Object[][]{
- {WebSocket.NORMAL_CLOSURE, utf8String( 0)},
- {WebSocket.NORMAL_CLOSURE, utf8String( 1)},
- // 123 / 3 = max reason bytes / max bytes per char
- {WebSocket.NORMAL_CLOSURE, utf8String(41)},
- };
- }
-
- private static String utf8String(int n) {
- char[] abc = {
- // -- English Alphabet (26 characters, 1 byte per char) --
- 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048,
- 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050,
- 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058,
- 0x0059, 0x005A,
- // -- Russian Alphabet (33 characters, 2 bytes per char) --
- 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0401, 0x0416,
- 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E,
- 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426,
- 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E,
- 0x042F,
- // -- Hiragana base characters (46 characters, 3 bytes per char) --
- 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
- 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
- 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
- 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
- 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
- 0x308B, 0x308C, 0x308D, 0x308F, 0x3092, 0x3093,
- };
-
- assert new String(abc).getBytes(StandardCharsets.UTF_8).length > abc.length;
-
- StringBuilder str = new StringBuilder(n);
- random.ints(0, abc.length).limit(n).forEach(i -> str.append(abc[i]));
- return str.toString();
- }
-
- @DataProvider(name = "text")
- public Object[][] texts() {
- return new Object[][]{
- {utf8String( 0)},
- {utf8String(1024)},
- };
- }
-
- @DataProvider(name = "binary")
- public Object[][] binary() {
- return new Object[][]{
- {bytes( 0)},
- {bytes(1024)},
- };
- }
-
- private static ByteBuffer bytes(int n) {
- byte[] array = new byte[n];
- random.nextBytes(array);
- return ByteBuffer.wrap(array);
- }
-}
--- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/BuildingWebSocketTest.java Wed Mar 14 16:01:41 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,225 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, 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.
- */
-
-package jdk.internal.net.http.websocket;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.WebSocket;
-import java.time.Duration;
-import java.util.List;
-import java.util.concurrent.CompletionStage;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static jdk.internal.net.http.websocket.TestSupport.assertCompletesExceptionally;
-import static jdk.internal.net.http.websocket.TestSupport.assertThrows;
-
-/*
- * In some places in this class a new String is created out of a string literal.
- * The idea is to make sure the code under test relies on something better than
- * the reference equality ( == ) for string equality checks.
- */
-public class BuildingWebSocketTest {
-
- private final static URI VALID_URI = URI.create("ws://websocket.example.com");
-
- @Test
- public void nullArguments() {
- HttpClient c = HttpClient.newHttpClient();
-
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .buildAsync(null, listener()));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .buildAsync(VALID_URI, null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .buildAsync(null, null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .header(null, "value"));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .header("name", null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .header(null, null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols(null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols(null, "sub2.example.com"));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols("sub1.example.com", (String) null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols("sub1.example.com", (String[]) null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols("sub1.example.com", "sub2.example.com", null));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .subprotocols("sub1.example.com", null, "sub3.example.com"));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder()
- .connectTimeout(null));
- }
-
- @Test(dataProvider = "badURIs")
- void illegalURI(URI uri) {
- WebSocket.Builder b = HttpClient.newHttpClient().newWebSocketBuilder();
- assertCompletesExceptionally(IllegalArgumentException.class,
- b.buildAsync(uri, listener()));
- }
-
- @Test
- public void illegalHeaders() {
- List<String> headers =
- List.of("Sec-WebSocket-Accept",
- "Sec-WebSocket-Extensions",
- "Sec-WebSocket-Key",
- "Sec-WebSocket-Protocol",
- "Sec-WebSocket-Version")
- .stream()
- .flatMap(s -> Stream.of(s, new String(s))) // a string and a copy of it
- .collect(Collectors.toList());
-
- Function<String, CompletionStage<?>> f =
- header -> HttpClient.newHttpClient()
- .newWebSocketBuilder()
- .header(header, "value")
- .buildAsync(VALID_URI, listener());
-
- headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h)));
- }
-
- // TODO: test for bad syntax headers
- // TODO: test for overwrites (subprotocols) and additions (headers)
-
- @Test(dataProvider = "badSubprotocols")
- public void illegalSubprotocolsSyntax(String s) {
- WebSocket.Builder b = HttpClient.newHttpClient()
- .newWebSocketBuilder()
- .subprotocols(s);
- assertCompletesExceptionally(IllegalArgumentException.class,
- b.buildAsync(VALID_URI, listener()));
- }
-
- @Test(dataProvider = "duplicatingSubprotocols")
- public void illegalSubprotocolsDuplicates(String mostPreferred,
- String[] lesserPreferred) {
- WebSocket.Builder b = HttpClient.newHttpClient()
- .newWebSocketBuilder()
- .subprotocols(mostPreferred, lesserPreferred);
- assertCompletesExceptionally(IllegalArgumentException.class,
- b.buildAsync(VALID_URI, listener()));
- }
-
- @Test(dataProvider = "badConnectTimeouts")
- public void illegalConnectTimeout(Duration d) {
- WebSocket.Builder b = HttpClient.newHttpClient()
- .newWebSocketBuilder()
- .connectTimeout(d);
- assertCompletesExceptionally(IllegalArgumentException.class,
- b.buildAsync(VALID_URI, listener()));
- }
-
- @DataProvider
- public Object[][] badURIs() {
- return new Object[][]{
- {URI.create("http://example.com")},
- {URI.create("ftp://example.com")},
- {URI.create("wss://websocket.example.com/hello#fragment")},
- {URI.create("ws://websocket.example.com/hello#fragment")},
- };
- }
-
- @DataProvider
- public Object[][] badConnectTimeouts() {
- return new Object[][]{
- {Duration.ofDays ( 0)},
- {Duration.ofDays (-1)},
- {Duration.ofHours ( 0)},
- {Duration.ofHours (-1)},
- {Duration.ofMinutes( 0)},
- {Duration.ofMinutes(-1)},
- {Duration.ofSeconds( 0)},
- {Duration.ofSeconds(-1)},
- {Duration.ofMillis ( 0)},
- {Duration.ofMillis (-1)},
- {Duration.ofNanos ( 0)},
- {Duration.ofNanos (-1)},
- {Duration.ZERO},
- };
- }
-
- // https://tools.ietf.org/html/rfc7230#section-3.2.6
- // https://tools.ietf.org/html/rfc20
- @DataProvider
- public static Object[][] badSubprotocols() {
- return new Object[][]{
- {""},
- {new String("")},
- {"round-brackets("},
- {"round-brackets)"},
- {"comma,"},
- {"slash/"},
- {"colon:"},
- {"semicolon;"},
- {"angle-brackets<"},
- {"angle-brackets>"},
- {"equals="},
- {"question-mark?"},
- {"at@"},
- {"brackets["},
- {"backslash\\"},
- {"brackets]"},
- {"curly-brackets{"},
- {"curly-brackets}"},
- {"space "},
- {"non-printable-character " + Character.toString((char) 31)},
- {"non-printable-character " + Character.toString((char) 127)},
- };
- }
-
- @DataProvider
- public static Object[][] duplicatingSubprotocols() {
- return new Object[][]{
- {"a.b.c", new String[]{"a.b.c"}},
- {"a.b.c", new String[]{"x.y.z", "p.q.r", "x.y.z"}},
- {"a.b.c", new String[]{new String("a.b.c")}},
- };
- }
-
- private static WebSocket.Listener listener() {
- return new WebSocket.Listener() { };
- }
-}
--- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MockListener.java Wed Mar 14 16:01:41 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +0,0 @@
-/*
- * Copyright (c) 2017, 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.
- */
-
-package jdk.internal.net.http.websocket;
-
-import java.net.http.WebSocket;
-import java.net.http.WebSocket.MessagePart;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
-
-import static jdk.internal.net.http.websocket.TestSupport.fullCopy;
-
-public class MockListener implements WebSocket.Listener {
-
- private final long bufferSize;
- private long count;
- private final List<Invocation> invocations = new ArrayList<>();
- private final CompletableFuture<?> lastCall = new CompletableFuture<>();
-
- /*
- * Typical buffer sizes: 1, n, Long.MAX_VALUE
- */
- public MockListener(long bufferSize) {
- if (bufferSize < 1) {
- throw new IllegalArgumentException();
- }
- this.bufferSize = bufferSize;
- }
-
- @Override
- public void onOpen(WebSocket webSocket) {
- System.out.printf("onOpen(%s)%n", webSocket);
- invocations.add(new OnOpen(webSocket));
- onOpen0(webSocket);
- }
-
- protected void onOpen0(WebSocket webSocket) {
- replenish(webSocket);
- }
-
- @Override
- public CompletionStage<?> onText(WebSocket webSocket,
- CharSequence message,
- MessagePart part) {
- System.out.printf("onText(%s, %s, %s)%n", webSocket, message, part);
- invocations.add(new OnText(webSocket, message.toString(), part));
- return onText0(webSocket, message, part);
- }
-
- protected CompletionStage<?> onText0(WebSocket webSocket,
- CharSequence message,
- MessagePart part) {
- replenish(webSocket);
- return null;
- }
-
- @Override
- public CompletionStage<?> onBinary(WebSocket webSocket,
- ByteBuffer message,
- MessagePart part) {
- System.out.printf("onBinary(%s, %s, %s)%n", webSocket, message, part);
- invocations.add(new OnBinary(webSocket, fullCopy(message), part));
- return onBinary0(webSocket, message, part);
- }
-
- protected CompletionStage<?> onBinary0(WebSocket webSocket,
- ByteBuffer message,
- MessagePart part) {
- replenish(webSocket);
- return null;
- }
-
- @Override
- public CompletionStage<?> onPing(WebSocket webSocket, ByteBuffer message) {
- System.out.printf("onPing(%s, %s)%n", webSocket, message);
- invocations.add(new OnPing(webSocket, fullCopy(message)));
- return onPing0(webSocket, message);
- }
-
- protected CompletionStage<?> onPing0(WebSocket webSocket, ByteBuffer message) {
- replenish(webSocket);
- return null;
- }
-
- @Override
- public CompletionStage<?> onPong(WebSocket webSocket, ByteBuffer message) {
- System.out.printf("onPong(%s, %s)%n", webSocket, message);
- invocations.add(new OnPong(webSocket, fullCopy(message)));
- return onPong0(webSocket, message);
- }
-
- protected CompletionStage<?> onPong0(WebSocket webSocket, ByteBuffer message) {
- replenish(webSocket);
- return null;
- }
-
- @Override
- public CompletionStage<?> onClose(WebSocket webSocket,
- int statusCode,
- String reason) {
- System.out.printf("onClose(%s, %s, %s)%n", webSocket, statusCode, reason);
- invocations.add(new OnClose(webSocket, statusCode, reason));
- lastCall.complete(null);
- return null;
- }
-
- @Override
- public void onError(WebSocket webSocket, Throwable error) {
- System.out.printf("onError(%s, %s)%n", webSocket, error);
- invocations.add(new OnError(webSocket, error == null ? null : error.getClass()));
- lastCall.complete(null);
- }
-
- public CompletableFuture<?> onCloseOrOnErrorCalled() {
- return lastCall.copy();
- }
-
- protected void replenish(WebSocket webSocket) {
- if (--count <= 0) {
- count = bufferSize - bufferSize / 2;
- }
- webSocket.request(count);
- }
-
- public List<Invocation> invocations() {
- return new ArrayList<>(invocations);
- }
-
- public abstract static class Invocation {
-
- public static OnOpen onOpen(WebSocket webSocket) {
- return new OnOpen(webSocket);
- }
-
- public static OnText onText(WebSocket webSocket,
- String text,
- MessagePart part) {
- return new OnText(webSocket, text, part);
- }
-
- public static OnBinary onBinary(WebSocket webSocket,
- ByteBuffer data,
- MessagePart part) {
- return new OnBinary(webSocket, data, part);
- }
-
- public static OnPing onPing(WebSocket webSocket,
- ByteBuffer data) {
- return new OnPing(webSocket, data);
- }
-
- public static OnPong onPong(WebSocket webSocket,
- ByteBuffer data) {
- return new OnPong(webSocket, data);
- }
-
- public static OnClose onClose(WebSocket webSocket,
- int statusCode,
- String reason) {
- return new OnClose(webSocket, statusCode, reason);
- }
-
- public static OnError onError(WebSocket webSocket,
- Class<? extends Throwable> clazz) {
- return new OnError(webSocket, clazz);
- }
-
- final WebSocket webSocket;
-
- private Invocation(WebSocket webSocket) {
- this.webSocket = webSocket;
- }
- }
-
- public static final class OnOpen extends Invocation {
-
- public OnOpen(WebSocket webSocket) {
- super(webSocket);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Invocation that = (Invocation) o;
- return Objects.equals(webSocket, that.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(webSocket);
- }
- }
-
- public static final class OnText extends Invocation {
-
- final String text;
- final MessagePart part;
-
- public OnText(WebSocket webSocket, String text, MessagePart part) {
- super(webSocket);
- this.text = text;
- this.part = part;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnText onText = (OnText) o;
- return Objects.equals(text, onText.text) &&
- part == onText.part &&
- Objects.equals(webSocket, onText.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(text, part, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onText(%s, %s, %s)", webSocket, text, part);
- }
- }
-
- public static final class OnBinary extends Invocation {
-
- final ByteBuffer data;
- final MessagePart part;
-
- public OnBinary(WebSocket webSocket, ByteBuffer data, MessagePart part) {
- super(webSocket);
- this.data = data;
- this.part = part;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnBinary onBinary = (OnBinary) o;
- return Objects.equals(data, onBinary.data) &&
- part == onBinary.part &&
- Objects.equals(webSocket, onBinary.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(data, part, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onBinary(%s, %s, %s)", webSocket, data, part);
- }
- }
-
- public static final class OnPing extends Invocation {
-
- final ByteBuffer data;
-
- public OnPing(WebSocket webSocket, ByteBuffer data) {
- super(webSocket);
- this.data = data;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnPing onPing = (OnPing) o;
- return Objects.equals(data, onPing.data) &&
- Objects.equals(webSocket, onPing.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(data, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onPing(%s, %s)", webSocket, data);
- }
- }
-
- public static final class OnPong extends Invocation {
-
- final ByteBuffer data;
-
- public OnPong(WebSocket webSocket, ByteBuffer data) {
- super(webSocket);
- this.data = data;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnPong onPong = (OnPong) o;
- return Objects.equals(data, onPong.data) &&
- Objects.equals(webSocket, onPong.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(data, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onPong(%s, %s)", webSocket, data);
- }
- }
-
- public static final class OnClose extends Invocation {
-
- final int statusCode;
- final String reason;
-
- public OnClose(WebSocket webSocket, int statusCode, String reason) {
- super(webSocket);
- this.statusCode = statusCode;
- this.reason = reason;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnClose onClose = (OnClose) o;
- return statusCode == onClose.statusCode &&
- Objects.equals(reason, onClose.reason) &&
- Objects.equals(webSocket, onClose.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(statusCode, reason, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onClose(%s, %s, %s)", webSocket, statusCode, reason);
- }
- }
-
- public static final class OnError extends Invocation {
-
- final Class<? extends Throwable> clazz;
-
- public OnError(WebSocket webSocket, Class<? extends Throwable> clazz) {
- super(webSocket);
- this.clazz = clazz;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- OnError onError = (OnError) o;
- return Objects.equals(clazz, onError.clazz) &&
- Objects.equals(webSocket, onError.webSocket);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(clazz, webSocket);
- }
-
- @Override
- public String toString() {
- return String.format("onError(%s, %s)", webSocket, clazz);
- }
- }
-}
--- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MockTransport.java Wed Mar 14 16:01:41 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,436 +0,0 @@
-/*
- * Copyright (c) 2017, 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.
- */
-
-package jdk.internal.net.http.websocket;
-
-import java.net.http.WebSocket.MessagePart;
-import jdk.internal.net.http.common.Demand;
-import jdk.internal.net.http.common.SequentialScheduler;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Objects;
-import java.util.Queue;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.function.Consumer;
-import java.util.function.Supplier;
-
-import static jdk.internal.net.http.websocket.TestSupport.fullCopy;
-
-public class MockTransport<T> implements Transport<T> {
-
- private final long startTime = System.currentTimeMillis();
- private final Queue<Invocation> output = new ConcurrentLinkedQueue<>();
- private final Queue<CompletableFuture<Consumer<MessageStreamConsumer>>>
- input = new ConcurrentLinkedQueue<>();
- private final Supplier<T> supplier;
- private final MessageStreamConsumer consumer;
- private final SequentialScheduler scheduler
- = new SequentialScheduler(new ReceiveTask());
- private final Demand demand = new Demand();
-
- public MockTransport(Supplier<T> sendResultSupplier,
- MessageStreamConsumer consumer) {
- this.supplier = sendResultSupplier;
- this.consumer = consumer;
- input.addAll(receive());
- }
-
- @Override
- public final CompletableFuture<T> sendText(CharSequence message,
- boolean isLast) {
- output.add(Invocation.sendText(message, isLast));
- return send(String.format("sendText(%s, %s)", message, isLast),
- () -> sendText0(message, isLast));
- }
-
- protected CompletableFuture<T> sendText0(CharSequence message,
- boolean isLast) {
- return defaultSend();
- }
-
- protected CompletableFuture<T> defaultSend() {
- return CompletableFuture.completedFuture(result());
- }
-
- @Override
- public final CompletableFuture<T> sendBinary(ByteBuffer message,
- boolean isLast) {
- output.add(Invocation.sendBinary(message, isLast));
- return send(String.format("sendBinary(%s, %s)", message, isLast),
- () -> sendBinary0(message, isLast));
- }
-
- protected CompletableFuture<T> sendBinary0(ByteBuffer message,
- boolean isLast) {
- return defaultSend();
- }
-
- @Override
- public final CompletableFuture<T> sendPing(ByteBuffer message) {
- output.add(Invocation.sendPing(message));
- return send(String.format("sendPing(%s)", message),
- () -> sendPing0(message));
- }
-
- protected CompletableFuture<T> sendPing0(ByteBuffer message) {
- return defaultSend();
- }
-
- @Override
- public final CompletableFuture<T> sendPong(ByteBuffer message) {
- output.add(Invocation.sendPong(message));
- return send(String.format("sendPong(%s)", message),
- () -> sendPong0(message));
- }
-
- protected CompletableFuture<T> sendPong0(ByteBuffer message) {
- return defaultSend();
- }
-
- @Override
- public final CompletableFuture<T> sendClose(int statusCode, String reason) {
- output.add(Invocation.sendClose(statusCode, reason));
- return send(String.format("sendClose(%s, %s)", statusCode, reason),
- () -> sendClose0(statusCode, reason));
- }
-
- protected CompletableFuture<T> sendClose0(int statusCode, String reason) {
- return defaultSend();
- }
-
- protected Collection<CompletableFuture<Consumer<MessageStreamConsumer>>> receive() {
- return List.of();
- }
-
- public static Consumer<MessageStreamConsumer> onText(CharSequence data,
- MessagePart part) {
- return c -> c.onText(data.toString(), part);
- }
-
- public static Consumer<MessageStreamConsumer> onBinary(ByteBuffer data,
- MessagePart part) {
- return c -> c.onBinary(fullCopy(data), part);
- }
-
- public static Consumer<MessageStreamConsumer> onPing(ByteBuffer data) {
- return c -> c.onPing(fullCopy(data));
- }
-
- public static Consumer<MessageStreamConsumer> onPong(ByteBuffer data) {
- return c -> c.onPong(fullCopy(data));
- }
-
- public static Consumer<MessageStreamConsumer> onClose(int statusCode,
- String reason) {
- return c -> c.onClose(statusCode, reason);
- }
-
- public static Consumer<MessageStreamConsumer> onError(Throwable error) {
- return c -> c.onError(error);
- }
-
- public static Consumer<MessageStreamConsumer> onComplete() {
- return c -> c.onComplete();
- }
-
- @Override
- public void request(long n) {
- demand.increase(n);
- scheduler.runOrSchedule();
- }
-
- @Override
- public void acknowledgeReception() {
- demand.tryDecrement();
- }
-
- @Override
- public final void closeOutput() throws IOException {
- output.add(Invocation.closeOutput());
- begin("closeOutput()");
- closeOutput0();
- end("closeOutput()");
- }
-
- protected void closeOutput0() throws IOException {
- defaultClose();
- }
-
- protected void defaultClose() throws IOException {
- }
-
- @Override
- public final void closeInput() throws IOException {
- output.add(Invocation.closeInput());
- begin("closeInput()");
- closeInput0();
- end("closeInput()");
- }
-
- protected void closeInput0() throws IOException {
- defaultClose();
- }
-
- public abstract static class Invocation {
-
- static Invocation.SendText sendText(CharSequence message,
- boolean isLast) {
- return new SendText(message, isLast);
- }
-
- static Invocation.SendBinary sendBinary(ByteBuffer message,
- boolean isLast) {
- return new SendBinary(message, isLast);
- }
-
- static Invocation.SendPing sendPing(ByteBuffer message) {
- return new SendPing(message);
- }
-
- static Invocation.SendPong sendPong(ByteBuffer message) {
- return new SendPong(message);
- }
-
- static Invocation.SendClose sendClose(int statusCode, String reason) {
- return new SendClose(statusCode, reason);
- }
-
- public static CloseOutput closeOutput() {
- return new CloseOutput();
- }
-
- public static CloseInput closeInput() {
- return new CloseInput();
- }
-
- public static final class SendText extends Invocation {
-
- final CharSequence message;
- final boolean isLast;
-
- SendText(CharSequence message, boolean isLast) {
- this.message = message.toString();
- this.isLast = isLast;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SendText sendText = (SendText) obj;
- return isLast == sendText.isLast &&
- Objects.equals(message, sendText.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(isLast, message);
- }
- }
-
- public static final class SendBinary extends Invocation {
-
- final ByteBuffer message;
- final boolean isLast;
-
- SendBinary(ByteBuffer message, boolean isLast) {
- this.message = fullCopy(message);
- this.isLast = isLast;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SendBinary that = (SendBinary) obj;
- return isLast == that.isLast &&
- Objects.equals(message, that.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(message, isLast);
- }
- }
-
- private static final class SendPing extends Invocation {
-
- final ByteBuffer message;
-
- SendPing(ByteBuffer message) {
- this.message = fullCopy(message);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SendPing sendPing = (SendPing) obj;
- return Objects.equals(message, sendPing.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(message);
- }
- }
-
- private static final class SendPong extends Invocation {
-
- final ByteBuffer message;
-
- SendPong(ByteBuffer message) {
- this.message = fullCopy(message);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SendPing sendPing = (SendPing) obj;
- return Objects.equals(message, sendPing.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(message);
- }
- }
-
- private static final class SendClose extends Invocation {
-
- final int statusCode;
- final String reason;
-
- SendClose(int statusCode, String reason) {
- this.statusCode = statusCode;
- this.reason = reason;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- SendClose sendClose = (SendClose) obj;
- return statusCode == sendClose.statusCode &&
- Objects.equals(reason, sendClose.reason);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(statusCode, reason);
- }
- }
-
- private static final class CloseOutput extends Invocation {
-
- CloseOutput() { }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof CloseOutput;
- }
- }
-
- private static final class CloseInput extends Invocation {
-
- CloseInput() { }
-
- @Override
- public int hashCode() {
- return 0;
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof CloseInput;
- }
- }
- }
-
- public Queue<Invocation> invocations() {
- return new LinkedList<>(output);
- }
-
- protected final T result() {
- return supplier.get();
- }
-
- private CompletableFuture<T> send(String name,
- Supplier<CompletableFuture<T>> supplier) {
- begin(name);
- CompletableFuture<T> cf = supplier.get().whenComplete((r, e) -> {
- System.out.printf("[%6s ms.] complete %s%n", elapsedTime(), name);
- });
- end(name);
- return cf;
- }
-
- private void begin(String name) {
- System.out.printf("[%6s ms.] begin %s%n", elapsedTime(), name);
- }
-
- private void end(String name) {
- System.out.printf("[%6s ms.] end %s%n", elapsedTime(), name);
- }
-
- private long elapsedTime() {
- return System.currentTimeMillis() - startTime;
- }
-
- private final class ReceiveTask implements SequentialScheduler.RestartableTask {
-
- @Override
- public void run(SequentialScheduler.DeferredCompleter taskCompleter) {
- if (!scheduler.isStopped() && !demand.isFulfilled() && !input.isEmpty()) {
- CompletableFuture<Consumer<MessageStreamConsumer>> cf = input.remove();
- if (cf.isDone()) { // Forcing synchronous execution
- cf.join().accept(consumer);
- repeat(taskCompleter);
- } else {
- cf.whenCompleteAsync((r, e) -> {
- r.accept(consumer);
- repeat(taskCompleter);
- });
- }
- } else {
- taskCompleter.complete();
- }
- }
-
- private void repeat(SequentialScheduler.DeferredCompleter taskCompleter) {
- taskCompleter.complete();
- scheduler.runOrSchedule();
- }
- }
-}
--- a/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/TestSupport.java Wed Mar 14 16:01:41 2018 +0000
+++ b/test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/TestSupport.java Fri Mar 16 01:28:51 2018 +0000
@@ -30,8 +30,6 @@
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Stack;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CompletionStage;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.regex.Pattern;
@@ -307,16 +305,6 @@
throw new AssertionFailedException("Caught exception didn't match the predicate", caught);
}
- /*
- * Blocking assertion, waits for completion
- */
- static Throwable assertCompletesExceptionally(Class<? extends Throwable> clazz,
- CompletionStage<?> stage) {
- CompletableFuture<?> cf =
- CompletableFuture.completedFuture(null).thenCompose(x -> stage);
- return assertThrows(t -> clazz.isInstance(t.getCause()), cf::get);
- }
-
interface ThrowingProcedure {
void run() throws Throwable;
}