./ct_report/coverage/mongoose_domain_core.COVER.html

1 %% Generally, you should not call anything from this module.
2 %% Use mongoose_domain_api module instead.
3 -module(mongoose_domain_core).
4 -behaviour(gen_server).
5
6 -include("mongoose_logger.hrl").
7
8 %% required for ets:fun2ms/1 pseudo function
9 -include_lib("stdlib/include/ms_transform.hrl").
10
11 -export([start_link/2]).
12 -export([get_host_type/1]).
13 -export([is_static/1]).
14
15 %% API, used by DB module
16 -export([insert/3,
17 delete/1]).
18
19 -export([get_all_static/0,
20 get_all_dynamic/0,
21 get_all_outdated/1,
22 get_domains_by_host_type/1,
23 domains_count/0]).
24
25 -export([for_each_domain/2]).
26
27 -export([is_host_type_allowed/1]).
28
29 %% For testing
30 -export([get_start_args/0]).
31
32 %% gen_server callbacks
33 -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
34 terminate/2, code_change/3]).
35
36 -ignore_xref([get_start_args/0, start_link/2]).
37
38 -define(TABLE, ?MODULE).
39 -define(HOST_TYPE_TABLE, mongoose_domain_core_host_types).
40
41 -type host_type() :: mongooseim:host_type().
42 -type domain() :: mongooseim:domain_name().
43
44 start_link(Pairs, AllowedHostTypes) ->
45 144 gen_server:start_link({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], []).
46
47 get_host_type(Domain) ->
48 115642 case ets:lookup(?TABLE, Domain) of
49 [] ->
50 5349 {error, not_found};
51 [{_Domain, HostType, _Source}] ->
52 110293 {ok, HostType}
53 end.
54
55 is_static(Domain) ->
56 231 case ets:lookup(?TABLE, Domain) of
57 [{_Domain, _HostType, _Source = config}] ->
58 21 true;
59 _ ->
60 210 false
61 end.
62
63 is_host_type_allowed(HostType) ->
64 617 ets:member(?HOST_TYPE_TABLE, HostType).
65
66 get_all_static() ->
67 2 pairs(ets:match(?TABLE, {'$1', '$2', config})).
68
69 get_all_dynamic() ->
70 1 pairs(ets:match(?TABLE, {'$1', '$2', {dynamic, '_'}})).
71
72 get_domains_by_host_type(HostType) when is_binary(HostType) ->
73 107 heads(ets:match(?TABLE, {'$1', HostType, '_'})).
74
75 domains_count() ->
76 37 ets:info(?TABLE, size).
77
78 -spec for_each_domain(host_type(), fun((host_type(), domain())-> any())) -> ok.
79 for_each_domain(HostType, Func) ->
80 491 ets:safe_fixtable(?TABLE, true),
81 491 MS = ets:fun2ms(fun({Domain, HT, _}) when HT =:= HostType ->
82 [HostType, Domain]
83 end),
84 491 Selection = ets:select(?TABLE, MS, 100),
85 491 for_each_selected_domain(Selection, Func),
86 491 ets:safe_fixtable(?TABLE, false),
87 491 ok.
88
89 get_all_outdated(CurrentSource) ->
90 1 MS = ets:fun2ms(fun({Domain, HostType, {dynamic, Src}}) when Src =/= CurrentSource ->
91 {Domain, HostType}
92 end),
93 1 ets:select(?TABLE, MS).
94
95 heads(List) ->
96 107 [H || [H|_] <- List].
97
98 pairs(List) ->
99 3 [{K, V} || [K, V] <- List].
100
101 insert(Domain, HostType, Source) ->
102 460 gen_server:call(?MODULE, {insert, Domain, HostType, Source}).
103
104 delete(Domain) ->
105 125 gen_server:call(?MODULE, {delete, Domain}).
106
107 get_start_args() ->
108
:-(
gen_server:call(?MODULE, get_start_args).
109
110 %%--------------------------------------------------------------------
111 %% gen_server callbacks
112 %%--------------------------------------------------------------------
113 init([Pairs, AllowedHostTypes]) ->
114 144 mongoose_loader_state:init(),
115 144 ets:new(?TABLE, [set, named_table, protected, {read_concurrency, true}]),
116 144 ets:new(?HOST_TYPE_TABLE, [set, named_table, protected, {read_concurrency, true}]),
117 144 insert_host_types(?HOST_TYPE_TABLE, AllowedHostTypes),
118 144 insert_initial(?TABLE, Pairs),
119 144 {ok, #{initial_pairs => Pairs,
120 initial_host_types => AllowedHostTypes}}.
121
122 handle_call({delete, Domain}, _From, State) ->
123 125 Result = handle_delete(Domain),
124 125 {reply, Result, State};
125 handle_call({insert, Domain, HostType, Source}, _From, State) ->
126 460 Result = handle_insert(Domain, HostType, Source),
127 460 {reply, Result, State};
128 handle_call(get_start_args, _From, State = #{initial_pairs := Pairs,
129 initial_host_types := AllowedHostTypes}) ->
130
:-(
{reply, [Pairs, AllowedHostTypes], State};
131 handle_call(Request, From, State) ->
132
:-(
?UNEXPECTED_CALL(Request, From),
133
:-(
{reply, ok, State}.
134
135 handle_cast(Msg, State) ->
136
:-(
?UNEXPECTED_CAST(Msg),
137
:-(
{noreply, State}.
138
139 handle_info(Info, State) ->
140
:-(
?UNEXPECTED_INFO(Info),
141
:-(
{noreply, State}.
142
143 terminate(_Reason, _State) ->
144
:-(
ok.
145
146 code_change(_OldVsn, State, _Extra) ->
147
:-(
{ok, State}.
148
149 %%--------------------------------------------------------------------
150 %% internal functions
151 %%--------------------------------------------------------------------
152
153 491 for_each_selected_domain('$end_of_table', _) -> ok;
154 for_each_selected_domain({MatchList, Continuation}, Func) ->
155 488 [safely:apply_and_log(Func, Args, log_context(Args)) || Args <- MatchList],
156 488 Selection = ets:select(Continuation),
157 488 for_each_selected_domain(Selection, Func).
158
159 log_context(Args) ->
160 697 #{what => domain_operation_failed,
161 args => Args}.
162
163 insert_initial(Tab, Pairs) ->
164 144 lists:foreach(fun({Domain, HostType}) ->
165 355 insert_initial_pair(Tab, Domain, HostType)
166 end, Pairs).
167
168 insert_initial_pair(Tab, Domain, HostType) ->
169 355 ets:insert_new(Tab, new_object(Domain, HostType, config)).
170
171 new_object(Domain, HostType, Source) ->
172 758 {Domain, HostType, Source}.
173
174 insert_host_types(Tab, AllowedHostTypes) ->
175 144 lists:foreach(fun(HostType) ->
176 536 ets:insert_new(Tab, {HostType})
177 end, AllowedHostTypes),
178 144 ok.
179
180 handle_delete(Domain) ->
181 125 case ets:lookup(?TABLE, Domain) of
182 [{Domain, _HostType, _Source = config}] ->
183 %% Ignore any static domains
184
:-(
?LOG_ERROR(#{what => domain_static_but_was_in_db, domain => Domain}),
185
:-(
{error, static};
186 [] ->
187 %% nothing to remove
188 48 ok;
189 [{Domain, HostType, _Source}] ->
190 77 ets:delete(?TABLE, Domain),
191 77 mongoose_lazy_routing:maybe_remove_domain(HostType, Domain),
192 77 mongoose_subdomain_core:remove_domain(HostType, Domain),
193 77 ok
194 end.
195
196 handle_insert(Domain, HostType, Source) ->
197 460 case is_host_type_allowed(HostType) of
198 true ->
199 405 case ets:lookup(?TABLE, Domain) of
200 [{Domain, _HostType, _Source = config}] ->
201 %% Ignore any static domains
202 1 ?LOG_ERROR(#{what => domain_static_but_in_db, domain => Domain}),
203 1 {error, static};
204 [] ->
205 393 ets:insert_new(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
206 393 mongoose_subdomain_core:add_domain(HostType, Domain),
207 393 ok;
208 [{Domain, HT, _Source}] when HT =:= HostType ->
209 10 ets:insert(?TABLE, new_object(Domain, HostType, {dynamic, Source})),
210 10 ok;
211 [{Domain, HT, _Source}] when HT =/= HostType ->
212 1 ?LOG_ERROR(#{what => ignore_domain_from_db_with_different_host_type,
213 domain => Domain,
214 core_host_type => HT,
215
:-(
db_host_type => HostType}),
216 1 {error, bad_insert}
217 end;
218 false ->
219 55 ?LOG_ERROR(#{what => ignore_domain_from_db_with_unknown_host_type,
220
:-(
domain => Domain, host_type => HostType}),
221 55 {error, unknown_host_type}
222
223 end.
224
Line Hits Source