Add HTTP/3 support with pure Erlang QUIC#1707
Add HTTP/3 support with pure Erlang QUIC#1707benoitc wants to merge 2 commits intoninenines:masterfrom
Conversation
|
Absolutely amazing! This will make HTTP3 much more accessible. Thanks. |
|
I've updated quic to support remaining parts of the spec. All interop tests are passing. |
|
Hello! Thanks for the PR, I will start looking at it next week. Note however that removing |
520cbb0 to
53cd105
Compare
|
hrm ok i can re-add it. then What do you prefer, having an enviironment variable? ANyway all tests pass now. ANd quic pass all validiation tests. Let me know |
|
Yes there should be an environment variable. Your early solution of having both You removed all the HTTP/3, Websocket over HTTP/3 and WebTransport tests as well, they should be added back. You also removed the common I will enable tests in the PR, just noticed they're waiting approval. |
|
well for me quicer doesn't work at all, their main branch doesn't even compile. Tests are failing. Anyway I will re-add the tests. is RFC 9220 supported by browsers these days ? |
|
Yes HTTP/3 Websocket is available but only behind flags. It's possible it never gets enabled by default in browsers but browsers aren't the only use case for Websocket. |
This adds HTTP/3 support to Cowboy using two QUIC backends: - erlang_quic: Pure Erlang QUIC implementation (default) - quicer: NIF-based wrapper around MsQuic (optional, set COWBOY_QUICER=1) Features: - RFC 9114 HTTP/3 support - RFC 9220 WebSocket over HTTP/3 - WebTransport over HTTP/3 (draft-ietf-webtrans-http3) - Unified adapter interface for both backends
- Add http3-erlang-quic job to test pure Erlang QUIC backend - Add http3-quicer job to test quicer/MsQuic backend - Configure git credentials for quicer dependency clone - Mark quicer job as continue-on-error (adapter needs updates)
|
@essen i've cleaned the patch. I removed the ci changes preventing testing with quicer. This should be OK now. tests with quic passent. |
|
Thanks. No action is required at this point on your part, what follows are my thoughts on how to proceed. So far I have reviewed I expect the performance of Finally, when I will now review the PR and putting notes for myself (again, no action required for now). |
essen
left a comment
There was a problem hiding this comment.
No action required on your part.
Supporting both erlang_quic and quicer in a configurable manner should be easy enough. There are two sides: fetching as dep (no dep should be fetched by default initially) and choosing the backend (we should allow both at the same time in different listeners).
My plan is to create a new Ranch-like dependency that optionally depends on one or two of the QUIC implementations. The initial default in Cowboy will be to not depend on this Ranch-like dependency (similar to what it does with COWBOY_QUICER). The dependency will contain what I described in the previous comment.
The PR contains a lot of changes that are not directly related and they are not separate commit so I will keep an eye out on the other changes after adding support for the two QUIC implementations.
| loop(State) | ||
| %% Stream not found. For erlang_quic, streams are created | ||
| %% lazily when data arrives (no stream_opened notification). | ||
| %% Determine stream type from ID and create the stream. |
There was a problem hiding this comment.
Probably best handled in the adapter (adapter could return two commands/events instead of one).
| {webtransport_session, _}}, <<>>, fin) -> | ||
| webtransport_event(State, SessionID, {closed, 0, <<>>}), | ||
| ?QUIC_ADAPTER:shutdown_stream(Conn, SessionID), | ||
| loop(webtransport_terminate_session(State, Stream)); |
There was a problem hiding this comment.
Changes like this one don't have a test and are unrelated to adding support so will be left for later.
| stream_store(State, Stream); | ||
| trailers_frame(State=#state{opts=Opts}, | ||
| Stream=#stream{id=StreamID, state=StreamState0}, Trailers) -> | ||
| try cowboy_stream:info(StreamID, {trailers, headers_to_map(Trailers, #{})}, StreamState0) of |
There was a problem hiding this comment.
The trailers info message is to send response trailers back. Request trailers are currently ignored in Cowboy. Anyway this is not relevant for erlang_quicer either.
| %% Only :method and :authority pseudo-headers are allowed. | ||
| headers_frame(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert}, | ||
| Stream=#stream{id=StreamID}, IsFin, Headers, | ||
| PseudoHeaders=#{method := <<"CONNECT">>, authority := Authority}, _) |
There was a problem hiding this comment.
Same, CONNECT is not currently supported and unrelated to the QUIC support, it would need to be added to all protocols at once.
| %% This can happen for datagrams arriving for sessions that | ||
| %% have already been terminated, or for future sessions | ||
| %% that haven't been established yet. | ||
| loop(State) |
There was a problem hiding this comment.
Probably don't want to silence this clause yet.
| %% HTTP/2 and HTTP/3 server push has been deprecated and removed by all major | ||
| %% browsers: Chrome removed support in v106 (2022), Firefox in v132 (Oct 2024). | ||
| %% The feature provided minimal real-world benefit and added complexity. | ||
| %% Therefore, push promises are intentionally not supported. |
There was a problem hiding this comment.
It's still part of the standard and applications may rely on it. Browsers aren't everything. So for now there's no need to decisively drop them (which needs to be done for all protocols at once) nor is there a need to implement them for HTTP/3 (code can stay commented out like it was).
| %% The QUIC stack sends MAX_STREAM_DATA frames to the peer when it's ready to | ||
| %% receive more data. Therefore, the {flow, Size} command is a no-op for HTTP/3. | ||
| commands(State, Stream, [{flow, _Size}|Tail]) -> | ||
| %% @todo We should tell the QUIC stream to increase its window size. |
There was a problem hiding this comment.
The todo is still valid, we should be able to provide hints to the QUIC stack that we are about to receive larger data sizes. This has improved performance for TCP so there's chances this would for some QUIC implementations as well.
| cowboy:log(warning, "Failed to send WT_DRAIN_SESSION: ~p", [Reason], Opts) | ||
| end, | ||
| wt_commands(State, Session, Tail); | ||
| wt_commands(State0=#state{conn=Conn, opts=Opts}, Session=#stream{id=SessionID}, [Cmd|Tail]) |
| post_with_body(Config) -> | ||
| doc("POST request with body that gets echoed back. (RFC9114 4.1)"), | ||
| Port = config(port, Config), | ||
| {ok, Conn} = quic:connect("localhost", Port, |
There was a problem hiding this comment.
As far as Cowboy is concerned it's probably fine to test everything (both erlang_quic and quicer) with just one or the other. Since quicer is better battle tested it's probably a better option initially.
There should be no problem depending on both as well as long as there are no module conflicts.
Summary
How to test
Build with QUIC enabled:
Run all tests:
Or use the test script: