./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
:-(
cets:start(?TABLE, #{}),
26 %% Non-random, non-node-specific keys
27 %% This means that default merging would not work
28
:-(
cets:start(?SECRET_TABLE, #{handle_conflict => fun ?MODULE:handle_secret_conflict/2}),
29
:-(
cets_discovery:add_table(mongoose_cets_discovery, ?TABLE),
30
:-(
cets_discovery:add_table(mongoose_cets_discovery, ?SECRET_TABLE),
31
:-(
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
:-(
Rec1;
45 handle_secret_conflict(_Rec1, Rec2) ->
46
:-(
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
:-(
R = {{FromTo, '$1'}},
52
:-(
ets:select(?TABLE, [{R, [], ['$1']}]).
53
54 -spec try_register(Pid :: pid(),
55 FromTo :: ejabberd_s2s:fromto()) -> boolean().
56 try_register(Pid, FromTo) ->
57
:-(
Pids = get_s2s_out_pids(FromTo),
58
:-(
case mongoose_s2s_lib:need_more_connections(FromTo, Pids) of
59 true ->
60
:-(
cets:insert(?TABLE, {{FromTo, Pid}}),
61
:-(
true;
62 false ->
63
:-(
false
64 end.
65
66 -spec remove_connection(FromTo :: ejabberd_s2s:fromto(), Pid :: pid()) -> ok.
67 remove_connection(FromTo, Pid) ->
68
:-(
cets:delete(?TABLE, {FromTo, Pid}),
69
:-(
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
:-(
TS = erlang:system_time(microsecond),
88
:-(
cets:insert(?SECRET_TABLE, {HostType, TS, Secret}),
89
:-(
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
:-(
case ets:lookup(?SECRET_TABLE, HostType) of
95 [{_HostType, _TS, Secret}] ->
96
:-(
{ok, Secret};
97 [] ->
98
:-(
{error, not_found}
99 end.
Line Hits Source