%%%------------------------------------------------------------------- %%% @author anlicheng %%% @copyright (C) 2026, %%% @doc %%% %%% @end %%% Created : 11. 2月 2026 21:26 %%%------------------------------------------------------------------- -module(sdlan_quic_server). -author("anlicheng"). %% API -export([start_link/0, init/0]). -spec start_link() -> {ok, pid()}. start_link() -> {ok, spawn_link(?MODULE, init, [])}. -spec init() -> ok. init() -> process_flag(trap_exit, true), {ok, Props} = application:get_env(sdlan, quic_server), Port = proplists:get_value(port, Props), Alpn = proplists:get_value(alpn, Props), Limits = proplists:get_value(limits, Props), AcceptorCount = proplists:get_value(acceptors, Props, 10), %% 证书配置 {CertFile, KeyFile} = sdlan_util:certificate_config(), LOptions = #{ % 必选:QUIC/TLS证书配置 certfile => CertFile, keyfile => KeyFile, alpn => Alpn, peer_bidi_stream_count => 1, conn_acceptors => AcceptorCount }, ListenAddr = "0.0.0.0:" ++ integer_to_list(Port), case quicer:listen(ListenAddr, LOptions) of {ok, L} -> logger:notice("[sdlan_quic_server] listen on ~s, acceptors: ~p", [ListenAddr, AcceptorCount]), Pids = [spawn_link(fun() -> loop_accept(L, Limits, I) end) || I <- lists:seq(1, AcceptorCount)], wait_acceptors(L, Pids); Error -> exit(Error) end. wait_acceptors(L, Pids) -> receive {'EXIT', Pid, Reason} -> logger:warning("[sdlan_quic_server] acceptor ~p exit with reason: ~p", [Pid, Reason]), quicer:close_listener(L), exit({acceptor_exit, Pid, Reason, Pids}); stop -> quicer:close_listener(L), ok; Info -> logger:notice("[sdlan_quic_server] get unexpected info: ~p", [Info]), wait_acceptors(L, Pids) end. loop_accept(L, Limits, AcceptorId) -> case quicer:accept(L, #{}, infinity) of {ok, Conn} -> logger:debug("[sdlan_quic_server] acceptor: ~p, accept a new connection: ~p", [AcceptorId, Conn]), case quicer:handshake(Conn) of {ok, NConn} -> case sdlan_quic_transport_sup:start_transport(NConn, Limits) of {ok, ChannelPid} -> logger:debug("[sdlan_quic_server] conn: ~p, handshake success, channel pid: ~p", [NConn, ChannelPid]), case quicer:controlling_process(NConn, ChannelPid) of ok -> sdlan_quic_transport:accept_stream(ChannelPid); {error, Reason} -> logger:warning("[sdlan_quic_server] conn: ~p, controlling_process failed: ~p", [NConn, Reason]), sdlan_quic_transport:stop(ChannelPid, {controlling_process_failed, Reason}), quicer:close_connection(NConn) end; Error -> quicer:close_connection(NConn), logger:notice("[sdlan_quic_server] start channel get error: ~p", [Error]) end, loop_accept(L, Limits, AcceptorId); {error, Reason} -> logger:debug("[sdlan_quic_server] acceptor: ~p, handshake failed: ~p", [AcceptorId, Reason]), quicer:close_connection(Conn), loop_accept(L, Limits, AcceptorId) end; {error, Reason} -> logger:debug("[sdlan_quic_server] acceptor: ~p, accept failed: ~p", [AcceptorId, Reason]), loop_accept(L, Limits, AcceptorId) end.