./ct_report/coverage/mongoose_s2s_cets.COVER.html

1 -module(mongoose_s2s_cets).
2 -behaviour(mongoose_s2s_backend).
3
4 -export([init/1,
5 get_s2s_out_pids/1,
6 try_register/2,
7 remove_connection/2,
8 node_cleanup/1]).
9
10 -export([register_secret/2,
11 get_shared_secret/1]).
12
13 %% Internal usage (export so the callback would survive multiple code reloads)
14 -export([handle_secret_conflict/2]).
15
16 -include("mongoose_logger.hrl").
17
18 -define(TABLE, cets_s2s_session).
19 -define(SECRET_TABLE, cets_s2s_secret).
20
21 -type secret_tuple() :: {HostType :: mongooseim:host_type(), TS :: integer(), Secret :: ejabberd_s2s:base16_secret()}.
22
23 -spec init(map()) -> ok.
24 init(_) ->
25 35 cets:start(?TABLE, #{}),
26 %% Non-random, non-node-specific keys
27 %% This means that default merging would not work
28 35 cets:start(?SECRET_TABLE, #{handle_conflict => fun ?MODULE:handle_secret_conflict/2}),
29 35 cets_discovery:add_table(mongoose_cets_discovery, ?TABLE),
30 35 cets_discovery:add_table(mongoose_cets_discovery, ?SECRET_TABLE),
31 35 ok.
32
33 %% Chooses the most recent value of two.
34 %% Even if we choose the wrong record - nothing bad would happen
35 %% (we still need to choose one).
36 %% Choosing the record with the highest timestamp is just a logical behaviour
37 %% (it also matches the logic of mongoose_s2s_lib:check_shared_secret/2, where updated secret
38 %% in the config is updated across all nodes in the cluster).
39 %% Example call:
40 %% handle_secret_conflict({<<"localhost">>, 1689858975612268, <<"4e48dc4898b23f512059">>},
41 %% {<<"localhost">>, 1689859177195451, <<"56fdcd3ec63ff8299eb0">>}).
42 -spec handle_secret_conflict(secret_tuple(), secret_tuple()) -> secret_tuple().
43 handle_secret_conflict(Rec1, Rec2) when Rec1 > Rec2 ->
44 164 Rec1;
45 handle_secret_conflict(_Rec1, Rec2) ->
46 9 Rec2.
47
48 %% Pid lists
49 -spec get_s2s_out_pids(ejabberd_s2s:fromto()) -> ejabberd_s2s:s2s_pids().
50 get_s2s_out_pids(FromTo) ->
51 164 R = {{FromTo, '$1'}},
52 164 ets:select(?TABLE, [{R, [], ['$1']}]).
53
54 -spec try_register(Pid :: pid(),
55 FromTo :: ejabberd_s2s:fromto()) -> boolean().
56 try_register(Pid, FromTo) ->
57 37 Pids = get_s2s_out_pids(FromTo),
58 37 case mongoose_s2s_lib:need_more_connections(FromTo, Pids) of
59 true ->
60 25 cets:insert(?TABLE, {{FromTo, Pid}}),
61 25 true;
62 false ->
63 12 false
64 end.
65
66 -spec remove_connection(FromTo :: ejabberd_s2s:fromto(), Pid :: pid()) -> ok.
67 remove_connection(FromTo, Pid) ->
68 25 cets:delete(?TABLE, {FromTo, Pid}),
69 25 ok.
70
71 %% node_cleanup is called on each node in the cluster, when Node is down
72 -spec node_cleanup(Node :: node()) -> ok.
73 node_cleanup(Node) ->
74
:-(
KeyPattern = {'_', '$1'},
75
:-(
R = {KeyPattern},
76
:-(
Guard = {'==', {node, '$1'}, Node},
77
:-(
ets:select_delete(?TABLE, [{R, [Guard], [true]}]),
78
:-(
ok.
79
80 %% Secrets
81 -spec register_secret(HostType :: mongooseim:host_type(),
82 Secret :: ejabberd_s2s:base16_secret()) -> ok.
83 register_secret(HostType, Secret) ->
84 %% We store timestamp so we could use it when merging two tables when clustering.
85 %% Secrets is a very small table and get_shared_secret is called rarely,
86 %% so having an extra field is not a problem.
87 186 TS = erlang:system_time(microsecond),
88 186 cets:insert(?SECRET_TABLE, {HostType, TS, Secret}),
89 186 ok.
90
91 -spec get_shared_secret(mongooseim:host_type()) ->
92 {ok, ejabberd_s2s:base16_secret()} | {error, not_found}.
93 get_shared_secret(HostType) ->
94 222 case ets:lookup(?SECRET_TABLE, HostType) of
95 [{_HostType, _TS, Secret}] ->
96 38 {ok, Secret};
97 [] ->
98 184 {error, not_found}
99 end.
Line Hits Source