95 lines
3.9 KiB
Erlang
95 lines
3.9 KiB
Erlang
%%%-------------------------------------------------------------------
|
||
%%% @author anlicheng
|
||
%%% @copyright (C) 2026, <COMPANY>
|
||
%%% @doc
|
||
%%%
|
||
%%% @end
|
||
%%% Created : 11. 2月 2026 21:26
|
||
%%%-------------------------------------------------------------------
|
||
-module(sdlan_quic_server).
|
||
-author("anlicheng").
|
||
|
||
%% API
|
||
-export([start_link/0, init/0]).
|
||
|
||
start_link() ->
|
||
{ok, spawn_link(?MODULE, init, [])}.
|
||
|
||
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),
|
||
CertFile = proplists:get_value(certfile, Props),
|
||
KeyFile = proplists:get_value(keyfile, Props),
|
||
AcceptorCount = proplists:get_value(acceptors, Props, 10),
|
||
|
||
%% 获取环境变量
|
||
Path = os:getenv("QUIC_CERT_PATH", code:priv_dir(sdlan)),
|
||
|
||
LOptions = #{
|
||
addr_family => inet,
|
||
% 必选:QUIC/TLS证书配置
|
||
certfile => Path ++ "/quic/" ++ CertFile,
|
||
keyfile => Path ++ "/quic/" ++ 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_channel_sup:start_channel(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.
|