Re: Ambiguity on HTTP/3 HEADERS and QUIC STREAM FIN requirement

On Fri, Jun 17, 2022, at 15:37, Willy Tarreau wrote:
>> The HEADERS frame has ended in this case, so you have a clear indication that
>> you have all the headers.
>
> Not exactly. In HTTP/1 we used to have Transfer-Encoding which was a
> connection-level header field to bridge the gap between what was explicitly
> advertised in headers and what could have been ambiguous at the connection
> level (such as receiving a FIN late). 

Ah, that is a property of HTTP/1.1 that ensures that - when using Content-Length - once you have the headers, you know if you have the whole thing (or at least how much more stuff to expect).  That's not true with Transfer-Encoding: chunked or those nasty requests that end when the connection closes.

HTTP/3 uses something like TCP connection closing to terminate requests, for which I can see how that might seem awkward, but the differences between how you deal with TCP closures and QUIC stream ending probably make the latter easier to deal with.

> And in H2, it's not the
> same to send a HEADERS + ES and a HEADERS followed by DATA+ES. The first
> one doesn't have a body, the second one has an empty body. 

I never thought that that distinction mattered.  I'm surprised to see you claim that this is the case.  I understand that you might use different strategies for forwarding the two, but semantically, I don't think there is a difference.

> With H3 you have neither the transfer-encoding header nor the ES bit on
> the frame to indicate that presence/absence. The only indication that
> matches the H2 ES is the QUIC FIN that also signals the end of stream,
> albeit at a lower level. That's why I think we've slowly deviated from
> something very explict (H1) to something subtly explicit (H2) then
> something ambiguous (H3).

I see this differently.  While I can see how you might find this annoying, this is much the same as the HTTP/2 case.  Sometimes you know the stream ended after HEADERS is done, sometimes you don't.  That this comes from a different layer of the stack adds a bit of complexity (you basically have to peek to see if the FIN is there), but if the request is done, you should be able to access that information.

> Paying the cost of making two ends understand each other is the daily
> job of a gateway :-)  Regardless it's also the one that takes all the
> dirty stuff in the face and it needs to be robust by design. My concern
> here precisely is that waiting will both make it less robust *and* will
> possibly not work with some clients which forget to send their FIN.

It's not OK to forget entirely, so I would support taking action against those clients that forget (maybe constant connection drops or ending up in a tar pit will motivate them to fix that problem).  This is more about sending the two pieces separately, which is legitimate, but annoying.

For me, I'd say "send an email to the developers of that client" if you start seeing problems often.

>> But if you take the fact that you have a clear signal that the headers are
>> done, you can - even as a gateway - make some decisions.  It might not be
>> 100% safe, but I can't see any origin servers complaining if you started
>> processing from that point, for GET and HEAD requests at least.
>
> Sadly that's not even true :-(  We've seen recently, I think it was
> Elastic Search that takes JSON requests sent as the body of a GET
> request. So now that we managed to better define the presence/absence
> of a body in a request, we're back trying to guess it with a certain
> probability based on a method, and I'd definitely not encourage
> implementations to start to guess again.

I would think it entirely reasonable if your default configuration started processing GET/HEAD requests after the header.  Maybe you can have a toggle that turns off that capability for people who want to go off-script and lose the performance gains, but I'd imagine most people would find the default to be very much acceptable.

Received on Friday, 17 June 2022 07:05:50 UTC