Re: POST+Upgrade, or unexpected limitation of RFC8441

Hi Willy,

Just a brief reply that this sounds like its also related to
draft-ietf-httpbis-optimistic-upgrade [1].

Cheers
Lucas

[1]
https://httpwg.org/http-extensions/draft-ietf-httpbis-optimistic-upgrade.html



On Wed, 18 Dec 2024, 10:33 Willy Tarreau, <w@1wt.eu> wrote:

> Hi!
>
> RFC8441 ("Bootstrapping WebSockets with HTTP/2") indicates how
> to deal with an Upgrade over HTTP/2. To make it short, HTTP/1's
> Connection+Upgrade->101 becomes a CONNECT+:protocol->200 in H2.
> The RFC specifies a :protocol pseudo-header that allows to use
> the mechanism beyond Websocket.
>
> A while ago, we've implemented the mechanism in haproxy and we
> support the upgrade from H1+Upgrade to H2+CONNECT. So far it
> works fine for our users.
>
> A few months ago we got a report of breakage[1] that unveiled
> something unexpected: given that CONNECT doesn't support a
> request body, we had simplified our support for Upgrade and
> completely rejected any request body at the H1 layer (so that
> we could fail early when not knowing if the output would be in
> H1 or H2).  But doing so we accidentally broke support for the
> "Docker Engine API" protocol over H1. That's how we discovered
> that some protocols were relying on POST + Upgrade! (maybe it's
> the only one, I don't know, it was the first report in a few
> years of existence).
>
> Thus, for now we've worked around the problem so that our code
> now rejects the upgrade only on the outgoing H2 side when facing
> a body, but the problem remains that the mechanism proposed by
> RFC8441 doesn't offer provisions for completely transporting H1
> over H2, nor replacing H1 with H2. Indeed, users of this docker
> engine protocol will need to limit themselves to H1 when talking
> to servers or other proxies. It's a bit sad, and I liked the idea
> of using the semantically-close CONNECT for this.
>
> To me, it looks like the limitations are shared among multiple
> causes:
>   - the docker engine API relies on upgrades after a POST, which are
>     not much common, and even never mentioned in any of the upgrade
>     specs (2817, 7230, 9110), though common sense dictates that it's
>     expected to work fine since the client cannot know whether the
>     server will accept the upgrade or not. I.e. it's natural to
>     think that the server is expected to consume the whole request
>     body before upgrading, or at least drain it after rejecting the
>     upgrade.
>
>   - RFC8441 that proposes to use the CONNECT method for the tunnel,
>     while CONNECT itself is defined as "not having content" (9110)
>     or "not having semantics" (7231), which looked fine for websocket
>     but not necessarily for others (that were not identified by then).
>
>   - the use of RFC8441 for non-websocket protocols (but if not usable,
>     what could be the point of ":protocol" ?)
>
> I really don't know what could reasonably be done at this point to
> address that incompatibility. Here are a few ideas:
>   - we could decide that in the context of RFC8441, CONNECT works a bit
>     like GET in that by default the request has no message body unless
>     it is explicitly advertised, and in this case the request cannot
>     succeed until it's entirely consumed. That sounds quite reasonable
>     since this would remove exceptions, and not change anything for
>     existing implementations that don't look for that message body.
>     That does not solve the situation for request bodies sent using
>     chunked encoding however.
>
>   - we could imagine a new method that does like CONNECT but also
>     covers the message body. Same as above, there are still no
>     provisions in H2+ to transmit a chunked body and delimit the
>     part that belongs to the request message and the part that
>     is to be tunnelled. And I'm personally not convinced about the
>     improved interoperability of a new method.
>
>   - we could explicitly state that POST+Upgrade is strictly forbidden,
>     but we now know it's already in use and working fine for at least
>     one implementation.
>
>   - or we could do nothing and consider that some parts of HTTP/1 will
>     remain HTTP/1 forever and will not be transportable over newer
>     versions. I'm not fundamentally against it but it would warrant
>     some extra documentation (especially in the H1 related specs) to
>     discourage such use so that we make sure no new protocol adopts
>     them and stay stuck in a corner.
>
> I'd say that the first approach (tolerating content-length with
> CONNECT when :protocol is used) has my preference. It will not
> permit chunked requests but should cover the vast majority of use
> cases where the server expects a body before deciding to upgrade
> (after all not all HTTP/1 servers support chunked requests either).
>
> But I'm interesting in ideas and opinions others might have.
>
> Thanks!
> Willy
>
> [1] https://github.com/haproxy/haproxy/issues/2684
>
>

Received on Wednesday, 18 December 2024 10:50:52 UTC