1 |
|
-module(mongoose_c2s_listener). |
2 |
|
|
3 |
|
-include("mongoose.hrl"). |
4 |
|
|
5 |
|
-behaviour(mongoose_listener). |
6 |
|
-export([start_listener/1]). |
7 |
|
|
8 |
|
-behaviour(ranch_protocol). |
9 |
|
-export([start_link/3]). |
10 |
|
|
11 |
|
-behaviour(supervisor). |
12 |
|
-export([start_link/1, init/1]). |
13 |
|
-ignore_xref([start_link/1]). |
14 |
|
|
15 |
|
%% Hook handlers |
16 |
|
-export([handle_user_open_session/3]). |
17 |
|
|
18 |
|
-type options() :: #{module := module(), |
19 |
|
atom() => any()}. |
20 |
|
|
21 |
|
%% mongoose_listener |
22 |
|
-spec start_listener(options()) -> ok. |
23 |
|
start_listener(Opts) -> |
24 |
205 |
ListenerId = mongoose_listener_config:listener_id(Opts), |
25 |
205 |
ChildSpec = listener_child_spec(ListenerId, Opts), |
26 |
205 |
mongoose_listener_sup:start_child(ChildSpec), |
27 |
205 |
ok. |
28 |
|
|
29 |
|
%% Hooks and handlers |
30 |
|
-spec handle_user_open_session(mongoose_acc:t(), mongoose_c2s_hooks:params(), gen_hook:extra()) -> |
31 |
|
mongoose_c2s_hooks:result(). |
32 |
|
handle_user_open_session(Acc, #{c2s_data := StateData}, |
33 |
|
#{host_type := HostType, listener_id := ListenerId}) -> |
34 |
11616 |
ListenerOpts = mongoose_c2s:get_listener_opts(StateData), |
35 |
11616 |
case mongoose_listener_config:listener_id(ListenerOpts) of |
36 |
|
ListenerId -> |
37 |
5657 |
Jid = mongoose_c2s:get_jid(StateData), |
38 |
5657 |
LServer = mongoose_c2s:get_lserver(StateData), |
39 |
5657 |
#{access := Access} = ListenerOpts, |
40 |
5657 |
case acl:match_rule(HostType, LServer, Access, Jid) of |
41 |
|
allow -> |
42 |
5655 |
case mongoose_hooks:session_opening_allowed_for_user(HostType, Jid) of |
43 |
5655 |
allow -> {ok, Acc}; |
44 |
:-( |
_ -> {stop, Acc} |
45 |
|
end; |
46 |
|
deny -> |
47 |
2 |
{stop, Acc} |
48 |
|
end; |
49 |
|
_Other -> |
50 |
5959 |
{ok, Acc} |
51 |
|
end. |
52 |
|
|
53 |
|
%% ranch_protocol |
54 |
|
start_link(Ref, Transport, Opts = #{hibernate_after := HibernateAfterTimeout}) -> |
55 |
6624 |
mongoose_c2s:start_link({mongoose_c2s_ranch, {Transport, Ref}, Opts}, [{hibernate_after, HibernateAfterTimeout}]). |
56 |
|
|
57 |
|
%% supervisor |
58 |
|
-spec start_link(options()) -> any(). |
59 |
|
start_link(Opts) -> |
60 |
205 |
supervisor:start_link(?MODULE, Opts). |
61 |
|
|
62 |
|
-spec init(options()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. |
63 |
|
init(#{module := Module} = Opts) -> |
64 |
205 |
HostTypes = ?ALL_HOST_TYPES, |
65 |
205 |
TransportOpts = prepare_socket_opts(Opts), |
66 |
205 |
ListenerId = mongoose_listener_config:listener_id(Opts), |
67 |
205 |
maybe_add_access_check(HostTypes, Opts, ListenerId), |
68 |
205 |
Child = ranch:child_spec(ListenerId, ranch_tcp, TransportOpts, Module, Opts), |
69 |
205 |
{ok, {#{strategy => one_for_one, intensity => 100, period => 1}, [Child]}}. |
70 |
|
|
71 |
|
maybe_add_access_check(_, #{access := all}, _) -> |
72 |
:-( |
ok; |
73 |
|
maybe_add_access_check(HostTypes, _, ListenerId) -> |
74 |
205 |
AclHooks = [ {user_open_session, HostType, fun ?MODULE:handle_user_open_session/3, |
75 |
|
#{listener_id => ListenerId}, 10} |
76 |
205 |
|| HostType <- HostTypes ], |
77 |
205 |
gen_hook:add_handlers(AclHooks). |
78 |
|
|
79 |
|
listener_child_spec(ListenerId, Opts) -> |
80 |
205 |
#{id => ListenerId, |
81 |
|
start => {?MODULE, start_link, [Opts]}, |
82 |
|
restart => permanent, |
83 |
|
shutdown => infinity, |
84 |
|
type => supervisor, |
85 |
|
modules => [?MODULE]}. |
86 |
|
|
87 |
|
prepare_socket_opts(#{port := Port, |
88 |
|
ip_version := IPVersion, |
89 |
|
ip_tuple := IPTuple, |
90 |
|
backlog := Backlog, |
91 |
|
num_acceptors := NumAcceptors, |
92 |
|
max_connections := MaxConnections, |
93 |
|
reuse_port := ReusePort}) -> |
94 |
205 |
SocketOpts = [{nodelay, true}, |
95 |
|
{keepalive, true}, |
96 |
|
{ip, IPTuple}, |
97 |
|
{port, Port}, |
98 |
|
{backlog, Backlog}, |
99 |
|
mongoose_listener_config:address_family(IPVersion) |
100 |
|
| maybe_reuseport(ReusePort)], |
101 |
205 |
#{max_connections => MaxConnections, |
102 |
|
num_acceptors => NumAcceptors, |
103 |
|
num_listen_sockets => num_listen_sockets(ReusePort), |
104 |
|
socket_opts => SocketOpts}. |
105 |
|
|
106 |
205 |
maybe_reuseport(false) -> []; |
107 |
:-( |
maybe_reuseport(true) -> [{raw, 1, 15, <<1:32/native>>}]. |
108 |
|
|
109 |
205 |
num_listen_sockets(false) -> 1; |
110 |
:-( |
num_listen_sockets(true) -> erlang:system_info(schedulers_online). |