emma

emma wrote (edited )

but rereading the spec, you're supposed to send a 505 in the representation used by the major version requested by the client

GET / HTTP/2.0 is parsed with http/1 semantics, so i think it makes sense to give any >= 2.x version the HTTP/1.1 505 treatment.

An HTTP/2 request can be sent without negotiation; this is how h2c (HTTP/2 over cleartext) reliably works for me (for some reason I couldn't get Upgrade from http/1.1 to h2c working). It's called "prior knowledge", and curl supports this.

yeah, but as the name implies, you somehow know in advance that the server's gonna accept HTTP/2 if you send those. i suppose 505 here would make sense, if the HTTP/2 support was ever removed. as i understand it, this mode is never going to happen under normal browsing, though.

No, 505 wouldn't be useful because an HTTP/2 request to an HTTP/1-only server would only result in the client just closing the connection itself. You can see this by using nghttp or curl --http2-prior-knowledge against a server that only supports HTTP/1

An HTTP/2-only client (which those two commands earlier are) would not bother to process an HTTP/1 response (if it even gets one) whether that'd be a 505 or 200.

i meant a hypothetical http/2 that's more http/1-like, not the actual http/2 that came into existence which made it very hard to accidentally use the wrong protocol.

anyway, the solution to your woes is apparently to send an error packet or whatever:

HTTP_1_1_REQUIRED (0x0d):
The endpoint requires that HTTP/1.1 be used instead of HTTP/2.

it sounds like it does what you want, but i have no idea if this applies on the stream or the connection level or what.

3

emma wrote

I mean the same applies if an HTTP/1 response is a 505, right..?

no, since http/1 requests are sent preemptively without knowing if the server accepts them. http/2+ requests are sent after negotiation, at which point it's established they are accepted. this obsoletes the need for a 505.

505 would have been useful for a future where http/2 requests might be preemptively sent to http/1-only servers. if i send GET / HTTP/2.0 (or any non-1.x version) to nginx, it indeed responds with that status code. but as things turned out, the negotiation mechanism in http/2+ just sidesteps this problem altogether, so 505 ends up being little more than a relic from a time when people didn't know what the future of http held.

since you very much have to opt in for http/2+, incompatibilities with it can be resolved by just not enabling it, and the use cases where one would want partial http/2 support on any given host are extremely contrived, i would argue it's a good thing that support for it is declared on the connection level. it's one less special case for clients to deal with.

3

emma wrote

I'm not much of a fan of ditching plain text for binary, since it makes debugging more complex

I don't think this always holds true, like there was one time at work where an outgoing http request was failing in a strange way, and it took us hours to discover that the environment variable holding the URL in production contained a trailing newline, which the client library didn't pick up on. So this resulted in the following request:

POST /some/shit
HTTP/1.1
X-Some-Header: etc

some payload

If the length of the URL was known ahead of time, as would be typical with a binary protocol, the server would have known the newline was part of it, and handled it accordingly. It wouldn't be friendly as a plain text protocol, but it would make parsing the request very unambiguous and robust.

On the other hand, we see things like http/2 support in curl on Debian 12 being just broken, and the maintainer being too scared to merge the fixes from upstream due to http/2's complexity. So this cuts both ways, I suppose.

Oh, you can write a server that doesn't implement keepalive (while doing everything else 1.1) and still be 1.1-compliant? Well that's neat I suppose!

Yeah, you can just ignore the client's wish for keep-alive and send Connection: close, according to RFC 7230. I imagine this has to be terrible if the client attempts pipelining.

This might be a cursed opinion but I do actually want all websites to be root/path-agnostic. So if you wanna host Postmill for example but you already have a separate service running in port 80/443, and can't do it in a separate domain (which would require another host in this reality) or port which would have its own root, then I should be able to put it in like /postmill instead.

I believe Postmill supports this, but I haven't tested. I think a lot of devs just ignore the possibility you'd want to host something a subpath, unfortunately.

2