refactor: swap quicer to native quick implementation#313
refactor: swap quicer to native quick implementation#313kevinschweikert wants to merge 1 commit into
Conversation
0696e50 to
91086c6
Compare
| }). | ||
|
|
||
| %% QUIC stream options, e.g. #{priority => non_neg_integer()}. | ||
| -type stream_opts() :: map() | proplists:proplist(). |
| Port = 4568, | ||
| SslOpts = [ {certfile, certfile(Config)} | ||
| , {keyfile, keyfile(Config)} | ||
| , {idle_timeout_ms, 10000} |
| [ {alpn, ["mqtt"]}, {active, false} | ||
| , {quic_event_mask, 1} | ||
| ], | ||
| [{alpn, ["mqtt"]}], |
There was a problem hiding this comment.
is {active, false} default?
| {error, _} -> | ||
| ok; | ||
| {ok, Sock2} -> | ||
| ok = emqtt_quic:close(Sock2) |
| case emqtt_quic:connect("localhost", | ||
| Port, | ||
| [{alpn, ["mqtt"]}, | ||
| {nst, Corrupted} |
There was a problem hiding this comment.
nst is a term in quicer, I guess it is not in quic
| {error, closed} -> ok | ||
| case quic:send_data(MConn, MalformStream, <<0,0,0,0,0,0,0,0,0,0>>, false) of | ||
| ok -> ok; | ||
| {error, _} -> ok |
There was a problem hiding this comment.
both ok and error map to ok why ?
| %% Client is alive. | ||
| ?assert(is_list(emqtt:info(C))). | ||
|
|
||
| t_ssl_keylogfile_dump(Config) -> |
There was a problem hiding this comment.
We need it. very useful in troubleshooting.
| {error, closed} -> ok | ||
| case quic:send_data(MConn, MalformStream, <<0,0,0,0,0,0,0,0,0,0>>, false) of | ||
| ok -> ok; | ||
| {error, _} -> ok |
There was a problem hiding this comment.
better to have error reason match.
| qoe = IsQoE | ||
| } = State) -> | ||
| State0 = maybe_init_quic_state(ConnMod, State), | ||
| IsUsingQuicHandle = proplists:is_defined(handle, SockOpts), |
There was a problem hiding this comment.
This removes the support of indirect start connection.
|
|
||
| -export([ connect/4 | ||
| , send/2 | ||
| , recv/2 |
| {connect_timeout, translate_timeout(Timeout)}, | ||
| {server_name, sni(SslOpts, Host)}, | ||
| {cacerts, proplists:get_value(cacerts, SslOpts)}, | ||
| {session_ticket, proplists:get_value(nst, SockOpts)}], |
There was a problem hiding this comment.
missing support for stream_count (bidi, unidi)
| quicer:getstat(Conn, Options). | ||
| %% Stub: QUIC keepalive uses `last_packet_id' (see `emqtt:should_ping/1'); | ||
| %% the byte counters fetched by the generic keepalive path are unused. | ||
| getstat({quic, _Conn, _Stream}, Options) -> |
| %% gracefully shutdown the stream to flush all the msg in sndbuf. | ||
| _ = quicer:shutdown_stream(Stream, 500), | ||
| quicer:close_connection(Conn, ?QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0, 500). | ||
| close({quic, Conn, _Stream}) -> |
There was a problem hiding this comment.
we need gracefully shutdown stream with send flush.
| maps:get(LogicId, LSM, undefined). | ||
|
|
||
| %% @doc Establish a connection and open the control stream. Blocks until | ||
| %% the QUIC handshake completes; the synchronous contract matches the one |
There was a problem hiding this comment.
This is not true, we need async conn.
| shutdown_stream(Conn, Stream, graceful) -> | ||
| quic:send_data(Conn, Stream, <<>>, true); | ||
| shutdown_stream(Conn, Stream, _Abort) -> | ||
| quic:reset_stream(Conn, Stream, 0). |
|
thanks for the PR, good start. Our team member also push a couple of times about this, I cannot ignore that. One general problem is that this PR almost change everything to sync ops. |
There was a problem hiding this comment.
Pull request overview
This PR replaces the previous QUIC transport backend (quicer, external/C-dependent) with the pure-Erlang quic library, aiming to simplify builds and reduce native dependencies while keeping QUIC support for emqtt and its test suite.
Changes:
- Swapped QUIC implementation from
quicertoquicacross client, CLI, and test utilities. - Simplified build/release setup by removing
rebar.config.scriptand addingquicas a normal dependency/application. - Refactored MQTT frame parsing flow to a new
parse_all/2helper and adjusted QUIC test expectations (including removal of an unsupported keylogfile test).
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
test/quic_server.erl |
Reworked test QUIC server to use quic:start_server/3 and handle stream data events. |
test/emqtt_test_lib.erl |
Starts quic for tests, but still contains remaining quicer startup logic elsewhere in the file. |
test/emqtt_quic_SUITE.erl |
Updated suite for new QUIC event model and stream shutdown semantics; still had a quicer startup call. |
src/emqtt.erl |
Adapted QUIC connection/open-stream flow and switched packet parsing to emqtt_frame:parse_all/2. |
src/emqtt.app.src |
Adds quic as an OTP application dependency and removes rebar.config.script from packaged files. |
src/emqtt_sock.erl |
Routes QUIC sends/sockname through emqtt_quic using {quic, ConnPid, StreamId} tuples. |
src/emqtt_quic.erl |
New QUIC transport implementation built on quic (connection lifecycle, stream parsing, session tickets). |
src/emqtt_quic_stream.erl |
Removed (old quicer stream callback implementation). |
src/emqtt_quic_connection.erl |
Removed (old quicer connection callback implementation). |
src/emqtt_frame.erl |
Adds parse_all/2 to parse a full binary chunk into ordered packets + residual parse state. |
src/emqtt_cli.erl |
Starts quic instead of quicer. |
rebar.config.script |
Removed to simplify build configuration. |
rebar.config |
Adds quic dependency; updates relx releases and dialyzer extra apps from quicer to quic. |
README.md |
Updates QUIC option type docs; other QUIC build docs may now be outdated. |
Dockerfile.test |
Removes cmake install step (no longer needed without quicer native build). |
.gitignore |
Ignores dialyzer PLT artifacts. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case do_connect(Host, Port, Opts, Timeout) of | ||
| {ok, Conn} -> | ||
| case open_stream(Conn) of | ||
| {ok, Stream} -> | ||
| {ok, {quic, Conn, Stream}}; | ||
| {error, _} = Error -> | ||
| _ = quic:safe_close(Conn), | ||
| Error | ||
| end; | ||
| {error, _} = Error -> | ||
| Error | ||
| end |
| %% @doc Connect without opening a stream; pairs with `emqtt:quic_mqtt_connect/1'. | ||
| -spec open_connection([{inet:hostname() | inet:ip_address(), inet:port_number()}], | ||
| proplists:proplist(), timeout()) | ||
| -> {ok, conn()} | {error, term()}. | ||
| open_connection([{Host, Port} | _], Opts, Timeout) -> | ||
| do_connect(Host, Port, Opts, Timeout). |
| on_stream_closed(Via, Conn, #{ data_stream_socks := DataStreams | ||
| , stream_parse_state := PSS | ||
| , control_stream_sock := CtrlSock | ||
| } = CBData) -> | ||
| case lists:member(Via, DataStreams) of | ||
| true -> | ||
| {CBData#{ data_stream_socks := lists:delete(Via, DataStreams) | ||
| , stream_parse_state := maps:remove(Via, PSS) | ||
| }, []}; | ||
| false when Via =:= CtrlSock -> | ||
| {CBData, [{next_event, info, {quic_closed, Conn}}]}; | ||
| false -> | ||
| ?LOG(warning, "unknown_stream_closed", #{stream => Via}, CBData), | ||
| {CBData, []} | ||
| end. |
| emqtt_test_lib:start_emqx(), | ||
| application:ensure_all_started(quicer), | ||
| ok = emqtt_test_lib:ensure_quic_listener(mqtt, UdpPort). |
| start_emqx() -> | ||
| ensure_test_module(emqx_common_test_helpers), | ||
| emqx_common_test_helpers:start_apps([]), | ||
| {ok, _} = application:ensure_all_started(quic), | ||
| ok = ensure_quic_listener(mqtt, 14567), | ||
| ok. |
| {tcp_opts, [gen_tcp:option()]} | | ||
| {ssl, boolean()} | | ||
| {ssl_opts, [ssl:tls_client_option()]} | | ||
| {quic_opts, {quicer:conn_opts(), quicer:stream_opts()}} | | ||
| {quic_opts, {ConnOpts :: proplists:proplist(), StreamOpts :: proplists:proplist()}} | | ||
| {ws_path, string()} | |
Closes #304
Full disclosure: This PR was AI assisted. I ran all the tests and did a sweep myself. I am no Erlang expert but want to simplify the build of
emqtt. I'll appreciate feedback and will iterate over this PR but if this is too big of a change or too far away from expectations i can understand.Changes made:
quicerforerlang_quicwhich is a native QUIC implementaion in Erlangt_ssl_keylogfile_dumpbecause this feature is not supported AFAIK