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 |
359 |
gen_server:start_link({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], []). |
46 |
|
|
47 |
|
get_host_type(Domain) -> |
48 |
131682 |
case ets:lookup(?TABLE, Domain) of |
49 |
|
[] -> |
50 |
6463 |
{error, not_found}; |
51 |
|
[{_Domain, HostType, _Source}] -> |
52 |
125219 |
{ok, HostType} |
53 |
|
end. |
54 |
|
|
55 |
|
is_static(Domain) -> |
56 |
222 |
case ets:lookup(?TABLE, Domain) of |
57 |
|
[{_Domain, _HostType, _Source = config}] -> |
58 |
55 |
true; |
59 |
|
_ -> |
60 |
167 |
false |
61 |
|
end. |
62 |
|
|
63 |
|
is_host_type_allowed(HostType) -> |
64 |
347 |
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 |
43 |
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 |
599 |
ets:safe_fixtable(?TABLE, true), |
81 |
599 |
MS = ets:fun2ms(fun({Domain, HT, _}) when HT =:= HostType -> |
82 |
|
[HostType, Domain] |
83 |
|
end), |
84 |
599 |
Selection = ets:select(?TABLE, MS, 100), |
85 |
599 |
for_each_selected_domain(Selection, Func), |
86 |
599 |
ets:safe_fixtable(?TABLE, false), |
87 |
599 |
ok. |
88 |
|
|
89 |
|
get_all_outdated(CurrentSource) -> |
90 |
11 |
MS = ets:fun2ms(fun({Domain, HostType, {dynamic, Src}}) when Src =/= CurrentSource -> |
91 |
|
{Domain, HostType} |
92 |
|
end), |
93 |
11 |
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 |
199 |
gen_server:call(?MODULE, {insert, Domain, HostType, Source}). |
103 |
|
|
104 |
|
delete(Domain) -> |
105 |
119 |
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 |
359 |
mongoose_loader_state:init(), |
115 |
359 |
ets:new(?TABLE, [set, named_table, protected, {read_concurrency, true}]), |
116 |
359 |
ets:new(?HOST_TYPE_TABLE, [set, named_table, protected, {read_concurrency, true}]), |
117 |
359 |
insert_host_types(?HOST_TYPE_TABLE, AllowedHostTypes), |
118 |
359 |
insert_initial(?TABLE, Pairs), |
119 |
359 |
{ok, #{initial_pairs => Pairs, |
120 |
|
initial_host_types => AllowedHostTypes}}. |
121 |
|
|
122 |
|
handle_call({delete, Domain}, _From, State) -> |
123 |
119 |
Result = handle_delete(Domain), |
124 |
119 |
{reply, Result, State}; |
125 |
|
handle_call({insert, Domain, HostType, Source}, _From, State) -> |
126 |
199 |
Result = handle_insert(Domain, HostType, Source), |
127 |
199 |
{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 |
599 |
for_each_selected_domain('$end_of_table', _) -> ok; |
154 |
|
for_each_selected_domain({MatchList, Continuation}, Func) -> |
155 |
497 |
[safely:apply_and_log(Func, Args, log_context(Args)) || Args <- MatchList], |
156 |
497 |
Selection = ets:select(Continuation), |
157 |
497 |
for_each_selected_domain(Selection, Func). |
158 |
|
|
159 |
|
log_context(Args) -> |
160 |
499 |
#{what => domain_operation_failed, |
161 |
|
args => Args}. |
162 |
|
|
163 |
|
insert_initial(Tab, Pairs) -> |
164 |
359 |
lists:foreach(fun({Domain, HostType}) -> |
165 |
691 |
insert_initial_pair(Tab, Domain, HostType) |
166 |
|
end, Pairs). |
167 |
|
|
168 |
|
insert_initial_pair(Tab, Domain, HostType) -> |
169 |
691 |
ets:insert_new(Tab, new_object(Domain, HostType, config)). |
170 |
|
|
171 |
|
new_object(Domain, HostType, Source) -> |
172 |
864 |
{Domain, HostType, Source}. |
173 |
|
|
174 |
|
insert_host_types(Tab, AllowedHostTypes) -> |
175 |
359 |
lists:foreach(fun(HostType) -> |
176 |
1882 |
ets:insert_new(Tab, {HostType}) |
177 |
|
end, AllowedHostTypes), |
178 |
359 |
ok. |
179 |
|
|
180 |
|
handle_delete(Domain) -> |
181 |
119 |
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 |
41 |
ok; |
189 |
|
[{Domain, HostType, _Source}] -> |
190 |
78 |
ets:delete(?TABLE, Domain), |
191 |
78 |
mongoose_lazy_routing:maybe_remove_domain(HostType, Domain), |
192 |
78 |
mongoose_subdomain_core:remove_domain(HostType, Domain), |
193 |
78 |
ok |
194 |
|
end. |
195 |
|
|
196 |
|
handle_insert(Domain, HostType, Source) -> |
197 |
199 |
case is_host_type_allowed(HostType) of |
198 |
|
true -> |
199 |
175 |
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 |
166 |
ets:insert_new(?TABLE, new_object(Domain, HostType, {dynamic, Source})), |
206 |
166 |
mongoose_subdomain_core:add_domain(HostType, Domain), |
207 |
166 |
ok; |
208 |
|
[{Domain, HT, _Source}] when HT =:= HostType -> |
209 |
7 |
ets:insert(?TABLE, new_object(Domain, HostType, {dynamic, Source})), |
210 |
7 |
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 |
24 |
?LOG_ERROR(#{what => ignore_domain_from_db_with_unknown_host_type, |
220 |
:-( |
domain => Domain, host_type => HostType}), |
221 |
24 |
{error, unknown_host_type} |
222 |
|
|
223 |
|
end. |
224 |
|
|