http-client-branch: (WebSocket) open tests; internal queue cap (close); logging; http-client-branch
authorprappo
Fri, 16 Mar 2018 01:28:51 +0000
branchhttp-client-branch
changeset 56314 f92e7a8a189f
parent 56307 5f07fa266686
child 56315 ec8799d412fb
http-client-branch: (WebSocket) open tests; internal queue cap (close); logging;
src/java.net.http/share/classes/jdk/internal/net/http/websocket/MessageEncoder.java
src/java.net.http/share/classes/jdk/internal/net/http/websocket/TransportImpl.java
src/java.net.http/share/classes/jdk/internal/net/http/websocket/WebSocketImpl.java
test/jdk/java/net/httpclient/websocket/BuildingWebSocketDriver.java
test/jdk/java/net/httpclient/websocket/MockListener.java
test/jdk/java/net/httpclient/websocket/Support.java
test/jdk/java/net/httpclient/websocket/WebSocketBuilderTest.java
test/jdk/java/net/httpclient/websocket/WebSocketExtendedTest.java
test/jdk/java/net/httpclient/websocket/WebSocketTest.java
test/jdk/java/net/httpclient/websocket/WebSocketTextTest.java
test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/BuildingWebSocketTest.java
test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MockListener.java
test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/MockTransport.java
test/jdk/java/net/httpclient/websocket/java.net.http/jdk/internal/net/http/websocket/TestSupport.java
--- 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;
     }