--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClient.java Sat Nov 18 20:13:09 2017 +0300
@@ -480,15 +480,13 @@
sendAsync(HttpRequest req, HttpResponse.MultiSubscriber<U, T> multiSubscriber);
/**
- * Creates a builder of {@link WebSocket} instances connected to the given
- * URI and receiving events and messages with the given {@code Listener}.
+ * Creates a new {@code WebSocket} builder (optional operation).
*
* <p> <b>Example</b>
* <pre>{@code
* HttpClient client = HttpClient.newHttpClient();
- * WebSocket.Builder builder = client.newWebSocketBuilder(
- * URI.create("ws://websocket.example.com"),
- * listener);
+ * CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+ * .buildAsync(URI.create("ws://websocket.example.com"), listener);
* }</pre>
*
* <p> Finer control over the WebSocket Opening Handshake can be achieved
@@ -500,29 +498,41 @@
* HttpClient client = HttpClient.newBuilder()
* .proxy(ProxySelector.of(addr))
* .build();
- * WebSocket.Builder builder = client.newWebSocketBuilder(
- * URI.create("ws://websocket.example.com"),
- * listener);
+ * CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+ * .buildAsync(URI.create("ws://websocket.example.com"), listener);
* }</pre>
*
- * @implSpec The default implementation of this method throws {@code
- * UnsupportedOperationException}. However, clients obtained through
+ * <p> A {@code WebSocket.Builder} returned from this method is not safe for
+ * use by multiple threads without external synchronization.
+ *
+ * @implSpec The default implementation of this method throws
+ * {@code UnsupportedOperationException}. Clients obtained through
* {@link HttpClient#newHttpClient()} or {@link HttpClient#newBuilder()}
- * provide WebSocket capability.
+ * return a {@code WebSocket} builder.
+ *
+ * @implNote Both builder and {@code WebSocket}s created with it operate in
+ * a non-blocking fashion. That is, their methods do not block before
+ * returning a {@code CompletableFuture}. Asynchronous tasks executed in
+ * this {@code HttpClient}'s executor.
*
- * @param uri
- * the WebSocket URI
- * @param listener
- * the listener
+ * <p> {@code WebSocket} does not allow to send Text messages that are
+ * partial UTF-16 sequences. If such a sequence is passed, a
+ * {@code CompletableFuture} returned from {@link WebSocket#sendText} will
+ * complete exceptionally with {@code IOException}.
+ * Similarly, {@code WebSocket} invokes
+ * {@link WebSocket.Listener#onText Listener.onText} with messages which
+ * are complete UTF-16 sequences.
*
- * @return a builder of {@code WebSocket} instances
+ * <p> When a {@code CompletionStage} returned from
+ * {@link WebSocket.Listener#onClose Listener.onClose} completes,
+ * the {@code WebSocket} will send a Close message that has the same code
+ * the received message has and an empty reason.
+ *
+ * @return a {@code WebSocket.Builder}
* @throws UnsupportedOperationException
* if this {@code HttpClient} does not provide WebSocket support
- * @throws IllegalArgumentException if the given URI is not a valid WebSocket URI
*/
- public WebSocket.Builder newWebSocketBuilder(URI uri,
- WebSocket.Listener listener)
- {
+ public WebSocket.Builder newWebSocketBuilder() {
throw new UnsupportedOperationException();
}
}
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientFacade.java Sat Nov 18 20:13:09 2017 +0300
@@ -125,11 +125,9 @@
}
@Override
- public WebSocket.Builder newWebSocketBuilder(URI uri,
- WebSocket.Listener listener)
- {
+ public WebSocket.Builder newWebSocketBuilder() {
try {
- return impl.newWebSocketBuilder(uri, listener);
+ return impl.newWebSocketBuilder();
} finally {
Reference.reachabilityFence(this);
}
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/HttpClientImpl.java Sat Nov 18 20:13:09 2017 +0300
@@ -925,14 +925,13 @@
}
@Override
- public WebSocket.Builder newWebSocketBuilder(URI uri,
- WebSocket.Listener listener) {
- // Make sure to pass the HttpClientFacade to the web socket builder.
+ public WebSocket.Builder newWebSocketBuilder() {
+ // Make sure to pass the HttpClientFacade to the WebSocket builder.
// This will ensure that the facade is not released before the
// WebSocket has been created, at which point the pendingOperationCount
// will have been incremented by the DetachedConnectionChannel
// (see PlainHttpConnection.detachChannel())
- return new BuilderImpl(this.facade(), uri, listener, proxySelector);
+ return new BuilderImpl(this.facade(), proxySelector);
}
@Override
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/WebSocket.java Sat Nov 18 20:13:09 2017 +0300
@@ -97,29 +97,24 @@
* A builder for creating {@code WebSocket} instances.
* {@Incubating}
*
- * <p> To build a {@code WebSocket}, {@linkplain HttpClient#newWebSocketBuilder(
- * URI, Listener) create} a builder, configure it as required by
+ * <p> To obtain a {@code WebSocket} configure a builder as required by
* calling intermediate methods (the ones that return the builder itself),
- * then finally call {@link #buildAsync()} to get a {@link
- * CompletableFuture} with resulting {@code WebSocket}.
+ * then call {@code buildAsync()}. If an intermediate method is not called,
+ * an appropriate default value (or behavior) will be assumed.
*
- * <p> If an intermediate method has not been called, an appropriate
- * default value (or behavior) will be used. Unless otherwise noted, a
- * repeated call to an intermediate method overwrites the previous value (or
- * overrides the previous behaviour).
- *
- * <p> Instances of {@code Builder} are not safe for use by multiple threads
- * without external synchronization.
+ * <p> Unless otherwise stated, {@code null} arguments will cause methods of
+ * {@code Builder} to throw {@code NullPointerException}.
*
* @since 9
*/
interface Builder {
/**
- * Adds the given name-value pair to the list of additional headers for
- * the opening handshake.
+ * Adds the given name-value pair to the list of additional HTTP headers
+ * sent during the opening handshake.
*
- * <p> Headers defined in WebSocket Protocol are not allowed to be added.
+ * <p> Headers defined in WebSocket Protocol are illegal. If this method
+ * is not invoked, no additional HTTP headers will be sent.
*
* @param name
* the header name
@@ -131,38 +126,12 @@
Builder header(String name, String value);
/**
- * Includes a request for the given subprotocols during the opening
- * handshake.
- *
- * <p> Among the requested subprotocols at most one will be chosen by
- * the server. This subprotocol will be available from {@link
- * WebSocket#getSubprotocol}. Subprotocols are specified in the order of
- * preference.
- *
- * <p> Each of the given subprotocols must conform to the relevant
- * rules defined in the WebSocket Protocol.
- *
- * <p> If this method is not invoked then no subprotocols are requested.
+ * Sets a timeout for establishing a WebSocket connection.
*
- * @param mostPreferred
- * the most preferred subprotocol
- * @param lesserPreferred
- * the lesser preferred subprotocols, with the least preferred
- * at the end
- *
- * @return this builder
- */
- Builder subprotocols(String mostPreferred, String... lesserPreferred);
-
- /**
- * Sets a timeout for the opening handshake.
- *
- * <p> If the opening handshake does not complete within the specified
- * duration then the {@code CompletableFuture} returned from {@link
- * #buildAsync()} completes exceptionally with a {@link
- * HttpTimeoutException}.
- *
- * <p> If this method is not invoked then the timeout is deemed infinite.
+ * <p> If the connection is not established within the specified
+ * duration then building of the {@code WebSocket} will fail with
+ * {@link HttpTimeoutException}. If this method is not invoked then the
+ * infinite timeout is assumed.
*
* @param timeout
* the timeout, non-{@linkplain Duration#isNegative() negative},
@@ -173,14 +142,37 @@
Builder connectTimeout(Duration timeout);
/**
- * Builds a {@code WebSocket}.
+ * Sets a request for the given subprotocols.
+ *
+ * <p> After the {@code WebSocket} has been built, the actual
+ * subprotocol can be queried via
+ * {@link WebSocket#getSubprotocol WebSocket.getSubprotocol()}.
+ *
+ * <p> Subprotocols are specified in the order of preference. The most
+ * preferred subprotocol is specified first. If there are any additional
+ * subprotocols they are enumerated from the most preferred to the least
+ * preferred.
+ *
+ * <p> Subprotocols not conforming to the syntax of subprotocol
+ * identifiers are illegal. If this method is not invoked then no
+ * subprotocols will be requested.
*
- * <p> Returns a {@code CompletableFuture<WebSocket>} which completes
- * normally with the {@code WebSocket} when it is connected or completes
- * exceptionally if an error occurs.
+ * @param mostPreferred
+ * the most preferred subprotocol
+ * @param lesserPreferred
+ * the lesser preferred subprotocols
*
- * <p> {@code CompletableFuture} may complete exceptionally with the
- * following errors:
+ * @return this builder
+ */
+ Builder subprotocols(String mostPreferred, String... lesserPreferred);
+
+ /**
+ * Builds a {@link WebSocket} connected to the given {@code URI} and
+ * associated with the given {@code Listener}.
+ *
+ * <p> Returns a {@code CompletableFuture} which will either complete
+ * normally with the resulting {@code WebSocket} or complete
+ * exceptionally with one of the following errors:
* <ul>
* <li> {@link IOException} -
* if an I/O error occurs
@@ -188,28 +180,28 @@
* if the opening handshake fails
* <li> {@link HttpTimeoutException} -
* if the opening handshake does not complete within
- * the specified {@linkplain #connectTimeout(Duration) duration}
+ * the timeout
* <li> {@link InterruptedException} -
- * if the operation was interrupted
+ * if the operation is interrupted
* <li> {@link SecurityException} -
- * If a security manager has been installed and it denies
- * {@link java.net.URLPermission access} to the WebSocket URI,
- * that was used to create this builder.
+ * if a security manager has been installed and it denies
+ * {@link java.net.URLPermission access} to {@code uri}.
* <a href="HttpRequest.html#securitychecks">Security checks</a>
* contains more information relating to the security context
* in which the the listener is invoked.
* <li> {@link IllegalArgumentException} -
- * if any of the additional {@link #header(String, String)
- * headers} are illegal;
- * or if any of the WebSocket Protocol rules relevant to {@link
- * #subprotocols(String, String...) subprotocols} are violated;
- * or if the {@link #connectTimeout(Duration) connect timeout}
- * is invalid;
+ * if any of the arguments of this builder's methods are
+ * illegal
* </ul>
*
+ * @param uri
+ * the WebSocket URI
+ * @param listener
+ * the listener
+ *
* @return a {@code CompletableFuture} with the {@code WebSocket}
*/
- CompletableFuture<WebSocket> buildAsync();
+ CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener);
}
/**
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/BuilderImpl.java Sat Nov 18 20:13:09 2017 +0300
@@ -40,26 +40,22 @@
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
-import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static jdk.incubator.http.internal.common.Pair.pair;
public final class BuilderImpl implements Builder {
private final HttpClient client;
- private final URI uri;
- private final Listener listener;
+ private URI uri;
+ private Listener listener;
private final Optional<ProxySelector> proxySelector;
private final Collection<Pair<String, String>> headers;
private final Collection<String> subprotocols;
private Duration timeout;
- public BuilderImpl(HttpClient client,
- URI uri,
- Listener listener,
- ProxySelector proxySelector)
+ public BuilderImpl(HttpClient client, ProxySelector proxySelector)
{
- this(client, uri, listener, Optional.ofNullable(proxySelector),
+ this(client, null, null, Optional.ofNullable(proxySelector),
new LinkedList<>(), new LinkedList<>(), null);
}
@@ -70,37 +66,19 @@
Collection<Pair<String, String>> headers,
Collection<String> subprotocols,
Duration timeout) {
- this.client = requireNonNull(client, "client");
- this.uri = checkURI(requireNonNull(uri, "uri"));
- this.listener = requireNonNull(listener, "listener");
+ this.client = client;
+ this.uri = uri;
+ this.listener = listener;
this.proxySelector = proxySelector;
// If a proxy selector was supplied by the user, it should be present
// on the client and should be the same that what we got as an argument
assert !client.proxy().isPresent()
|| client.proxy().equals(proxySelector);
- this.headers = requireNonNull(headers);
- this.subprotocols = requireNonNull(subprotocols);
+ this.headers = headers;
+ this.subprotocols = subprotocols;
this.timeout = timeout;
}
- private static IllegalArgumentException newIAE(String message, Object... args) {
- return new IllegalArgumentException(format(message, args));
- }
-
- private static URI checkURI(URI uri) {
- String scheme = uri.getScheme();
- if (scheme == null)
- throw newIAE("URI with undefined scheme");
- scheme = scheme.toLowerCase();
- if (!(scheme.equals("ws") || scheme.equals("wss")))
- throw newIAE("invalid URI scheme %s", scheme);
- if (uri.getHost() == null)
- throw newIAE("URI must contain a host: %s", uri);
- if (uri.getFragment() != null)
- throw newIAE("URI must not contain a fragment: %s", uri);
- return uri;
- }
-
@Override
public Builder header(String name, String value) {
requireNonNull(name, "name");
@@ -110,8 +88,7 @@
}
@Override
- public Builder subprotocols(String mostPreferred,
- String... lesserPreferred)
+ public Builder subprotocols(String mostPreferred, String... lesserPreferred)
{
requireNonNull(mostPreferred, "mostPreferred");
requireNonNull(lesserPreferred, "lesserPreferred");
@@ -134,7 +111,11 @@
}
@Override
- public CompletableFuture<WebSocket> buildAsync() {
+ public CompletableFuture<WebSocket> buildAsync(URI uri, Listener listener) {
+ this.uri = requireNonNull(uri, "uri");
+ this.listener = requireNonNull(listener, "listener");
+ // A snapshot of builder inaccessible for further modification
+ // from the outside
BuilderImpl copy = immutableCopy();
return WebSocketImpl.newInstanceAsync(copy);
}
@@ -153,7 +134,7 @@
Optional<ProxySelector> getProxySelector() { return proxySelector; }
- BuilderImpl immutableCopy() {
+ private BuilderImpl immutableCopy() {
@SuppressWarnings({"unchecked", "rawtypes"})
BuilderImpl copy = new BuilderImpl(
client,
--- a/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java Fri Nov 17 18:38:05 2017 +0000
+++ b/src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/websocket/WebSocketImpl.java Sat Nov 18 20:13:09 2017 +0300
@@ -59,6 +59,7 @@
import jdk.incubator.http.internal.websocket.OutgoingMessage.Pong;
import jdk.incubator.http.internal.websocket.OutgoingMessage.Text;
+import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.CompletableFuture.failedFuture;
import static java.util.stream.Collectors.joining;
@@ -159,7 +160,30 @@
}
}
+ private static IllegalArgumentException newIAE(String message, Object... args) {
+ return new IllegalArgumentException(format(message, args));
+ }
+
+ private static URI checkURI(URI uri) {
+ String scheme = uri.getScheme();
+ if (scheme == null)
+ throw newIAE("URI with undefined scheme");
+ scheme = scheme.toLowerCase();
+ if (!(scheme.equals("ws") || scheme.equals("wss")))
+ throw newIAE("invalid URI scheme %s", scheme);
+ if (uri.getHost() == null)
+ throw newIAE("URI must contain a host: %s", uri);
+ if (uri.getFragment() != null)
+ throw newIAE("URI must not contain a fragment: %s", uri);
+ return uri;
+ }
+
static CompletableFuture<WebSocket> newInstanceAsync(BuilderImpl b) {
+ try {
+ checkURI(b.getUri());
+ } catch (IllegalArgumentException e) {
+ return failedFuture(e);
+ }
Proxy proxy = proxyFor(b.getProxySelector(), b.getUri());
try {
checkPermissions(b, proxy);
--- a/test/jdk/java/net/httpclient/examples/WebSocketExample.java Fri Nov 17 18:38:05 2017 +0000
+++ b/test/jdk/java/net/httpclient/examples/WebSocketExample.java Sat Nov 18 20:13:09 2017 +0300
@@ -24,6 +24,8 @@
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
+import java.util.concurrent.CompletableFuture;
+
import jdk.incubator.http.HttpClient;
import jdk.incubator.http.WebSocket;
@@ -42,9 +44,8 @@
public void newBuilderExample0() {
HttpClient client = HttpClient.newHttpClient();
- WebSocket.Builder builder = client.newWebSocketBuilder(
- URI.create("ws://websocket.example.com"),
- listener);
+ CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+ .buildAsync(URI.create("ws://websocket.example.com"), listener);
}
public void newBuilderExample1() {
@@ -52,8 +53,7 @@
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(addr))
.build();
- WebSocket.Builder builder = client.newWebSocketBuilder(
- URI.create("ws://websocket.example.com"),
- listener);
+ CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
+ .buildAsync(URI.create("ws://websocket.example.com"), listener);
}
}
--- a/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java Fri Nov 17 18:38:05 2017 +0000
+++ b/test/jdk/java/net/httpclient/websocket/ConnectionHandover.java Sat Nov 18 20:13:09 2017 +0300
@@ -48,14 +48,16 @@
server.open();
URI uri = server.getURI();
WebSocket.Builder webSocketBuilder =
- HttpClient.newHttpClient().newWebSocketBuilder(uri, new WebSocket.Listener() { });
+ HttpClient.newHttpClient().newWebSocketBuilder();
- WebSocket ws1 = webSocketBuilder.buildAsync().join();
+ WebSocket ws1 = webSocketBuilder
+ .buildAsync(uri, new WebSocket.Listener() { }).join();
try {
ws1.abort();
} catch (IOException ignored) { }
- WebSocket ws2 = webSocketBuilder.buildAsync().join(); // Exception here if the connection was pooled
+ WebSocket ws2 = webSocketBuilder
+ .buildAsync(uri, new WebSocket.Listener() { }).join(); // Exception here if the connection was pooled
try {
ws2.abort();
} catch (IOException ignored) { }
--- a/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java Fri Nov 17 18:38:05 2017 +0000
+++ b/test/jdk/java/net/httpclient/websocket/jdk.incubator.httpclient/jdk/incubator/http/internal/websocket/BuildingWebSocketTest.java Sat Nov 18 20:13:09 2017 +0300
@@ -34,6 +34,7 @@
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static jdk.incubator.http.internal.websocket.TestSupport.assertCompletesExceptionally;
import static jdk.incubator.http.internal.websocket.TestSupport.assertThrows;
@@ -45,71 +46,77 @@
*/
public class BuildingWebSocketTest {
+ private final static URI VALID_URI = URI.create("ws://websocket.example.com");
+
@Test
- public void nulls() {
+ public void nullArguments() {
HttpClient c = HttpClient.newHttpClient();
- URI uri = URI.create("ws://websocket.example.com");
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(null, listener()));
- assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, null));
+ () -> c.newWebSocketBuilder()
+ .buildAsync(null, listener()));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(null, null));
+ () -> c.newWebSocketBuilder()
+ .buildAsync(VALID_URI, null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
+ .buildAsync(null, null));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
.header(null, "value"));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
.header("name", null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
.header(null, null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
.subprotocols(null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
- .subprotocols(null, "sub1"));
+ () -> c.newWebSocketBuilder()
+ .subprotocols(null, "sub2.example.com"));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
.subprotocols("sub1.example.com", (String) null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
.subprotocols("sub1.example.com", (String[]) null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
- .subprotocols("sub1.example.com",
- "sub2.example.com",
- null));
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", "sub2.example.com", null));
assertThrows(NullPointerException.class,
- () -> c.newWebSocketBuilder(uri, listener())
+ () -> c.newWebSocketBuilder()
+ .subprotocols("sub1.example.com", null, "sub3.example.com"));
+ assertThrows(NullPointerException.class,
+ () -> c.newWebSocketBuilder()
.connectTimeout(null));
}
@Test(dataProvider = "badURIs")
- void illegalURI(String u) {
- assertThrows(IllegalArgumentException.class,
- () -> HttpClient.newHttpClient()
- .newWebSocketBuilder(URI.create(u), listener()));
+ 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()
- .map(String::new).collect(Collectors.toList());
+ 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(URI.create("ws://websocket.example.com"),
- listener())
+ header -> HttpClient.newHttpClient()
+ .newWebSocketBuilder()
.header(header, "value")
- .buildAsync();
+ .buildAsync(VALID_URI, listener());
headers.forEach(h -> assertCompletesExceptionally(IllegalArgumentException.class, f.apply(h)));
}
@@ -120,38 +127,38 @@
@Test(dataProvider = "badSubprotocols")
public void illegalSubprotocolsSyntax(String s) {
WebSocket.Builder b = HttpClient.newHttpClient()
- .newWebSocketBuilder(URI.create("ws://websocket.example.com"),
- listener());
- b.subprotocols(s);
- assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+ .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(URI.create("ws://websocket.example.com"),
- listener());
- b.subprotocols(mostPreferred, lesserPreferred);
- assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+ .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(URI.create("ws://websocket.example.com"),
- listener());
- b.connectTimeout(d);
- assertCompletesExceptionally(IllegalArgumentException.class, b.buildAsync());
+ .newWebSocketBuilder()
+ .connectTimeout(d);
+ assertCompletesExceptionally(IllegalArgumentException.class,
+ b.buildAsync(VALID_URI, listener()));
}
@DataProvider
public Object[][] badURIs() {
return new Object[][]{
- {"http://example.com"},
- {"ftp://example.com"},
- {"wss://websocket.example.com/hello#fragment"},
- {"ws://websocket.example.com/hello#fragment"},
+ {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")},
};
}
@@ -179,6 +186,7 @@
@DataProvider
public static Object[][] badSubprotocols() {
return new Object[][]{
+ {""},
{new String("")},
{"round-brackets("},
{"round-brackets)"},
--- a/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java Fri Nov 17 18:38:05 2017 +0000
+++ b/test/jdk/java/net/httpclient/websocket/security/WSURLPermissionTest.java Sat Nov 18 20:13:09 2017 +0300
@@ -96,151 +96,151 @@
HttpClient noProxyClient = HttpClient.newHttpClient();
return new Object[][]{
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // no actions
new URLPermission[] { new URLPermission(wsURI.toString()) },
"0" /* for log file identification */ },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // scheme wildcard
new URLPermission[] { new URLPermission("ws://*") },
"0.1" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // port wildcard
new URLPermission[] { new URLPermission("ws://"+wsURI.getHost()+":*") },
"0.2" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // empty actions
new URLPermission[] { new URLPermission(wsURI.toString(), "") },
"1" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // colon
new URLPermission[] { new URLPermission(wsURI.toString(), ":") },
"2" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // wildcard
new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
"3" },
// WS permission checking is agnostic of method, any/none will do
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // specific method
new URLPermission[] { new URLPermission(wsURI.toString(), "GET") },
"3.1" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // specific method
new URLPermission[] { new URLPermission(wsURI.toString(), "POST") },
"3.2" },
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // path
new URLPermission[] { new URLPermission(wsURI.resolve("/path/x").toString()) },
"4" },
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // same dir wildcard
new URLPermission[] { new URLPermission(wsURI.resolve("/path/*").toString()) },
"5" },
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // recursive
new URLPermission[] { new URLPermission(wsURI.resolve("/path/-").toString()) },
"6" },
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // recursive top
new URLPermission[] { new URLPermission(wsURI.resolve("/-").toString()) },
"7" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // header
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
"8" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // header
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // wildcard
new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
"9" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // headers
.header("B-Header", "B-Value") // headers
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header,B-Header") },
"10" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // headers
.header("B-Header", "B-Value") // headers
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // wildcard
new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
"11" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // headers
.header("B-Header", "B-Value") // headers
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // wildcards
new URLPermission[] { new URLPermission(wsURI.toString(), "*:*") },
"12" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // multi-value
.header("A-Header", "B-Value") // headers
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // wildcard
new URLPermission[] { new URLPermission(wsURI.toString(), ":*") },
"13" },
{ (PrivilegedExceptionAction<?>)() -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value") // multi-value
.header("A-Header", "B-Value") // headers
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // single grant
new URLPermission[] { new URLPermission(wsURI.toString(), ":A-Header") },
"14" },
@@ -249,8 +249,8 @@
{ (PrivilegedExceptionAction<?>)() -> {
ProxySelector ps = ProxySelector.of(null);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] { new URLPermission(wsURI.toString()) },
"15" },
@@ -264,8 +264,8 @@
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { }
};
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] { new URLPermission(wsURI.toString()) },
"16" },
@@ -275,8 +275,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission(wsURI.toString()), // CONNECT action string
@@ -288,8 +288,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission(wsURI.toString()), // no action string
@@ -301,8 +301,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission(wsURI.toString()), // wildcard headers
@@ -314,8 +314,8 @@
assert proxyAddress != null;
CountingProxySelector ps = CountingProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
assertEquals(ps.count(), 1); // ps.select only invoked once
return null; },
new URLPermission[] {
@@ -328,8 +328,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission(wsURI.toString()),
@@ -340,8 +340,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission("ws://*"), // wildcard ws URL
@@ -419,59 +419,59 @@
HttpClient noProxyClient = HttpClient.newHttpClient();
return new Object[][]{
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
},
new URLPermission[]{ /* no permissions */ },
"50" /* for log file identification */},
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // wrong scheme
new URLPermission[]{ new URLPermission("http://*") },
"51" },
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // wrong scheme
new URLPermission[]{ new URLPermission("socket://*") },
"52" },
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // wrong host
new URLPermission[]{ new URLPermission("ws://foo.com/") },
"53" },
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // wrong port
new URLPermission[]{ new URLPermission("ws://"+ wsURI.getHost()+":5") },
"54" },
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value")
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // only perm to set B not A
new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
"55" },
{ (PrivilegedExceptionAction<?>) () -> {
- noProxyClient.newWebSocketBuilder(wsURI, noOpListener)
+ noProxyClient.newWebSocketBuilder()
.header("A-Header", "A-Value")
.header("B-Header", "B-Value")
- .buildAsync().get().abort();
+ .buildAsync(wsURI, noOpListener).get().abort();
return null;
}, // only perm to set B not A
new URLPermission[] { new URLPermission(wsURI.toString(), "*:B-Header") },
@@ -479,16 +479,16 @@
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // wrong path
new URLPermission[] { new URLPermission(wsURI.resolve("/aDiffPath/").toString()) },
"57" },
{ (PrivilegedExceptionAction<?>)() -> {
URI uriWithPath = wsURI.resolve("/path/x");
- noProxyClient.newWebSocketBuilder(uriWithPath, noOpListener)
- .buildAsync().get().abort();
+ noProxyClient.newWebSocketBuilder()
+ .buildAsync(uriWithPath, noOpListener).get().abort();
return null; }, // more specific path
new URLPermission[] { new URLPermission(wsURI.resolve("/path/x/y").toString()) },
"58" },
@@ -498,8 +498,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; }, // missing proxy perm
new URLPermission[] { new URLPermission(wsURI.toString()) },
"100" },
@@ -509,8 +509,8 @@
assert proxyAddress != null;
ProxySelector ps = ProxySelector.of(proxyAddress);
HttpClient client = HttpClient.newBuilder().proxy(ps).build();
- client.newWebSocketBuilder(wsURI, noOpListener)
- .buildAsync().get().abort();
+ client.newWebSocketBuilder()
+ .buildAsync(wsURI, noOpListener).get().abort();
return null; },
new URLPermission[] {
new URLPermission(wsURI.toString()), // missing proxy CONNECT