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

Martin Thomson <> Fri, 17 June 2022 07:08 UTC

Return-Path: <>
Received: from localhost (localhost []) by (Postfix) with ESMTP id F360CC14CF01 for <>; Fri, 17 Jun 2022 00:08:33 -0700 (PDT)
X-Virus-Scanned: amavisd-new at
X-Spam-Flag: NO
X-Spam-Score: -2.761
X-Spam-Status: No, score=-2.761 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, 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, URIBL_BLOCKED=0.001] autolearn=ham autolearn_force=no
Authentication-Results: (amavisd-new); dkim=pass (2048-bit key) header.b=LDh/Ac3Y; dkim=pass (2048-bit key) header.b=WH/AMXXl
Received: from ([]) by localhost ( []) (amavisd-new, port 10024) with ESMTP id qd-gUWt93tYH for <>; Fri, 17 Jun 2022 00:08:29 -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 492FEC14F748 for <>; Fri, 17 Jun 2022 00:08:29 -0700 (PDT)
Received: from lists by with local (Exim 4.92) (envelope-from <>) id 1o263D-0004TO-GN for; Fri, 17 Jun 2022 07:05:51 +0000
Resent-Date: Fri, 17 Jun 2022 07:05:51 +0000
Resent-Message-Id: <>
Received: from ([]) by with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from <>) id 1o263C-0004SV-3Z for; Fri, 17 Jun 2022 07:05:50 +0000
Received: from ([]) by with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from <>) id 1o263A-000699-FG for; Fri, 17 Jun 2022 07:05:50 +0000
Received: from compute3.internal (compute3.nyi.internal []) by mailout.nyi.internal (Postfix) with ESMTP id D07D85C0078 for <>; Fri, 17 Jun 2022 03:05:36 -0400 (EDT)
Received: from imap41 ([]) by compute3.internal (MEProxy); Fri, 17 Jun 2022 03:05:36 -0400
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;; h=cc:content-type:date:date:from:from:in-reply-to:in-reply-to :message-id:mime-version:references:reply-to:sender:subject :subject:to:to; s=fm3; t=1655449536; x=1655535936; bh=TcZwnYf6iE qPDrfbQMrVTsncuFUFBRZbxQWYRlG5UxQ=; b=LDh/Ac3YHFypXNyvC8HxOMh62r 5l1JAOqRCO2X4uk9MDkx5bkUBPe++MTfPsRKzovMCkynhdJ65RxJtdVMRzwDcuio iojlOtBegv0+vHr+XW+zLB2UIL5hbuwAA361s8g5EWopZ3PG2eTcfhNOcShGarRd +76bZpRcBoOCnzn7TD+XIbwYYVvbZRnLl+R6Gfpu2jmQjtmp8OjRKAcCPxrD5xr8 6ltZIb1iqOc1Z4aT1YGX7OV7snbhXzXzABtelei9JYE/6prOSdEuu/4awx0yTmsV WjOTgl9B7lefEXe1P6XPl3sNcrLfbx1E9G4uYd3Y0UQduzUUybkEKd7NZKgg==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=; h=cc:content-type:date:date:feedback-id :feedback-id:from:from:in-reply-to:in-reply-to:message-id :mime-version:references:reply-to:sender:subject:subject:to:to :x-me-proxy:x-me-proxy:x-me-sender:x-me-sender:x-sasl-enc; s= fm2; t=1655449536; x=1655535936; bh=TcZwnYf6iEqPDrfbQMrVTsncuFUF BRZbxQWYRlG5UxQ=; b=WH/AMXXl5gZI31GKIxUv4X0le5nHWQbmlhnk/3Rp+fPB PQ4mlOXMgRTlyecMcndfalxMtqH1FNrg1Mk1ykwrNkPa90EOKxLN67JA8KgvdJzV w5fRaARTkW0+eC2JfGUgxi9wmRAsaCulLuV9gKCZTp8b2oVCtCaOI01LOFI21iEP Q7IWDTi1CUfaGbav7qzQ4YqGjMSEPCVifrOOFAEP+g8Qomn88uNMIAqceT97gEbV tCBIk5p4uzno5FdaQ5JJcty9cgdHkeeG6d9awGZcCX7svdCNRXGO0Bx2YEqvbq4z Kxjb1SOl9rPAA9Vq6qU8HN+LCpkPr4dJ7PZlYrj3cg==
X-ME-Sender: <xms:wCesYvrZPnkBfZKCVmI7aCXpbAC1Kxw9f3Dn9Ak_i1F4M6L3yhyUig> <xme:wCesYpqHBx6GHmP7Oexdv_GiNaxVH-18uXgHVXVtIiuCAOS6Hch_7i-8nKfCWmmf9 iA9ZxlQMs-3m3f_02A>
X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedvfedruddvgedguddufecutefuodetggdotefrod ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfgfvfdpuffrtefokffrpgfnqfgh necuuegrihhlohhuthemuceftddtnecunecujfgurhepofgfggfkjghffffhvffutgesth dtredtreertdenucfhrhhomhepfdforghrthhinhcuvfhhohhmshhonhdfuceomhhtsehl ohifvghnthhrohhphidrnhgvtheqnecuggftrfgrthhtvghrnhepkeetueeikedtkeelfe ekvefhkeffvedvvefgkefgleeugfdvjeejgeffieegtdejnecuvehluhhsthgvrhfuihii vgeptdenucfrrghrrghmpehmrghilhhfrhhomhepmhhtsehlohifvghnthhrohhphidrnh gvth
X-ME-Proxy: <xmx:wCesYsNwYRJV4FSDbdrVx5YZQ9VMFEsTTdjPQsOwKQGONsLR3qxsRg> <xmx:wCesYi6hc7H2VHxYfHcGiy4YsyihsuIbAF9x9owiaRZaoNOYWXpTVg> <xmx:wCesYu64tievJX-OXQrFgInffJEZpRbC0ijebtyAbTROdrzk-sE5XA> <xmx:wCesYgHK7v2MrXv1XySOCvqptVuu7aO8XBZwybck1DEPUaDC8tNPgw>
Feedback-ID: ic129442d:Fastmail
Received: by mailuser.nyi.internal (Postfix, from userid 501) id 8BB272340081; Fri, 17 Jun 2022 03:05:36 -0400 (EDT)
X-Mailer: Webmail Interface
User-Agent: Cyrus-JMAP/3.7.0-alpha0-712-gb9e94258b0-fm-20220610.001-gb9e94258
Mime-Version: 1.0
Message-Id: <>
In-Reply-To: <>
References: <YqsBZ0M4lDXGRQTo@miskatonic> <> <> <> <> <> <> <>
Date: Fri, 17 Jun 2022 17:05:14 +1000
From: Martin Thomson <>
Content-Type: text/plain
Received-SPF: pass client-ip=;;
X-W3C-Hub-DKIM-Status: validation passed: (, signature is good
X-W3C-Hub-DKIM-Status: validation passed: (, signature is good
X-W3C-Hub-Spam-Status: No, score=-6.8
X-W3C-Hub-Spam-Report: BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01, W3C_AA=-1, W3C_DB=-1, W3C_IRA=-1, W3C_WL=-1
X-W3C-Scan-Sig: 1o263A-000699-FG 2e2cb37001269e09ff33e0d8a2364d89
Subject: Re: Ambiguity on HTTP/3 HEADERS and QUIC STREAM FIN requirement
Archived-At: <>
X-Mailing-List: <> archive/latest/40149
Precedence: list
List-Id: <>
List-Help: <>
List-Post: <>
List-Unsubscribe: <>

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.