HTTP 2 client API
Anthony Vanelverdinghe
anthony.vanelverdinghe at gmail.com
Sat Mar 21 10:44:46 UTC 2015
Hi Michael
Please find my feedback on the current API docs below. It's divided in 3
parts: API (mostly questions), documentation (mostly suggestions for
clarifications) and spelling.
Thanks in advance for your consideration.
Kind regards, Anthony
=== API ===
* will there be support for HTTP 2.0 stream prioritization? For example
by providing a method requestHttp2(int priority) in
HttpClient.Builder/HttpRequest.Builder? This way one could create 2
clients with a different priority: a low-priority one for background
downloads of documents/large files, and a high-priority one for status
updates. Then the 2 clients would use the same TCP connection, but
different streams with different priorities.
* which convenience methods will be added to existing classes, if any?
I'm particularly thinking of URL, which already has
openConnection/openStream convenience methods for the old API.
* how is compression (the
Accept-Encoding/Content-Encoding/Transfer-Encoding headers) handled? Is
this handled transparently by the HttpClient? For example, if I request
a text document, will the client automatically send a header
"Accept-Encoding: gzip, deflate" (this is the current default in
Firefox)? And once I receive the response, transparently decompress it,
so I can just do "response.body(asString())"?
* how is caching handled? Is there anything analog to "useCaches" in
java.net.URLConnection?
* HttpClient.Builder: concerning "followRedirects", I feel there are 2
more options that ought to be possible to be set: "same-protocol" (i.e.
the current java.net.HttpURLConnection behavior) and "secure" (all
redirects are allowed, except for redirects from https to http). A
possible solution might be to introduce a "RedirectPolicy" enum with 4
constants: NEVER, ALWAYS, SAME_PROTOCOL, SECURE. Then the method would
become "followRedirects(RedirectPolicy policy)".
* HttpClient.Builder: the documentation for "requestHttp2" says: "If
that fails, then following requests will not attempt to upgrade again."
How is it determined that the upgrade to HTTP/2 fails? For example, is
the client able to distinguish "the upgrade to HTTP/2 failed" from "some
error occured which caused the request to fail" (e.g. the server is
down)? And how can I force to attempt to upgrade again, for a specific
origin and/or all origins? For example in the case of application
servers, having to restart the JVM just to have the HTTP/2 upgrade to be
attempted again, seems rather harsh.
* HttpHeaders: why is the method "firstValueAsInt" and not
"firstValueAsLong"? If I want to download e.g. the .iso file of Oracle
Linux, the value of Content-Length will be too big and a
NumberFormatException will be thrown.
* HttpHeaders: why is there only a special case for "firstValueAsInt"?
Personally, I would like some more methods to be added:
** java.time.OffsetDateTime firstValueAsDate(String name,
Supplier<OffsetDateTime> defaultValue) which parses headers such as
Expired & Last-Modified, using the datetime format recommended by RFC
7231 (i.e. the format defined in RFC 5322)
** javax.activation.MimeType firstValueAsMimeType(String name,
Supplier<MimeType> defaultValue) which parses headers such as
Content-Type & Accept-Patch
** List<java.net.HttpCookie> allCookies() which returns a list of all
cookies
* HttpHeaders: for the proposed first*() methods, please also consider
the signature: Optional<T> firstValueAsT(String name). This way, the
application developer can decide whether to use a default value or to
throw an exception (or anything else) when the header is absent.
* HttpRequest/HttpResponse: why weren't the static methods added to the
interfaces
(HttpRequestBodyProcessor/HttpResponseBodyProcessor/MultiResponseProcessor)
instead? In my opinion, this would be more logical, and would simplify
the API of HttpRequest/HttpResponse.
* HttpRequest.Builder: add validation to header/method/setHeader
(according to the rules in RFC 7230) & throw an IllegalArgumentException
if validation fails
* HttpRequest.Builder: for "method", how is the parameter handled? Since
method is case-sensitive ( cf.
https://tools.ietf.org/html/rfc7230#section-3.1.1 ), creating a request
with "delete" may fail, simply because it should be "DELETE". I think it
would be good to at least add a Javadoc note about the case-sensitivity,
and that the standardized methods (
http://tools.ietf.org/html/rfc7231#section-4.1 ) are defined as
all-uppercase.
* HttpRequest: I'd propose to rename fromByteArray(Iterator<byte[]>) to
fromByteArrays
* HttpRequest::fromString and HttpResponse::asString should take a
java.nio.charset.Charset as parameter
* HttpResponse: in my opinion, "asFile" should take a Path as parameter
& the parameter name should be "file", instead of "filename"
* HttpResponse: concerning the "asString" methods: they refer to "the
character set specified in the Content-encoding response header".
However, this should be "the character set specified in the charset
parameter of the Content-type response header".
* WebSocket: the documentation says "Once the WebSocket is available,
WebSocketMessages can be created and sent either blocking or
asynchronously.". What does it mean to create a message asynchronously?
And how can I send a message asynchronously (without manually creating a
CompletableFuture), as there isn't a "sendAsync" method in WebSocket?
* WebSocket: in "Asynchronous example", I believe "sockets" should be
defined as "new CopyOnWriteArrayList<>()", since LinkedList is not
thread-safe? Also, I personally would declare "futures" as "new
ArrayList<>()" (unless there's a compelling reason to use LinkedList of
which I'm unaware?).
* please consider adding a HttpResponseBodyProcessor implementation
"asDefined()", which uses the same mechanism as
java.net.URLConnection::getContent (i.e. using the Content-Type header &
ContentHandlers) to determine the object to return. This would allow for
easy migration from the old API to the new API. (the "defined" in the
method name refers to the fact that the value of the Content-Type header
is used)
* please consider adding a HttpResponseBodyProcessor implementation
"asFileDownload(Path downloadDirectory, OpenOption... openOptions)".
This would determine the filename automatically, exactly as browsers do
by inspecting e.g. the Content-Disposition header.
=== documentation ===
* as I'm sure you are aware, the package Javadoc should be updated to
document the new API. I also think it would be good to clarify its
relation to the old API, JAX-RS and Java API for WebSocket (the latter 2
of which will, I assume, in future versions use this API as the basis
for their Client API implementations).
* HttpClient: "Request builders are then created by calling request(URI)
if associated with an application created client."
rephrase as "Request builders that are associated with an
application-created client, are created by calling request(URI)."
* HttpClient.Builder: "If set, the first request to an origin server
using "http" URLs will attempt to upgrade to HTTP version 2. If that
fails, then following requests will not attempt to upgrade again."
** make "origin server" a hyperlink to
https://tools.ietf.org/html/rfc6454#section-4
** explicitly state "following requests to the same origin server"
* HttpRequest: "A request's uri, headers and body can be set."
change "uri" to "URI" & link it to java.net.URI
* HttpRequest: currently, all examples specify ".body(noBody())". This
gives the impression it's actually required. I propose to remove this
from all examples of GET requests (especially the one in the "Two
simple, example HTTP interactions" at the top).
* HttpRequest::fromString and HttpResponse::asString: replace
"ISO8859_1" with ISO-8859-1 and link to
java.nio.charset.StandardCharsets.ISO_8859_1
* HttpResponse: "such as String, byte arrays, Files."
change "Files" to "files", since "Files" suggests that it returns
java.io.File, when instead it's java.nio.file.Path
* HttpResponseBodyProcessor: "write responses to String, byte[], File,
Consumer<byte[]>"
change "File" to "file", since "File" suggests that it returns
java.io.File, when instead it's java.nio.file.Path
* HttpResponseBodyProcessor: "If an exact content length was provided in
onRequestStart(), then that number of bytes must be provided."
explicitly add "before returning null" at the end, so: "If an exact
content length was provided in onRequestStart(), then that number of
bytes must be provided before returning null."
* WebSocket: add a note that using a WebSocket in a try-with-resources
statement will cause the close to be done by closing the underlying TCP
connection & what the possible implications are.
=== spelling ===
* HttpHeaders: "read only"
should be "read-only"
* HttpRequest: "any type as body,"
either has missing text after the comma, or the comma should be a point
* HttpRequest: "client initiated"
should be "client-initiated"
* HttpRequest: "The response body can then be received (either
synchronous or asynchronously)"
should be "The response body can then be received (either synchronously
or asynchronously)."
* HttpRequest: "Path body = r2.body(asFile("/tmp/response.txt));"
should be "Path body = r2.body(asFile("/tmp/response.txt"));"
* HttpRequest: "All of above examples"
should be "All of the above examples"
* HttpRequest: "CompletableFuture<String>; last ="
should be "CompletableFuture<String> last ="
* HttpRequest: "Returns the HttpClient.requestHttp2() setting for this
request."
should note that this setting may have been overridden using
HttpRequest.Builder.requestHttp2(), and therefore is not always equal to
HttpClient.requestHttp2()
* HttpRequest.Builder: "A builder of HttpRequests"
should be "A builder of HttpRequests." (if you look at the package
overview, all classes except this one have a point at the end)
* HttpResponseBodyProcessor: "return null and the body"
has missing text after body
* MultiResponseProcessor: "provides the"
should be "Provides the"
* WebSocketMessage: "A WebSocketMessages (Binary or Text)"
should be "A WebSocketMessage (Binary or Text)"
On 9/03/2015 17:27, Michael McMahon wrote:
> Hi,
>
> JEP 110 HTTP 2 client
>
> in JDK 9, is defining and implementing a new API for HTTP which also
> supports
> the new HTTP version 2 that has recently been working its way through
> the IETF.
> The work also includes support for websockets (RFC 6455).
>
> In fact, the majority of the API is agnostic about the HTTP protocol
> version, with only minor
> configuration settings, and support for multiple responses (Http
> server push) having any direct impact.
>
> The HTTP API is defined around three main types (HttpClient, which is
> the central
> point for configuration of SSL, executor service cookie management
> etc), HttpRequest
> and HttpResponse (which should be self explanatory).
>
> Requests are sent/received either synchronously (blocking) or in a
> non-blocking (asynchronous)
> mode using java.util.future.CompletableFuture which is a powerful new
> framework for
> asynchronous execution introduced in JDK 8.
>
> The API docs can be seen at the link below:
>
> http://cr.openjdk.java.net/~michaelm/httpclient/01/
>
> All new classes and interfaces belong to the java.net package.
>
> A prototype implementation of this API supporting HTTP/1.1 only, is
> available and will
> be uploaded into the JDK 9 sandbox forest in the coming day or two.
>
> Comments welcome!
>
> Thanks,
> Michael.
>
More information about the net-dev
mailing list