Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
chrzaszcz committed Apr 15, 2022
1 parent 4f6a845 commit e8a9a98
Show file tree
Hide file tree
Showing 33 changed files with 1,047 additions and 1,325 deletions.
15 changes: 9 additions & 6 deletions big_tests/tests/connect_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,9 @@ init_per_group(feature_order, Config) ->
configure_c2s_listener(Config, #{zlib => 10000,
tls => [starttls_required | common_tls_opts(Config)]}),
Config;
init_per_group(just_tls,Config)->
init_per_group(just_tls, Config)->
[{tls_module, just_tls} | Config];
init_per_group(fast_tls,Config)->
init_per_group(fast_tls, Config)->
[{tls_module, fast_tls} | Config];
init_per_group(proxy_protocol, Config) ->
configure_c2s_listener(Config, #{proxy_protocol => true}),
Expand Down Expand Up @@ -765,10 +765,13 @@ configure_c2s_listener(Config, ExtraC2SOpts) ->
mongoose_helper:restart_listener(mim(), NewC2SListener).

common_tls_opts(Config) ->
TLSModule = ?config(tls_module, Config),
[{tls_module, TLSModule},
{certfile, ?CERT_FILE},
{dhfile, ?DH_FILE}].
tls_module_opts(Config) ++ [{certfile, ?CERT_FILE}, {dhfile, ?DH_FILE}].

tls_module_opts(Config) ->
case ?config(tls_module, Config) of
undefined -> [];
Module -> [{tls_module, Module}]
end.

set_secure_connection_protocol(UserSpec, Version) ->
[{ssl_opts, [{versions, [Version]}]} | UserSpec].
Expand Down
23 changes: 10 additions & 13 deletions big_tests/tests/domain_rest_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
stop_listener/1]).

-import(distributed_helper, [mim/0, mim2/0, require_rpc_nodes/1, rpc/4]).
-import(config_parser_helper, [default_config/1, config/2]).

-define(TEST_PORT, 8866).

Expand Down Expand Up @@ -98,23 +99,19 @@ patch_custom(Config, Role, Path, Body) ->

%% REST handler setup
start_listener(Params) ->
rpc(mim(), ejabberd_listener, start_listener, [listener_opts(Params)]).
rpc(mim(), mongoose_listener, start_listener, [listener_opts(Params)]).

stop_listener(Params) ->
rpc(mim(), ejabberd_listener, stop_listener, [listener_opts(Params)]).
rpc(mim(), mongoose_listener, stop_listener, [listener_opts(Params)]).

listener_opts(Params) ->
#{port => ?TEST_PORT,
ip_tuple => {127, 0, 0, 1},
ip_address => "127.0.0.1",
ip_version => 4,
proto => tcp,
module => ejabberd_cowboy,
modules => [domain_handler(Params)],
transport_options => transport_options()}.

transport_options() ->
[{max_connections, 1024}, {num_acceptors, 10}].
config([listen, http],
#{port => ?TEST_PORT,
ip_tuple => {127, 0, 0, 1},
ip_address => "127.0.0.1",
module => ejabberd_cowboy,
handlers => [domain_handler(Params)],
transport => config([listen, http, transport], #{num_acceptors => 10})}).

domain_handler(Params) ->
{"localhost", "/api", mongoose_domain_handler, handler_opts(Params)}.
Expand Down
4 changes: 2 additions & 2 deletions big_tests/tests/mongoose_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,8 @@ get_listeners(#{} = Spec, Pattern) ->

%% 'port', 'ip_tuple' and 'proto' options need to stay unchanged for a successful restart
restart_listener(Spec, Listener) ->
rpc(Spec, ejabberd_listener, stop_listener, [Listener]),
rpc(Spec, ejabberd_listener, start_listener, [Listener]).
rpc(Spec, mongoose_listener, stop_listener, [Listener]),
rpc(Spec, mongoose_listener, start_listener, [Listener]).

should_minio_be_running(Config) ->
case proplists:get_value(preset, Config, undefined) of
Expand Down
16 changes: 8 additions & 8 deletions big_tests/tests/rest_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -255,34 +255,34 @@ get_port(Role, Node, _Params) ->
get_ssl_status(Role, Node) ->
Listeners = rpc(Node, mongoose_config, get_opt, [listen]),
[Opts] = lists:filter(fun (Opts) -> is_roles_config(Opts, Role) end, Listeners),
maps:is_key(ssl, Opts).
maps:is_key(tls, Opts).

% @doc Changes the control credentials for admin by restarting the listener
% with new options.
-spec change_admin_creds({User :: binary(), Password :: binary()}) -> 'ok' | 'error'.
change_admin_creds(Creds) ->
stop_admin_listener(),
{ok, _} = start_admin_listener(Creds).
ok = start_admin_listener(Creds).

-spec stop_admin_listener() -> 'ok' | {'error', 'not_found' | 'restarting' | 'running' | 'simple_one_for_one'}.
stop_admin_listener() ->
Listeners = rpc(mim(), mongoose_config, get_opt, [listen]),
[Opts] = lists:filter(fun (Opts) -> is_roles_config(Opts, admin) end, Listeners),
rpc(mim(), ejabberd_listener, stop_listener, [Opts]).
rpc(mim(), mongoose_listener, stop_listener, [Opts]).

-spec start_admin_listener(Creds :: {binary(), binary()}) -> {'error', pid()} | {'ok', _}.
start_admin_listener(Creds) ->
Listeners = rpc(mim(), mongoose_config, get_opt, [listen]),
[Opts] = lists:filter(fun (Opts) -> is_roles_config(Opts, admin) end, Listeners),
NewOpts = insert_creds(Opts, Creds),
rpc(mim(), ejabberd_listener, start_listener, [NewOpts]).
rpc(mim(), mongoose_listener, start_listener, [NewOpts]).

insert_creds(Opts = #{modules := Modules}, Creds) ->
insert_creds(Opts = #{handlers := Modules}, Creds) ->
{Host, Path, mongoose_api_admin, PathOpts} = lists:keyfind(mongoose_api_admin, 3, Modules),
NewPathOpts = inject_creds_to_opts(PathOpts, Creds),
NewModules = lists:keyreplace(mongoose_api_admin, 3, Modules,
{Host, Path, mongoose_api_admin, NewPathOpts}),
Opts#{modules := NewModules}.
Opts#{handlers := NewModules}.

inject_creds_to_opts(PathOpts, any) ->
lists:keydelete(auth, 1, PathOpts);
Expand All @@ -298,9 +298,9 @@ inject_creds_to_opts(PathOpts, Creds) ->
% This is determined based on modules used. If there is any mongoose_api_admin module used,
% it is admin config. If not and there is at least one mongoose_api_client* module used,
% it's clients.
is_roles_config(#{module := ejabberd_cowboy, modules := Modules}, admin) ->
is_roles_config(#{module := ejabberd_cowboy, handlers := Modules}, admin) ->
lists:any(fun({_, _Path, Mod, _Args}) -> Mod == mongoose_api_admin; (_) -> false end, Modules);
is_roles_config(#{module := ejabberd_cowboy, modules := Modules}, client) ->
is_roles_config(#{module := ejabberd_cowboy, handlers := Modules}, client) ->
ModulesTokens = lists:map(fun({_, _Path, Mod, _}) -> string:tokens(atom_to_list(Mod), "_");
(_) -> []
end, Modules),
Expand Down
6 changes: 3 additions & 3 deletions doc/configuration/listen.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ Hibernation greatly reduces memory consumption of client processes, but *may* re
The default, recommended value of 0 means that the client processes will hibernate at every opportunity.

### `listen.*.max_stanza_size`
* **Syntax:** positive integer
* **Default:** not set, unlimited size
* **Syntax:** positive integer or the string `"infinity"`
* **Default:** `"infinity"`
* **Example:** `max_stanza_size = 10_000`

Maximum allowed incoming stanza size in bytes.
Maximum allowed incoming stanza size in bytes.
!!! Warning
This limit is checked **after** the input data parsing, so it does not apply to the input data size itself.

Expand Down
184 changes: 110 additions & 74 deletions src/config/mongoose_config_spec.erl
Original file line number Diff line number Diff line change
Expand Up @@ -262,74 +262,97 @@ listen() ->

%% path: listen.*[]
listener(Type) ->
ExtraItems = listener_items(Type),
#section{
items = ExtraItems#{<<"port">> => #option{type = integer,
validate = port},
<<"ip_address">> => #option{type = string,
validate = ip_address},
<<"proto">> => #option{type = atom,
validate = {enum, [tcp, udp]}},
<<"ip_version">> => #option{type = integer,
validate = {enum, [4, 6]}}
},
format_items = map,
required = [<<"port">>],
defaults = #{<<"proto">> => tcp},
process = fun ?MODULE:process_listener/2
}.

listener_items(<<"http">>) ->
#{<<"tls">> => http_listener_tls(),
<<"transport">> => http_transport(),
<<"protocol">> => http_protocol(),
<<"handlers">> => http_handlers()
};
listener_items(Type) ->
ExtraItems = xmpp_listener_items(Type),
ExtraItems#{<<"hibernate_after">> => #option{type = integer,
validate = non_negative},
<<"max_stanza_size">> => #option{type = integer,
validate = positive},
<<"backlog">> => #option{type = integer,
validate = non_negative},
<<"proxy_protocol">> => #option{type = boolean},
<<"num_acceptors">> => #option{type = integer,
validate = positive,
wrap = {kv, acceptors_num}}
}.

xmpp_listener_items(<<"c2s">>) ->
#{<<"access">> => #option{type = atom,
validate = non_empty},
<<"shaper">> => #option{type = atom,
validate = non_empty},
<<"xml_socket">> => #option{type = boolean},
<<"zlib">> => #option{type = integer,
validate = positive},
<<"max_fsm_queue">> => #option{type = integer,
validate = positive},
<<"allowed_auth_methods">> => #list{items = #option{type = atom,
validate = {module, ejabberd_auth}}},
<<"tls">> => c2s_tls()};
xmpp_listener_items(<<"s2s">>) ->
#{<<"shaper">> => #option{type = atom,
validate = non_empty},
<<"tls">> => s2s_tls()};
xmpp_listener_items(<<"service">>) ->
#{<<"access">> => #option{type = atom,
validate = non_empty},
<<"shaper_rule">> => #option{type = atom,
validate = non_empty},
<<"check_from">> => #option{type = boolean,
wrap = {kv, service_check_from}},
<<"hidden_components">> => #option{type = boolean},
<<"conflict_behaviour">> => #option{type = atom,
validate = {enum, [kick_old, disconnect]}},
<<"password">> => #option{type = string,
validate = non_empty},
<<"max_fsm_queue">> => #option{type = integer,
validate = positive}}.
merge_sections(basic_listener(), listener_extra(Type)).

basic_listener() ->
#section{items = #{<<"port">> => #option{type = integer,
validate = port},
<<"ip_address">> => #option{type = string,
validate = ip_address},
<<"proto">> => #option{type = atom,
validate = {enum, [tcp]}},
<<"ip_version">> => #option{type = integer,
validate = {enum, [4, 6]}}
},
format_items = map,
required = [<<"port">>],
defaults = #{<<"proto">> => tcp},
process = fun ?MODULE:process_listener/2
}.

listener_extra(<<"http">>) ->
#section{items = #{<<"tls">> => http_listener_tls(),
<<"transport">> => http_transport(),
<<"protocol">> => http_protocol(),
<<"handlers">> => http_handlers()}};
listener_extra(Type) ->
merge_sections(xmpp_listener_common(), xmpp_listener_extra(Type)).

xmpp_listener_common() ->
#section{items = #{<<"backlog">> => #option{type = integer,
validate = non_negative},
<<"proxy_protocol">> => #option{type = boolean},
<<"hibernate_after">> => #option{type = integer,
validate = non_negative},
<<"max_stanza_size">> => #option{type = integer,
validate = positive},
<<"num_acceptors">> => #option{type = integer,
validate = positive}
},
defaults = #{<<"backlog">> => 100,
<<"proxy_protocol">> => false,
<<"hibernate_after">> => 0,
<<"max_stanza_size">> => infinity,
<<"num_acceptors">> => 100},
format_items = map
}.

xmpp_listener_extra(<<"c2s">>) ->
#section{items = #{<<"access">> => #option{type = atom,
validate = non_empty},
<<"shaper">> => #option{type = atom,
validate = non_empty},
<<"zlib">> => #option{type = integer,
validate = positive},
<<"max_fsm_queue">> => #option{type = integer,
validate = positive},
<<"allowed_auth_methods">> => #list{items = #option{type = atom,
validate = {module, ejabberd_auth}},
validate = unique},
<<"tls">> => c2s_tls()},
defaults = #{<<"access">> => all,
<<"shaper">> => none},
format_items = map
};
xmpp_listener_extra(<<"s2s">>) ->
#section{items = #{<<"shaper">> => #option{type = atom,
validate = non_empty},
<<"tls">> => s2s_tls()},
defaults = #{<<"shaper">> => none},
format_items = map
};
xmpp_listener_extra(<<"service">>) ->
#section{items = #{<<"access">> => #option{type = atom,
validate = non_empty},
<<"shaper_rule">> => #option{type = atom,
validate = non_empty},
<<"check_from">> => #option{type = boolean},
<<"hidden_components">> => #option{type = boolean},
<<"conflict_behaviour">> => #option{type = atom,
validate = {enum, [kick_old, disconnect]}},
<<"password">> => #option{type = string,
validate = non_empty},
<<"max_fsm_queue">> => #option{type = integer,
validate = positive}
},
required = [<<"password">>],
defaults = #{<<"access">> => all,
<<"shaper_rule">> => none,
<<"check_from">> => true,
<<"hidden_components">> => false,
<<"conflict_behaviour">> => disconnect},
format_items = map
}.

%% path: listen.c2s[].tls
c2s_tls() ->
Expand Down Expand Up @@ -389,7 +412,6 @@ http_listener_tls() ->
items = Items#{<<"verify_mode">> => #option{type = atom,
validate = {enum, [peer, selfsigned_peer, none]}}
},
wrap = {kv, ssl},
process = fun ?MODULE:process_tls_sni/1
}.

Expand All @@ -401,14 +423,19 @@ http_transport() ->
<<"max_connections">> => #option{type = int_or_infinity,
validate = non_negative}
},
wrap = {kv, transport_options}
format_items = map,
defaults = #{<<"num_acceptors">> => 100,
<<"max_connections">> => 1024},
include = always
}.

%% path: listen.http[].protocol
http_protocol() ->
#section{
items = #{<<"compress">> => #option{type = boolean}},
wrap = {kv, protocol_options}
format_items = map,
defaults = #{<<"compress">> => false},
include = always
}.

%% path: listen.http[].handlers
Expand All @@ -424,7 +451,7 @@ http_handlers() ->
items = maps:from_list([{Key, #list{items = http_handler(Key),
wrap = none}} || Key <- Keys]),
validate_keys = module,
wrap = {kv, modules}
include = always
}.

%% path: listen.http[].handlers.*[]
Expand All @@ -450,8 +477,8 @@ http_handler_items(<<"mod_websockets">>) ->
validate = positive},
<<"max_stanza_size">> => #option{type = int_or_infinity,
validate = positive},
<<"service">> => #section{items = xmpp_listener_items(<<"service">>),
wrap = {kv, ejabberd_service}}};
<<"service">> => xmpp_listener_extra(<<"service">>)
};
http_handler_items(<<"lasse_handler">>) ->
#{<<"module">> => #option{type = atom,
validate = module}};
Expand Down Expand Up @@ -1313,3 +1340,12 @@ process_s2s_address(M) ->

process_domain_cert(#{domain := Domain, certfile := Certfile}) ->
{Domain, Certfile}.

%% Helpers

merge_sections(BasicSection, ExtraSection) ->
#section{items = Items1, required = Required1, defaults = Defaults1} = BasicSection,
#section{items = Items2, required = Required2, defaults = Defaults2} = ExtraSection,
BasicSection#section{items = maps:merge(Items1, Items2),
required = Required1 ++ Required2,
defaults = maps:merge(Defaults1, Defaults2)}.
4 changes: 2 additions & 2 deletions src/ejabberd_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ start(normal, _Args) ->
mongoose_modules:start(),
service_mongoose_system_metrics:verify_if_configured(),
mongoose_metrics:init(),
ejabberd_listener:start_listeners(),
mongoose_listener:start(),
ejabberd_admin:start(),
update_status_file(started),
?LOG_NOTICE(#{what => mongooseim_node_started, version => ?MONGOOSE_VERSION, node => node()}),
Expand All @@ -81,7 +81,7 @@ start(_, _) ->
%% before shutting down the processes of the application.
prep_stop(State) ->
mongoose_deprecations:stop(),
ejabberd_listener:stop_listeners(),
mongoose_listener:stop(),
mongoose_modules:stop(),
mongoose_service:stop(),
broadcast_c2s_shutdown(),
Expand Down
Loading

0 comments on commit e8a9a98

Please sign in to comment.