8204679: HTTP Client refresh

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
This commit is contained in:
Chris Hegarty 2018-06-20 09:05:57 -07:00
parent 8c5dfa21b3
commit 659fdd8dc4
161 changed files with 8019 additions and 1853 deletions

View file

@ -57,7 +57,7 @@ import jdk.internal.net.http.HttpClientBuilderImpl;
* and can be used to send multiple requests.
*
* <p> An {@code HttpClient} provides configuration information, and resource
* sharing, for all requests send through it.
* sharing, for all requests sent through it.
*
* <p> A {@link BodyHandler BodyHandler} must be supplied for each {@link
* HttpRequest} sent. The {@code BodyHandler} determines how to handle the
@ -232,11 +232,10 @@ public abstract class HttpClient {
*
* <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.
* HttpClient}.
*
* @implNote If a security manager has been installed, the thread
* @implNote The default executor uses a thread pool, with a custom
* thread factory. If a security manager has been installed, the thread
* factory creates threads that run with an access control context that
* has no permissions.
*
@ -451,7 +450,7 @@ public abstract class HttpClient {
* 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
* <p> {@code Redirect} policy is set through the {@linkplain
* HttpClient.Builder#followRedirects(Redirect) Builder.followRedirects}
* method.
*

View file

@ -25,62 +25,68 @@
package java.net.http;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
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 java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.BiPredicate;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
import static java.util.Collections.unmodifiableMap;
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> An {@code HttpHeaders} is not typically created directly, but rather
* returned from an {@link HttpRequest#headers() HttpRequest} or an
* {@link HttpResponse#headers() HttpResponse}. Specific HTTP headers can be
* set for a {@linkplain HttpRequest request} through 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.
* {@code Map} returned by the {@link #map() map} method, operate without regard
* to case when retrieving the header value(s).
*
* <p> An HTTP header name may appear more than once in the HTTP protocol. As
* such, headers are represented as a name and a list of values. Each occurrence
* of a header value is added verbatim, to the appropriate header name list,
* without interpreting its value. In particular, {@code HttpHeaders} does not
* perform any splitting or joining of comma separated header value strings. The
* order of elements in a header value list is preserved when {@link
* HttpRequest.Builder#header(String, String) building} a request. For
* responses, the order of elements in a header value list is the order in which
* they were received. The {@code Map} returned by the {@code map} method,
* however, does not provide any guarantee with regard to the ordering of its
* entries.
*
* <p> {@code HttpHeaders} instances are immutable.
*
* @since 11
*/
public abstract class HttpHeaders {
public final 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()}
* Returns an {@link Optional} containing the first header string value of
* the given named (and possibly multi-valued) header. If the header is not
* present, then the returned {@code Optional} is empty.
*
* @param name the header name
* @return an {@code Optional<String>} for the first named value
* @return an {@code Optional<String>} containing the first named header
* string value, if present
*/
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()}
* Returns an {@link OptionalLong} containing the first header string 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.
*
* @param name the header name
* @return an {@code OptionalLong}
@ -92,23 +98,19 @@ public abstract class HttpHeaders {
}
/**
* 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.
* Returns an unmodifiable List of all of the header string values of the
* given named header. Always returns a List, which may be empty if the
* header is not present.
*
* @param name the header name
* @return a List of String values
* @return a List of headers 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());
return values != null ? values : List.of();
}
/**
@ -116,7 +118,9 @@ public abstract class HttpHeaders {
*
* @return the Map
*/
public abstract Map<String, List<String>> map();
public Map<String,List<String>> map() {
return headers;
}
/**
* Tests this HTTP headers instance for equality with the given object.
@ -149,7 +153,11 @@ public abstract class HttpHeaders {
* @return the hash-code value for this HTTP headers
*/
public final int hashCode() {
return map().hashCode();
int h = 0;
for (Map.Entry<String, List<String>> e : map().entrySet()) {
h += entryHash(e);
}
return h;
}
/**
@ -165,4 +173,93 @@ public abstract class HttpHeaders {
sb.append(" }");
return sb.toString();
}
/**
* Returns an HTTP headers from the given map. The given map's key
* represents the header name, and its value the list of string header
* values for that header name.
*
* <p> An HTTP header name may appear more than once in the HTTP protocol.
* Such, <i>multi-valued</i>, headers must be represented by a single entry
* in the given map, whose entry value is a list that represents the
* multiple header string values. Leading and trailing whitespaces are
* removed from all string values retrieved from the given map and its lists
* before processing. Only headers that, after filtering, contain at least
* one, possibly empty string, value will be added to the HTTP headers.
*
* @apiNote The primary purpose of this method is for testing frameworks.
* Per-request headers can be set through one of the {@code HttpRequest}
* {@link HttpRequest.Builder#header(String, String) headers} methods.
*
* @param headerMap the map containing the header names and values
* @param filter a filter that can be used to inspect each
* header-name-and-value pair in the given map to determine if
* it should, or should not, be added to the to the HTTP
* headers
* @return an HTTP headers instance containing the given headers
* @throws NullPointerException if any of: {@code headerMap}, a key or value
* in the given map, or an entry in the map's value list, or
* {@code filter}, is {@code null}
* @throws IllegalArgumentException if the given {@code headerMap} contains
* any two keys that are equal ( without regard to case ); or if the
* given map contains any key whose length, after trimming
* whitespaces, is {@code 0}
*/
public static HttpHeaders of(Map<String,List<String>> headerMap,
BiPredicate<String,String> filter) {
requireNonNull(headerMap);
requireNonNull(filter);
return headersOf(headerMap, filter);
}
// --
private static final HttpHeaders NO_HEADERS = new HttpHeaders(Map.of());
private final Map<String,List<String>> headers;
private HttpHeaders(Map<String,List<String>> headers) {
this.headers = headers;
}
private static final int entryHash(Map.Entry<String, List<String>> e) {
String key = e.getKey();
List<String> value = e.getValue();
// we know that by construction key and values can't be null
int keyHash = key.toLowerCase(Locale.ROOT).hashCode();
int valueHash = value.hashCode();
return keyHash ^ valueHash;
}
// Returns a new HTTP headers after performing a structural copy and filtering.
private static HttpHeaders headersOf(Map<String,List<String>> map,
BiPredicate<String,String> filter) {
TreeMap<String,List<String>> other = new TreeMap<>(CASE_INSENSITIVE_ORDER);
TreeSet<String> notAdded = new TreeSet<>(CASE_INSENSITIVE_ORDER);
ArrayList<String> tempList = new ArrayList<>();
map.forEach((key, value) -> {
String headerName = requireNonNull(key).trim();
if (headerName.isEmpty()) {
throw new IllegalArgumentException("empty key");
}
List<String> headerValues = requireNonNull(value);
headerValues.forEach(headerValue -> {
headerValue = requireNonNull(headerValue).trim();
if (filter.test(headerName, headerValue)) {
tempList.add(headerValue);
}
});
if (tempList.isEmpty()) {
if (other.containsKey(headerName)
|| notAdded.contains(headerName.toLowerCase(Locale.ROOT)))
throw new IllegalArgumentException("duplicate key: " + headerName);
notAdded.add(headerName.toLowerCase(Locale.ROOT));
} else if (other.put(headerName, List.copyOf(tempList)) != null) {
throw new IllegalArgumentException("duplicate key: " + headerName);
}
tempList.clear();
});
return other.isEmpty() ? NO_HEADERS : new HttpHeaders(unmodifiableMap(other));
}
}

View file

@ -89,10 +89,12 @@ public abstract class HttpRequest {
* <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
* <p> The builder can be used to configure per-request state, such as: the
* request URI, the request method (default is GET unless explicitly set),
* specific request headers, etc. 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.
*

View file

@ -830,13 +830,13 @@ public interface HttpResponse<T> {
* BodySubscriber} provides implementations of many common body subscribers.
*
* <p> The object acts as a {@link Flow.Subscriber}&lt;{@link List}&lt;{@link
* ByteBuffer}&gt;&gt; to the HTTP client implementation, which publishes
* unmodifiable lists of read-only ByteBuffers containing the response body.
* The Flow of data, as well as the order of ByteBuffers in the Flow lists,
* is a strictly ordered representation of the response body. Both the Lists
* and the ByteBuffers, once passed to the subscriber, are no longer used by
* the HTTP client. The subscriber converts the incoming buffers of data to
* some higher-level Java type {@code T}.
* ByteBuffer}&gt;&gt; to the HTTP Client implementation, which publishes
* lists of ByteBuffers containing the response body. The Flow of data, as
* well as the order of ByteBuffers in the Flow lists, is a strictly ordered
* representation of the response body. Both the Lists and the ByteBuffers,
* once passed to the subscriber, are no longer used by the HTTP Client. The
* subscriber converts the incoming buffers of data to some higher-level
* Java type {@code T}.
*
* <p> The {@link #getBody()} method returns a
* {@link CompletionStage}&lt;{@code T}&gt; that provides the response body
@ -859,6 +859,9 @@ public interface HttpResponse<T> {
* may cause the underlying HTTP connection to be closed and prevent it
* from being reused for subsequent operations.
*
* @implNote The flow of data containing the response body is immutable.
* Specifically, it is a flow of unmodifiable lists of read-only ByteBuffers.
*
* @param <T> the response body type
* @see BodySubscribers
* @since 11
@ -888,20 +891,20 @@ public interface HttpResponse<T> {
*
* <pre>{@code // Streams the response body to a File
* HttpResponse<byte[]> response = client
* .send(request, (statusCode, responseHeaders) -> BodySubscribers.ofByteArray());
* .send(request, responseInfo -> BodySubscribers.ofByteArray());
*
* // Accumulates the response body and returns it as a byte[]
* HttpResponse<byte[]> response = client
* .send(request, (statusCode, responseHeaders) -> BodySubscribers.ofByteArray());
* .send(request, responseInfo -> BodySubscribers.ofByteArray());
*
* // Discards the response body
* HttpResponse<Void> response = client
* .send(request, (statusCode, responseHeaders) -> BodySubscribers.discarding());
* .send(request, responseInfo -> BodySubscribers.discarding());
*
* // Accumulates the response body as a String then maps it to its bytes
* HttpResponse<byte[]> response = client
* .send(request, (sc, hdrs) ->
* BodySubscribers.mapping(BodySubscribers.ofString(), String::getBytes));
* .send(request, responseInfo ->
* BodySubscribers.mapping(BodySubscribers.ofString(UTF_8), String::getBytes));
* }</pre>
*
* @since 11

View file

@ -35,9 +35,9 @@ import java.util.concurrent.CompletionStage;
/**
* A WebSocket Client.
*
* <p> {@code WebSocket} instances can be created via {@link WebSocket.Builder}.
* <p> {@code WebSocket} instances are created through {@link WebSocket.Builder}.
*
* <p> WebSocket has an input and an output sides. These sides are independent
* <p> WebSocket has an input and an output side. 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
@ -55,21 +55,22 @@ import java.util.concurrent.CompletionStage;
*
* <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.
* {@code Listener}. WebSocket initiates a receive operation by invoking a
* receive method on the listener. The listener then must return 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> To control receiving of messages, 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.
*
* <p> Unless otherwise stated, {@code null} arguments will cause methods
* of {@code WebSocket} to throw {@code NullPointerException}, similarly,
@ -105,13 +106,13 @@ public interface WebSocket {
/**
* 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.
* <p> Builders are created by invoking
* {@link HttpClient#newWebSocketBuilder HttpClient.newWebSocketBuilder}.
* 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
*/
@ -155,7 +156,7 @@ public interface WebSocket {
* Sets a request for the given subprotocols.
*
* <p> After the {@code WebSocket} has been built, the actual
* subprotocol can be queried via
* subprotocol can be queried through
* {@link WebSocket#getSubprotocol WebSocket.getSubprotocol()}.
*
* <p> Subprotocols are specified in the order of preference. The most
@ -218,11 +219,17 @@ public interface WebSocket {
* 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.
* passing itself as an argument. These methods are invoked in a thread-safe
* manner, such that the next invocation may start only after the previous
* one has finished.
*
* <p> 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. 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.
*
* <p> An {@code IOException} raised in {@code WebSocket} will result in an
* invocation of {@code onError} with that exception (if the input is not
@ -231,11 +238,14 @@ public interface WebSocket {
* 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 The strict sequential order of invocations from
* {@code WebSocket} to {@code Listener} means, in particular, that the
* {@code Listener}'s methods are treated as non-reentrant. This means that
* {@code Listener} implementations do not need to be concerned with
* possible recursion or the order in which they invoke
* {@code WebSocket.request} in relation to their processing logic.
*
* @apiNote Careful attention may be required if a listener is associated
* <p> 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.

View file

@ -28,7 +28,7 @@ package java.net.http;
import java.io.IOException;
/**
* An exception used to signal the opening handshake failed.
* Thrown when the opening handshake has failed.
*
* @since 11
*/
@ -55,6 +55,10 @@ public final class WebSocketHandshakeException extends IOException {
* <p> The value may be unavailable ({@code null}) if this exception has
* been serialized and then deserialized.
*
* @apiNote The primary purpose of this method is to allow programmatic
* examination of the reasons behind the failure of the opening handshake.
* Some of these reasons might allow recovery.
*
* @return server response
*/
public HttpResponse<?> getResponse() {

View file

@ -42,20 +42,24 @@
* 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> In general, asynchronous tasks execute in either the thread invoking
* the operation, e.g. {@linkplain HttpClient#send(HttpRequest, BodyHandler)
* sending} an HTTP request, or by the threads supplied by the client's {@link
* HttpClient#executor() executor}. Dependent tasks, those that are triggered by
* returned CompletionStages or CompletableFutures, that do not explicitly
* specify an executor, execute in the same {@link
* CompletableFuture#defaultExecutor() default executor} as that of {@code
* CompletableFuture}, or the invoking thread if the operation completes before
* the dependent task is registered.
*
* <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.
* UnsupportedOperationException} for their {@link
* CompletableFuture#obtrudeValue(Object) obtrudeValue}
* and {@link CompletableFuture#obtrudeException(Throwable)
* obtrudeException} methods. Invoking the {@link CompletableFuture#cancel
* cancel} method on a {@code CompletableFuture} returned by this API may 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}.
@ -63,3 +67,9 @@
* @since 11
*/
package java.net.http;
import java.lang.UnsupportedOperationException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandler;
import java.util.concurrent.CompletableFuture;