8197564: HTTP Client implementation

Co-authored-by: Daniel Fuchs <daniel.fuchs@oracle.com>
Co-authored-by: Michael McMahon <michael.x.mcmahon@oracle.com>
Co-authored-by: Pavel Rappo <pavel.rappo@oracle.com>
Reviewed-by: chegar, dfuchs, michaelm, prappo
This commit is contained in:
Chris Hegarty 2018-04-17 08:54:17 -07:00
parent a9234c0ab6
commit a3b61fd4c7
398 changed files with 37643 additions and 10897 deletions

View file

@ -0,0 +1,605 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.io.IOException;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URLPermission;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.PushPromiseHandler;
import jdk.internal.net.http.HttpClientBuilderImpl;
/**
* An HTTP Client.
*
* <p> An {@code HttpClient} can be used to send {@linkplain HttpRequest
* requests} and retrieve their {@linkplain HttpResponse responses}. An {@code
* HttpClient} is created through a {@link HttpClient#newBuilder() builder}. The
* builder can be used to configure per-client state, like: the preferred
* protocol version ( HTTP/1.1 or HTTP/2 ), whether to follow redirects, a
* proxy, an authenticator, etc. Once built, an {@code HttpClient} is immutable,
* and can be used to send multiple requests.
*
* <p> An {@code HttpClient} provides configuration information, and resource
* sharing, for all requests send through it.
*
* <p> A {@link BodyHandler BodyHandler} must be supplied for each {@link
* HttpRequest} sent. The {@code BodyHandler} determines how to handle the
* response body, if any. Once an {@link HttpResponse} is received, the
* headers, response code, and body (typically) are available. Whether the
* response body bytes have been read or not depends on the type, {@code T}, of
* the response body.
*
* <p> Requests can be sent either synchronously or asynchronously:
* <ul>
* <li>{@link HttpClient#send(HttpRequest, BodyHandler)} blocks
* until the request has been sent and the response has been received.</li>
*
* <li>{@link HttpClient#sendAsync(HttpRequest, BodyHandler)} sends the
* request and receives the response asynchronously. The {@code sendAsync}
* method returns immediately with a {@link CompletableFuture
* CompletableFuture}&lt;{@link HttpResponse}&gt;. The {@code
* CompletableFuture} completes when the response becomes available. The
* returned {@code CompletableFuture} can be combined in different ways to
* declare dependencies among several asynchronous tasks.</li>
* </ul>
*
* <p><b>Synchronous Example</b>
* <pre>{@code HttpClient client = HttpClient.newBuilder()
* .version(Version.HTTP_1_1)
* .followRedirects(Redirect.NORMAL)
* .proxy(ProxySelector.of(new InetSocketAddress("proxy.example.com", 80)))
* .authenticator(Authenticator.getDefault())
* .build();
* HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
* System.out.println(response.statusCode());
* System.out.println(response.body()); }</pre>
*
* <p><b>Asynchronous Example</b>
* <pre>{@code HttpRequest request = HttpRequest.newBuilder()
* .uri(URI.create("https://foo.com/"))
* .timeout(Duration.ofMinutes(1))
* .header("Content-Type", "application/json")
* .POST(BodyPublishers.ofFile(Paths.get("file.json")))
* .build();
* client.sendAsync(request, BodyHandlers.ofString())
* .thenApply(HttpResponse::body)
* .thenAccept(System.out::println); }</pre>
*
* <p> <a id="securitychecks"></a><b>Security checks</b></a>
*
* <p> If a security manager is present then security checks are performed by
* the HTTP Client's sending methods. An appropriate {@link URLPermission} is
* required to access the destination server, and proxy server if one has
* been configured. The form of the {@code URLPermission} required to access a
* proxy has a {@code method} parameter of {@code "CONNECT"} (for all kinds of
* proxying) and a {@code URL} string of the form {@code "socket://host:port"}
* where host and port specify the proxy's address.
*
* @implNote If an explicit {@linkplain HttpClient.Builder#executor(Executor)
* executor} has not been set for an {@code HttpClient}, and a security manager
* has been installed, then the default executor will execute asynchronous and
* dependent tasks in a context that is granted no permissions. Custom
* {@linkplain HttpRequest.BodyPublisher request body publishers}, {@linkplain
* HttpResponse.BodyHandler response body handlers}, {@linkplain
* HttpResponse.BodySubscriber response body subscribers}, and {@linkplain
* WebSocket.Listener WebSocket Listeners}, if executing operations that require
* privileges, should do so within an appropriate {@linkplain
* AccessController#doPrivileged(PrivilegedAction) privileged context}.
*
* @since 11
*/
public abstract class HttpClient {
/**
* Creates an HttpClient.
*/
protected HttpClient() {}
/**
* Returns a new {@code HttpClient} with default settings.
*
* <p> Equivalent to {@code newBuilder().build()}.
*
* <p> The default settings include: the "GET" request method, a preference
* of {@linkplain HttpClient.Version#HTTP_2 HTTP/2}, a redirection policy of
* {@linkplain Redirect#NEVER NEVER}, the {@linkplain
* ProxySelector#getDefault() default proxy selector}, and the {@linkplain
* SSLContext#getDefault() default SSL context}.
*
* @implNote The system-wide default values are retrieved at the time the
* {@code HttpClient} instance is constructed. Changing the system-wide
* values after an {@code HttpClient} instance has been built, for
* instance, by calling {@link ProxySelector#setDefault(ProxySelector)}
* or {@link SSLContext#setDefault(SSLContext)}, has no effect on already
* built instances.
*
* @return a new HttpClient
*/
public static HttpClient newHttpClient() {
return newBuilder().build();
}
/**
* Creates a new {@code HttpClient} builder.
*
* @return an {@code HttpClient.Builder}
*/
public static Builder newBuilder() {
return new HttpClientBuilderImpl();
}
/**
* A builder of {@linkplain HttpClient HTTP Clients}.
*
* <p> Builders are created by invoking {@link HttpClient#newBuilder()
* newBuilder}. Each of the setter methods modifies the state of the builder
* and returns the same instance. Builders are not thread-safe and should not be
* used concurrently from multiple threads without external synchronization.
*
* @since 11
*/
public interface Builder {
/**
* A proxy selector that always return {@link Proxy#NO_PROXY} implying
* a direct connection.
*
* <p> This is a convenience object that can be passed to
* {@link #proxy(ProxySelector)} in order to build an instance of
* {@link HttpClient} that uses no proxy.
*/
public static final ProxySelector NO_PROXY = ProxySelector.of(null);
/**
* Sets a cookie handler.
*
* @param cookieHandler the cookie handler
* @return this builder
*/
public Builder cookieHandler(CookieHandler cookieHandler);
/**
* Sets an {@code SSLContext}.
*
* <p> If this method is not invoked prior to {@linkplain #build()
* building}, then newly built clients will use the {@linkplain
* SSLContext#getDefault() default context}, which is normally adequate
* for client applications that do not need to specify protocols, or
* require client authentication.
*
* @param sslContext the SSLContext
* @return this builder
*/
public Builder sslContext(SSLContext sslContext);
/**
* Sets an {@code SSLParameters}.
*
* <p> If this method is not invoked prior to {@linkplain #build()
* building}, then newly built clients will use a default,
* implementation specific, set of parameters.
*
* <p> Some parameters which are used internally by the HTTP Client
* implementation (such as the application protocol list) should not be
* set by callers, as they may be ignored. The contents of the given
* object are copied.
*
* @param sslParameters the SSLParameters
* @return this builder
*/
public Builder sslParameters(SSLParameters sslParameters);
/**
* Sets the executor to be used for asynchronous and dependent tasks.
*
* <p> If this method is not invoked prior to {@linkplain #build()
* building}, a default executor is created for each newly built {@code
* HttpClient}. The default executor uses a {@linkplain
* Executors#newCachedThreadPool(ThreadFactory) cached thread pool},
* with a custom thread factory.
*
* @implNote If a security manager has been installed, the thread
* factory creates threads that run with an access control context that
* has no permissions.
*
* @param executor the Executor
* @return this builder
*/
public Builder executor(Executor executor);
/**
* Specifies whether requests will automatically follow redirects issued
* by the server.
*
* <p> If this method is not invoked prior to {@linkplain #build()
* building}, then newly built clients will use a default redirection
* policy of {@link Redirect#NEVER NEVER}.
*
* @param policy the redirection policy
* @return this builder
*/
public Builder followRedirects(Redirect policy);
/**
* Requests a specific HTTP protocol version where possible.
*
* <p> If this method is not invoked prior to {@linkplain #build()
* building}, then newly built clients will prefer {@linkplain
* Version#HTTP_2 HTTP/2}.
*
* <p> If set to {@linkplain Version#HTTP_2 HTTP/2}, then each request
* will attempt to upgrade to HTTP/2. If the upgrade succeeds, then the
* response to this request will use HTTP/2 and all subsequent requests
* and responses to the same
* <a href="https://tools.ietf.org/html/rfc6454#section-4">origin server</a>
* will use HTTP/2. If the upgrade fails, then the response will be
* handled using HTTP/1.1
*
* @implNote Constraints may also affect the selection of protocol version.
* For example, if HTTP/2 is requested through a proxy, and if the implementation
* does not support this mode, then HTTP/1.1 may be used
*
* @param version the requested HTTP protocol version
* @return this builder
*/
public Builder version(HttpClient.Version version);
/**
* Sets the default priority for any HTTP/2 requests sent from this
* client. The value provided must be between {@code 1} and {@code 256}
* (inclusive).
*
* @param priority the priority weighting
* @return this builder
* @throws IllegalArgumentException if the given priority is out of range
*/
public Builder priority(int priority);
/**
* Sets a {@link java.net.ProxySelector}.
*
* @apiNote {@link ProxySelector#of(InetSocketAddress) ProxySelector::of}
* provides a {@code ProxySelector} which uses a single proxy for all
* requests. The system-wide proxy selector can be retrieved by
* {@link ProxySelector#getDefault()}.
*
* @implNote
* If this method is not invoked prior to {@linkplain #build() building},
* then newly built clients will use the {@linkplain
* ProxySelector#getDefault() default proxy selector}, which is usually
* adequate for client applications. The default proxy selector supports
* a set of system properties</a> related to
* <a href="{@docRoot}/java.base/java/net/doc-files/net-properties.html#Proxies">
* proxy settings</a>. This default behavior can be disabled by
* supplying an explicit proxy selector, such as {@link #NO_PROXY} or
* one returned by {@link ProxySelector#of(InetSocketAddress)
* ProxySelector::of}, before {@linkplain #build() building}.
*
* @param proxySelector the ProxySelector
* @return this builder
*/
public Builder proxy(ProxySelector proxySelector);
/**
* Sets an authenticator to use for HTTP authentication.
*
* @param authenticator the Authenticator
* @return this builder
*/
public Builder authenticator(Authenticator authenticator);
/**
* Returns a new {@link HttpClient} built from the current state of this
* builder.
*
* @return a new {@code HttpClient}
*/
public HttpClient build();
}
/**
* Returns an {@code Optional} containing this client's {@link
* CookieHandler}. If no {@code CookieHandler} was set in this client's
* builder, then the {@code Optional} is empty.
*
* @return an {@code Optional} containing this client's {@code CookieHandler}
*/
public abstract Optional<CookieHandler> cookieHandler();
/**
* Returns the follow redirects policy for this client. The default value
* for client's built by builders that do not specify a redirect policy is
* {@link HttpClient.Redirect#NEVER NEVER}.
*
* @return this client's follow redirects setting
*/
public abstract Redirect followRedirects();
/**
* Returns an {@code Optional} containing the {@code ProxySelector}
* supplied to this client. If no proxy selector was set in this client's
* builder, then the {@code Optional} is empty.
*
* <p> Even though this method may return an empty optional, the {@code
* HttpClient} may still have a non-exposed {@linkplain
* Builder#proxy(ProxySelector) default proxy selector} that is
* used for sending HTTP requests.
*
* @return an {@code Optional} containing the proxy selector supplied
* to this client.
*/
public abstract Optional<ProxySelector> proxy();
/**
* Returns this client's {@code SSLContext}.
*
* <p> If no {@code SSLContext} was set in this client's builder, then the
* {@linkplain SSLContext#getDefault() default context} is returned.
*
* @return this client's SSLContext
*/
public abstract SSLContext sslContext();
/**
* Returns a copy of this client's {@link SSLParameters}.
*
* <p> If no {@code SSLParameters} were set in the client's builder, then an
* implementation specific default set of parameters, that the client will
* use, is returned.
*
* @return this client's {@code SSLParameters}
*/
public abstract SSLParameters sslParameters();
/**
* Returns an {@code Optional} containing the {@link Authenticator} set on
* this client. If no {@code Authenticator} was set in the client's builder,
* then the {@code Optional} is empty.
*
* @return an {@code Optional} containing this client's {@code Authenticator}
*/
public abstract Optional<Authenticator> authenticator();
/**
* Returns the preferred HTTP protocol version for this client. The default
* value is {@link HttpClient.Version#HTTP_2}
*
* @implNote Constraints may also affect the selection of protocol version.
* For example, if HTTP/2 is requested through a proxy, and if the
* implementation does not support this mode, then HTTP/1.1 may be used
*
* @return the HTTP protocol version requested
*/
public abstract HttpClient.Version version();
/**
* Returns an {@code Optional} containing this client's {@link
* Executor}. If no {@code Executor} was set in the client's builder,
* then the {@code Optional} is empty.
*
* <p> Even though this method may return an empty optional, the {@code
* HttpClient} may still have an non-exposed {@linkplain
* HttpClient.Builder#executor(Executor) default executor} that is used for
* executing asynchronous and dependent tasks.
*
* @return an {@code Optional} containing this client's {@code Executor}
*/
public abstract Optional<Executor> executor();
/**
* The HTTP protocol version.
*
* @since 11
*/
public enum Version {
/**
* HTTP version 1.1
*/
HTTP_1_1,
/**
* HTTP version 2
*/
HTTP_2
}
/**
* Defines the automatic redirection policy.
*
* <p> The automatic redirection policy is checked whenever a {@code 3XX}
* response code is received. If redirection does not happen automatically,
* then the response, containing the {@code 3XX} response code, is returned,
* where it can be handled manually.
*
* <p> {@code Redirect} policy is set via the {@linkplain
* HttpClient.Builder#followRedirects(Redirect) Builder.followRedirects}
* method.
*
* @implNote When automatic redirection occurs, the request method of the
* redirected request may be modified depending on the specific {@code 30X}
* status code, as specified in <a href="https://tools.ietf.org/html/rfc7231">
* RFC 7231</a>. In addition, the {@code 301} and {@code 302} status codes
* cause a {@code POST} request to be converted to a {@code GET} in the
* redirected request.
*
* @since 11
*/
public enum Redirect {
/**
* Never redirect.
*/
NEVER,
/**
* Always redirect.
*/
ALWAYS,
/**
* Always redirect, except from HTTPS URLs to HTTP URLs.
*/
NORMAL
}
/**
* Sends the given request using this client, blocking if necessary to get
* the response. The returned {@link HttpResponse}{@code <T>} contains the
* response status, headers, and body ( as handled by given response body
* handler ).
*
* @param <T> the response body type
* @param request the request
* @param responseBodyHandler the response body handler
* @return the response
* @throws IOException if an I/O error occurs when sending or receiving
* @throws InterruptedException if the operation is interrupted
* @throws IllegalArgumentException if the {@code request} argument is not
* a request that could have been validly built as specified by {@link
* HttpRequest.Builder HttpRequest.Builder}.
* @throws SecurityException If a security manager has been installed
* and it denies {@link java.net.URLPermission access} to the
* URL in the given request, or proxy if one is configured.
* See <a href="#securitychecks">security checks</a> for further
* information.
*/
public abstract <T> HttpResponse<T>
send(HttpRequest request, HttpResponse.BodyHandler<T> responseBodyHandler)
throws IOException, InterruptedException;
/**
* Sends the given request asynchronously using this client with the given
* response body handler.
*
* <p> Equivalent to: {@code sendAsync(request, responseBodyHandler, null)}.
*
* @param <T> the response body type
* @param request the request
* @param responseBodyHandler the response body handler
* @return a {@code CompletableFuture<HttpResponse<T>>}
* @throws IllegalArgumentException if the {@code request} argument is not
* a request that could have been validly built as specified by {@link
* HttpRequest.Builder HttpRequest.Builder}.
*/
public abstract <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest request,
BodyHandler<T> responseBodyHandler);
/**
* Sends the given request asynchronously using this client with the given
* response body handler and push promise handler.
*
* <p> The returned completable future, if completed successfully, completes
* with an {@link HttpResponse}{@code <T>} that contains the response status,
* headers, and body ( as handled by given response body handler ).
*
* <p> {@linkplain PushPromiseHandler Push promises} received, if any, are
* handled by the given {@code pushPromiseHandler}. A {@code null} valued
* {@code pushPromiseHandler} rejects any push promises.
*
* <p> The returned completable future completes exceptionally with:
* <ul>
* <li>{@link IOException} - if an I/O error occurs when sending or receiving</li>
* <li>{@link SecurityException} - If a security manager has been installed
* and it denies {@link java.net.URLPermission access} to the
* URL in the given request, or proxy if one is configured.
* See <a href="#securitychecks">security checks</a> for further
* information.</li>
* </ul>
*
* @param <T> the response body type
* @param request the request
* @param responseBodyHandler the response body handler
* @param pushPromiseHandler push promise handler, may be null
* @return a {@code CompletableFuture<HttpResponse<T>>}
* @throws IllegalArgumentException if the {@code request} argument is not
* a request that could have been validly built as specified by {@link
* HttpRequest.Builder HttpRequest.Builder}.
*/
public abstract <T> CompletableFuture<HttpResponse<T>>
sendAsync(HttpRequest request,
BodyHandler<T> responseBodyHandler,
PushPromiseHandler<T> pushPromiseHandler);
/**
* Creates a new {@code WebSocket} builder (optional operation).
*
* <p> <b>Example</b>
* <pre>{@code HttpClient client = HttpClient.newHttpClient();
* 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
* by using a custom {@code HttpClient}.
*
* <p> <b>Example</b>
* <pre>{@code InetSocketAddress addr = new InetSocketAddress("proxy.example.com", 80);
* HttpClient client = HttpClient.newBuilder()
* .proxy(ProxySelector.of(addr))
* .build();
* CompletableFuture<WebSocket> ws = client.newWebSocketBuilder()
* .buildAsync(URI.create("ws://websocket.example.com"), listener); }</pre>
*
* @implSpec The default implementation of this method throws
* {@code UnsupportedOperationException}. Clients obtained through
* {@link HttpClient#newHttpClient()} or {@link HttpClient#newBuilder()}
* 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 are executed in
* this {@code HttpClient}'s executor.
*
* <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
*/
public WebSocket.Builder newWebSocketBuilder() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,168 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.Objects.requireNonNull;
/**
* A read-only view of a set of HTTP headers.
*
* <p> An {@code HttpHeaders} is not created directly, but rather returned from
* an {@link HttpResponse HttpResponse}. Specific HTTP headers can be set for
* {@linkplain HttpRequest requests} through the one of the request builder's
* {@link HttpRequest.Builder#header(String, String) headers} methods.
*
* <p> The methods of this class ( that accept a String header name ), and the
* Map returned by the {@link #map() map} method, operate without regard to
* case when retrieving the header value.
*
* <p> {@code HttpHeaders} instances are immutable.
*
* @since 11
*/
public abstract class HttpHeaders {
/**
* Creates an HttpHeaders.
*/
protected HttpHeaders() {}
/**
* Returns an {@link Optional} containing the first value of the given named
* (and possibly multi-valued) header. If the header is not present, then
* the returned {@code Optional} is empty.
*
* @implSpec
* The default implementation invokes
* {@code allValues(name).stream().findFirst()}
*
* @param name the header name
* @return an {@code Optional<String>} for the first named value
*/
public Optional<String> firstValue(String name) {
return allValues(name).stream().findFirst();
}
/**
* Returns an {@link OptionalLong} containing the first value of the
* named header field. If the header is not present, then the Optional is
* empty. If the header is present but contains a value that does not parse
* as a {@code Long} value, then an exception is thrown.
*
* @implSpec
* The default implementation invokes
* {@code allValues(name).stream().mapToLong(Long::valueOf).findFirst()}
*
* @param name the header name
* @return an {@code OptionalLong}
* @throws NumberFormatException if a value is found, but does not parse as
* a Long
*/
public OptionalLong firstValueAsLong(String name) {
return allValues(name).stream().mapToLong(Long::valueOf).findFirst();
}
/**
* Returns an unmodifiable List of all of the values of the given named
* header. Always returns a List, which may be empty if the header is not
* present.
*
* @implSpec
* The default implementation invokes, among other things, the
* {@code map().get(name)} to retrieve the list of header values.
*
* @param name the header name
* @return a List of String values
*/
public List<String> allValues(String name) {
requireNonNull(name);
List<String> values = map().get(name);
// Making unmodifiable list out of empty in order to make a list which
// throws UOE unconditionally
return values != null ? values : unmodifiableList(emptyList());
}
/**
* Returns an unmodifiable multi Map view of this HttpHeaders.
*
* @return the Map
*/
public abstract Map<String, List<String>> map();
/**
* Tests this HTTP headers instance for equality with the given object.
*
* <p> If the given object is not an {@code HttpHeaders} then this
* method returns {@code false}. Two HTTP headers are equal if each
* of their corresponding {@linkplain #map() maps} are equal.
*
* <p> This method satisfies the general contract of the {@link
* Object#equals(Object) Object.equals} method.
*
* @param obj the object to which this object is to be compared
* @return {@code true} if, and only if, the given object is an {@code
* HttpHeaders} that is equal to this HTTP headers
*/
public final boolean equals(Object obj) {
if (!(obj instanceof HttpHeaders))
return false;
HttpHeaders that = (HttpHeaders)obj;
return this.map().equals(that.map());
}
/**
* Computes a hash code for this HTTP headers instance.
*
* <p> The hash code is based upon the components of the HTTP headers
* {@link #map() map}, and satisfies the general contract of the
* {@link Object#hashCode Object.hashCode} method.
*
* @return the hash-code value for this HTTP headers
*/
public final int hashCode() {
return map().hashCode();
}
/**
* Returns this HTTP headers as a string.
*
* @return a string describing the HTTP headers
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(super.toString()).append(" { ");
sb.append(map());
sb.append(" }");
return sb.toString();
}
}

View file

@ -0,0 +1,650 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Flow;
import java.util.function.Supplier;
import jdk.internal.net.http.HttpRequestBuilderImpl;
import jdk.internal.net.http.RequestPublishers;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* An HTTP request.
*
* <p> An {@code HttpRequest} instance is built through an {@code HttpRequest}
* {@linkplain HttpRequest.Builder builder}. An {@code HttpRequest} builder
* is obtained from one of the {@link HttpRequest#newBuilder(URI) newBuilder}
* methods. A request's {@link URI}, headers, and body can be set. Request
* bodies are provided through a {@link BodyPublisher BodyPublisher} supplied
* to one of the {@link Builder#POST(BodyPublisher) POST},
* {@link Builder#PUT(BodyPublisher) PUT} or
* {@link Builder#method(String,BodyPublisher) method} methods.
* Once all required parameters have been set in the builder, {@link
* Builder#build() build} will return the {@code HttpRequest}. Builders can be
* copied and modified many times in order to build multiple related requests
* that differ in some parameters.
*
* <p> The following is an example of a GET request that prints the response
* body as a String:
*
* <pre>{@code HttpClient client = HttpClient.newHttpClient();
* HttpRequest request = HttpRequest.newBuilder()
* .uri(URI.create("http://foo.com/"))
* .build();
* client.sendAsync(request, BodyHandlers.ofString())
* .thenApply(HttpResponse::body)
* .thenAccept(System.out::println)
* .join(); }</pre>
*
* <p>The class {@link BodyPublishers BodyPublishers} provides implementations
* of many common publishers. Alternatively, a custom {@code BodyPublisher}
* implementation can be used.
*
* @since 11
*/
public abstract class HttpRequest {
/**
* Creates an HttpRequest.
*/
protected HttpRequest() {}
/**
* A builder of {@linkplain HttpRequest HTTP requests}.
*
* <p> Instances of {@code HttpRequest.Builder} are created by calling {@link
* HttpRequest#newBuilder(URI)} or {@link HttpRequest#newBuilder()}.
*
* <p> Each of the setter methods modifies the state of the builder
* and returns the same instance. The methods are not synchronized and
* should not be called from multiple threads without external
* synchronization. The {@link #build() build} method returns a new
* {@code HttpRequest} each time it is invoked. Once built an {@code
* HttpRequest} is immutable, and can be sent multiple times.
*
* <p> Note, that not all request headers may be set by user code. Some are
* restricted for security reasons and others such as the headers relating
* to authentication, redirection and cookie management may be managed by
* specific APIs rather than through directly user set headers.
*
* @since 11
*/
public interface Builder {
/**
* Sets this {@code HttpRequest}'s request {@code URI}.
*
* @param uri the request URI
* @return this builder
* @throws IllegalArgumentException if the {@code URI} scheme is not
* supported
*/
public Builder uri(URI uri);
/**
* Requests the server to acknowledge the request before sending the
* body. This is disabled by default. If enabled, the server is
* requested to send an error response or a {@code 100 Continue}
* response before the client sends the request body. This means the
* request publisher for the request will not be invoked until this
* interim response is received.
*
* @param enable {@code true} if Expect continue to be sent
* @return this builder
*/
public Builder expectContinue(boolean enable);
/**
* Sets the preferred {@link HttpClient.Version} for this request.
*
* <p> The corresponding {@link HttpResponse} should be checked for the
* version that was actually used. If the version is not set in a
* request, then the version requested will be that of the sending
* {@link HttpClient}.
*
* @param version the HTTP protocol version requested
* @return this builder
*/
public Builder version(HttpClient.Version version);
/**
* Adds the given name value pair to the set of headers for this request.
* The given value is added to the list of values for that name.
*
* @implNote An implementation may choose to restrict some header names
* or values, as the HTTP Client may determine their value itself.
* For example, "Content-Length", which will be determined by
* the request Publisher. In such a case, an implementation of
* {@code HttpRequest.Builder} may choose to throw an
* {@code IllegalArgumentException} if such a header is passed
* to the builder.
*
* @param name the header name
* @param value the header value
* @return this builder
* @throws IllegalArgumentException if the header name or value is not
* valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
* RFC 7230 section-3.2</a>, or the header name or value is restricted
* by the implementation.
*/
public Builder header(String name, String value);
/**
* Adds the given name value pairs to the set of headers for this
* request. The supplied {@code String} instances must alternate as
* header names and header values.
* To add several values to the same name then the same name must
* be supplied with each new value.
*
* @param headers the list of name value pairs
* @return this builder
* @throws IllegalArgumentException if there are an odd number of
* parameters, or if a header name or value is not valid, see
* <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
* RFC 7230 section-3.2</a>, or a header name or value is
* {@linkplain #header(String, String) restricted} by the
* implementation.
*/
public Builder headers(String... headers);
/**
* Sets a timeout for this request. If the response is not received
* within the specified timeout then an {@link HttpTimeoutException} is
* thrown from {@link HttpClient#send(java.net.http.HttpRequest,
* java.net.http.HttpResponse.BodyHandler) HttpClient::send} or
* {@link HttpClient#sendAsync(java.net.http.HttpRequest,
* java.net.http.HttpResponse.BodyHandler) HttpClient::sendAsync}
* completes exceptionally with an {@code HttpTimeoutException}. The effect
* of not setting a timeout is the same as setting an infinite Duration, ie.
* block forever.
*
* @param duration the timeout duration
* @return this builder
* @throws IllegalArgumentException if the duration is non-positive
*/
public abstract Builder timeout(Duration duration);
/**
* Sets the given name value pair to the set of headers for this
* request. This overwrites any previously set values for name.
*
* @param name the header name
* @param value the header value
* @return this builder
* @throws IllegalArgumentException if the header name or value is not valid,
* see <a href="https://tools.ietf.org/html/rfc7230#section-3.2">
* RFC 7230 section-3.2</a>, or the header name or value is
* {@linkplain #header(String, String) restricted} by the
* implementation.
*/
public Builder setHeader(String name, String value);
/**
* Sets the request method of this builder to GET.
* This is the default.
*
* @return this builder
*/
public Builder GET();
/**
* Sets the request method of this builder to POST and sets its
* request body publisher to the given value.
*
* @param bodyPublisher the body publisher
*
* @return this builder
*/
public Builder POST(BodyPublisher bodyPublisher);
/**
* Sets the request method of this builder to PUT and sets its
* request body publisher to the given value.
*
* @param bodyPublisher the body publisher
*
* @return this builder
*/
public Builder PUT(BodyPublisher bodyPublisher);
/**
* Sets the request method of this builder to DELETE.
*
* @return this builder
*/
public Builder DELETE();
/**
* Sets the request method and request body of this builder to the
* given values.
*
* @apiNote The {@link BodyPublishers#noBody() noBody} request
* body publisher can be used where no request body is required or
* appropriate. Whether a method is restricted, or not, is
* implementation specific. For example, some implementations may choose
* to restrict the {@code CONNECT} method.
*
* @param method the method to use
* @param bodyPublisher the body publisher
* @return this builder
* @throws IllegalArgumentException if the method name is not
* valid, see <a href="https://tools.ietf.org/html/rfc7230#section-3.1.1">
* RFC 7230 section-3.1.1</a>, or the method is restricted by the
* implementation.
*/
public Builder method(String method, BodyPublisher bodyPublisher);
/**
* Builds and returns an {@link HttpRequest}.
*
* @return a new {@code HttpRequest}
* @throws IllegalStateException if a URI has not been set
*/
public HttpRequest build();
/**
* Returns an exact duplicate copy of this {@code Builder} based on
* current state. The new builder can then be modified independently of
* this builder.
*
* @return an exact copy of this builder
*/
public Builder copy();
}
/**
* Creates an {@code HttpRequest} builder with the given URI.
*
* @param uri the request URI
* @return a new request builder
* @throws IllegalArgumentException if the URI scheme is not supported.
*/
public static HttpRequest.Builder newBuilder(URI uri) {
return new HttpRequestBuilderImpl(uri);
}
/**
* Creates an {@code HttpRequest} builder.
*
* @return a new request builder
*/
public static HttpRequest.Builder newBuilder() {
return new HttpRequestBuilderImpl();
}
/**
* Returns an {@code Optional} containing the {@link BodyPublisher} set on
* this request. If no {@code BodyPublisher} was set in the requests's
* builder, then the {@code Optional} is empty.
*
* @return an {@code Optional} containing this request's {@code BodyPublisher}
*/
public abstract Optional<BodyPublisher> bodyPublisher();
/**
* Returns the request method for this request. If not set explicitly,
* the default method for any request is "GET".
*
* @return this request's method
*/
public abstract String method();
/**
* Returns an {@code Optional} containing this request's timeout duration.
* If the timeout duration was not set in the request's builder, then the
* {@code Optional} is empty.
*
* @return an {@code Optional} containing this request's timeout duration
*/
public abstract Optional<Duration> timeout();
/**
* Returns this request's {@linkplain HttpRequest.Builder#expectContinue(boolean)
* expect continue} setting.
*
* @return this request's expect continue setting
*/
public abstract boolean expectContinue();
/**
* Returns this request's {@code URI}.
*
* @return this request's URI
*/
public abstract URI uri();
/**
* Returns an {@code Optional} containing the HTTP protocol version that
* will be requested for this {@code HttpRequest}. If the version was not
* set in the request's builder, then the {@code Optional} is empty.
* In that case, the version requested will be that of the sending
* {@link HttpClient}. The corresponding {@link HttpResponse} should be
* queried to determine the version that was actually used.
*
* @return HTTP protocol version
*/
public abstract Optional<HttpClient.Version> version();
/**
* The (user-accessible) request headers that this request was (or will be)
* sent with.
*
* @return this request's HttpHeaders
*/
public abstract HttpHeaders headers();
/**
* Tests this HTTP request instance for equality with the given object.
*
* <p> If the given object is not an {@code HttpRequest} then this
* method returns {@code false}. Two HTTP requests are equal if their URI,
* method, and headers fields are all equal.
*
* <p> This method satisfies the general contract of the {@link
* Object#equals(Object) Object.equals} method.
*
* @param obj the object to which this object is to be compared
* @return {@code true} if, and only if, the given object is an {@code
* HttpRequest} that is equal to this HTTP request
*/
@Override
public final boolean equals(Object obj) {
if (! (obj instanceof HttpRequest))
return false;
HttpRequest that = (HttpRequest)obj;
if (!that.method().equals(this.method()))
return false;
if (!that.uri().equals(this.uri()))
return false;
if (!that.headers().equals(this.headers()))
return false;
return true;
}
/**
* Computes a hash code for this HTTP request instance.
*
* <p> The hash code is based upon the HTTP request's URI, method, and
* header components, and satisfies the general contract of the
* {@link Object#hashCode Object.hashCode} method.
*
* @return the hash-code value for this HTTP request
*/
public final int hashCode() {
return method().hashCode()
+ uri().hashCode()
+ headers().hashCode();
}
/**
* A {@code BodyPublisher} converts high-level Java objects into a flow of
* byte buffers suitable for sending as a request body. The class
* {@link BodyPublishers BodyPublishers} provides implementations of many
* common publishers.
*
* <p> The {@code BodyPublisher} interface extends {@link Flow.Publisher
* Flow.Publisher&lt;ByteBuffer&gt;}, which means that a {@code BodyPublisher}
* acts as a publisher of {@linkplain ByteBuffer byte buffers}.
*
* <p> When sending a request that contains a body, the HTTP Client
* subscribes to the request's {@code BodyPublisher} in order to receive the
* flow of outgoing request body data. The normal semantics of {@link
* Flow.Subscriber} and {@link Flow.Publisher} are implemented by the HTTP
* Client and are expected from {@code BodyPublisher} implementations. Each
* outgoing request results in one HTTP Client {@code Subscriber}
* subscribing to the {@code BodyPublisher} in order to provide the sequence
* of byte buffers containing the request body. Instances of {@code
* ByteBuffer} published by the publisher must be allocated by the
* publisher, and must not be accessed after being published to the HTTP
* Client. These subscriptions complete normally when the request body is
* fully sent, and can be canceled or terminated early through error. If a
* request needs to be resent for any reason, then a new subscription is
* created which is expected to generate the same data as before.
*
* <p> A {@code BodyPublisher} that reports a {@linkplain #contentLength()
* content length} of {@code 0} may not be subscribed to by the HTTP Client,
* as it has effectively no data to publish.
*
* @see BodyPublishers
* @since 11
*/
public interface BodyPublisher extends Flow.Publisher<ByteBuffer> {
/**
* Returns the content length for this request body. May be zero
* if no request body being sent, greater than zero for a fixed
* length content, or less than zero for an unknown content length.
*
* <p> This method may be invoked before the publisher is subscribed to.
* This method may be invoked more than once by the HTTP client
* implementation, and MUST return the same constant value each time.
*
* @return the content length for this request body, if known
*/
long contentLength();
}
/**
* Implementations of {@link BodyPublisher BodyPublisher} that implement
* various useful publishers, such as publishing the request body from a
* String, or from a file.
*
* <p> The following are examples of using the predefined body publishers to
* convert common high-level Java objects into a flow of data suitable for
* sending as a request body:
*
* <pre>{@code // Request body from a String
* HttpRequest request = HttpRequest.newBuilder()
* .uri(URI.create("https://foo.com/"))
* .header("Content-Type", "text/plain; charset=UTF-8")
* .POST(BodyPublishers.ofString("some body text"))
* .build();
*
* // Request body from a File
* HttpRequest request = HttpRequest.newBuilder()
* .uri(URI.create("https://foo.com/"))
* .header("Content-Type", "application/json")
* .POST(BodyPublishers.ofFile(Paths.get("file.json")))
* .build();
*
* // Request body from a byte array
* HttpRequest request = HttpRequest.newBuilder()
* .uri(URI.create("https://foo.com/"))
* .POST(BodyPublishers.ofByteArray(new byte[] { ... }))
* .build(); }</pre>
*
* @since 11
*/
public static class BodyPublishers {
private BodyPublishers() { }
/**
* Returns a request body publisher whose body is retrieved from the
* given {@code Flow.Publisher}. The returned request body publisher
* has an unknown content length.
*
* @apiNote This method can be used as an adapter between {@code
* BodyPublisher} and {@code Flow.Publisher}, where the amount of
* request body that the publisher will publish is unknown.
*
* @param publisher the publisher responsible for publishing the body
* @return a BodyPublisher
*/
public static BodyPublisher
fromPublisher(Flow.Publisher<? extends ByteBuffer> publisher) {
return new RequestPublishers.PublisherAdapter(publisher, -1L);
}
/**
* Returns a request body publisher whose body is retrieved from the
* given {@code Flow.Publisher}. The returned request body publisher
* has the given content length.
*
* <p> The given {@code contentLength} is a positive number, that
* represents the exact amount of bytes the {@code publisher} must
* publish.
*
* @apiNote This method can be used as an adapter between {@code
* BodyPublisher} and {@code Flow.Publisher}, where the amount of
* request body that the publisher will publish is known.
*
* @param publisher the publisher responsible for publishing the body
* @param contentLength a positive number representing the exact
* amount of bytes the publisher will publish
* @throws IllegalArgumentException if the content length is
* non-positive
* @return a BodyPublisher
*/
public static BodyPublisher
fromPublisher(Flow.Publisher<? extends ByteBuffer> publisher,
long contentLength) {
if (contentLength < 1)
throw new IllegalArgumentException("non-positive contentLength: "
+ contentLength);
return new RequestPublishers.PublisherAdapter(publisher, contentLength);
}
/**
* Returns a request body publisher whose body is the given {@code
* String}, converted using the {@link StandardCharsets#UTF_8 UTF_8}
* character set.
*
* @param body the String containing the body
* @return a BodyPublisher
*/
public static BodyPublisher ofString(String body) {
return ofString(body, UTF_8);
}
/**
* Returns a request body publisher whose body is the given {@code
* String}, converted using the given character set.
*
* @param s the String containing the body
* @param charset the character set to convert the string to bytes
* @return a BodyPublisher
*/
public static BodyPublisher ofString(String s, Charset charset) {
return new RequestPublishers.StringPublisher(s, charset);
}
/**
* A request body publisher that reads its data from an {@link
* InputStream}. A {@link Supplier} of {@code InputStream} is used in
* case the request needs to be repeated, as the content is not buffered.
* The {@code Supplier} may return {@code null} on subsequent attempts,
* in which case the request fails.
*
* @param streamSupplier a Supplier of open InputStreams
* @return a BodyPublisher
*/
// TODO (spec): specify that the stream will be closed
public static BodyPublisher ofInputStream(Supplier<? extends InputStream> streamSupplier) {
return new RequestPublishers.InputStreamPublisher(streamSupplier);
}
/**
* Returns a request body publisher whose body is the given byte array.
*
* @param buf the byte array containing the body
* @return a BodyPublisher
*/
public static BodyPublisher ofByteArray(byte[] buf) {
return new RequestPublishers.ByteArrayPublisher(buf);
}
/**
* Returns a request body publisher whose body is the content of the
* given byte array of {@code length} bytes starting from the specified
* {@code offset}.
*
* @param buf the byte array containing the body
* @param offset the offset of the first byte
* @param length the number of bytes to use
* @return a BodyPublisher
* @throws IndexOutOfBoundsException if the sub-range is defined to be
* out-of-bounds
*/
public static BodyPublisher ofByteArray(byte[] buf, int offset, int length) {
Objects.checkFromIndexSize(offset, length, buf.length);
return new RequestPublishers.ByteArrayPublisher(buf, offset, length);
}
/**
* A request body publisher that takes data from the contents of a File.
*
* <p> Security manager permission checks are performed in this factory
* method, when the {@code BodyPublisher} is created. Care must be taken
* that the {@code BodyPublisher} is not shared with untrusted code.
*
* @param path the path to the file containing the body
* @return a BodyPublisher
* @throws java.io.FileNotFoundException if the path is not found
* @throws SecurityException if a security manager has been installed
* and it denies {@link SecurityManager#checkRead(String)
* read access} to the given file
*/
public static BodyPublisher ofFile(Path path) throws FileNotFoundException {
Objects.requireNonNull(path);
return RequestPublishers.FilePublisher.create(path);
}
/**
* A request body publisher that takes data from an {@code Iterable}
* of byte arrays. An {@link Iterable} is provided which supplies
* {@link Iterator} instances. Each attempt to send the request results
* in one invocation of the {@code Iterable}.
*
* @param iter an Iterable of byte arrays
* @return a BodyPublisher
*/
public static BodyPublisher ofByteArrays(Iterable<byte[]> iter) {
return new RequestPublishers.IterablePublisher(iter);
}
/**
* A request body publisher which sends no request body.
*
* @return a BodyPublisher which completes immediately and sends
* no request body.
*/
public static BodyPublisher noBody() {
return new RequestPublishers.EmptyPublisher();
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.io.IOException;
/**
* Thrown when a response is not received within a specified time period.
*
* @since 11
*/
public class HttpTimeoutException extends IOException {
private static final long serialVersionUID = 981344271622632951L;
/**
* Constructs an {@code HttpTimeoutException} with the given detail message.
*
* @param message
* The detail message; can be {@code null}
*/
public HttpTimeoutException(String message) {
super(message);
}
}

View file

@ -0,0 +1,771 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
/**
* A WebSocket Client.
*
* <p> {@code WebSocket} instances can be created via {@link WebSocket.Builder}.
*
* <p> WebSocket has an input and an output sides. These sides are independent
* from each other. A side can either be open or closed. Once closed, the side
* remains closed. WebSocket messages are sent through a {@code WebSocket} and
* received through a {@code WebSocket.Listener} associated with it. Messages
* can be sent until the WebSocket's output is closed, and received until the
* WebSocket's input is closed.
*
* <p> A send method is any of the {@code sendText}, {@code sendBinary},
* {@code sendPing}, {@code sendPong} and {@code sendClose} methods of
* {@code WebSocket}. A send method initiates a send operation and returns a
* {@code CompletableFuture} which completes once the operation has completed.
* If the {@code CompletableFuture} completes normally the operation is
* considered succeeded. If the {@code CompletableFuture} completes
* exceptionally, the operation is considered failed. An operation that has been
* initiated but not yet completed is considered pending.
*
* <p> A receive method is any of the {@code onText}, {@code onBinary},
* {@code onPing}, {@code onPong} and {@code onClose} methods of
* {@code Listener}. A receive method initiates a receive operation and returns
* a {@code CompletionStage} which completes once the operation has completed.
*
* <p> A WebSocket maintains an <a id="counter">internal counter</a>.
* This counter's value is a number of times the WebSocket has yet to invoke a
* receive method. While this counter is zero the WebSocket does not invoke
* receive methods. The counter is incremented by {@code n} when {@code
* request(n)} is called. The counter is decremented by one when the WebSocket
* invokes a receive method. {@code onOpen} and {@code onError} are not receive
* methods. WebSocket invokes {@code onOpen} prior to any other methods on the
* listener. WebSocket invokes {@code onOpen} at most once. WebSocket may invoke
* {@code onError} at any given time. If the WebSocket invokes {@code onError}
* or {@code onClose}, then no further listener's methods will be invoked, no
* matter the value of the counter. For a newly built WebSocket the counter is
* zero. A WebSocket invokes methods on the listener in a thread-safe manner.
*
* <p> Unless otherwise stated, {@code null} arguments will cause methods
* of {@code WebSocket} to throw {@code NullPointerException}, similarly,
* {@code WebSocket} will not pass {@code null} arguments to methods of
* {@code Listener}. The state of a WebSocket is not changed by the invocations
* that throw or return a {@code CompletableFuture} that completes with one of
* the {@code NullPointerException}, {@code IllegalArgumentException},
* {@code IllegalStateException} exceptions.
*
* <p> {@code WebSocket} handles received Ping and Close messages automatically
* (as per the WebSocket Protocol) by replying with Pong and Close messages. If
* the listener receives Ping or Close messages, no mandatory actions from the
* listener are required.
*
* @apiNote The relationship between a WebSocket and the associated Listener is
* analogous to that of a Subscription and the associated Subscriber of type
* {@link java.util.concurrent.Flow}.
*
* @since 11
*/
public interface WebSocket {
/**
* The WebSocket Close message status code (<code>{@value}</code>),
* indicating normal closure, meaning that the purpose for which the
* connection was established has been fulfilled.
*
* @see #sendClose(int, String)
* @see Listener#onClose(WebSocket, int, String)
*/
int NORMAL_CLOSURE = 1000;
/**
* A builder of {@linkplain WebSocket WebSocket Clients}.
*
* <p> A builder can be created by invoking the
* {@link HttpClient#newWebSocketBuilder HttpClient.newWebSocketBuilder}
* method. The intermediate (setter-like) methods change the state of the
* builder and return the same builder they have been invoked on. If an
* intermediate method is not invoked, an appropriate default value (or
* behavior) will be assumed. A {@code Builder} is not safe for use by
* multiple threads without external synchronization.
*
* @since 11
*/
interface Builder {
/**
* Adds the given name-value pair to the list of additional HTTP headers
* sent during the opening handshake.
*
* <p> Headers defined in the
* <a href="https://tools.ietf.org/html/rfc6455#section-11.3">WebSocket
* Protocol</a> are illegal. If this method is not invoked, no
* additional HTTP headers will be sent.
*
* @param name
* the header name
* @param value
* the header value
*
* @return this builder
*/
Builder header(String name, String value);
/**
* Sets a timeout for establishing a WebSocket connection.
*
* <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},
* non-{@linkplain Duration#ZERO ZERO}
*
* @return this builder
*/
Builder connectTimeout(Duration timeout);
/**
* 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.
*
* @param mostPreferred
* the most preferred subprotocol
* @param lesserPreferred
* the lesser preferred subprotocols
*
* @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
* <li> {@link WebSocketHandshakeException} -
* if the opening handshake fails
* <li> {@link HttpTimeoutException} -
* if the opening handshake does not complete within
* the timeout
* <li> {@link InterruptedException} -
* if the operation is interrupted
* <li> {@link SecurityException} -
* 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 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(URI uri, Listener listener);
}
/**
* The receiving interface of {@code WebSocket}.
*
* <p> A {@code WebSocket} invokes methods of the associated listener
* passing itself as an argument. When data has been received, the
* {@code WebSocket} invokes a receive method. Methods {@code onText},
* {@code onBinary}, {@code onPing} and {@code onPong} must return a
* {@code CompletionStage} that completes once the message has been received
* by the listener.
*
* <p> An {@code IOException} raised in {@code WebSocket} will result in an
* invocation of {@code onError} with that exception (if the input is not
* closed). Unless otherwise stated if the listener's method throws an
* exception or a {@code CompletionStage} returned from a method completes
* exceptionally, the WebSocket will invoke {@code onError} with this
* exception.
*
* <p> If a listener's method returns {@code null} rather than a
* {@code CompletionStage}, {@code WebSocket} will behave as if the listener
* returned a {@code CompletionStage} that is already completed normally.
*
* @apiNote Careful attention may be required if a listener is associated
* with more than a single {@code WebSocket}. In this case invocations
* related to different instances of {@code WebSocket} may not be ordered
* and may even happen concurrently.
*
* <p> {@code CompletionStage}s returned from the receive methods have
* nothing to do with the
* <a href="WebSocket.html#counter">counter of invocations</a>.
* Namely, a {@code CompletionStage} does not have to be completed in order
* to receive more invocations of the listener's methods.
* Here is an example of a listener that requests invocations, one at a
* time, until a complete message has been accumulated, then processes
* the result, and completes the {@code CompletionStage}:
* <pre>{@code WebSocket.Listener listener = new WebSocket.Listener() {
*
* List<CharSequence> parts = new ArrayList<>();
* CompletableFuture<?> accumulatedMessage = new CompletableFuture<>();
*
* public CompletionStage<?> onText(WebSocket webSocket,
* CharSequence message,
* boolean last) {
* parts.add(message);
* webSocket.request(1);
* if (last) {
* processWholeText(parts);
* parts = new ArrayList<>();
* accumulatedMessage.complete(null);
* CompletionStage<?> cf = accumulatedMessage;
* accumulatedMessage = new CompletableFuture<>();
* return cf;
* }
* return accumulatedMessage;
* }
* ...
* } } </pre>
*
* @since 11
*/
interface Listener {
/**
* A {@code WebSocket} has been connected.
*
* <p> This is the initial invocation and it is made once. It is
* typically used to make a request for more invocations.
*
* @implSpec The default implementation is equivalent to:
* <pre>{@code webSocket.request(1); }</pre>
*
* @param webSocket
* the WebSocket that has been connected
*/
default void onOpen(WebSocket webSocket) { webSocket.request(1); }
/**
* A textual data has been received.
*
* <p> Return a {@code CompletionStage} which will be used by the
* {@code WebSocket} as an indication it may reclaim the
* {@code CharSequence}. Do not access the {@code CharSequence} after
* this {@code CompletionStage} has completed.
*
* @implSpec The default implementation is equivalent to:
* <pre>{@code webSocket.request(1);
* return null; }</pre>
*
* @implNote The {@code data} is always a legal UTF-16 sequence.
*
* @param webSocket
* the WebSocket on which the data has been received
* @param data
* the data
* @param last
* whether this invocation completes the message
*
* @return a {@code CompletionStage} which completes when the
* {@code CharSequence} may be reclaimed; or {@code null} if it may be
* reclaimed immediately
*/
default CompletionStage<?> onText(WebSocket webSocket,
CharSequence data,
boolean last) {
webSocket.request(1);
return null;
}
/**
* A binary data has been received.
*
* <p> This data is located in bytes from the buffer's position to its
* limit.
*
* <p> Return a {@code CompletionStage} which will be used by the
* {@code WebSocket} as an indication it may reclaim the
* {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
* this {@code CompletionStage} has completed.
*
* @implSpec The default implementation is equivalent to:
* <pre>{@code webSocket.request(1);
* return null; }</pre>
*
* @param webSocket
* the WebSocket on which the data has been received
* @param data
* the data
* @param last
* whether this invocation completes the message
*
* @return a {@code CompletionStage} which completes when the
* {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
* reclaimed immediately
*/
default CompletionStage<?> onBinary(WebSocket webSocket,
ByteBuffer data,
boolean last) {
webSocket.request(1);
return null;
}
/**
* A Ping message has been received.
*
* <p> As guaranteed by the WebSocket Protocol, the message consists of
* not more than {@code 125} bytes. These bytes are located from the
* buffer's position to its limit.
*
* <p> Given that the WebSocket implementation will automatically send a
* reciprocal pong when a ping is received, it is rarely required to
* send a pong message explicitly when a ping is received.
*
* <p> Return a {@code CompletionStage} which will be used by the
* {@code WebSocket} as a signal it may reclaim the
* {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
* this {@code CompletionStage} has completed.
*
* @implSpec The default implementation is equivalent to:
* <pre>{@code webSocket.request(1);
* return null; }</pre>
*
* @param webSocket
* the WebSocket on which the message has been received
* @param message
* the message
*
* @return a {@code CompletionStage} which completes when the
* {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
* reclaimed immediately
*/
default CompletionStage<?> onPing(WebSocket webSocket,
ByteBuffer message) {
webSocket.request(1);
return null;
}
/**
* A Pong message has been received.
*
* <p> As guaranteed by the WebSocket Protocol, the message consists of
* not more than {@code 125} bytes. These bytes are located from the
* buffer's position to its limit.
*
* <p> Return a {@code CompletionStage} which will be used by the
* {@code WebSocket} as a signal it may reclaim the
* {@code ByteBuffer}. Do not access the {@code ByteBuffer} after
* this {@code CompletionStage} has completed.
*
* @implSpec The default implementation is equivalent to:
* <pre>{@code webSocket.request(1);
* return null; }</pre>
*
* @param webSocket
* the WebSocket on which the message has been received
* @param message
* the message
*
* @return a {@code CompletionStage} which completes when the
* {@code ByteBuffer} may be reclaimed; or {@code null} if it may be
* reclaimed immediately
*/
default CompletionStage<?> onPong(WebSocket webSocket,
ByteBuffer message) {
webSocket.request(1);
return null;
}
/**
* Receives a Close message indicating the WebSocket's input has been
* closed.
*
* <p> This is the last invocation from the specified {@code WebSocket}.
* By the time this invocation begins the WebSocket's input will have
* been closed.
*
* <p> A Close message consists of a status code and a reason for
* closing. The status code is an integer from the range
* {@code 1000 <= code <= 65535}. The {@code reason} is a string which
* has a UTF-8 representation not longer than {@code 123} bytes.
*
* <p> If the WebSocket's output is not already closed, the
* {@code CompletionStage} returned by this method will be used as an
* indication that the WebSocket's output may be closed. The WebSocket
* will close its output at the earliest of completion of the returned
* {@code CompletionStage} or invoking either of the {@code sendClose}
* or {@code abort} methods.
*
* @apiNote Returning a {@code CompletionStage} that never completes,
* effectively disables the reciprocating closure of the output.
*
* <p> To specify a custom closure code or reason code the
* {@code sendClose} method may be invoked from inside the
* {@code onClose} invocation:
* <pre>{@code public CompletionStage<?> onClose(WebSocket webSocket,
* int statusCode,
* String reason) {
* webSocket.sendClose(CUSTOM_STATUS_CODE, CUSTOM_REASON);
* return new CompletableFuture<Void>();
* } } </pre>
*
* @implSpec The default implementation of this method returns
* {@code null}, indicating that the output should be closed
* immediately.
*
* @param webSocket
* the WebSocket on which the message has been received
* @param statusCode
* the status code
* @param reason
* the reason
*
* @return a {@code CompletionStage} which completes when the
* {@code WebSocket} may be closed; or {@code null} if it may be
* closed immediately
*/
default CompletionStage<?> onClose(WebSocket webSocket,
int statusCode,
String reason) {
return null;
}
/**
* An error has occurred.
*
* <p> This is the last invocation from the specified WebSocket. By the
* time this invocation begins both the WebSocket's input and output
* will have been closed. A WebSocket may invoke this method on the
* associated listener at any time after it has invoked {@code onOpen},
* regardless of whether or not any invocations have been requested from
* the WebSocket.
*
* <p> If an exception is thrown from this method, resulting behavior is
* undefined.
*
* @param webSocket
* the WebSocket on which the error has occurred
* @param error
* the error
*/
default void onError(WebSocket webSocket, Throwable error) { }
}
/**
* Sends textual data with characters from the given character sequence.
*
* <p> The character sequence must not be modified until the
* {@code CompletableFuture} returned from this method has completed.
*
* <p> A {@code CompletableFuture} returned from this method can
* complete exceptionally with:
* <ul>
* <li> {@link IllegalStateException} -
* if there is a pending text or binary send operation
* or if the previous binary data does not complete the message
* <li> {@link IOException} -
* if an I/O error occurs, or if the output is closed
* </ul>
*
* @implNote If {@code data} is a malformed UTF-16 sequence, the operation
* will fail with {@code IOException}.
*
* @param data
* the data
* @param last
* {@code true} if this invocation completes the message,
* {@code false} otherwise
*
* @return a {@code CompletableFuture} that completes, with this WebSocket,
* when the data has been sent
*/
CompletableFuture<WebSocket> sendText(CharSequence data, boolean last);
/**
* Sends binary data with bytes from the given buffer.
*
* <p> The data is located in bytes from the buffer's position to its limit.
* Upon normal completion of a {@code CompletableFuture} returned from this
* method the buffer will have no remaining bytes. The buffer must not be
* accessed until after that.
*
* <p> The {@code CompletableFuture} returned from this method can
* complete exceptionally with:
* <ul>
* <li> {@link IllegalStateException} -
* if there is a pending text or binary send operation
* or if the previous textual data does not complete the message
* <li> {@link IOException} -
* if an I/O error occurs, or if the output is closed
* </ul>
*
* @param data
* the data
* @param last
* {@code true} if this invocation completes the message,
* {@code false} otherwise
*
* @return a {@code CompletableFuture} that completes, with this WebSocket,
* when the data has been sent
*/
CompletableFuture<WebSocket> sendBinary(ByteBuffer data, boolean last);
/**
* Sends a Ping message with bytes from the given buffer.
*
* <p> The message consists of not more than {@code 125} bytes from the
* buffer's position to its limit. Upon normal completion of a
* {@code CompletableFuture} returned from this method the buffer will
* have no remaining bytes. The buffer must not be accessed until after that.
*
* <p> The {@code CompletableFuture} returned from this method can
* complete exceptionally with:
* <ul>
* <li> {@link IllegalStateException} -
* if there is a pending ping or pong send operation
* <li> {@link IllegalArgumentException} -
* if the message is too long
* <li> {@link IOException} -
* if an I/O error occurs, or if the output is closed
* </ul>
*
* @param message
* the message
*
* @return a {@code CompletableFuture} that completes, with this WebSocket,
* when the Ping message has been sent
*/
CompletableFuture<WebSocket> sendPing(ByteBuffer message);
/**
* Sends a Pong message with bytes from the given buffer.
*
* <p> The message consists of not more than {@code 125} bytes from the
* buffer's position to its limit. Upon normal completion of a
* {@code CompletableFuture} returned from this method the buffer will have
* no remaining bytes. The buffer must not be accessed until after that.
*
* <p> Given that the WebSocket implementation will automatically send a
* reciprocal pong when a ping is received, it is rarely required to send a
* pong message explicitly.
*
* <p> The {@code CompletableFuture} returned from this method can
* complete exceptionally with:
* <ul>
* <li> {@link IllegalStateException} -
* if there is a pending ping or pong send operation
* <li> {@link IllegalArgumentException} -
* if the message is too long
* <li> {@link IOException} -
* if an I/O error occurs, or if the output is closed
* </ul>
*
* @param message
* the message
*
* @return a {@code CompletableFuture} that completes, with this WebSocket,
* when the Pong message has been sent
*/
CompletableFuture<WebSocket> sendPong(ByteBuffer message);
/**
* Initiates an orderly closure of this WebSocket's output by
* sending a Close message with the given status code and the reason.
*
* <p> The {@code statusCode} is an integer from the range
* {@code 1000 <= code <= 4999}. Status codes {@code 1002}, {@code 1003},
* {@code 1006}, {@code 1007}, {@code 1009}, {@code 1010}, {@code 1012},
* {@code 1013} and {@code 1015} are illegal. Behaviour in respect to other
* status codes is implementation-specific. A legal {@code reason} is a
* string that has a UTF-8 representation not longer than {@code 123} bytes.
*
* <p> A {@code CompletableFuture} returned from this method can
* complete exceptionally with:
* <ul>
* <li> {@link IllegalArgumentException} -
* if {@code statusCode} is illegal, or
* if {@code reason} is illegal
* <li> {@link IOException} -
* if an I/O error occurs, or if the output is closed
* </ul>
*
* <p> Unless the {@code CompletableFuture} returned from this method
* completes with {@code IllegalArgumentException}, or the method throws
* {@code NullPointerException}, the output will be closed.
*
* <p> If not already closed, the input remains open until a Close message
* {@linkplain Listener#onClose(WebSocket, int, String) received}, or
* {@code abort} is invoked, or an
* {@linkplain Listener#onError(WebSocket, Throwable) error} occurs.
*
* @apiNote Use the provided integer constant {@link #NORMAL_CLOSURE} as a
* status code and an empty string as a reason in a typical case:
* <pre>{@code CompletableFuture<WebSocket> webSocket = ...
* webSocket.thenCompose(ws -> ws.sendText("Hello, ", false))
* .thenCompose(ws -> ws.sendText("world!", true))
* .thenCompose(ws -> ws.sendClose(WebSocket.NORMAL_CLOSURE, ""))
* .join(); }</pre>
*
* The {@code sendClose} method does not close this WebSocket's input. It
* merely closes this WebSocket's output by sending a Close message. To
* enforce closing the input, invoke the {@code abort} method. Here is an
* example of an application that sends a Close message, and then starts a
* timer. Once no data has been received within the specified timeout, the
* timer goes off and the alarm aborts {@code WebSocket}:
* <pre>{@code MyAlarm alarm = new MyAlarm(webSocket::abort);
* WebSocket.Listener listener = new WebSocket.Listener() {
*
* public CompletionStage<?> onText(WebSocket webSocket,
* CharSequence data,
* boolean last) {
* alarm.snooze();
* ...
* }
* ...
* };
* ...
* Runnable startTimer = () -> {
* MyTimer idleTimer = new MyTimer();
* idleTimer.add(alarm, 30, TimeUnit.SECONDS);
* };
* webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "ok").thenRun(startTimer);
* } </pre>
*
* @param statusCode
* the status code
* @param reason
* the reason
*
* @return a {@code CompletableFuture} that completes, with this WebSocket,
* when the Close message has been sent
*/
CompletableFuture<WebSocket> sendClose(int statusCode, String reason);
/**
* Increments the counter of invocations of receive methods.
*
* <p> This WebSocket will invoke {@code onText}, {@code onBinary},
* {@code onPing}, {@code onPong} or {@code onClose} methods on the
* associated listener (i.e. receive methods) up to {@code n} more times.
*
* @apiNote The parameter of this method is the number of invocations being
* requested from this WebSocket to the associated listener, not the number
* of messages. Sometimes a message may be delivered to the listener in a
* single invocation, but not always. For example, Ping, Pong and Close
* messages are delivered in a single invocation of {@code onPing},
* {@code onPong} and {@code onClose} methods respectively. However, whether
* or not Text and Binary messages are delivered in a single invocation of
* {@code onText} and {@code onBinary} methods depends on the boolean
* argument ({@code last}) of these methods. If {@code last} is
* {@code false}, then there is more to a message than has been delivered to
* the invocation.
*
* <p> Here is an example of a listener that requests invocations, one at a
* time, until a complete message has been accumulated, and then processes
* the result:
* <pre>{@code WebSocket.Listener listener = new WebSocket.Listener() {
*
* StringBuilder text = new StringBuilder();
*
* public CompletionStage<?> onText(WebSocket webSocket,
* CharSequence message,
* boolean last) {
* text.append(message);
* if (last) {
* processCompleteTextMessage(text);
* text = new StringBuilder();
* }
* webSocket.request(1);
* return null;
* }
* ...
* } } </pre>
*
* @param n
* the number of invocations
*
* @throws IllegalArgumentException
* if {@code n <= 0}
*/
void request(long n);
/**
* Returns the subprotocol used by this WebSocket.
*
* @return the subprotocol, or an empty string if there's no subprotocol
*/
String getSubprotocol();
/**
* Tells whether this WebSocket's output is closed.
*
* <p> If this method returns {@code true}, subsequent invocations will also
* return {@code true}.
*
* @return {@code true} if closed, {@code false} otherwise
*/
boolean isOutputClosed();
/**
* Tells whether this WebSocket's input is closed.
*
* <p> If this method returns {@code true}, subsequent invocations will also
* return {@code true}.
*
* @return {@code true} if closed, {@code false} otherwise
*/
boolean isInputClosed();
/**
* Closes this WebSocket's input and output abruptly.
*
* <p> When this method returns both the input and the output will have been
* closed. Any pending send operations will fail with {@code IOException}.
* Subsequent invocations of {@code abort} will have no effect.
*/
void abort();
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 java.net.http;
import java.io.IOException;
/**
* An exception used to signal the opening handshake failed.
*
* @since 11
*/
public final class WebSocketHandshakeException extends IOException {
private static final long serialVersionUID = 1L;
private final transient HttpResponse<?> response;
/**
* Constructs a {@code WebSocketHandshakeException} with the given
* {@code HttpResponse}.
*
* @param response
* the {@code HttpResponse} that resulted in the handshake failure
*/
public WebSocketHandshakeException(HttpResponse<?> response) {
this.response = response;
}
/**
* Returns the server's counterpart of the opening handshake.
*
* <p> The value may be unavailable ({@code null}) if this exception has
* been serialized and then deserialized.
*
* @return server response
*/
public HttpResponse<?> getResponse() {
return response;
}
@Override
public WebSocketHandshakeException initCause(Throwable cause) {
return (WebSocketHandshakeException) super.initCause(cause);
}
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
/**
* <h2>HTTP Client and WebSocket APIs</h2>
*
* <p> Provides high-level client interfaces to HTTP (versions 1.1 and 2) and
* low-level client interfaces to WebSocket. The main types defined are:
*
* <ul>
* <li>{@link java.net.http.HttpClient}</li>
* <li>{@link java.net.http.HttpRequest}</li>
* <li>{@link java.net.http.HttpResponse}</li>
* <li>{@link java.net.http.WebSocket}</li>
* </ul>
*
* <p> The protocol-specific requirements are defined in the
* <a href="https://tools.ietf.org/html/rfc7540">Hypertext Transfer Protocol
* Version 2 (HTTP/2)</a>, the <a href="https://tools.ietf.org/html/rfc2616">
* Hypertext Transfer Protocol (HTTP/1.1)</a>, and
* <a href="https://tools.ietf.org/html/rfc6455">The WebSocket Protocol</a>.
*
* <p> Asynchronous tasks and dependent actions of returned {@link
* java.util.concurrent.CompletableFuture} instances are executed on the threads
* supplied by the client's {@link java.util.concurrent.Executor}, where
* practical.
*
* <p> {@code CompletableFuture}s returned by this API will throw {@link
* java.lang.UnsupportedOperationException} for their {@link
* java.util.concurrent.CompletableFuture#obtrudeValue(Object) obtrudeValue}
* and {@link java.util.concurrent.CompletableFuture#obtrudeException(Throwable)
* obtrudeException} methods. Invoking the {@link
* java.util.concurrent.CompletableFuture#cancel cancel} method on a {@code
* CompletableFuture} returned by this API will not interrupt the underlying
* operation, but may be useful to complete, exceptionally, dependent stages
* that have not already completed.
*
* <p> Unless otherwise stated, {@code null} parameter values will cause methods
* of all classes in this package to throw {@code NullPointerException}.
*
* @since 11
*/
package java.net.http;