%%--------------------------------------------------------------------
%% Copyright (c) 2020-2024 EMQ Technologies Co., Ltd. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%--------------------------------------------------------------------
-module(quicer_server_conn_callback).
-behavior(quicer_connection).
-include_lib("snabbkaffe/include/snabbkaffe.hrl").
-include("quicer_types.hrl").
%% Callback init
-export([init/1]).
%% Connection Callbacks
-export([
new_conn/3,
connected/3,
transport_shutdown/3,
shutdown/3,
closed/3,
local_address_changed/3,
peer_address_changed/3,
streams_available/3,
peer_needs_streams/3,
resumed/3,
nst_received/3,
new_stream/3
]).
-export([handle_info/2]).
init(#{stream_opts := SOpts} = S) when is_list(SOpts) ->
init(S#{stream_opts := maps:from_list(SOpts)});
init(ConnOpts) when is_map(ConnOpts) ->
{ok, ConnOpts}.
closed(_Conn, #{} = _Flags, S) ->
{stop, normal, S}.
new_conn(Conn, #{version := _Vsn}, #{stream_opts := SOpts} = S) ->
%% @TODO configurable behavior of spawning stream acceptor
case quicer_remote_stream:start_link(maps:get(stream_callback, SOpts), Conn, SOpts) of
{ok, Pid} ->
ok = quicer:async_handshake(Conn),
{ok, S#{
conn => Conn,
%% @TODO track the streams?
streams => [{Pid, accepting}]
}};
{error, _} = Error ->
Error
end.
resumed(Conn, Data, #{resumed_callback := ResumeFun} = S) when
is_function(ResumeFun)
->
ResumeFun(Conn, Data, S);
resumed(_Conn, _Data, S) ->
{ok, S}.
nst_received(_Conn, _Data, S) ->
{stop, no_nst_for_server, S}.
%% handles stream when there is no stream acceptors.
new_stream(
Stream,
#{is_orphan := true} = StreamProps,
#{conn := Conn, streams := Streams, stream_opts := SOpts} = CBState
) ->
%% Spawn new stream
case
quicer_remote_stream:start_link(
maps:get(stream_callback, SOpts),
Stream,
Conn,
SOpts,
StreamProps
)
of
{ok, StreamOwner} ->
case quicer:handoff_stream(Stream, StreamOwner) of
ok ->
{ok, CBState#{streams := [{StreamOwner, Stream} | Streams]}};
{error, _} = E ->
{stop, {shutdown, {handoff, E}, CBState}}
end;
Other ->
Other
end.
shutdown(Conn, _ErrorCode, S) ->
quicer:async_close_connection(Conn),
{ok, S}.
transport_shutdown(_C, _DownInfo, S) ->
{ok, S}.
peer_address_changed(_C, _NewAddr, S) ->
{ok, S}.
local_address_changed(_C, _NewAddr, S) ->
{ok, S}.
streams_available(_C, {BidirCnt, UnidirCnt}, S) ->
{ok, S#{
peer_unidi_stream_count => UnidirCnt,
peer_bidi_stream_count => BidirCnt
}}.
%% @doc May integrate with App flow control
peer_needs_streams(_C, _UnidiOrBidi, S) ->
{ok, S}.
connected(
Conn,
_Flags,
#{
slow_start := false,
stream_opts := SOpts,
stream_callback := Callback
} = S
) ->
%% @TODO configurable behavior of spawing stream acceptor
_ = quicer_stream:start_link(Callback, Conn, SOpts),
{ok, S#{conn => Conn}};
connected(_Connection, _Flags, S) ->
{ok, S}.
handle_info({'EXIT', _Pid, _Reason}, State) ->
{ok, State}.
%% Internals
-ifdef(EUNIT).
-endif.