Skip to content

Commit

Permalink
adding mongoose_lazy_routing module
Browse files Browse the repository at this point in the history
  • Loading branch information
DenysGonchar committed May 17, 2021
1 parent 742aa04 commit bdc1bc2
Show file tree
Hide file tree
Showing 10 changed files with 260 additions and 54 deletions.
53 changes: 49 additions & 4 deletions src/domain/mongoose_domain_api.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,36 @@
-module(mongoose_domain_api).

-export([init/0,
insert_domain/2,
get_host_type/1]).

%% domain API
-export([insert_domain/2,
delete_domain/2,
disable_domain/1,
enable_domain/1,
get_host_type/1,
get_domain_host_type/1,
get_all_static/0,
get_domains_by_host_type/1]).

%% subdomain API
-export([register_subdomain/3,
unregister_subdomain/2,
get_subdomain_host_type/1,
get_subdomain_info/1]).

-type domain() :: jid:lserver().
-type host_type() :: mongooseim:host_type().
-type pair() :: {domain(), host_type()}.
-type subdomain_pattern() :: mongoose_subdomain_utils:subdomain_pattern().


-spec init() -> ok | {error, term()}.
init() ->
Pairs = get_static_pairs(),
AllowedHostTypes = ejabberd_config:get_global_option_or_default(host_types, []),
mongoose_domain_core:start(Pairs, AllowedHostTypes).
mongoose_domain_core:start(Pairs, AllowedHostTypes),
mongoose_subdomain_core:start(),
mongoose_lazy_routing:start().

%% Domain should be nameprepped using `jid:nameprep'.
-spec insert_domain(domain(), host_type()) ->
Expand Down Expand Up @@ -90,8 +102,30 @@ check_db(Result) ->
-spec get_host_type(domain()) ->
{ok, host_type()} | {error, not_found}.
get_host_type(Domain) ->
case get_domain_host_type(Domain) of
{ok, HostType} -> {ok, HostType};
{error, not_found} ->
get_subdomain_host_type(Domain)
end.

%% Domain should be nameprepped using `jid:nameprep'
-spec get_domain_host_type(domain()) ->
{ok, host_type()} | {error, not_found}.
get_domain_host_type(Domain) ->
mongoose_domain_core:get_host_type(Domain).

%% Subdomain should be nameprepped using `jid:nameprep'
-spec get_subdomain_host_type(domain()) ->
{ok, host_type()} | {error, not_found}.
get_subdomain_host_type(Subdomain) ->
mongoose_subdomain_core:get_host_type(Subdomain).

%% Subdomain should be nameprepped using `jid:nameprep'
-spec get_subdomain_info(domain()) ->
{ok, mongoose_subdomain_core:subdomain_info()} | {error, not_found}.
get_subdomain_info(Subdomain) ->
mongoose_subdomain_core:get_subdomain_info(Subdomain).

%% Get the list of the host_types provided during initialisation
%% This has complexity N, where N is the number of online domains.
-spec get_all_static() -> [{domain(), host_type()}].
Expand Down Expand Up @@ -120,6 +154,17 @@ check_domain(Domain, HostType) ->
end.

%% Domains should be nameprepped using `jid:nameprep'
-spec get_static_pairs() -> [{domain(), host_type()}].
-spec get_static_pairs() -> [pair()].
get_static_pairs() ->
[{H, H} || H <- ejabberd_config:get_global_option_or_default(hosts, [])].

-spec register_subdomain(host_type(), subdomain_pattern(),
mongoose_packet_handler:t()) ->
ok | {error, already_registered | subdomain_already_exists}.
register_subdomain(HostType, SubdomainPattern, PacketHandler) ->
mongoose_subdomain_core:register_subdomain(HostType, SubdomainPattern,
PacketHandler).

-spec unregister_subdomain(host_type(), subdomain_pattern()) -> ok.
unregister_subdomain(HostType, SubdomainPattern) ->
mongoose_subdomain_core:unregister_subdomain(HostType, SubdomainPattern).
6 changes: 5 additions & 1 deletion src/domain/mongoose_domain_core.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
%% Generally, you should not call anything from this module.
%% Use mongoose_domain_api module instead.
-module(mongoose_domain_core).
-behaviour(gen_server).

-include("mongoose_logger.hrl").

%% required for ets:fun2ms/1 pseudo function
Expand Down Expand Up @@ -37,6 +39,7 @@
-type domain() :: mongooseim:domain_name().

-ifdef(TEST).

%% required for unit tests
start(Pairs, AllowedHostTypes) ->
just_ok(gen_server:start({local, ?MODULE}, ?MODULE, [Pairs, AllowedHostTypes], [])).
Expand Down Expand Up @@ -123,6 +126,7 @@ get_start_args() ->

%%--------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
init([Pairs, AllowedHostTypes]) ->
ets:new(?TABLE, [set, named_table, protected, {read_concurrency, true}]),
ets:new(?HOST_TYPE_TABLE, [set, named_table, protected, {read_concurrency, true}]),
Expand Down Expand Up @@ -199,7 +203,7 @@ handle_delete(Domain) ->
[{Domain, HostType, _Source}] ->
ets:delete(?TABLE, Domain),
mongoose_subdomain_core:remove_domain(HostType, Domain),
mongoose_hooks:disable_domain(HostType, Domain),
mongoose_lazy_routing:maybe_remove_domain(HostType, Domain),
ok
end.

Expand Down
171 changes: 171 additions & 0 deletions src/domain/mongoose_lazy_routing.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
%% Generally, you should not call anything from this module.
%% Use mongoose_domain_api module instead.
-module(mongoose_lazy_routing).
-behaviour(gen_server).

-include("mongoose_logger.hrl").

%% API
-export([start/0, stop/0]).
-export([start_link/0]).

-export([maybe_add_domain_or_subdomain/1,
maybe_remove_domain/2,
maybe_remove_subdomain/1]).

%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).

%%--------------------------------------------------------------------
%% API
%%--------------------------------------------------------------------
-ifdef(TEST).

%% required for unit tests
start() ->
just_ok(gen_server:start({local, ?MODULE}, ?MODULE, [], [])).

stop() ->
gen_server:stop(?MODULE).

-else.

start() ->
ChildSpec = {?MODULE, {?MODULE, start_link, []},
permanent, infinity, worker, [?MODULE]},
just_ok(supervisor:start_child(ejabberd_sup, ChildSpec)).

%% required for integration tests
stop() ->
supervisor:terminate_child(ejabberd_sup, ?MODULE),
supervisor:delete_child(ejabberd_sup, ?MODULE),
ok.

-endif.

start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

-spec maybe_add_domain_or_subdomain(mongooseim:domain()) -> boolean().
maybe_add_domain_or_subdomain(Domain) ->
%% it can be domain or subdomain name
gen_server:call(?MODULE, {maybe_add_domain_or_subdomain, Domain}).

-spec maybe_remove_domain(mongooseim:host_type(), mongooseim:domain()) -> ok.
maybe_remove_domain(HostType, Domain) ->
gen_server:cast(?MODULE, {maybe_remove_domain, HostType, Domain}).

-spec maybe_remove_subdomain(mongoose_subdomain_core:subdomain_info()) -> ok.
maybe_remove_subdomain(SubdomainInfo) ->
gen_server:cast(?MODULE, {maybe_remove_subdomain, SubdomainInfo}).

%%--------------------------------------------------------------------
%% gen_server callbacks
%%--------------------------------------------------------------------
init(_) ->
ets:new(?MODULE, [set, named_table, protected]),
{ok,ok}.

handle_call({maybe_add_domain_or_subdomain, Domain}, _From, State) ->
RetValue = handle_maybe_add_domain_or_subdomain(Domain),
{reply, RetValue, State};
handle_call(Request, From, State) ->
?UNEXPECTED_CALL(Request, From),
{reply, ok, State}.

handle_cast({maybe_remove_domain, HostType, Domain}, State) ->
handle_maybe_remove_domain(HostType, Domain),
{noreply, State};
handle_cast({maybe_remove_subdomain, SubdomainInfo}, State) ->
handle_maybe_remove_subdomain(SubdomainInfo),
{noreply, State};
handle_cast(Msg, State) ->
?UNEXPECTED_CAST(Msg),
{noreply, State}.

handle_info(Info, State) ->
?UNEXPECTED_INFO(Info),
{noreply, State}.

terminate(_Reason, _State) ->
ok.

code_change(_OldVsn, State, _Extra) ->
{ok, State}.

%%--------------------------------------------------------------------
%% local functions
%%--------------------------------------------------------------------
just_ok({ok, _}) -> ok;
just_ok(Other) -> Other.

handle_maybe_add_domain_or_subdomain(Domain) ->
case ets:lookup(?MODULE, Domain) of
[_] ->
%% It's absolutely normal situation. We can receive
%% a couple of maybe_add_domain_or_subdomain requests
%% from different processes, so this domain can be
%% already added.
true;
[] ->
try_to_add_domain_or_subdomain(Domain)
end.

try_to_add_domain_or_subdomain(Domain) ->
case mongoose_domain_api:get_host_type(Domain) of
{ok, HostType} ->
add_domain(HostType, Domain),
true;
{error, not_found} ->
case mongoose_domain_api:get_subdomain_info(Domain) of
{ok, Info} ->
add_subdomain(Info),
true;
{error, not_found} ->
false
end
end.

add_domain(HostType, Domain) ->
case ets:insert_new(?MODULE, {Domain, HostType}) of
true ->
%% TODO: register IQ handlers before domain registration
ejabberd_local:register_host(Domain);
false ->
%% we should never get here, but it's ok to just ignore this.
ok
end.

add_subdomain(#{subdomain := Subdomain, host_type := HostType,
subdomain_pattern := SubdomainPattern,
packet_handler := PacketHandler}) ->
case ets:insert_new(?MODULE, {Subdomain, {HostType, SubdomainPattern}}) of
true ->
%% TODO: register IQ handlers before subdomain registration
ejabberd_router:register_route(Subdomain, PacketHandler);
false ->
%% we should never get here, but it's ok to just ignore this.
ok
end.

handle_maybe_remove_domain(HostType, Domain) ->
case ets:lookup(?MODULE, Domain) of
[{Domain, HostType}] ->
ejabberd_local:unregister_host(Domain);
%% TODO: unregister IQ handlers after domain
_ -> ok
end.

handle_maybe_remove_subdomain(#{subdomain := Subdomain, host_type := HostType,
subdomain_pattern := SubdomainPattern}) ->
case ets:lookup(?MODULE, Subdomain) of
[{Domain, {HostType, SubdomainPattern}}] ->
ejabberd_router:unregister_route(Domain);
%% TODO: unregister IQ handlers after domain
_ -> ok
end.
14 changes: 8 additions & 6 deletions src/domain/mongoose_subdomain_core.erl
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
%% API
%%--------------------------------------------------------------------
-ifdef(TEST).

%% required for unit tests
start() ->
just_ok(gen_server:start({local, ?MODULE}, ?MODULE, [], [])).
Expand Down Expand Up @@ -247,15 +248,16 @@ handle_remove_domain(HostType, Domain) ->

-spec remove_subdomains([subdomain_item()]) -> ok.
remove_subdomains(SubdomainItems) ->
Fn = fun(#subdomain_item{host_type = HostType, subdomain = Subdomain}) ->
remove_subdomain(HostType, Subdomain)
Fn = fun(SubdomainItem) ->
remove_subdomain(SubdomainItem)
end,
lists:foreach(Fn, SubdomainItems).

-spec remove_subdomain(host_type(), domain()) -> true.
remove_subdomain(HostType, Subdomain) ->
mongoose_hooks:disable_subdomain(HostType, Subdomain),
ets:delete(?SUBDOMAINS_TABLE, Subdomain).
-spec remove_subdomain(subdomain_item()) -> ok.
remove_subdomain(#subdomain_item{subdomain = Subdomain} = SubdomainItem) ->
ets:delete(?SUBDOMAINS_TABLE, Subdomain),
SubdomainInfo = convert_subdomain_item_to_map(SubdomainItem),
mongoose_lazy_routing:maybe_remove_subdomain(SubdomainInfo).

-spec add_subdomains([reg_item()], domain()) -> ok.
add_subdomains(RegItems, Domain) ->
Expand Down
6 changes: 0 additions & 6 deletions src/ejabberd_local.erl
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,6 @@ node_cleanup(Acc, Node) ->
Res = mnesia:async_dirty(F),
maps:put(?MODULE, Res, Acc).

disable_domain(_Acc, _HostType, Domain) ->
unregister_host(Domain).

%%====================================================================
%% gen_server callbacks
%%====================================================================
Expand All @@ -266,14 +263,12 @@ disable_domain(_Acc, _HostType, Domain) ->
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
lists:foreach(fun do_register_host/1, ?MYHOSTS),
catch ets:new(?IQTABLE, [named_table, protected]),
update_table(),
mnesia:create_table(iq_response,
[{ram_copies, [node()]},
{attributes, record_info(fields, iq_response)}]),
mnesia:add_table_copy(iq_response, node(), ram_copies),
ejabberd_hooks:add(disable_domain, global, fun disable_domain/3,50),
ejabberd_hooks:add(node_cleanup, global, ?MODULE, node_cleanup, 50),
{ok, #state{}}.

Expand Down Expand Up @@ -355,7 +350,6 @@ handle_info(_Info, State) ->
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ejabberd_hooks:delete(node_cleanup, global, ?MODULE, node_cleanup, 50),
ejabberd_hooks:delete(disable_domain, global, fun disable_domain/3, 50),
ok.

%%--------------------------------------------------------------------
Expand Down
19 changes: 1 addition & 18 deletions src/mongoose_hooks.erl
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@

-export([c2s_remote_hook/5]).

-export([disable_domain/2,
disable_subdomain/2,
remove_domain/2,
-export([remove_domain/2,
node_cleanup/1]).

%% Just a map, used by some hooks as a first argument.
Expand Down Expand Up @@ -233,21 +231,6 @@ anonymous_purge_hook(LServer, Acc, LUser) ->
auth_failed(HostType, Server, Username) ->
ejabberd_hooks:run_for_host_type(auth_failed, HostType, ok, [Username, Server]).

-spec disable_domain(HostType, Domain) -> Result when
HostType :: mongooseim:host_type(),
Domain :: jid:lserver(),
Result :: ok.
disable_domain(HostType, Domain) ->
ejabberd_hooks:run_global(disable_domain, ok, [HostType, Domain]).


-spec disable_subdomain(HostType, Subdomain) -> Result when
HostType :: mongooseim:host_type(),
Subdomain :: jid:lserver(),
Result :: ok.
disable_subdomain(HostType, Subdomain) ->
ejabberd_hooks:run_global(disable_subdomain, ok, [HostType, Subdomain]).

-spec remove_domain(HostType, Domain) -> Result when
HostType :: binary(),
Domain :: jid:lserver(),
Expand Down
Loading

0 comments on commit bdc1bc2

Please sign in to comment.