HTTP 2 client API
Simone Bordet
simone.bordet at gmail.com
Fri Jul 31 15:06:03 UTC 2015
Hi,
On Fri, Jul 31, 2015 at 3:56 PM, Michael McMahon
<michael.x.mcmahon at oracle.com> wrote:
> Ok. Now, on the whole flow-control issue, here is my thinking.
> First that isFast() method is a relic from a previous version and should not
> have been there.
>
> My thinking is that the body processing methods of the response
> body processor should be simply allowed to block. That exerts back pressure
> in HTTP/1 by the socket buffer filling up and TCP flow control kicking in.
> For HTTP/2, the protocol's own buffering and window management functions
> achieve the same
> thing.
Sure, if an application use blocking APIs, backpressure it achieved.
But as a generic library designer, it's not possible to know if the
application will use blocking APIs. If they don't want to, they can't
use JDK's HttpClient.
That may be ok, but would be a shame.
I brought this up because it's an API design issue, not something that
you can change with a better implementation while keeping the same
APIs.
If the API supports an asynchronous mode, it's trivial to implement a
blocking mode, see for example:
https://github.com/eclipse/jetty.project/blob/master/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java#L238-L255
> For sending, we already control the rate of outgoing data by how regularly
> the implementation pulls data from the processor.
But you may need to notify the producer asynchronously.
Imagine you have HttpClient1 downloading data from server1, and
HttpClient2 that must upload this data to server2.
How would you do this with the current APIs in a completely asynchronous way ?
> Yes, the API is designed that responses are handled in two stages
> response headers and then response body.
>
> Using the asynchronous API it is easy to create a composition of
> the two stages and have one CompletableFuture object complete
> after both stages. Probably, the most useful will be CompletableFuture<T>
> where T is the actual body type. Similar can be achieved with the blocking
> API.
I'm not sure you got my point, I apologize if I did not understand your answer.
The issue is not about the 2 stages of a response.
It's about *request* and *response*, together as an exchange,
completeness, for which there is no API.
Bad things happens when the response is complete and you think the
request is completed too, but it's not.
I'm not saying it's impossible to code this with the current APIs, but
I wanted to raise the issue.
Example: client uploads a video of 500 GiB using a single mapped
buffer from a file over a slow connection;
server has a bug and throws a NPE while reading the first content byte;
the NPE is converted to a HTTP response 500, which is downloaded
immediately to the client;
the client receives the full response 500 with its body, but the
request is still uploading the file.
An application would be interested in knowing when the whole exchange
(request + response) is complete, not when the response alone is
complete.
An application may also want to apply timeout to the whole exchange
(see below re: timeouts).
> Cancellation is part of the CompletableFuture API
But it has the wrong semantic, if you refer to Future.cancel(boolean).
Future.cancel(boolean) does not have any effect on the underlying
operation (in this case if the request has already started).
> but not in the blocking API currently. HTTP/2 should be
> able to implement it by resetting streams.
See above it's a semantic problem.
If you implement it respecting the Future semantic, it's not the right
semantic for HttpClient, and viceversa.
> I think the docs could use a simple example, but I thought
> it seemed a useful addition.
>
> eg builder.headers("Foo", "foovalue", "Bar", "barvalue).
>
> The effect is additive. So, there can be multiple values
> for any header name
How do you know if a string is a name or a value ?
builder.headers("Foo", "Bar", "Baz")
sets header Foo with values Bar and Baz, or sets header Foo with value
Bar, and header Baz with value... what ?
IMHO the API is confusing, I prefer just doing builder.header("Foo",
"foovalue").header("Bar", "barvalue").
>> == Timeouts
>> There are no definition of timeouts, including idle timeouts and
>> exchange timeouts ?
>
>
> Timeouts are all handled by the CompletableFuture API
But again they have the wrong semantic, if you refer to
Future.get(long, TimeUnit).
The expiration of that timeout does not mean that the request is
cancelled or anything else.
It just means that that amount of time has passed.
You can happily write:
while (true)
{
try
{
future.get(1, SECONDS);
break;
}
catch (TimeoutException x)
{
// Not done yet, try again.
}
}
without implying any effect on the actual underlying operation.
This means that the timeout expires, but the request is not cancelled,
and if it is never responded, it will occupy resources forever.
--
Simone Bordet
http://bordet.blogspot.com
---
Finally, no matter how good the architecture and design are,
to deliver bug-free software with optimal performance and reliability,
the implementation technique must be flawless. Victoria Livschitz
More information about the net-dev
mailing list