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

Willy Tarreau <> Fri, 17 June 2022 05:37 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id 7CCDBC15BEC8 for <>; Thu, 16 Jun 2022 22:37:30 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2.662
X-Spam-Status: No, score=-2.662 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, HEADER_FROM_DIFFERENT_DOMAINS=0.25, MAILING_LIST_MULTI=-1, RCVD_IN_MSPIKE_H2=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01] autolearn=ham autolearn_force=no
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id 8y7e3yZi-u9u for <>; Thu, 16 Jun 2022 22:37:26 -0700 (PDT)
Received: from ( []) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-256) server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by (Postfix) with ESMTPS id 53A05C14F74E for <>; Thu, 16 Jun 2022 22:37:25 -0700 (PDT)
Received: from lists by with local (Exim 4.92) (envelope-from <>) id 1o24fW-000198-31 for; Fri, 17 Jun 2022 05:37:18 +0000
Resent-Date: Fri, 17 Jun 2022 05:37:18 +0000
Resent-Message-Id: <>
Received: from ([]) by with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from <>) id 1o24fV-00018F-AB for; Fri, 17 Jun 2022 05:37:17 +0000
Received: from ([] by with esmtp (Exim 4.92) (envelope-from <>) id 1o24fT-0003F7-3L for; Fri, 17 Jun 2022 05:37:17 +0000
Received: (from willy@localhost) by pcw.home.local (8.15.2/8.15.2/Submit) id 25H5b0Lp030407; Fri, 17 Jun 2022 07:37:00 +0200
Date: Fri, 17 Jun 2022 07:37:00 +0200
From: Willy Tarreau <>
To: Martin Thomson <>
Cc:, Amaury Denoyelle <>
Message-ID: <>
References: <YqsBZ0M4lDXGRQTo@miskatonic> <> <> <> <> <> <>
MIME-Version: 1.0
Content-Type: text/plain; charset="us-ascii"
Content-Disposition: inline
In-Reply-To: <>
User-Agent: Mutt/1.10.1 (2018-07-13)
Received-SPF: pass client-ip=;;
X-W3C-Hub-Spam-Status: No, score=-4.9
X-W3C-Hub-Spam-Report: BAYES_00=-1.9, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, W3C_AA=-1, W3C_IRA=-1, W3C_WL=-1
X-W3C-Scan-Sig: 1o24fT-0003F7-3L 96afc4c5c98121a1486eeed9a8f14256
Subject: Re: Ambiguity on HTTP/3 HEADERS and QUIC STREAM FIN requirement
Archived-At: <>
X-Mailing-List: <> archive/latest/40144
Precedence: list
List-Id: <>
List-Help: <>
List-Post: <>
List-Unsubscribe: <>

On Fri, Jun 17, 2022 at 03:02:56PM +1000, Martin Thomson wrote:
> Hey Willy,
> On Fri, Jun 17, 2022, at 14:53, Willy Tarreau wrote:
> > Probably, but I'm still seeing it as a workaround. I mean, since HTTP/1.0
> > we've been used to know when receiving a full headers block the entire
> > list of the header fields. And it looks like with H3 the headers block
> > is uncertain at the end of a HEADERS frame. 
> I don't think that's right.  There are two layers at work here, but you do
> have a clear marker for the end of the header block.
> 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). In HTTP/2 we could get rid of this
header field because the HEADERS frame was able to arbor the END_STREAM
flag to indicate that there was no body following. 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. Passing that
to HTTP/1 results in a transfer-encoding header being added to the second
one. So for H1 and H2 the presence or absence of a body has always been
explicit and known upon receipt of a complete headers block.

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).

> The QUIC stream, which in this case hasn't ended, so you aren't sure if
> content or trailers might follow.


> >    A sender which knows that no data follows the headers block SHOULD
> >    signal the end of the message by setting the FIN bit along with the
> >    HEADERS frame. A receiver that processes a HEADERS frame without
> >    seeing a FIN bit MAY expect more data to follow regardless of the
> >    HTTP method used.
> This is entirely sensible advise to give, but it isn't always possible due to
> how different pieces of software work.  There are some rather fundamental
> design choices involved that can make it hard to guarantee that these two
> things leave at the same time.  As you say, most people can deliver on this,
> but not all.

I'm well aware. But it's easier to respect when you have the rule upfront
than when nobody told you there was a problem with this. Just like we've
all had to adjust certain painful things in our H2 implementations over
time, violating multiple layers, that resulted in new text being written
into RFC9113, there will be some adjustments needed to be done to various
implementations to fix some trouble detected in field.

> > Sure but while you can often do this when you're a server, you practically
> > can't when you're a gateway, because that would require to adjust the
> > behavior per-URI. 
> Yeah, a gateway is in a bad position here because they don't really speak for
> the resource and so - without extra information about resources, which could
> be made for all resources on a server, but probably won't be - they have to
> sit on their hands, buffer requests, and pay the ridiculous cost of doing
> that.

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.

> 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.