Packages

Advanced Erlang socket library for TCP protocols and provides fast, useful and simple API for implementing server, client and connection pool.

Current section

Files

Jump to
sockerl src sockerl_server_sup.erl
Raw

src/sockerl_server_sup.erl

%%% ------------------------------------------------------------------------------------------------
%%% Sockerl is available for use under the following license, commonly known as the 3-clause (or
%%% "modified") BSD license:
%%%
%%% Copyright (c) 2017-2018, Pouriya Jahanbakhsh
%%% (pouriya.jahanbakhsh@gmail.com)
%%% All rights reserved.
%%%
%%% Redistribution and use in source and binary forms, with or without modification, are permitted
%%% provided that the following conditions are met:
%%%
%%% 1. Redistributions of source code must retain the above copyright notice, this list of
%%% conditions and the following disclaimer.
%%%
%%% 2. Redistributions in binary form must reproduce the above copyright notice, this list of
%%% conditions and the following disclaimer in the documentation and/or other materials provided
%%% with the distribution.
%%%
%%% 3. Neither the name of the copyright holder nor the names of its contributors may be used to
%%% endorse or promote products derived from this software without specific prior written
%%% permission.
%%%
%%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
%%% IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
%%% FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
%%% CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
%%% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
%%% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
%%% THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
%%% OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%%% POSSIBILITY OF SUCH DAMAGE.
%%% ------------------------------------------------------------------------------------------------
%% @author Pouriya Jahanbakhsh <pouriya.jahanbakhsh@gmail.com>
%% @version 17.9.2
%% -------------------------------------------------------------------------------------------------
-module(sockerl_server_sup).
-author("pouriya.jahanbakhsh@gmail.com").
%% -------------------------------------------------------------------------------------------------
%% Exports:
-export([start_link/3
,start_link/4
,start_link/5
,fetch_connections/1
,fetch_acceptors/1
,sleep_acceptors/1
,wakeup_acceptors/1
,get_acceptor_modes/1
,change_acceptor_modes/1
,stop/1
,stop/2]).
%% 'director' export:
-export([init/1]).
%% -------------------------------------------------------------------------------------------------
%% Record & Macros & Includes:
-define(ACCEPTOR_SUP, acceptor_sup).
-define(CONNECTION_SUP, connection_sup).
-define(DEF_START_OPTS, [{log_validator, fun log_validator/2}]).
-define(DEF_ACCEPTOR_COUNT, 1).
-define(DEF_SOCKET_OPTIONS, []).
-define(DEF_SSL_FLAG, false).
-define(DEF_TERMINATE_TIMEOUT, 10*1000). %% 10s
-define(DEF_TRANSPORT_MODULE, 'sockerl_tcp_transporter').
%% -------------------------------------------------------------------------------------------------
%% API functions:
-spec
start_link(module(), term(), sockerl_types:port_number()) ->
sockerl_types:start_return().
start_link(Mod, InitArg, Port) when erlang:is_atom(Mod),
erlang:is_integer(Port) ->
start_link(Mod, InitArg, Port, ?DEF_START_OPTS).
-spec
start_link(sockerl_types:register_name() | module()
,module() | term()
,term() | sockerl_types:port_number()
,sockerl_types:port_number() |
sockerl_types:start_options()) ->
sockerl_types:start_return().
start_link(Mod, InitArg, Port, Opts) when erlang:is_atom(Mod),
erlang:is_integer(Port),
erlang:is_list(Opts)->
case director:start_link(?MODULE, {Mod, InitArg, Port, Opts}, ?DEF_START_OPTS) of
{ok, Pid} ->
continue_starting(Opts, Pid);
{error, _Reason}=Error ->
Error;
ignore ->
ignore
end;
start_link(Name, Mod, InitArg, Port) when erlang:is_tuple(Name),
erlang:is_atom(Mod),
erlang:is_integer(Port) ->
case director:start_link(Name, ?MODULE, {Mod, InitArg, Port, ?DEF_START_OPTS}) of
{ok, Pid} ->
continue_starting(?DEF_START_OPTS, Pid);
{error, _Reason}=Error ->
Error;
ignore ->
ignore
end.
-spec
start_link(sockerl_types:register_name()
,module()
,term()
,sockerl_types:port_number()
,sockerl_types:start_options()) ->
sockerl_types:start_return().
start_link(Name, Mod, InitArg, Port, Opts) when erlang:is_tuple(Name),
erlang:is_atom(Mod),
erlang:is_integer(Port),
erlang:is_list(Opts) ->
case director:start_link(Name, ?MODULE, {Mod, InitArg, Port, Opts}, ?DEF_START_OPTS) of
{ok, Pid} ->
continue_starting(Opts, Pid);
{error, _Reason}=Error ->
Error;
ignore ->
ignore
end.
-spec
fetch_acceptors(sockerl_types:name()) ->
[{pos_integer(), pid()}].
fetch_acceptors(Server) ->
{ok, Pid} = director:get_pid(Server, ?ACCEPTOR_SUP),
sockerl_acceptor_sup:fetch(Pid).
-spec
fetch_connections(sockerl_types:name()) ->
[] | [{sockerl_types:socket(), pid()}].
fetch_connections(Server) ->
{ok, Pid} = director:get_pid(Server, ?CONNECTION_SUP),
sockerl_server_connection_sup:fetch(Pid).
-spec
sleep_acceptors(sockerl_types:name()) ->
'ok'.
sleep_acceptors(Server) ->
{ok, Pid} = director:get_pid(Server, ?ACCEPTOR_SUP),
sockerl_acceptor_sup:sleep(Pid).
-spec
wakeup_acceptors(sockerl_types:name()) ->
'ok'.
wakeup_acceptors(Server) ->
{ok, Pid} = director:get_pid(Server, ?ACCEPTOR_SUP),
sockerl_acceptor_sup:wakeup(Pid).
-spec
get_acceptor_modes(sockerl_types:name()) ->
'sleep' | 'accept' | [{pos_integer(), 'sleep' | 'accept'}].
get_acceptor_modes(Server) ->
{ok, Pid} = director:get_pid(Server, ?ACCEPTOR_SUP),
sockerl_acceptor_sup:get_mode(Pid).
-spec
change_acceptor_modes(sockerl_types:name()) ->
sockerl_types:acceptor_mode() | 'not_allowed'.
change_acceptor_modes(Server) ->
{ok, Pid} = director:get_pid(Server, ?ACCEPTOR_SUP),
sockerl_acceptor_sup:change_mode(Pid).
-spec
stop(sockerl_types:name()) ->
'ok'.
stop(Server) ->
director:stop(Server, normal, ?DEF_TERMINATE_TIMEOUT).
-spec
stop(sockerl_types:name(), Reason::any()) ->
ok.
stop(Server, Reason) ->
director:stop(Server, Reason, ?DEF_TERMINATE_TIMEOUT).
%% -------------------------------------------------------------------------------------------------
%% 'director' callback:
%% @hidden
init({Mod, InitArg, Port, Opts}=Arg) ->
TrMod = sockerl_utils:get_value(transporter
,Opts
,?DEF_TRANSPORT_MODULE
,fun erlang:is_atom/1),
case sockerl_socket:listen(TrMod, Port, Opts) of
{ok, LSock} ->
case catch Mod:listen_init(InitArg, LSock) of
ok ->
{ok, [#{id => ?CONNECTION_SUP
,start => {sockerl_server_connection_sup
,start_link
,[Mod, InitArg, Opts]}
,type => supervisor
,plan => [stop]
,count => 1}
,#{id => ?ACCEPTOR_SUP
,start => {sockerl_acceptor_sup, start_link, [Opts, LSock]}
,type => supervisor
,plan => [stop]
,count => 1}]};
{ok, InitArg2} ->
{ok, [#{id => ?CONNECTION_SUP
,start => {sockerl_server_connection_sup
,start_link
,[Mod, InitArg2, Opts]}
,type => supervisor
,plan => [stop]
,count => 1}
,#{id => ?ACCEPTOR_SUP
,start => {sockerl_acceptor_sup, start_link, [Opts, LSock]}
,type => supervisor
,plan => [stop]
,count => 1}]};
ignore ->
ignore;
{stop, _Reason}=Stop ->
Stop;
{'EXIT', Reason} ->
{stop, Reason};
Other ->
{stop, {bad_return_value, [{returned_value, Other}
,{module, Mod}
,{function, listen_init}
,{argument, Arg}]}}
end;
{error, Reason} ->
{stop, Reason}
end.
%% -------------------------------------------------------------------------------------------------
%% Internal functions:
continue_starting(Opts, Pid) ->
AccCount = sockerl_utils:get_value(acceptor_count
,Opts
,?DEF_ACCEPTOR_COUNT
,fun sockerl_utils:is_whole_integer/1),
{ok, ConRootSupPid} = director:get_pid(Pid, ?CONNECTION_SUP),
ConSups = start_connection_sups(AccCount, ConRootSupPid),
{ok, AccSupPid} = director:get_pid(Pid, ?ACCEPTOR_SUP),
ok = start_acceptors(ConSups, AccSupPid),
{ok, Pid}.
start_connection_sups(AccCount, ConRootSupPid) ->
start_connection_sups(AccCount, ConRootSupPid, []).
start_connection_sups(0, _ConRootSupPid, ConSups) ->
ConSups;
start_connection_sups(Count, ConRootSupPid, ConSups) ->
{ok, ConSupPid} = sockerl_server_connection_sup:add(ConRootSupPid, Count),
start_connection_sups(Count-1, ConRootSupPid, [{Count, ConSupPid} | ConSups]).
start_acceptors(ConSups, AccSup) ->
lists:foreach(fun({Id, ConSup}) -> sockerl_acceptor_sup:add(ConSup, AccSup, Id) end,ConSups).
log_validator('$director', {warning, _}) ->
long;
log_validator('$director', {error, normal}) ->
none;
log_validator('$director', {error, _}) ->
long;
log_validator(_, {info, start}) ->
none;
log_validator(_, {error, normal}) ->
none;
log_validator(_, {error, _}) ->
long.