diff --git a/apps/ejabberd/src/ejabberd_cowboy.erl b/apps/ejabberd/src/ejabberd_cowboy.erl index fbcbd3f4b68..c2901ecfca5 100644 --- a/apps/ejabberd/src/ejabberd_cowboy.erl +++ b/apps/ejabberd/src/ejabberd_cowboy.erl @@ -36,6 +36,9 @@ -export([start/2, stop/1]). +%% helper for internal use +-export([handler/1]). + -include("ejabberd.hrl"). %%-------------------------------------------------------------------- @@ -45,22 +48,13 @@ socket_type() -> independent. -start_listener({Port, IP, tcp}, Opts) -> - %% ejabberd_listener brutally kills its children, and doesn't provide any - %% mechanism for doing a clean shutdown. To work around this, we could - %% start two linked processes: one to be killed, and another to trap the - %% exit signal and shut down Cowboy cleanly. However, a simpler solution is - %% to manually configure supervision and not use brutal_kill. Calling - %% supervisor:start_child(ejabberd_listeners, ...) would hang since we're - %% running in the ejabberd_listeners process and start_child() is - %% synchronous. So, simply use ejabberd_sup as the supervisor instead. - IPPort = [inet_parse:ntoa(IP), <<"_">>, integer_to_list(Port)], - ChildSpec = {cowboy_ref(IPPort), {?MODULE, start_link, [IPPort]}, permanent, +start_listener({Port, IP, tcp}=Listener, Opts) -> + IPPort = handler(Listener), + ChildSpec = {Listener, {?MODULE, start_link, [IPPort]}, transient, infinity, worker, [?MODULE]}, - supervisor:start_child(ejabberd_sup, ChildSpec), - start_cowboy(IPPort, [{port, Port}, {ip, IP} | Opts]), - %% Tell ejabberd_listener not to supervise us - ignore. + {ok, Pid} = supervisor:start_child(ejabberd_listeners, ChildSpec), + {ok, _} = start_cowboy(IPPort, [{port, Port}, {ip, IP} | Opts]), + {ok, Pid}. %% @doc gen_server for handling shutdown when started via ejabberd_listener -spec start_link(_) -> 'ignore' | {'error',_} | {'ok',pid()}. @@ -80,6 +74,10 @@ code_change(_OldVsn, Ref, _Extra) -> terminate(_Reason, Ref) -> stop_cowboy(Ref). +-spec handler({inet:ip_address(), integer, tcp}) -> iolist(). +handler({Port, IP, tcp}) -> + [inet_parse:ntoa(IP), <<"_">>, integer_to_list(Port)]. + %%-------------------------------------------------------------------- %% gen_mod API %%-------------------------------------------------------------------- @@ -125,6 +123,7 @@ stop_cowboy(Ref) -> cowboy:stop_listener(cowboy_ref(Ref)), ok. + cowboy_ref(Ref) -> ModRef = [?MODULE_STRING, <<"_">>, Ref], list_to_atom(binary_to_list(iolist_to_binary(ModRef))). diff --git a/apps/ejabberd/src/ejabberd_listener.erl b/apps/ejabberd/src/ejabberd_listener.erl index d42d402b738..8e66ce84ebf 100644 --- a/apps/ejabberd/src/ejabberd_listener.erl +++ b/apps/ejabberd/src/ejabberd_listener.erl @@ -49,7 +49,7 @@ -type portnum() :: inet:port_number(). -type port_ip_proto() :: portnum() | {portnum(), addr() | proto()} | {portnum(), addr(), proto()}. --spec start_link() -> 'ignore' | {'error', _} | {'ok', pid()}. +-spec start_link() -> 'ignore' | {'error',_} | {'ok',pid()}. start_link() -> supervisor:start_link({local, ejabberd_listeners}, ?MODULE, []). @@ -94,11 +94,8 @@ report_duplicated_portips(L) -> Module :: atom() | tuple(), Opts :: [any()]) -> any(). start(Port, Module, Opts) -> - %% Check if the module is an ejabberd listener or an independent listener - case Module:socket_type() of - independent -> Module:start_listener(Port, Opts); - _ -> start_dependent(Port, Module, Opts) - end. + %% at this point, Module:socket_type() must not be 'independent' + start_dependent(Port, Module, Opts). -spec start_dependent(Port :: _, Module :: atom() | tuple(), @@ -375,13 +372,19 @@ start_module_sup(_PortIPProto, Module) -> -spec start_listener_sup(port_ip_proto(), Module :: atom(), Opts :: [any()]) -> {'error',_} | {'ok','undefined' | pid()} | {'ok','undefined' | pid(),_}. start_listener_sup(PortIPProto, Module, Opts) -> - ChildSpec = {PortIPProto, - {?MODULE, start, [PortIPProto, Module, Opts]}, - transient, - brutal_kill, - worker, - [?MODULE]}, - supervisor:start_child(ejabberd_listeners, ChildSpec). + case Module:socket_type() of + independent -> + Module:start_listener(PortIPProto, Opts); + _ -> + + ChildSpec = {PortIPProto, + {?MODULE, start, [PortIPProto, Module, Opts]}, + transient, + brutal_kill, + worker, + [?MODULE]}, + supervisor:start_child(ejabberd_listeners, ChildSpec) + end. -spec stop_listeners() -> 'ok'. stop_listeners() -> diff --git a/apps/ejabberd/src/mod_cowboy.erl b/apps/ejabberd/src/mod_cowboy.erl new file mode 100644 index 00000000000..3e9611d5c8a --- /dev/null +++ b/apps/ejabberd/src/mod_cowboy.erl @@ -0,0 +1,196 @@ +%%%=================================================================== +%%% @copyright (C) 2014, Erlang Solutions Ltd. +%%% @doc HTTP routing layer for MongooseIM's Cowboy listener +%%% @end +%%%=================================================================== +-module(mod_cowboy). + +-behaviour(cowboy_http_handler). +-behaviour(cowboy_websocket_handler). + +%% common callbacks +-export([init/3]). + +%% cowboy_http_handler callbacks +-export([handle/2, + terminate/3]). + +%% cowboy_websocket_handler callbacks +-export([websocket_init/3, + websocket_handle/3, + websocket_info/3, + websocket_terminate/3]). + +-record(state, {handler, handler_state, handler_opts}). + +-type option() :: {atom(), any()}. +-type state() :: #state{}. + +%%-------------------------------------------------------------------- +%% common callback +%%-------------------------------------------------------------------- +-spec init({atom(), http}, cowboy_req:req(), [option()]) + -> {ok, cowboy_req:req(), state()} | + {shutdown, cowboy_req:req(), state()} | + {upgrade, protocol, cowboy_websocket, cowboy_req:req(), state()}. +init(Transport, Req, Opts) -> + case protocol(Req) of + {ws, Req1} -> + {upgrade, protocol, cowboy_websocket, Req1, Opts}; + {http, Req1} -> + handle_http_init(Transport, Req1, Opts); + _ -> + {ok, Req1} = cowboy_req:reply(404, Req), + {shutdown, Req1, #state{}} + end. + +%%-------------------------------------------------------------------- +%% cowboy_http_handler callbacks +%%-------------------------------------------------------------------- +-spec handle(cowboy_req:req(), state()) -> {ok, cowboy_req:req(), state()}. +handle(Req, #state{handler=Handler, handler_state=HandlerState}=State) -> + {ok, Req1, HandlerState1} = Handler:handle(Req, HandlerState), + {ok, Req1, update_handler_state(State, HandlerState1)}. + +-spec terminate(any(), cowboy_req:req(), state()) -> ok. +terminate(_Reason, _Req, #state{handler=undefined}) -> + ok; +terminate(Reason, Req, #state{handler=Handler, handler_state=HandlerState}) -> + Handler:terminate(Reason, Req, HandlerState). + +%%-------------------------------------------------------------------- +%% cowboy_websocket_handler callbacks +%%-------------------------------------------------------------------- +websocket_init(Transport, Req, Opts) -> + handle_ws_init(Transport, Req, Opts). + +websocket_handle(InFrame, Req, + #state{handler=Handler, handler_state=HandlerState}=State) -> + case Handler:websocket_handle(InFrame, Req, HandlerState) of + {ok, Req1, HandlerState1} -> + {ok, Req1, update_handler_state(State, HandlerState1)}; + {ok, Req1, HandlerState1, hibernate} -> + {ok, Req1, update_handler_state(State, HandlerState1), hibernate}; + {reply, OutFrame, Req1, HandlerState1} -> + {reply, OutFrame, Req1, update_handler_state(State, HandlerState1)}; + {reply, OutFrame, Req1, HandlerState1, hibernate} -> + {reply, OutFrame, Req1, update_handler_state(State, HandlerState1), + hibernate}; + {shutdown, Req1, HandlerState1} -> + {shutdown, Req1, update_handler_state(State, HandlerState1)} + end. + +websocket_info(Info, Req, + #state{handler=Handler, handler_state=HandlerState}=State) -> + case Handler:websocket_info(Info, Req, HandlerState) of + {ok, Req1, HandlerState1} -> + {ok, Req1, update_handler_state(State, HandlerState1)}; + {ok, Req1, HandlerState1, hibernate} -> + {ok, Req1, update_handler_state(State, HandlerState1), hibernate}; + {reply, OutFrame, Req1, HandlerState1} -> + {reply, OutFrame, Req1, update_handler_state(State, HandlerState1)}; + {reply, OutFrame, Req1, HandlerState1, hibernate} -> + {reply, OutFrame, Req1, update_handler_state(State, HandlerState1), + hibernate}; + {shutdown, Req1, HandlerState1} -> + {shutdown, Req1, update_handler_state(State, HandlerState1)} + end. + +websocket_terminate(_Reason, _Req, #state{handler=undefined}) -> + ok; +websocket_terminate(Reason, Req, + #state{handler=Handler, handler_state=HandlerState}) -> + Handler:websocket_terminate(Reason, Req, HandlerState). + +%%-------------------------------------------------------------------- +%% Internal functions +%%-------------------------------------------------------------------- +handle_http_init(Transport, Req, Opts) -> + case http_handler(Opts) of + {Handler, HandlerOpts} -> + init_http_handler(Handler, Transport, Req, HandlerOpts); + _ -> + {ok, Req1} = cowboy_req:reply(404, Req), + {shutdown, Req1, #state{}} + end. + +handle_ws_init(Transport, Req, Opts) -> + {Protocol, Req1} = ws_protocol(Req), + case ws_handler(Protocol, Opts) of + {Handler, HandlerOpts} -> + init_ws_handler(Handler, Transport, Req1, HandlerOpts); + _ -> + {ok, Req2} = cowboy_req:reply(404, Req1), + {shutdown, Req2} + end. + +init_http_handler(Handler, Transport, Req, Opts) -> + case Handler:init(Transport, Req, Opts) of + {ok, Req1, HandlerState} -> + {ok, Req1, init_state(Handler, Opts, HandlerState)}; + {shutdown, Req1, HandlerState} -> + {shutdown, Req1, init_state(Handler, Opts, HandlerState)} + end. + +init_ws_handler(Handler, Transport, Req, Opts) -> + case Handler:websocket_init(Transport, Req, Opts) of + {ok, Req1, HandlerState} -> + {ok, Req1, init_state(Handler, Opts, HandlerState)}; + {ok, Req1, HandlerState, hibernate} -> + {ok, Req1, init_state(Handler, Opts, HandlerState), hibernate}; + {ok, Req1, HandlerState, Timeout} -> + {ok, Req1, init_state(Handler, Opts, HandlerState), Timeout}; + {ok, Req1, HandlerState, Timeout, hibernate} -> + {ok, Req1, init_state(Handler, Opts, HandlerState), + Timeout, hibernate}; + {shutdown, Req1} -> + {shutdown, Req1} + end. + +http_handler(Handlers) -> + case lists:keyfind(http, 1, Handlers) of + {http, Handler, Opts} -> + {Handler, Opts}; + {http, Handler} -> + {Handler, []}; + _ -> + undefined + end. + +ws_handler(undefined, _) -> + undefined; +ws_handler(_Protocol, []) -> + undefined; +ws_handler(Protocol, [{ws, ProtocolAtom, Handler}|Tail]) -> + case atom_to_binary(ProtocolAtom, utf8) of + Protocol -> {Handler, []}; + _ -> ws_handler(Protocol, Tail) + end; +ws_handler(Protocol, [{ws, ProtocolAtom, Handler, Opts}|Tail]) -> + case atom_to_binary(ProtocolAtom, utf8) of + Protocol -> {Handler, Opts}; + _ -> ws_handler(Protocol, Tail) + end; +ws_handler(Protocol, [_|Tail]) -> + ws_handler(Protocol, Tail). + +protocol(Req) -> + case cowboy_req:header(<<"upgrade">>, Req) of + {<<"websocket">>, Req1} -> + {ws, Req1}; + {undefined, Req1} -> + {http, Req1}; + {_, Req1} -> + {undefined, Req1} + end. + +ws_protocol(Req) -> + cowboy_req:header(<<"sec-websocket-protocol">>, Req). + +init_state(Handler, Opts, State) -> + #state{handler = Handler, + handler_opts = Opts, + handler_state = State}. + +update_handler_state(State, HandlerState) -> + State#state{handler_state = HandlerState}. diff --git a/apps/ejabberd/test/auth_http_SUITE.erl b/apps/ejabberd/test/auth_http_SUITE.erl index bdc8998bff7..c109cb4ecbe 100644 --- a/apps/ejabberd/test/auth_http_SUITE.erl +++ b/apps/ejabberd/test/auth_http_SUITE.erl @@ -142,7 +142,7 @@ get_password(_Config) -> end, false = ejabberd_auth_http:get_password(<<"anakin">>, ?DOMAIN1), <<>> = ejabberd_auth_http:get_password_s(<<"anakin">>, ?DOMAIN1). - + is_user_exists(_Config) -> true = ejabberd_auth_http:does_user_exist(<<"alice">>, ?DOMAIN1), false = ejabberd_auth_http:does_user_exist(<<"madhatter">>, ?DOMAIN1). diff --git a/apps/ejabberd/test/cowboy_SUITE.erl b/apps/ejabberd/test/cowboy_SUITE.erl new file mode 100644 index 00000000000..76c52f6f6e5 --- /dev/null +++ b/apps/ejabberd/test/cowboy_SUITE.erl @@ -0,0 +1,386 @@ +%%============================================================================== +%% Copyright 2014 Erlang Solutions Ltd. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%============================================================================== + +-module(cowboy_SUITE). +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(SERVER, "http://localhost:8080"). + +-import(ejabberd_helper, [start_ejabberd/1, + stop_ejabberd/0, + use_config_file/2, + start_ejabberd_with_config/2]). + +%%-------------------------------------------------------------------- +%% Suite configuration +%%-------------------------------------------------------------------- + +all() -> + [{group, routing}, + conf_reload]. + +groups() -> + [{routing, [sequence], [http_requests, + ws_request_bad_protocol, + ws_requests_xmpp, + ws_requests_other, + mixed_requests]}]. + +suite() -> + []. + +%%-------------------------------------------------------------------- +%% Init & teardown +%%-------------------------------------------------------------------- + +-define(APPS, [crypto, ssl, fusco, ranch, cowlib, cowboy]). + +init_per_suite(Config) -> + [application:start(App) || App <- ?APPS], + {ok, Pid} = create_handlers(), + [{meck_pid, Pid}|Config]. + +end_per_suite(Config) -> + remove_handlers(Config), + Config. + +init_per_group(routing, Config) -> + start_cowboy(), + Config; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(routing, Config) -> + stop_cowboy(), + Config; +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(_CaseName, Config) -> + Config. + +end_per_testcase(_CaseName, Config) -> + reset_history(), + Config. + +%%-------------------------------------------------------------------- +%% Tests +%%-------------------------------------------------------------------- +http_requests(_Config) -> + %% Given + Host = ?SERVER, + Path = <<"/">>, + Method = "GET", + Headers = [], + Body = [], + + %% When + Codes = [begin + Response = execute_request(Host, Path, Method, Headers, Body), + is_status_code(Response, 200) + end || _ <- lists:seq(1, 50)], + + %% Then + Codes = lists:duplicate(50, true), + 50 = meck:num_calls(dummy_http_handler, init, '_'), + 50 = meck:num_calls(dummy_http_handler, handle, '_'), + 50 = meck:num_calls(dummy_http_handler, terminate, '_'). + +ws_request_bad_protocol(_Config) -> + %% Given + Host = ?SERVER, + Path = <<"/">>, + Method = "GET", + Headers = ws_headers(<<"unknown-protocol">>), + Body = [], + + %% When + Response = execute_request(Host, Path, Method, Headers, Body), + + %% Then + true = is_status_code(Response, 404). + +ws_requests_xmpp(_Config) -> + %% Given + Host = "localhost", + Port = 8080, + Protocol = <<"xmpp">>, + BinaryPing = ws_tx_frame(<<"ping">>, 2), + BinaryPong = ws_rx_frame(<<"pong">>, 2), + + %% When + {ok, Socket} = ws_handshake(Host, Port, Protocol), + Responses = [begin + ok = ws_send(Socket, BinaryPing), + ws_recv(Socket) + end || _ <- lists:seq(1, 50)], + ok = gen_tcp:close(Socket), + + %% Then + %% dummy_ws1_handler:init/3 is not called since mod_cowboy takes over + Responses = lists:duplicate(50, BinaryPong), + 1 = meck:num_calls(dummy_ws1_handler, websocket_init, '_'), + 50 = meck:num_calls(dummy_ws1_handler, websocket_handle, '_'), + ok = meck:wait(dummy_ws1_handler, websocket_terminate, '_', 1000). + +ws_requests_other(_Config) -> + %% Given + Host = "localhost", + Port = 8080, + Protocol = <<"other">>, + TextPing = ws_tx_frame(<<"ping">>, 1), + TextPong = ws_rx_frame(<<"pong">>, 1), + + %% When + {ok, Socket} = ws_handshake(Host, Port, Protocol), + Responses = [begin + ok = ws_send(Socket, TextPing), + ws_recv(Socket) + end || _ <- lists:seq(1, 50)], + ok = gen_tcp:close(Socket), + + %% Then + Responses = lists:duplicate(50, TextPong), + 1 = meck:num_calls(dummy_ws2_handler, websocket_init, '_'), + 50 = meck:num_calls(dummy_ws2_handler, websocket_handle, '_'), + ok = meck:wait(dummy_ws2_handler, websocket_terminate, '_', 1000). + +mixed_requests(_Config) -> + %% Given + Protocol1 = <<"xmpp">>, + Protocol2 = <<"other">>, + Protocol3 = <<"non-existent">>, + + TextPing = ws_tx_frame(<<"ping">>, 1), + TextPong = ws_rx_frame(<<"pong">>, 1), + + Host = "localhost", + Port = 8080, + + HTTPHost = ?SERVER, + Path = <<"/">>, + Method = "GET", + Headers3 = ws_headers(Protocol3), + Headers4 = [], + Body = [], + + %% When + {ok, Socket1} = ws_handshake(Host, Port, Protocol1), + {ok, Socket2} = ws_handshake(Host, Port, Protocol2), + + Responses = [begin + ok = ws_send(Socket1, TextPing), + Resp1 = ws_recv(Socket1), + + Resp2 = execute_request(HTTPHost, Path, Method, Headers4, Body), + Status2 = is_status_code(Resp2, 200), + + ok = ws_send(Socket2, TextPing), + Resp3 = ws_recv(Socket2), + + Resp4 = execute_request(HTTPHost, Path, Method, Headers3, Body), + Status4 = is_status_code(Resp4, 404), + + {Resp1, Status2, Resp3, Status4} + end || _ <- lists:seq(1, 50)], + + %% Then + Responses = lists:duplicate(50, {TextPong, true, TextPong, true}). + +conf_reload(Config) -> + %% Given initial configuration + HTTPHost = "http://localhost:5280", + Path = <<"/">>, + Method = "GET", + Headers1 = [], + Headers2 = ws_headers(<<"xmpp">>), + Body = [], + + copy(data(Config, "ejabberd.onlyhttp.cfg"), data(Config, "ejabberd.cfg")), + start_ejabberd_with_config(Config, "ejabberd.cfg"), + + %% When making requests for http and ws + Response1 = execute_request(HTTPHost, Path, Method, Headers1, Body), + Response2 = execute_request(HTTPHost, Path, Method, Headers2, Body), + + %% Then http returns 200 and ws returns 404 + true = is_status_code(Response1, 200), + true = is_status_code(Response2, 404), + + %% Given new configuration + copy(data(Config, "ejabberd.onlyws.cfg"), data(Config, "ejabberd.cfg")), + ejabberd_config:reload_local(), + + %% When making requests for http and ws + Response3 = execute_request(HTTPHost, Path, Method, Headers1, Body), + + %% Then http returns 404 and ws works fine + true = is_status_code(Response3, 404), + + ok = stop_ejabberd(). + +%%-------------------------------------------------------------------- +%% Helpers +%%-------------------------------------------------------------------- +copy(Src, Dst) -> + {ok, _} = file:copy(Src, Dst). + +data(Config, Path) -> + Dir = proplists:get_value(data_dir, Config), + filename:join([Dir, Path]). + +start_cowboy() -> + Dispatch = cowboy_router:compile([ + {'_', + [{"/[...]", mod_cowboy, + [{http, dummy_http_handler}, + {ws, xmpp, dummy_ws1_handler}, + {ws, other, dummy_ws2_handler} + ]}] + }]), + {ok, _Pid} = cowboy:start_http(http_listener, 20, [{port, 8080}], + [{env, [{dispatch, Dispatch}]}]). + +stop_cowboy() -> + ok = cowboy:stop_listener(http_listener). + +execute_request(Host, Path, Method, Headers, Body) -> + {ok, Pid} = fusco:start_link(Host, []), + Response = fusco:request(Pid, Path, Method, Headers, Body, 5000), + fusco:disconnect(Pid), + Response. + +is_status_code({ok, {{CodeBin, _}, _, _, _, _}}, Code) -> + case binary_to_integer(CodeBin) of + Code -> true; + _ -> false + end. + +ws_send(Socket, Frame) -> + ok = gen_tcp:send(Socket, Frame). + +ws_recv(Socket) -> + {ok, Packet} = gen_tcp:recv(Socket, 0, 5000), + Packet. + +ws_handshake(Host, Port, Protocol) -> + {ok, Socket} = gen_tcp:connect(Host, Port, [binary, {packet, raw}, + {active, false}]), + ok = gen_tcp:send(Socket, [ + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Connection: upgrade\r\n" + "Origin: http://localhost\r\n" + "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n" + "Sec-WebSocket-Protocol: ", Protocol, "\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Upgrade: websocket\r\n" + "\r\n"]), + {ok, Handshake} = gen_tcp:recv(Socket, 0, 5000), + Packet = erlang:decode_packet(http, Handshake, []), + {ok, {http_response, {1,1}, 101, "Switching Protocols"}, _Rest} = Packet, + {ok, Socket}. + +ws_headers(Protocol) -> + [{<<"upgrade">>, <<"websocket">>}, + {<<"connection">>, <<"upgrade">>}, + {<<"sec-websocket-key">>, <<"x3JJHMbDL1EzLkh9GBhXDw==">>}, + {<<"sec-websocket-protocol">>, Protocol}, + {<<"sec-websocket-version">>, <<"13">>}]. + +ws_tx_frame(Payload, Opcode) -> + Mask = 16#ffffffff, + Length = byte_size(Payload), + MaskedPayload = << <<(Byte bxor 16#ff):8>> || <> <= Payload >>, + <<1:1, 0:3, Opcode:4, 1:1, Length:7, Mask:32, MaskedPayload/binary>>. + +ws_rx_frame(Payload, Opcode) -> + Length = byte_size(Payload), + <<1:1, 0:3, Opcode:4, 0:1, Length:7, Payload/binary>>. + +%%-------------------------------------------------------------------- +%% http/ws handlers mock +%%-------------------------------------------------------------------- +create_handlers() -> + Owner = self(), + F = fun() -> + [create_handler(Handler) || Handler <- handlers()], + Owner ! ok, + timer:sleep(infinity) + end, + Pid = spawn(F), + receive + ok -> + {ok, Pid} + after 5000 -> + {error, timeout} + end. + +handlers() -> + WSFuns = [{init, fun ws_init/3}, + {websocket_init, fun ws_websocket_init/3}, + {websocket_handle, fun ws_websocket_handle/3}, + {websocket_info, fun ws_websocket_info/3}, + {websocket_terminate, fun ws_websocket_terminate/3}], + [{dummy_http_handler, [{init, fun handler_init/3}, + {handle, fun handler_handle/2}, + {terminate, fun handler_terminate/3}]}, + {dummy_ws1_handler, WSFuns}, + {dummy_ws2_handler, WSFuns}]. + +create_handler({Name, Functions}) -> + ok = meck:new(Name, [non_strict]), + [ok = meck:expect(Name, Function, Fun) || {Function, Fun} <- Functions]. + +remove_handlers(Config) -> + [ok = meck:unload(Handler) || {Handler, _} <- handlers()], + exit(?config(meck_pid, Config), kill). + +reset_history() -> + [ok = meck:reset(Handler) || {Handler, _} <- handlers()]. + +%% cowboy_http_handler +handler_init(_Type, Req, _Opts) -> + {ok, Req, no_state}. + +handler_handle(Req, State) -> + {ok, Req1} = cowboy_req:reply(200, Req), + {ok, Req1, State}. + +handler_terminate(_Reason, _Req, _State) -> + ok. + +%% cowboy_websocket_handler +ws_init(_Type, _Req, _Opts) -> + {upgrade, protocol, cowboy_websocket}. + +ws_websocket_init(_Transport, Req, _Opts) -> + {ok, Req, no_state}. + +ws_websocket_handle({text,<<"ping">>}, Req, State) -> + {reply, {text, <<"pong">>}, Req, State}; +ws_websocket_handle({binary, <<"ping">>}, Req, State) -> + {reply, {binary, <<"pong">>}, Req, State}; +ws_websocket_handle(_Other, Req, State) -> + {ok, Req, State}. + +ws_websocket_info(_Info, Req, State) -> + {ok, Req, State}. + +ws_websocket_terminate(_Reason, _Req, _State) -> + ok. diff --git a/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyhttp.cfg b/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyhttp.cfg new file mode 100644 index 00000000000..2017addd739 --- /dev/null +++ b/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyhttp.cfg @@ -0,0 +1,752 @@ +%%% +%%% ejabberd configuration file +%%% +%%%' + +%%% The parameters used in this configuration file are explained in more detail +%%% in the ejabberd Installation and Operation Guide. +%%% Please consult the Guide in case of doubts, it is included with +%%% your copy of ejabberd, and is also available online at +%%% http://www.process-one.net/en/ejabberd/docs/ + +%%% This configuration file contains Erlang terms. +%%% In case you want to understand the syntax, here are the concepts: +%%% +%%% - The character to comment a line is % +%%% +%%% - Each term ends in a dot, for example: +%%% override_global. +%%% +%%% - A tuple has a fixed definition, its elements are +%%% enclosed in {}, and separated with commas: +%%% {loglevel, 4}. +%%% +%%% - A list can have as many elements as you want, +%%% and is enclosed in [], for example: +%%% [http_poll, web_admin, tls] +%%% +%%% Pay attention that list elements are delimited with commas, +%%% but no comma is allowed after the last list element. This will +%%% give a syntax error unlike in more lenient languages (e.g. Python). +%%% +%%% - A keyword of ejabberd is a word in lowercase. +%%% Strings are enclosed in "" and can contain spaces, dots, ... +%%% {language, "en"}. +%%% {ldap_rootdn, "dc=example,dc=com"}. +%%% +%%% - This term includes a tuple, a keyword, a list, and two strings: +%%% {hosts, ["jabber.example.net", "im.example.com"]}. +%%% +%%% - This config is preprocessed during release generation by a tool which +%%% interprets double curly braces as substitution markers, so avoid this +%%% syntax in this file (though it's valid Erlang). +%%% +%%% So this is OK (though arguably looks quite ugly): +%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%%% +%%% And I can't give an example of what's not OK exactly because +%%% of this rule. +%%% + + +%%%. ======================= +%%%' OVERRIDE STORED OPTIONS + +%% +%% Override the old values stored in the database. +%% + +%% +%% Override global options (shared by all ejabberd nodes in a cluster). +%% +%%override_global. + +%% +%% Override local options (specific for this particular ejabberd node). +%% +%%override_local. + +%% +%% Remove the Access Control Lists before new ones are added. +%% +%%override_acls. + + +%%%. ========= +%%%' DEBUGGING + +%% +%% loglevel: Verbosity of log files generated by ejabberd. +%% 0: No ejabberd log at all (not recommended) +%% 1: Critical +%% 2: Error +%% 3: Warning +%% 4: Info +%% 5: Debug +%% +{loglevel, 3}. + +%% +%% alarms: an optional alarm handler, subscribed to system events +%% long_gc: minimum GC time in ms for long_gc alarm +%% large_heap: minimum process heap size for large_heap alarm +%% handlers: a list of alarm handlers +%% - alarms_basic_handler: logs alarms and stores a brief alarm summary +%% - alarms_folsom_handler: stores alarm details in folsom metrics +%% +%% Example: +%% {alarms, +%% [{long_gc, 10000}, +%% {large_heap, 1000000}, +%% {handlers, [alarms_basic_handler, +%% alarms_folsom_handler]}] +%% }. + +%% +%% watchdog_admins: Only useful for developers: if an ejabberd process +%% consumes a lot of memory, send live notifications to these XMPP +%% accounts. Requires alarms (see above). +%% +%%{watchdog_admins, ["bob@example.com"]}. + + +%%%. ================ +%%%' SERVED HOSTNAMES + +%% +%% hosts: Domains served by ejabberd. +%% You can define one or several, for example: +%% {hosts, ["example.net", "example.com", "example.org"]}. +%% +{hosts, ["localhost"] }. + +%% +%% route_subdomains: Delegate subdomains to other XMPP servers. +%% For example, if this ejabberd serves example.org and you want +%% to allow communication with an XMPP server called im.example.org. +%% +%%{route_subdomains, s2s}. + + +%%%. =============== +%%%' LISTENING PORTS + +%% +%% listen: The ports ejabberd will listen on, which service each is handled +%% by and what options to start it with. +%% +{listen, + [ + + { 5280, ejabberd_cowboy, [ + {num_acceptors, 10}, + {max_connections, 1024}, + %% Uncomment for HTTPS + %{cert, "priv/server.crt"}, + %{key, "priv/server.key"}, + %{key_pass, ""}, + {modules, [ + %% Modules used here should also be listed in the MODULES section. + {"_", "/[...]", mod_cowboy, [{http, dummy_http_handler}]} + ]} + ]}, + + { 5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + %%{certfile, "/path/to/ssl.pem"}, starttls, + %%{zlib, 10000}, + %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS + %% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}, + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + + + %% + %% To enable the old SSL connection method on port 5223: + %% + %%{5223, ejabberd_c2s, [ + %% {access, c2s}, + %% {shaper, c2s_shaper}, + %% {certfile, "/path/to/ssl.pem"}, tls, + %% {max_stanza_size, 65536} + %% ]}, + + { 5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]} + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{ {3478, udp}, ejabberd_stun, []} + + ]}. + +%% +%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. +%% Allowed values are: false optional required required_trusted +%% You must specify a certificate file. +%% +%%{s2s_use_starttls, optional}. + +%% +%% s2s_certfile: Specify a certificate file. +%% +%%{s2s_certfile, "/path/to/ssl.pem"}. + +%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS +%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}. + +%% +%% domain_certfile: Specify a different certificate for each served hostname. +%% +%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. +%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + +%% +%% S2S whitelist or blacklist +%% +%% Default s2s policy for undefined hosts. +%% +{s2s_default_policy, deny }. + +%% +%% Allow or deny communication with specific servers. +%% +%%{ {s2s_host, "goodhost.org"}, allow}. +%%{ {s2s_host, "badhost.org"}, deny}. + +{outgoing_s2s_port, 5269 }. + +%% +%% IP addresses predefined for specific hosts to skip DNS lookups. +%% Ports defined here take precedence over outgoing_s2s_port. +%% Examples: +%% +%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. + +%% +%% Outgoing S2S options +%% +%% Preferred address families (which to try first) and connect timeout +%% in milliseconds. +%% +%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + +%%%. ============== +%%%' SESSION BACKEND + +%%{sm_backend, {mnesia, []}}. + +%%{sm_backend, {redis, [{pool_size, 3}, {worker_config, [{host, "localhost"}, {port, 6379}]}]}}. +{sm_backend, {mnesia, []} }. + + +%%%. ============== +%%%' AUTHENTICATION + +%% +%% auth_method: Method used to authenticate the users. +%% The default method is the internal. +%% If you want to use a different method, +%% comment this line and enable the correct ones. +%% +{auth_method, internal}. +%% +%% Store the plain passwords or hashed for SCRAM: +%%{auth_password_format, plain}. % default +%%{auth_password_format, scram}. +%%{auth_scram_iterations, 4096}. % default + +%% +%% Authentication using external script +%% Make sure the script is executable by ejabberd. +%% +%%{auth_method, external}. +%%{extauth_program, "/path/to/authentication/script"}. + +%% +%% Authentication using ODBC +%% Remember to setup a database in the next section. +%% +%%{auth_method, odbc}. + +%% +%% Authentication using PAM +%% +%%{auth_method, pam}. +%%{pam_service, "pamservicename"}. + +%% +%% Authentication using LDAP +%% +%%{auth_method, ldap}. +%% + +%% List of LDAP servers: +%%{ldap_servers, ["localhost"]}. +%% +%% Encryption of connection to LDAP servers: +%%{ldap_encrypt, none}. +%%{ldap_encrypt, tls}. +%% +%% Port to connect to on LDAP servers: +%%{ldap_port, 389}. +%%{ldap_port, 636}. +%% +%% LDAP manager: +%%{ldap_rootdn, "dc=example,dc=com"}. +%% +%% Password of LDAP manager: +%%{ldap_password, "******"}. +%% +%% Search base of LDAP directory: +%%{ldap_base, "dc=example,dc=com"}. +%% +%% LDAP attribute that holds user ID: +%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. +%% +%% LDAP filter: +%%{ldap_filter, "(objectClass=shadowAccount)"}. + +%% +%% Anonymous login support: +%% auth_method: anonymous +%% anonymous_protocol: sasl_anon | login_anon | both +%% allow_multiple_connections: true | false +%% +%%{host_config, "public.example.org", [{auth_method, anonymous}, +%% {allow_multiple_connections, false}, +%% {anonymous_protocol, sasl_anon}]}. +%% +%% To use both anonymous and internal authentication: +%% +%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + +%%%. ============== +%%%' DATABASE SETUP + +%% ejabberd by default uses the internal Mnesia database, +%% so you do not necessarily need this section. +%% This section provides configuration examples in case +%% you want to use other database backends. +%% Please consult the ejabberd Guide for details on database creation. + +%% +%% MySQL server: +%% +%% {odbc_server, {mysql, "localhost", 3306, "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + +%% +%% PostgreSQL server: +%% +%%{odbc_server, {pgsql, "server", "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. +%% +%% If you use PostgreSQL, have a large database, and need a +%% faster but inexact replacement for "select count(*) from users" +%% +%%{pgsql_users_number_estimate, true}. + +%% +%% ODBC compatible or MSSQL server: +%% +%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + +%% +%% Number of connections to open to the database for each virtual host +%% +%%{odbc_pool_size, 10}. + +%% +%% Interval to make a dummy SQL request to keep the connections to the +%% database alive. Specify in seconds: for example 28800 means 8 hours +%% +%%{odbc_keepalive_interval, undefined}. + + +%%%. =============== +%%%' TRAFFIC SHAPERS + +%% +%% The "normal" shaper limits traffic speed to 1000 B/s +%% +{shaper, normal, {maxrate, 1000}}. + +%% +%% The "fast" shaper limits traffic speed to 50000 B/s +%% +{shaper, fast, {maxrate, 50000}}. + +%% +%% This option specifies the maximum number of elements in the queue +%% of the FSM. Refer to the documentation for details. +%% +{max_fsm_queue, 1000}. + + +%%%. ==================== +%%%' ACCESS CONTROL LISTS + +%% +%% The 'admin' ACL grants administrative privileges to XMPP accounts. +%% You can put here as many accounts as you want. +%% +%{acl, admin, {user, "alice", "localhost"}}. +%{acl, admin, {user, "a", "localhost"}}. + +%% +%% Blocked users +%% +%%{acl, blocked, {user, "baduser", "example.org"}}. +%%{acl, blocked, {user, "test"}}. + +%% +%% Local users: don't modify this line. +%% +{acl, local, {user_regexp, ""}}. + +%% +%% More examples of ACLs +%% +%%{acl, jabberorg, {server, "jabber.org"}}. +%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. +%%{acl, test, {user_regexp, "^test"}}. +%%{acl, test, {user_glob, "test*"}}. + +%% +%% Define specific ACLs in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {acl, admin, {user, "bob-local", "localhost"}} +%% ] +%%}. + + +%%%. ============ +%%%' ACCESS RULES + +%% Maximum number of simultaneous sessions allowed for a single user: +{access, max_user_sessions, [{10, all}]}. + +%% Maximum number of offline messages that users can have: +{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + +%% This rule allows access only for local users: +{access, local, [{allow, local}]}. + +%% Only non-blocked users can use c2s connections: +{access, c2s, [{deny, blocked}, + {allow, all}]}. + +%% For C2S connections, all users except admins use the "normal" shaper +{access, c2s_shaper, [{none, admin}, + {normal, all}]}. + +%% All S2S connections use the "fast" shaper +{access, s2s_shaper, [{fast, all}]}. + +%% Admins of this server are also admins of the MUC service: +{access, muc_admin, [{allow, admin}]}. + +%% Only accounts of the local ejabberd server can create rooms: +{access, muc_create, [{allow, local}]}. + +%% All users are allowed to use the MUC service: +{access, muc, [{allow, all}]}. + +%% In-band registration allows registration of any possible username. +%% To disable in-band registration, replace 'allow' with 'deny'. +{access, register, [{allow, all}]}. + +%% By default the frequency of account registrations from the same IP +%% is limited to 1 account every 10 minutes. To disable, specify: infinity +{registration_timeout, infinity}. + +%% Default settings for MAM. +%% To set non-standard value, replace 'default' with 'allow' or 'deny'. +%% Only user can access his/her archive by default. +%% An online user can read room's archive by default. +%% Only an owner can change settings and purge messages by default. +%% Empty list (i.e. `[]`) means `[{deny, all}]`. +{access, mam_set_prefs, [{default, all}]}. +{access, mam_get_prefs, [{default, all}]}. +{access, mam_lookup_messages, [{default, all}]}. +{access, mam_purge_single_message, [{default, all}]}. +{access, mam_purge_multiple_messages, [{default, all}]}. + +%% 1 command of the specified type per second. +{shaper, mam_shaper, {maxrate, 1}}. +%% This shaper is primeraly for Mnesia overload protection during stress testing. +%% The limit is 1000 operations of each type per second. +{shaper, mam_global_shaper, {maxrate, 1000}}. + +{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. +{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}. +{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}. + +{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}. + + +%% +%% Define specific Access Rules in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {access, c2s, [{allow, admin}, {deny, all}]}, +%% {access, register, [{deny, all}]} +%% ] +%%}. + + +%%%. ================ +%%%' DEFAULT LANGUAGE + +%% +%% language: Default language used for server messages. +%% +{language, "en"}. + +%% +%% Set a different default language in a virtual host. +%% +%%{host_config, "localhost", +%% [{language, "ru"}] +%%}. + + +%%%. ======= +%%%' MODULES + +%% +%% Modules enabled in all ejabberd virtual hosts. +%% For list of possible modules options, check documentation. +%% If module comes in two versions, like mod_last and mod_last_odbc, +%% use only one of them. +%% +{modules, + [ + %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, + % roster, last, private, stanza, stats]}]}, + {mod_adhoc, []}, + {mod_disco, []}, + {mod_last, []}, + {mod_stream_management, [ + % default 100 + % size of a buffer of unacked messages + % {buffer_max, 100} + + % default 1 - server sends the ack request after each stanza + % {ack_freq, 1} + + % default: 600 seconds + % {resume_timeout, 600} + ]}, + {mod_muc, [ + {host, "muc.@HOST@"}, + {access, muc}, + {access_create, muc_create} + ]}, + {mod_muc_log, + [ + {outdir, "/tmp/muclogs"}, + {access_log, muc} + ]}, + {mod_privacy, []}, + {mod_register, [ + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {""}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + {mod_roster, []}, + {mod_sic, []}, + {mod_vcard, [ {allow_return_all, true}, +{search_all_hosts, true} +%{matches, 1}, +%{search, true}, +%{host, directory.@HOST@} +]} + + %% + %% Message Archive Management (MAM) for registered users. + %% + + %% A module for storing preferences in RDBMS (used by default). + %% Enable for private message archives. +% {mod_mam_odbc_prefs, [pm]}, + %% Enable for multiuser message archives. +% {mod_mam_odbc_prefs, [muc]}, + %% Enable for both private and multiuser message archives. +% {mod_mam_odbc_prefs, [pm, muc]}, + + %% A module for storing preferences in Mnesia (recommended). + %% This module will be called each time, as a message is routed. + %% That is why, Mnesia is better for this job. +% {mod_mam_mnesia_prefs, [pm, muc]}, + + %% Mnesia back-end with optimized writes and dirty synchronious writes. +% {mod_mam_mnesia_dirty_prefs, [pm, muc]}, + + %% A back-end for storing messages. + %% Synchronious writer (used by default). + %% This writer is easy to debug, but writing performance is low. +% {mod_mam_odbc_arch, [pm]}, + + %% Enable the module with a custom writer. +% {mod_mam_odbc_arch, [no_writer, pm]}, + + %% Asynchronious writer for RDBMS (recommended). + %% Messages will be grouped and inserted all at once. +% {mod_mam_odbc_async_writer, [pm]}, + + %% A pool of asynchronious writers (recommended). + %% Messages will be grouped together based on archive id. +% {mod_mam_odbc_async_pool_writer, [pm]}, + + %% A module for converting an archive id to an integer. + %% Extract information using ODBC. +% {mod_mam_odbc_user, [pm, muc]}, + + %% Cache information about users (recommended). + %% Requires mod_mam_odbc_user or alternative. +% {mod_mam_cache_user, [pm, muc]}, + + %% Enable MAM. +% {mod_mam, []}, + + + %% + %% Message Archive Management (MAM) for multi-user chats (MUC). + %% Enable XEP-0313 for "muc.@HOST@". + %% + + %% A back-end for storing messages (default for MUC). + %% Modules mod_mam_muc_* are optimized for MUC. + %% + %% Synchronious writer (used by default for MUC). + %% This module is easy to debug, but performance is low. +% {mod_mam_muc_odbc_arch, []}, +% {mod_mam_muc_odbc_arch, [no_writer]}, + + %% Asynchronious writer for RDBMS (recommended for MUC). + %% Messages will be grouped and inserted all at once. +% {mod_mam_muc_odbc_async_writer, []}, +% {mod_mam_muc_odbc_async_pool_writer, []}, + + %% Load mod_mam_odbc_user too. + + %% Enable MAM for MUC +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + + %% + %% MAM configuration examples + %% + + %% Only MUC, no user-defined preferences, good performance. +% {mod_mam_odbc_user, [muc]}, +% {mod_mam_cache_user, [muc]}, +% {mod_mam_muc_odbc_arch, [no_writer]}, +% {mod_mam_muc_odbc_async_pool_writer, []}, +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + %% Only archives for c2c messages, good performance. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_cache_user, [pm]}, +% {mod_mam_mnesia_dirty_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm, no_writer]}, +% {mod_mam_odbc_async_pool_writer, [pm]}, +% {mod_mam, []} + + %% Basic configuration for c2c messages, bad performance, easy to debug. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_odbc_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm]}, +% {mod_mam, []} + + ]}. + + +%% +%% Enable modules with custom options in a specific virtual host +%% +%%{host_config, "localhost", +%% [{ {add, modules}, +%% [ +%% {mod_some_module, []} +%% ] +%% } +%% ]}. + +%%%. +%%%' + +%%% $Id$ + +%%% Local Variables: +%%% mode: erlang +%%% End: +%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: +%%%. diff --git a/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyws.cfg b/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyws.cfg new file mode 100644 index 00000000000..e310f79a950 --- /dev/null +++ b/apps/ejabberd/test/cowboy_SUITE_data/ejabberd.onlyws.cfg @@ -0,0 +1,752 @@ +%%% +%%% ejabberd configuration file +%%% +%%%' + +%%% The parameters used in this configuration file are explained in more detail +%%% in the ejabberd Installation and Operation Guide. +%%% Please consult the Guide in case of doubts, it is included with +%%% your copy of ejabberd, and is also available online at +%%% http://www.process-one.net/en/ejabberd/docs/ + +%%% This configuration file contains Erlang terms. +%%% In case you want to understand the syntax, here are the concepts: +%%% +%%% - The character to comment a line is % +%%% +%%% - Each term ends in a dot, for example: +%%% override_global. +%%% +%%% - A tuple has a fixed definition, its elements are +%%% enclosed in {}, and separated with commas: +%%% {loglevel, 4}. +%%% +%%% - A list can have as many elements as you want, +%%% and is enclosed in [], for example: +%%% [http_poll, web_admin, tls] +%%% +%%% Pay attention that list elements are delimited with commas, +%%% but no comma is allowed after the last list element. This will +%%% give a syntax error unlike in more lenient languages (e.g. Python). +%%% +%%% - A keyword of ejabberd is a word in lowercase. +%%% Strings are enclosed in "" and can contain spaces, dots, ... +%%% {language, "en"}. +%%% {ldap_rootdn, "dc=example,dc=com"}. +%%% +%%% - This term includes a tuple, a keyword, a list, and two strings: +%%% {hosts, ["jabber.example.net", "im.example.com"]}. +%%% +%%% - This config is preprocessed during release generation by a tool which +%%% interprets double curly braces as substitution markers, so avoid this +%%% syntax in this file (though it's valid Erlang). +%%% +%%% So this is OK (though arguably looks quite ugly): +%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%%% +%%% And I can't give an example of what's not OK exactly because +%%% of this rule. +%%% + + +%%%. ======================= +%%%' OVERRIDE STORED OPTIONS + +%% +%% Override the old values stored in the database. +%% + +%% +%% Override global options (shared by all ejabberd nodes in a cluster). +%% +%%override_global. + +%% +%% Override local options (specific for this particular ejabberd node). +%% +%%override_local. + +%% +%% Remove the Access Control Lists before new ones are added. +%% +%%override_acls. + + +%%%. ========= +%%%' DEBUGGING + +%% +%% loglevel: Verbosity of log files generated by ejabberd. +%% 0: No ejabberd log at all (not recommended) +%% 1: Critical +%% 2: Error +%% 3: Warning +%% 4: Info +%% 5: Debug +%% +{loglevel, 3}. + +%% +%% alarms: an optional alarm handler, subscribed to system events +%% long_gc: minimum GC time in ms for long_gc alarm +%% large_heap: minimum process heap size for large_heap alarm +%% handlers: a list of alarm handlers +%% - alarms_basic_handler: logs alarms and stores a brief alarm summary +%% - alarms_folsom_handler: stores alarm details in folsom metrics +%% +%% Example: +%% {alarms, +%% [{long_gc, 10000}, +%% {large_heap, 1000000}, +%% {handlers, [alarms_basic_handler, +%% alarms_folsom_handler]}] +%% }. + +%% +%% watchdog_admins: Only useful for developers: if an ejabberd process +%% consumes a lot of memory, send live notifications to these XMPP +%% accounts. Requires alarms (see above). +%% +%%{watchdog_admins, ["bob@example.com"]}. + + +%%%. ================ +%%%' SERVED HOSTNAMES + +%% +%% hosts: Domains served by ejabberd. +%% You can define one or several, for example: +%% {hosts, ["example.net", "example.com", "example.org"]}. +%% +{hosts, ["localhost"] }. + +%% +%% route_subdomains: Delegate subdomains to other XMPP servers. +%% For example, if this ejabberd serves example.org and you want +%% to allow communication with an XMPP server called im.example.org. +%% +%%{route_subdomains, s2s}. + + +%%%. =============== +%%%' LISTENING PORTS + +%% +%% listen: The ports ejabberd will listen on, which service each is handled +%% by and what options to start it with. +%% +{listen, + [ + + { 5280, ejabberd_cowboy, [ + {num_acceptors, 10}, + {max_connections, 1024}, + %% Uncomment for HTTPS + %{cert, "priv/server.crt"}, + %{key, "priv/server.key"}, + %{key_pass, ""}, + {modules, [ + %% Modules used here should also be listed in the MODULES section. + {"_", "/[...]", mod_cowboy, [{ws, xmpp, dummy_ws1_handler}]} + ]} + ]}, + + { 5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + %%{certfile, "/path/to/ssl.pem"}, starttls, + %%{zlib, 10000}, + %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS + %% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}, + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + + + %% + %% To enable the old SSL connection method on port 5223: + %% + %%{5223, ejabberd_c2s, [ + %% {access, c2s}, + %% {shaper, c2s_shaper}, + %% {certfile, "/path/to/ssl.pem"}, tls, + %% {max_stanza_size, 65536} + %% ]}, + + { 5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]} + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{ {3478, udp}, ejabberd_stun, []} + + ]}. + +%% +%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. +%% Allowed values are: false optional required required_trusted +%% You must specify a certificate file. +%% +%%{s2s_use_starttls, optional}. + +%% +%% s2s_certfile: Specify a certificate file. +%% +%%{s2s_certfile, "/path/to/ssl.pem"}. + +%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS +%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}. + +%% +%% domain_certfile: Specify a different certificate for each served hostname. +%% +%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. +%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + +%% +%% S2S whitelist or blacklist +%% +%% Default s2s policy for undefined hosts. +%% +{s2s_default_policy, deny }. + +%% +%% Allow or deny communication with specific servers. +%% +%%{ {s2s_host, "goodhost.org"}, allow}. +%%{ {s2s_host, "badhost.org"}, deny}. + +{outgoing_s2s_port, 5269 }. + +%% +%% IP addresses predefined for specific hosts to skip DNS lookups. +%% Ports defined here take precedence over outgoing_s2s_port. +%% Examples: +%% +%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. + +%% +%% Outgoing S2S options +%% +%% Preferred address families (which to try first) and connect timeout +%% in milliseconds. +%% +%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + +%%%. ============== +%%%' SESSION BACKEND + +%%{sm_backend, {mnesia, []}}. + +%%{sm_backend, {redis, [{pool_size, 3}, {worker_config, [{host, "localhost"}, {port, 6379}]}]}}. +{sm_backend, {mnesia, []} }. + + +%%%. ============== +%%%' AUTHENTICATION + +%% +%% auth_method: Method used to authenticate the users. +%% The default method is the internal. +%% If you want to use a different method, +%% comment this line and enable the correct ones. +%% +{auth_method, internal}. +%% +%% Store the plain passwords or hashed for SCRAM: +%%{auth_password_format, plain}. % default +%%{auth_password_format, scram}. +%%{auth_scram_iterations, 4096}. % default + +%% +%% Authentication using external script +%% Make sure the script is executable by ejabberd. +%% +%%{auth_method, external}. +%%{extauth_program, "/path/to/authentication/script"}. + +%% +%% Authentication using ODBC +%% Remember to setup a database in the next section. +%% +%%{auth_method, odbc}. + +%% +%% Authentication using PAM +%% +%%{auth_method, pam}. +%%{pam_service, "pamservicename"}. + +%% +%% Authentication using LDAP +%% +%%{auth_method, ldap}. +%% + +%% List of LDAP servers: +%%{ldap_servers, ["localhost"]}. +%% +%% Encryption of connection to LDAP servers: +%%{ldap_encrypt, none}. +%%{ldap_encrypt, tls}. +%% +%% Port to connect to on LDAP servers: +%%{ldap_port, 389}. +%%{ldap_port, 636}. +%% +%% LDAP manager: +%%{ldap_rootdn, "dc=example,dc=com"}. +%% +%% Password of LDAP manager: +%%{ldap_password, "******"}. +%% +%% Search base of LDAP directory: +%%{ldap_base, "dc=example,dc=com"}. +%% +%% LDAP attribute that holds user ID: +%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. +%% +%% LDAP filter: +%%{ldap_filter, "(objectClass=shadowAccount)"}. + +%% +%% Anonymous login support: +%% auth_method: anonymous +%% anonymous_protocol: sasl_anon | login_anon | both +%% allow_multiple_connections: true | false +%% +%%{host_config, "public.example.org", [{auth_method, anonymous}, +%% {allow_multiple_connections, false}, +%% {anonymous_protocol, sasl_anon}]}. +%% +%% To use both anonymous and internal authentication: +%% +%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + +%%%. ============== +%%%' DATABASE SETUP + +%% ejabberd by default uses the internal Mnesia database, +%% so you do not necessarily need this section. +%% This section provides configuration examples in case +%% you want to use other database backends. +%% Please consult the ejabberd Guide for details on database creation. + +%% +%% MySQL server: +%% +%% {odbc_server, {mysql, "localhost", 3306, "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + +%% +%% PostgreSQL server: +%% +%%{odbc_server, {pgsql, "server", "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. +%% +%% If you use PostgreSQL, have a large database, and need a +%% faster but inexact replacement for "select count(*) from users" +%% +%%{pgsql_users_number_estimate, true}. + +%% +%% ODBC compatible or MSSQL server: +%% +%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + +%% +%% Number of connections to open to the database for each virtual host +%% +%%{odbc_pool_size, 10}. + +%% +%% Interval to make a dummy SQL request to keep the connections to the +%% database alive. Specify in seconds: for example 28800 means 8 hours +%% +%%{odbc_keepalive_interval, undefined}. + + +%%%. =============== +%%%' TRAFFIC SHAPERS + +%% +%% The "normal" shaper limits traffic speed to 1000 B/s +%% +{shaper, normal, {maxrate, 1000}}. + +%% +%% The "fast" shaper limits traffic speed to 50000 B/s +%% +{shaper, fast, {maxrate, 50000}}. + +%% +%% This option specifies the maximum number of elements in the queue +%% of the FSM. Refer to the documentation for details. +%% +{max_fsm_queue, 1000}. + + +%%%. ==================== +%%%' ACCESS CONTROL LISTS + +%% +%% The 'admin' ACL grants administrative privileges to XMPP accounts. +%% You can put here as many accounts as you want. +%% +%{acl, admin, {user, "alice", "localhost"}}. +%{acl, admin, {user, "a", "localhost"}}. + +%% +%% Blocked users +%% +%%{acl, blocked, {user, "baduser", "example.org"}}. +%%{acl, blocked, {user, "test"}}. + +%% +%% Local users: don't modify this line. +%% +{acl, local, {user_regexp, ""}}. + +%% +%% More examples of ACLs +%% +%%{acl, jabberorg, {server, "jabber.org"}}. +%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. +%%{acl, test, {user_regexp, "^test"}}. +%%{acl, test, {user_glob, "test*"}}. + +%% +%% Define specific ACLs in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {acl, admin, {user, "bob-local", "localhost"}} +%% ] +%%}. + + +%%%. ============ +%%%' ACCESS RULES + +%% Maximum number of simultaneous sessions allowed for a single user: +{access, max_user_sessions, [{10, all}]}. + +%% Maximum number of offline messages that users can have: +{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + +%% This rule allows access only for local users: +{access, local, [{allow, local}]}. + +%% Only non-blocked users can use c2s connections: +{access, c2s, [{deny, blocked}, + {allow, all}]}. + +%% For C2S connections, all users except admins use the "normal" shaper +{access, c2s_shaper, [{none, admin}, + {normal, all}]}. + +%% All S2S connections use the "fast" shaper +{access, s2s_shaper, [{fast, all}]}. + +%% Admins of this server are also admins of the MUC service: +{access, muc_admin, [{allow, admin}]}. + +%% Only accounts of the local ejabberd server can create rooms: +{access, muc_create, [{allow, local}]}. + +%% All users are allowed to use the MUC service: +{access, muc, [{allow, all}]}. + +%% In-band registration allows registration of any possible username. +%% To disable in-band registration, replace 'allow' with 'deny'. +{access, register, [{allow, all}]}. + +%% By default the frequency of account registrations from the same IP +%% is limited to 1 account every 10 minutes. To disable, specify: infinity +{registration_timeout, infinity}. + +%% Default settings for MAM. +%% To set non-standard value, replace 'default' with 'allow' or 'deny'. +%% Only user can access his/her archive by default. +%% An online user can read room's archive by default. +%% Only an owner can change settings and purge messages by default. +%% Empty list (i.e. `[]`) means `[{deny, all}]`. +{access, mam_set_prefs, [{default, all}]}. +{access, mam_get_prefs, [{default, all}]}. +{access, mam_lookup_messages, [{default, all}]}. +{access, mam_purge_single_message, [{default, all}]}. +{access, mam_purge_multiple_messages, [{default, all}]}. + +%% 1 command of the specified type per second. +{shaper, mam_shaper, {maxrate, 1}}. +%% This shaper is primeraly for Mnesia overload protection during stress testing. +%% The limit is 1000 operations of each type per second. +{shaper, mam_global_shaper, {maxrate, 1000}}. + +{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. +{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}. +{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}. + +{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}. + + +%% +%% Define specific Access Rules in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {access, c2s, [{allow, admin}, {deny, all}]}, +%% {access, register, [{deny, all}]} +%% ] +%%}. + + +%%%. ================ +%%%' DEFAULT LANGUAGE + +%% +%% language: Default language used for server messages. +%% +{language, "en"}. + +%% +%% Set a different default language in a virtual host. +%% +%%{host_config, "localhost", +%% [{language, "ru"}] +%%}. + + +%%%. ======= +%%%' MODULES + +%% +%% Modules enabled in all ejabberd virtual hosts. +%% For list of possible modules options, check documentation. +%% If module comes in two versions, like mod_last and mod_last_odbc, +%% use only one of them. +%% +{modules, + [ + %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, + % roster, last, private, stanza, stats]}]}, + {mod_adhoc, []}, + {mod_disco, []}, + {mod_last, []}, + {mod_stream_management, [ + % default 100 + % size of a buffer of unacked messages + % {buffer_max, 100} + + % default 1 - server sends the ack request after each stanza + % {ack_freq, 1} + + % default: 600 seconds + % {resume_timeout, 600} + ]}, + {mod_muc, [ + {host, "muc.@HOST@"}, + {access, muc}, + {access_create, muc_create} + ]}, + {mod_muc_log, + [ + {outdir, "/tmp/muclogs"}, + {access_log, muc} + ]}, + {mod_privacy, []}, + {mod_register, [ + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {""}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + {mod_roster, []}, + {mod_sic, []}, + {mod_vcard, [ {allow_return_all, true}, +{search_all_hosts, true} +%{matches, 1}, +%{search, true}, +%{host, directory.@HOST@} +]} + + %% + %% Message Archive Management (MAM) for registered users. + %% + + %% A module for storing preferences in RDBMS (used by default). + %% Enable for private message archives. +% {mod_mam_odbc_prefs, [pm]}, + %% Enable for multiuser message archives. +% {mod_mam_odbc_prefs, [muc]}, + %% Enable for both private and multiuser message archives. +% {mod_mam_odbc_prefs, [pm, muc]}, + + %% A module for storing preferences in Mnesia (recommended). + %% This module will be called each time, as a message is routed. + %% That is why, Mnesia is better for this job. +% {mod_mam_mnesia_prefs, [pm, muc]}, + + %% Mnesia back-end with optimized writes and dirty synchronious writes. +% {mod_mam_mnesia_dirty_prefs, [pm, muc]}, + + %% A back-end for storing messages. + %% Synchronious writer (used by default). + %% This writer is easy to debug, but writing performance is low. +% {mod_mam_odbc_arch, [pm]}, + + %% Enable the module with a custom writer. +% {mod_mam_odbc_arch, [no_writer, pm]}, + + %% Asynchronious writer for RDBMS (recommended). + %% Messages will be grouped and inserted all at once. +% {mod_mam_odbc_async_writer, [pm]}, + + %% A pool of asynchronious writers (recommended). + %% Messages will be grouped together based on archive id. +% {mod_mam_odbc_async_pool_writer, [pm]}, + + %% A module for converting an archive id to an integer. + %% Extract information using ODBC. +% {mod_mam_odbc_user, [pm, muc]}, + + %% Cache information about users (recommended). + %% Requires mod_mam_odbc_user or alternative. +% {mod_mam_cache_user, [pm, muc]}, + + %% Enable MAM. +% {mod_mam, []}, + + + %% + %% Message Archive Management (MAM) for multi-user chats (MUC). + %% Enable XEP-0313 for "muc.@HOST@". + %% + + %% A back-end for storing messages (default for MUC). + %% Modules mod_mam_muc_* are optimized for MUC. + %% + %% Synchronious writer (used by default for MUC). + %% This module is easy to debug, but performance is low. +% {mod_mam_muc_odbc_arch, []}, +% {mod_mam_muc_odbc_arch, [no_writer]}, + + %% Asynchronious writer for RDBMS (recommended for MUC). + %% Messages will be grouped and inserted all at once. +% {mod_mam_muc_odbc_async_writer, []}, +% {mod_mam_muc_odbc_async_pool_writer, []}, + + %% Load mod_mam_odbc_user too. + + %% Enable MAM for MUC +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + + %% + %% MAM configuration examples + %% + + %% Only MUC, no user-defined preferences, good performance. +% {mod_mam_odbc_user, [muc]}, +% {mod_mam_cache_user, [muc]}, +% {mod_mam_muc_odbc_arch, [no_writer]}, +% {mod_mam_muc_odbc_async_pool_writer, []}, +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + %% Only archives for c2c messages, good performance. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_cache_user, [pm]}, +% {mod_mam_mnesia_dirty_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm, no_writer]}, +% {mod_mam_odbc_async_pool_writer, [pm]}, +% {mod_mam, []} + + %% Basic configuration for c2c messages, bad performance, easy to debug. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_odbc_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm]}, +% {mod_mam, []} + + ]}. + + +%% +%% Enable modules with custom options in a specific virtual host +%% +%%{host_config, "localhost", +%% [{ {add, modules}, +%% [ +%% {mod_some_module, []} +%% ] +%% } +%% ]}. + +%%%. +%%%' + +%%% $Id$ + +%%% Local Variables: +%%% mode: erlang +%%% End: +%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: +%%%. diff --git a/apps/ejabberd/test/ejabberd_helper.erl b/apps/ejabberd/test/ejabberd_helper.erl index c560e4c98d3..31325601148 100644 --- a/apps/ejabberd/test/ejabberd_helper.erl +++ b/apps/ejabberd/test/ejabberd_helper.erl @@ -2,7 +2,8 @@ -export([start_ejabberd/1, stop_ejabberd/0, - use_config_file/2]). + use_config_file/2, + start_ejabberd_with_config/2]). -spec start_ejabberd(any()) -> 'ok' | {error, any()}. @@ -19,3 +20,9 @@ use_config_file(Config, ConfigFile) -> DataDir = proplists:get_value(data_dir, Config), ConfigPath = filename:join([DataDir, ConfigFile]), application:set_env(ejabberd, config, ConfigPath). + +-spec start_ejabberd_with_config(any(), file:name_all()) -> ok. +start_ejabberd_with_config(Config, ConfigFile) -> + use_config_file(Config, ConfigFile), + {ok, _} = start_ejabberd(Config). + diff --git a/apps/ejabberd/test/ejabberd_hooks_SUITE.erl b/apps/ejabberd/test/ejabberd_hooks_SUITE.erl index 2aa850ba528..270328da626 100644 --- a/apps/ejabberd/test/ejabberd_hooks_SUITE.erl +++ b/apps/ejabberd/test/ejabberd_hooks_SUITE.erl @@ -25,6 +25,13 @@ all() -> exit_in_run_fold_is_ignored ]. +init_per_suite(C) -> + application:ensure_all_started(exometer), + C. + +end_per_suite(_C) -> + application:stop(exometer). + a_fun_can_be_added(_) -> given_hooks_started(), diff --git a/apps/ejabberd/test/jlib_SUITE.erl b/apps/ejabberd/test/jlib_SUITE.erl index bcdc9559230..b34bcfcda28 100644 --- a/apps/ejabberd/test/jlib_SUITE.erl +++ b/apps/ejabberd/test/jlib_SUITE.erl @@ -31,7 +31,6 @@ init_per_suite(C) -> C. end_per_suite(C) -> - application:stop(p1_stringprep), C. diff --git a/apps/ejabberd/test/mod_websockets_SUITE.erl b/apps/ejabberd/test/mod_websockets_SUITE.erl index c33e6028b9e..8422c42e5ef 100644 --- a/apps/ejabberd/test/mod_websockets_SUITE.erl +++ b/apps/ejabberd/test/mod_websockets_SUITE.erl @@ -11,6 +11,14 @@ all() -> [ ping_test, set_ping_test, disable_ping_test, disable_and_set]. +init_per_testcase(_, C) -> + setup(), + C. + +end_per_testcase(_, C) -> + teardown(), + C. + setup() -> meck:unload(), application:ensure_all_started(cowboy), @@ -24,40 +32,34 @@ setup() -> %% mock ejabberd_c2s meck:expect(ejabberd_c2s, start, fun({_, Socket},_) -> Self ! {catch_socket, Socket}, {ok, mocked_pid} end), meck:expect(supervisor, start_child, - fun(ejabberd_sup, {_, {_, start_link, [_]}, permanent, - infinity, worker, [_]}) -> ok; + fun(ejabberd_listeners, {_, {_, start_link, [_]}, transient, + infinity, worker, [_]}) -> {ok, self()}; (A,B) ->meck:passthrough([A,B]) end), %% Start websocket cowboy listening - spawn(fun() -> + Opts = [{num_acceptors, 10}, {max_connections, 1024}, {modules, [{"_", "/http-bind", mod_bosh}, {"_", "/ws-xmpp", mod_websockets, [{timeout, 600000}, {ping_rate, ?FAST_PING_RATE}]}]}], - ejabberd_cowboy:start_listener({?PORT, ?IP, tcp}, Opts) - end). + ejabberd_cowboy:start_listener({?PORT, ?IP, tcp}, Opts). teardown() -> meck:unload(), - cowboy:stop_listener(http_listener), + ejabberd_cowboy:stop(ejabberd_cowboy:handler({?PORT, ?IP, tcp})), application:stop(cowboy), ok. ping_test(_Config) -> - %% Given - setup(), timer:sleep(500), {ok, Socket1, _} = ws_handshake("localhost", ?PORT), %% When Resp = wait_for_ping(Socket1, 0, 5000), %% then - ?eq(Resp, ok), - teardown(). + ?eq(Resp, ok). set_ping_test(_Config) -> - %% Given - setup(), {ok, Socket1, InternalSocket} = ws_handshake("localhost", ?PORT), %% When mod_websockets:set_ping(InternalSocket, ?NEW_TIMEOUT), @@ -69,24 +71,18 @@ set_ping_test(_Config) -> Resp1 = wait_for_ping(Socket1, 0, ?NEW_TIMEOUT + 200), %% then ?eq(ok, Resp1), - ?eq({error, timeout}, ErrorTimeout), - teardown(). + ?eq({error, timeout}, ErrorTimeout). disable_ping_test(_Config) -> - %% Given - setup(), {ok, Socket1, InternalSocket} = ws_handshake("localhost", ?PORT), %% When mod_websockets:disable_ping(InternalSocket), %% Should not receive any packets ErrorTimeout = wait_for_ping(Socket1, 0, ?FAST_PING_RATE), %% then - ?eq(ErrorTimeout, {error, timeout}), - teardown(). + ?eq(ErrorTimeout, {error, timeout}). disable_and_set(_Config) -> - %% Given - setup(), {ok, Socket1, InternalSocket} = ws_handshake("localhost", ?PORT), %% When mod_websockets:disable_ping(InternalSocket), @@ -96,9 +92,7 @@ disable_and_set(_Config) -> Resp1 = wait_for_ping(Socket1, 0, ?NEW_TIMEOUT + 100), %% then ?eq(ErrorTimeout, {error, timeout}), - ?eq(Resp1, ok), - teardown(). - + ?eq(Resp1, ok). %% Client side ws_handshake(Host, Port) -> diff --git a/apps/ejabberd/test/mongoose_cleanup_SUITE.erl b/apps/ejabberd/test/mongoose_cleanup_SUITE.erl index c2917c4b1a8..b6cd8544161 100644 --- a/apps/ejabberd/test/mongoose_cleanup_SUITE.erl +++ b/apps/ejabberd/test/mongoose_cleanup_SUITE.erl @@ -66,10 +66,10 @@ cleaner_runs_hook_on_nodedown(_Config) -> Self = self(), NotifySelf = fun (Node) -> Self ! {got_nodedown, Node} end, ejabberd_hooks:add(node_cleanup, global, undefined, NotifySelf, 50), - + FakeNode = fakename@fakehost, Cleaner ! {nodedown, FakeNode}, - + receive {got_nodedown, FakeNode} -> ok after timer:seconds(1) -> diff --git a/apps/ejabberd/test/revproxy_SUITE.erl b/apps/ejabberd/test/revproxy_SUITE.erl index 4bda1dabb43..9bf75b8cf06 100644 --- a/apps/ejabberd/test/revproxy_SUITE.erl +++ b/apps/ejabberd/test/revproxy_SUITE.erl @@ -21,6 +21,11 @@ -include_lib("ejabberd/include/mod_revproxy.hrl"). +-import(ejabberd_helper, [start_ejabberd/1, + stop_ejabberd/0, + use_config_file/2, + start_ejabberd_with_config/2]). + %%-------------------------------------------------------------------- %% Suite configuration %%-------------------------------------------------------------------- @@ -29,7 +34,8 @@ all() -> [{group, compile_routes}, {group, match_routes}, {group, generate_upstream}, - {group, requests_http}]. + {group, requests_http}, + conf_reload]. groups() -> [{compile_routes, [sequence], [compile_example_routes, @@ -62,7 +68,7 @@ suite() -> init_per_suite(Config) -> [application:start(App) || App <- ?APPS], - Pid = create_handler(), + {ok, Pid} = create_handler(), [{meck_pid, Pid}|Config]. end_per_suite(Config) -> @@ -78,6 +84,9 @@ init_per_group(match_routes, Config) -> init_per_group(_GroupName, Config) -> Config. +end_per_group(requests_http, Config) -> + stop_revproxy(), + Config; end_per_group(_GroupName, Config) -> Config. @@ -92,15 +101,21 @@ init_per_testcase(http_upstream, Config) -> init_per_testcase(https_upstream, Config) -> start_https_upstream(Config), Config; +init_per_testcase(conf_reload, Config) -> + start_http_upstream(), + Config; init_per_testcase(_CaseName, Config) -> Config. end_per_testcase(http_upstream, Config) -> - stop_upstream(http_upstream), + stop_upstream(http_listener), meck:unload(inet), Config; end_per_testcase(https_upstream, Config) -> - stop_upstream(https_upstream), + stop_upstream(https_listener), + Config; +end_per_testcase(conf_reload, Config) -> + stop_upstream(http_listener), Config; end_per_testcase(_CaseName, Config) -> Config. @@ -128,6 +143,38 @@ example_dynamic_compile(_Config) -> %% Then Expected = mod_revproxy_dynamic:rules(). +%%-------------------------------------------------------------------- +%% Configuration reload test +%%-------------------------------------------------------------------- +conf_reload(Config) -> + %% Given initial configuration + Host = "http://localhost:5280", + Path = <<"/">>, + Method = "GET", + Headers = [], + Body = [], + + copy(data("ejabberd.onerule.cfg", Config), data("ejabberd.cfg", Config)), + start_ejabberd_with_config(Config, "ejabberd.cfg"), + + %% When making request for http + Response1 = execute_request(Host, Path, Method, Headers, Body), + + %% Then it returns 200 + true = is_status_code(Response1, 200), + + %% Given new configuration + copy(data("ejabberd.norules.cfg", Config), data("ejabberd.cfg", Config)), + ejabberd_config:reload_local(), + + %% When request is replayed + Response2 = execute_request(Host, Path, Method, Headers, Body), + + %% Then it returns 404 + true = is_status_code(Response2, 404), + + ok = stop_ejabberd(). + %%-------------------------------------------------------------------- %% HTTP requests tests %%-------------------------------------------------------------------- @@ -480,6 +527,12 @@ upstream_slash_remainder(_Config) -> %%-------------------------------------------------------------------- %% Helpers %%-------------------------------------------------------------------- +copy(Src, Dst) -> + {ok, _} = file:copy(Src, Dst). + +data(File, Config) -> + filename:join([?config(data_dir, Config), File]). + example_routes() -> [{"domain.com", "/abc", "_", "http://localhost:8080/"}, {"domain.com", get, "http://localhost:1234"}, @@ -533,6 +586,9 @@ start_revproxy() -> cowboy:start_http(revproxy_listener, 20, [{port, 8080}], [{env, [{dispatch, Dispatch}]}]). +stop_revproxy() -> + ok = cowboy:stop_listener(revproxy_listener). + start_http_upstream() -> Dispatch = cowboy_router:compile([ {'_', [{"/[...]", revproxy_handler, []}]} @@ -550,11 +606,8 @@ start_https_upstream(Config) -> cowboy:start_https(https_listener, 20, Opts, [{env, [{dispatch, Dispatch}]}]). -data(File, Config) -> - filename:join([?config(data_dir, Config), File]). - stop_upstream(Upstream) -> - cowboy:stop_listener(Upstream). + ok = cowboy:stop_listener(Upstream). execute_request(Host, Path, Method, Headers, Body) -> {ok, Pid} = fusco:start_link(Host, []), @@ -596,14 +649,22 @@ does_contain_header({ok, {{_, _}, _, Response, _, _}}, Header, Value) -> %% revproxy handler mock %%-------------------------------------------------------------------- create_handler() -> + Owner = self(), F = fun() -> ok = meck:new(revproxy_handler, [non_strict]), ok = meck:expect(revproxy_handler, init, fun handler_init/3), ok = meck:expect(revproxy_handler, handle, fun handler_handle/2), ok = meck:expect(revproxy_handler, terminate, fun handler_terminate/3), + Owner ! ok, timer:sleep(infinity) end, - spawn(F). + Pid = spawn(F), + receive + ok -> + {ok, Pid} + after 5000 -> + {error, timeout} + end. remove_handler(Config) -> meck:unload(revproxy_handler), diff --git a/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.norules.cfg b/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.norules.cfg new file mode 100644 index 00000000000..aa397160ac7 --- /dev/null +++ b/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.norules.cfg @@ -0,0 +1,752 @@ +%%% +%%% ejabberd configuration file +%%% +%%%' + +%%% The parameters used in this configuration file are explained in more detail +%%% in the ejabberd Installation and Operation Guide. +%%% Please consult the Guide in case of doubts, it is included with +%%% your copy of ejabberd, and is also available online at +%%% http://www.process-one.net/en/ejabberd/docs/ + +%%% This configuration file contains Erlang terms. +%%% In case you want to understand the syntax, here are the concepts: +%%% +%%% - The character to comment a line is % +%%% +%%% - Each term ends in a dot, for example: +%%% override_global. +%%% +%%% - A tuple has a fixed definition, its elements are +%%% enclosed in {}, and separated with commas: +%%% {loglevel, 4}. +%%% +%%% - A list can have as many elements as you want, +%%% and is enclosed in [], for example: +%%% [http_poll, web_admin, tls] +%%% +%%% Pay attention that list elements are delimited with commas, +%%% but no comma is allowed after the last list element. This will +%%% give a syntax error unlike in more lenient languages (e.g. Python). +%%% +%%% - A keyword of ejabberd is a word in lowercase. +%%% Strings are enclosed in "" and can contain spaces, dots, ... +%%% {language, "en"}. +%%% {ldap_rootdn, "dc=example,dc=com"}. +%%% +%%% - This term includes a tuple, a keyword, a list, and two strings: +%%% {hosts, ["jabber.example.net", "im.example.com"]}. +%%% +%%% - This config is preprocessed during release generation by a tool which +%%% interprets double curly braces as substitution markers, so avoid this +%%% syntax in this file (though it's valid Erlang). +%%% +%%% So this is OK (though arguably looks quite ugly): +%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%%% +%%% And I can't give an example of what's not OK exactly because +%%% of this rule. +%%% + + +%%%. ======================= +%%%' OVERRIDE STORED OPTIONS + +%% +%% Override the old values stored in the database. +%% + +%% +%% Override global options (shared by all ejabberd nodes in a cluster). +%% +%%override_global. + +%% +%% Override local options (specific for this particular ejabberd node). +%% +%%override_local. + +%% +%% Remove the Access Control Lists before new ones are added. +%% +%%override_acls. + + +%%%. ========= +%%%' DEBUGGING + +%% +%% loglevel: Verbosity of log files generated by ejabberd. +%% 0: No ejabberd log at all (not recommended) +%% 1: Critical +%% 2: Error +%% 3: Warning +%% 4: Info +%% 5: Debug +%% +{loglevel, 3}. + +%% +%% alarms: an optional alarm handler, subscribed to system events +%% long_gc: minimum GC time in ms for long_gc alarm +%% large_heap: minimum process heap size for large_heap alarm +%% handlers: a list of alarm handlers +%% - alarms_basic_handler: logs alarms and stores a brief alarm summary +%% - alarms_folsom_handler: stores alarm details in folsom metrics +%% +%% Example: +%% {alarms, +%% [{long_gc, 10000}, +%% {large_heap, 1000000}, +%% {handlers, [alarms_basic_handler, +%% alarms_folsom_handler]}] +%% }. + +%% +%% watchdog_admins: Only useful for developers: if an ejabberd process +%% consumes a lot of memory, send live notifications to these XMPP +%% accounts. Requires alarms (see above). +%% +%%{watchdog_admins, ["bob@example.com"]}. + + +%%%. ================ +%%%' SERVED HOSTNAMES + +%% +%% hosts: Domains served by ejabberd. +%% You can define one or several, for example: +%% {hosts, ["example.net", "example.com", "example.org"]}. +%% +{hosts, ["localhost"] }. + +%% +%% route_subdomains: Delegate subdomains to other XMPP servers. +%% For example, if this ejabberd serves example.org and you want +%% to allow communication with an XMPP server called im.example.org. +%% +%%{route_subdomains, s2s}. + + +%%%. =============== +%%%' LISTENING PORTS + +%% +%% listen: The ports ejabberd will listen on, which service each is handled +%% by and what options to start it with. +%% +{listen, + [ + + { 5280, ejabberd_cowboy, [ + {num_acceptors, 10}, + {max_connections, 1024}, + %% Uncomment for HTTPS + %{cert, "priv/server.crt"}, + %{key, "priv/server.key"}, + %{key_pass, ""}, + {modules, [ + %% Modules used here should also be listed in the MODULES section. + {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, []}]} + ]} + ]}, + + { 5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + %%{certfile, "/path/to/ssl.pem"}, starttls, + %%{zlib, 10000}, + %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS + %% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}, + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + + + %% + %% To enable the old SSL connection method on port 5223: + %% + %%{5223, ejabberd_c2s, [ + %% {access, c2s}, + %% {shaper, c2s_shaper}, + %% {certfile, "/path/to/ssl.pem"}, tls, + %% {max_stanza_size, 65536} + %% ]}, + + { 5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]} + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{ {3478, udp}, ejabberd_stun, []} + + ]}. + +%% +%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. +%% Allowed values are: false optional required required_trusted +%% You must specify a certificate file. +%% +%%{s2s_use_starttls, optional}. + +%% +%% s2s_certfile: Specify a certificate file. +%% +%%{s2s_certfile, "/path/to/ssl.pem"}. + +%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS +%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}. + +%% +%% domain_certfile: Specify a different certificate for each served hostname. +%% +%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. +%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + +%% +%% S2S whitelist or blacklist +%% +%% Default s2s policy for undefined hosts. +%% +{s2s_default_policy, deny }. + +%% +%% Allow or deny communication with specific servers. +%% +%%{ {s2s_host, "goodhost.org"}, allow}. +%%{ {s2s_host, "badhost.org"}, deny}. + +{outgoing_s2s_port, 5269 }. + +%% +%% IP addresses predefined for specific hosts to skip DNS lookups. +%% Ports defined here take precedence over outgoing_s2s_port. +%% Examples: +%% +%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. + +%% +%% Outgoing S2S options +%% +%% Preferred address families (which to try first) and connect timeout +%% in milliseconds. +%% +%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + +%%%. ============== +%%%' SESSION BACKEND + +%%{sm_backend, {mnesia, []}}. + +%%{sm_backend, {redis, [{pool_size, 3}, {worker_config, [{host, "localhost"}, {port, 6379}]}]}}. +{sm_backend, {mnesia, []} }. + + +%%%. ============== +%%%' AUTHENTICATION + +%% +%% auth_method: Method used to authenticate the users. +%% The default method is the internal. +%% If you want to use a different method, +%% comment this line and enable the correct ones. +%% +{auth_method, internal}. +%% +%% Store the plain passwords or hashed for SCRAM: +%%{auth_password_format, plain}. % default +%%{auth_password_format, scram}. +%%{auth_scram_iterations, 4096}. % default + +%% +%% Authentication using external script +%% Make sure the script is executable by ejabberd. +%% +%%{auth_method, external}. +%%{extauth_program, "/path/to/authentication/script"}. + +%% +%% Authentication using ODBC +%% Remember to setup a database in the next section. +%% +%%{auth_method, odbc}. + +%% +%% Authentication using PAM +%% +%%{auth_method, pam}. +%%{pam_service, "pamservicename"}. + +%% +%% Authentication using LDAP +%% +%%{auth_method, ldap}. +%% + +%% List of LDAP servers: +%%{ldap_servers, ["localhost"]}. +%% +%% Encryption of connection to LDAP servers: +%%{ldap_encrypt, none}. +%%{ldap_encrypt, tls}. +%% +%% Port to connect to on LDAP servers: +%%{ldap_port, 389}. +%%{ldap_port, 636}. +%% +%% LDAP manager: +%%{ldap_rootdn, "dc=example,dc=com"}. +%% +%% Password of LDAP manager: +%%{ldap_password, "******"}. +%% +%% Search base of LDAP directory: +%%{ldap_base, "dc=example,dc=com"}. +%% +%% LDAP attribute that holds user ID: +%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. +%% +%% LDAP filter: +%%{ldap_filter, "(objectClass=shadowAccount)"}. + +%% +%% Anonymous login support: +%% auth_method: anonymous +%% anonymous_protocol: sasl_anon | login_anon | both +%% allow_multiple_connections: true | false +%% +%%{host_config, "public.example.org", [{auth_method, anonymous}, +%% {allow_multiple_connections, false}, +%% {anonymous_protocol, sasl_anon}]}. +%% +%% To use both anonymous and internal authentication: +%% +%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + +%%%. ============== +%%%' DATABASE SETUP + +%% ejabberd by default uses the internal Mnesia database, +%% so you do not necessarily need this section. +%% This section provides configuration examples in case +%% you want to use other database backends. +%% Please consult the ejabberd Guide for details on database creation. + +%% +%% MySQL server: +%% +%% {odbc_server, {mysql, "localhost", 3306, "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + +%% +%% PostgreSQL server: +%% +%%{odbc_server, {pgsql, "server", "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. +%% +%% If you use PostgreSQL, have a large database, and need a +%% faster but inexact replacement for "select count(*) from users" +%% +%%{pgsql_users_number_estimate, true}. + +%% +%% ODBC compatible or MSSQL server: +%% +%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + +%% +%% Number of connections to open to the database for each virtual host +%% +%%{odbc_pool_size, 10}. + +%% +%% Interval to make a dummy SQL request to keep the connections to the +%% database alive. Specify in seconds: for example 28800 means 8 hours +%% +%%{odbc_keepalive_interval, undefined}. + + +%%%. =============== +%%%' TRAFFIC SHAPERS + +%% +%% The "normal" shaper limits traffic speed to 1000 B/s +%% +{shaper, normal, {maxrate, 1000}}. + +%% +%% The "fast" shaper limits traffic speed to 50000 B/s +%% +{shaper, fast, {maxrate, 50000}}. + +%% +%% This option specifies the maximum number of elements in the queue +%% of the FSM. Refer to the documentation for details. +%% +{max_fsm_queue, 1000}. + + +%%%. ==================== +%%%' ACCESS CONTROL LISTS + +%% +%% The 'admin' ACL grants administrative privileges to XMPP accounts. +%% You can put here as many accounts as you want. +%% +%{acl, admin, {user, "alice", "localhost"}}. +%{acl, admin, {user, "a", "localhost"}}. + +%% +%% Blocked users +%% +%%{acl, blocked, {user, "baduser", "example.org"}}. +%%{acl, blocked, {user, "test"}}. + +%% +%% Local users: don't modify this line. +%% +{acl, local, {user_regexp, ""}}. + +%% +%% More examples of ACLs +%% +%%{acl, jabberorg, {server, "jabber.org"}}. +%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. +%%{acl, test, {user_regexp, "^test"}}. +%%{acl, test, {user_glob, "test*"}}. + +%% +%% Define specific ACLs in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {acl, admin, {user, "bob-local", "localhost"}} +%% ] +%%}. + + +%%%. ============ +%%%' ACCESS RULES + +%% Maximum number of simultaneous sessions allowed for a single user: +{access, max_user_sessions, [{10, all}]}. + +%% Maximum number of offline messages that users can have: +{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + +%% This rule allows access only for local users: +{access, local, [{allow, local}]}. + +%% Only non-blocked users can use c2s connections: +{access, c2s, [{deny, blocked}, + {allow, all}]}. + +%% For C2S connections, all users except admins use the "normal" shaper +{access, c2s_shaper, [{none, admin}, + {normal, all}]}. + +%% All S2S connections use the "fast" shaper +{access, s2s_shaper, [{fast, all}]}. + +%% Admins of this server are also admins of the MUC service: +{access, muc_admin, [{allow, admin}]}. + +%% Only accounts of the local ejabberd server can create rooms: +{access, muc_create, [{allow, local}]}. + +%% All users are allowed to use the MUC service: +{access, muc, [{allow, all}]}. + +%% In-band registration allows registration of any possible username. +%% To disable in-band registration, replace 'allow' with 'deny'. +{access, register, [{allow, all}]}. + +%% By default the frequency of account registrations from the same IP +%% is limited to 1 account every 10 minutes. To disable, specify: infinity +{registration_timeout, infinity}. + +%% Default settings for MAM. +%% To set non-standard value, replace 'default' with 'allow' or 'deny'. +%% Only user can access his/her archive by default. +%% An online user can read room's archive by default. +%% Only an owner can change settings and purge messages by default. +%% Empty list (i.e. `[]`) means `[{deny, all}]`. +{access, mam_set_prefs, [{default, all}]}. +{access, mam_get_prefs, [{default, all}]}. +{access, mam_lookup_messages, [{default, all}]}. +{access, mam_purge_single_message, [{default, all}]}. +{access, mam_purge_multiple_messages, [{default, all}]}. + +%% 1 command of the specified type per second. +{shaper, mam_shaper, {maxrate, 1}}. +%% This shaper is primeraly for Mnesia overload protection during stress testing. +%% The limit is 1000 operations of each type per second. +{shaper, mam_global_shaper, {maxrate, 1000}}. + +{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. +{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}. +{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}. + +{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}. + + +%% +%% Define specific Access Rules in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {access, c2s, [{allow, admin}, {deny, all}]}, +%% {access, register, [{deny, all}]} +%% ] +%%}. + + +%%%. ================ +%%%' DEFAULT LANGUAGE + +%% +%% language: Default language used for server messages. +%% +{language, "en"}. + +%% +%% Set a different default language in a virtual host. +%% +%%{host_config, "localhost", +%% [{language, "ru"}] +%%}. + + +%%%. ======= +%%%' MODULES + +%% +%% Modules enabled in all ejabberd virtual hosts. +%% For list of possible modules options, check documentation. +%% If module comes in two versions, like mod_last and mod_last_odbc, +%% use only one of them. +%% +{modules, + [ + {mod_revproxy, []}, + %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, + % roster, last, private, stanza, stats]}]}, + {mod_adhoc, []}, + {mod_disco, []}, + {mod_last, []}, + {mod_stream_management, [ + % default 100 + % size of a buffer of unacked messages + % {buffer_max, 100} + + % default 1 - server sends the ack request after each stanza + % {ack_freq, 1} + + % default: 600 seconds + % {resume_timeout, 600} + ]}, + {mod_muc, [ + {host, "muc.@HOST@"}, + {access, muc}, + {access_create, muc_create} + ]}, + {mod_muc_log, + [ + {outdir, "/tmp/muclogs"}, + {access_log, muc} + ]}, + {mod_privacy, []}, + {mod_register, [ + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {""}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + {mod_roster, []}, + {mod_sic, []}, + {mod_vcard, [ {allow_return_all, true}, +{search_all_hosts, true} +%{matches, 1}, +%{search, true}, +%{host, directory.@HOST@} +]} + %% + %% Message Archive Management (MAM) for registered users. + %% + + %% A module for storing preferences in RDBMS (used by default). + %% Enable for private message archives. +% {mod_mam_odbc_prefs, [pm]}, + %% Enable for multiuser message archives. +% {mod_mam_odbc_prefs, [muc]}, + %% Enable for both private and multiuser message archives. +% {mod_mam_odbc_prefs, [pm, muc]}, + + %% A module for storing preferences in Mnesia (recommended). + %% This module will be called each time, as a message is routed. + %% That is why, Mnesia is better for this job. +% {mod_mam_mnesia_prefs, [pm, muc]}, + + %% Mnesia back-end with optimized writes and dirty synchronious writes. +% {mod_mam_mnesia_dirty_prefs, [pm, muc]}, + + %% A back-end for storing messages. + %% Synchronious writer (used by default). + %% This writer is easy to debug, but writing performance is low. +% {mod_mam_odbc_arch, [pm]}, + + %% Enable the module with a custom writer. +% {mod_mam_odbc_arch, [no_writer, pm]}, + + %% Asynchronious writer for RDBMS (recommended). + %% Messages will be grouped and inserted all at once. +% {mod_mam_odbc_async_writer, [pm]}, + + %% A pool of asynchronious writers (recommended). + %% Messages will be grouped together based on archive id. +% {mod_mam_odbc_async_pool_writer, [pm]}, + + %% A module for converting an archive id to an integer. + %% Extract information using ODBC. +% {mod_mam_odbc_user, [pm, muc]}, + + %% Cache information about users (recommended). + %% Requires mod_mam_odbc_user or alternative. +% {mod_mam_cache_user, [pm, muc]}, + + %% Enable MAM. +% {mod_mam, []}, + + + %% + %% Message Archive Management (MAM) for multi-user chats (MUC). + %% Enable XEP-0313 for "muc.@HOST@". + %% + + %% A back-end for storing messages (default for MUC). + %% Modules mod_mam_muc_* are optimized for MUC. + %% + %% Synchronious writer (used by default for MUC). + %% This module is easy to debug, but performance is low. +% {mod_mam_muc_odbc_arch, []}, +% {mod_mam_muc_odbc_arch, [no_writer]}, + + %% Asynchronious writer for RDBMS (recommended for MUC). + %% Messages will be grouped and inserted all at once. +% {mod_mam_muc_odbc_async_writer, []}, +% {mod_mam_muc_odbc_async_pool_writer, []}, + + %% Load mod_mam_odbc_user too. + + %% Enable MAM for MUC +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + + %% + %% MAM configuration examples + %% + + %% Only MUC, no user-defined preferences, good performance. +% {mod_mam_odbc_user, [muc]}, +% {mod_mam_cache_user, [muc]}, +% {mod_mam_muc_odbc_arch, [no_writer]}, +% {mod_mam_muc_odbc_async_pool_writer, []}, +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + %% Only archives for c2c messages, good performance. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_cache_user, [pm]}, +% {mod_mam_mnesia_dirty_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm, no_writer]}, +% {mod_mam_odbc_async_pool_writer, [pm]}, +% {mod_mam, []} + + %% Basic configuration for c2c messages, bad performance, easy to debug. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_odbc_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm]}, +% {mod_mam, []} + + ]}. + + +%% +%% Enable modules with custom options in a specific virtual host +%% +%%{host_config, "localhost", +%% [{ {add, modules}, +%% [ +%% {mod_some_module, []} +%% ] +%% } +%% ]}. + +%%%. +%%%' + +%%% $Id$ + +%%% Local Variables: +%%% mode: erlang +%%% End: +%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: +%%%. diff --git a/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.onerule.cfg b/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.onerule.cfg new file mode 100644 index 00000000000..c3d1c51622a --- /dev/null +++ b/apps/ejabberd/test/revproxy_SUITE_data/ejabberd.onerule.cfg @@ -0,0 +1,755 @@ +%%% +%%% ejabberd configuration file +%%% +%%%' + +%%% The parameters used in this configuration file are explained in more detail +%%% in the ejabberd Installation and Operation Guide. +%%% Please consult the Guide in case of doubts, it is included with +%%% your copy of ejabberd, and is also available online at +%%% http://www.process-one.net/en/ejabberd/docs/ + +%%% This configuration file contains Erlang terms. +%%% In case you want to understand the syntax, here are the concepts: +%%% +%%% - The character to comment a line is % +%%% +%%% - Each term ends in a dot, for example: +%%% override_global. +%%% +%%% - A tuple has a fixed definition, its elements are +%%% enclosed in {}, and separated with commas: +%%% {loglevel, 4}. +%%% +%%% - A list can have as many elements as you want, +%%% and is enclosed in [], for example: +%%% [http_poll, web_admin, tls] +%%% +%%% Pay attention that list elements are delimited with commas, +%%% but no comma is allowed after the last list element. This will +%%% give a syntax error unlike in more lenient languages (e.g. Python). +%%% +%%% - A keyword of ejabberd is a word in lowercase. +%%% Strings are enclosed in "" and can contain spaces, dots, ... +%%% {language, "en"}. +%%% {ldap_rootdn, "dc=example,dc=com"}. +%%% +%%% - This term includes a tuple, a keyword, a list, and two strings: +%%% {hosts, ["jabber.example.net", "im.example.com"]}. +%%% +%%% - This config is preprocessed during release generation by a tool which +%%% interprets double curly braces as substitution markers, so avoid this +%%% syntax in this file (though it's valid Erlang). +%%% +%%% So this is OK (though arguably looks quite ugly): +%%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%%% +%%% And I can't give an example of what's not OK exactly because +%%% of this rule. +%%% + + +%%%. ======================= +%%%' OVERRIDE STORED OPTIONS + +%% +%% Override the old values stored in the database. +%% + +%% +%% Override global options (shared by all ejabberd nodes in a cluster). +%% +%%override_global. + +%% +%% Override local options (specific for this particular ejabberd node). +%% +%%override_local. + +%% +%% Remove the Access Control Lists before new ones are added. +%% +%%override_acls. + + +%%%. ========= +%%%' DEBUGGING + +%% +%% loglevel: Verbosity of log files generated by ejabberd. +%% 0: No ejabberd log at all (not recommended) +%% 1: Critical +%% 2: Error +%% 3: Warning +%% 4: Info +%% 5: Debug +%% +{loglevel, 3}. + +%% +%% alarms: an optional alarm handler, subscribed to system events +%% long_gc: minimum GC time in ms for long_gc alarm +%% large_heap: minimum process heap size for large_heap alarm +%% handlers: a list of alarm handlers +%% - alarms_basic_handler: logs alarms and stores a brief alarm summary +%% - alarms_folsom_handler: stores alarm details in folsom metrics +%% +%% Example: +%% {alarms, +%% [{long_gc, 10000}, +%% {large_heap, 1000000}, +%% {handlers, [alarms_basic_handler, +%% alarms_folsom_handler]}] +%% }. + +%% +%% watchdog_admins: Only useful for developers: if an ejabberd process +%% consumes a lot of memory, send live notifications to these XMPP +%% accounts. Requires alarms (see above). +%% +%%{watchdog_admins, ["bob@example.com"]}. + + +%%%. ================ +%%%' SERVED HOSTNAMES + +%% +%% hosts: Domains served by ejabberd. +%% You can define one or several, for example: +%% {hosts, ["example.net", "example.com", "example.org"]}. +%% +{hosts, ["localhost"] }. + +%% +%% route_subdomains: Delegate subdomains to other XMPP servers. +%% For example, if this ejabberd serves example.org and you want +%% to allow communication with an XMPP server called im.example.org. +%% +%%{route_subdomains, s2s}. + + +%%%. =============== +%%%' LISTENING PORTS + +%% +%% listen: The ports ejabberd will listen on, which service each is handled +%% by and what options to start it with. +%% +{listen, + [ + + { 5280, ejabberd_cowboy, [ + {num_acceptors, 10}, + {max_connections, 1024}, + %% Uncomment for HTTPS + %{cert, "priv/server.crt"}, + %{key, "priv/server.key"}, + %{key_pass, ""}, + {modules, [ + %% Modules used here should also be listed in the MODULES section. + {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, []}]} + ]} + ]}, + + { 5222, ejabberd_c2s, [ + + %% + %% If TLS is compiled in and you installed a SSL + %% certificate, specify the full path to the + %% file and uncomment this line: + %% + %%{certfile, "/path/to/ssl.pem"}, starttls, + %%{zlib, 10000}, + %% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS + %% {ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}, + {access, c2s}, + {shaper, c2s_shaper}, + {max_stanza_size, 65536} + ]}, + + + + %% + %% To enable the old SSL connection method on port 5223: + %% + %%{5223, ejabberd_c2s, [ + %% {access, c2s}, + %% {shaper, c2s_shaper}, + %% {certfile, "/path/to/ssl.pem"}, tls, + %% {max_stanza_size, 65536} + %% ]}, + + { 5269, ejabberd_s2s_in, [ + {shaper, s2s_shaper}, + {max_stanza_size, 131072} + ]} + + %% + %% ejabberd_service: Interact with external components (transports, ...) + %% + %%{8888, ejabberd_service, [ + %% {access, all}, + %% {shaper_rule, fast}, + %% {ip, {127, 0, 0, 1}}, + %% {hosts, ["icq.example.org", "sms.example.org"], + %% [{password, "secret"}] + %% } + %% ]}, + + %% + %% ejabberd_stun: Handles STUN Binding requests + %% + %%{ {3478, udp}, ejabberd_stun, []} + + ]}. + +%% +%% s2s_use_starttls: Enable STARTTLS + Dialback for S2S connections. +%% Allowed values are: false optional required required_trusted +%% You must specify a certificate file. +%% +%%{s2s_use_starttls, optional}. + +%% +%% s2s_certfile: Specify a certificate file. +%% +%%{s2s_certfile, "/path/to/ssl.pem"}. + +%% https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS +%% {s2s_ciphers, "DEFAULT:!EXPORT:!LOW:!SSLv2"}. + +%% +%% domain_certfile: Specify a different certificate for each served hostname. +%% +%%{domain_certfile, "example.org", "/path/to/example_org.pem"}. +%%{domain_certfile, "example.com", "/path/to/example_com.pem"}. + +%% +%% S2S whitelist or blacklist +%% +%% Default s2s policy for undefined hosts. +%% +{s2s_default_policy, deny }. + +%% +%% Allow or deny communication with specific servers. +%% +%%{ {s2s_host, "goodhost.org"}, allow}. +%%{ {s2s_host, "badhost.org"}, deny}. + +{outgoing_s2s_port, 5269 }. + +%% +%% IP addresses predefined for specific hosts to skip DNS lookups. +%% Ports defined here take precedence over outgoing_s2s_port. +%% Examples: +%% +%% { {s2s_addr, "example-host.net"}, {127,0,0,1} }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. +%% { {s2s_addr, "example-host.net"}, { {127,0,0,1}, 5269 } }. + +%% +%% Outgoing S2S options +%% +%% Preferred address families (which to try first) and connect timeout +%% in milliseconds. +%% +%%{outgoing_s2s_options, [ipv4, ipv6], 10000}. + +%%%. ============== +%%%' SESSION BACKEND + +%%{sm_backend, {mnesia, []}}. + +%%{sm_backend, {redis, [{pool_size, 3}, {worker_config, [{host, "localhost"}, {port, 6379}]}]}}. +{sm_backend, {mnesia, []} }. + + +%%%. ============== +%%%' AUTHENTICATION + +%% +%% auth_method: Method used to authenticate the users. +%% The default method is the internal. +%% If you want to use a different method, +%% comment this line and enable the correct ones. +%% +{auth_method, internal}. +%% +%% Store the plain passwords or hashed for SCRAM: +%%{auth_password_format, plain}. % default +%%{auth_password_format, scram}. +%%{auth_scram_iterations, 4096}. % default + +%% +%% Authentication using external script +%% Make sure the script is executable by ejabberd. +%% +%%{auth_method, external}. +%%{extauth_program, "/path/to/authentication/script"}. + +%% +%% Authentication using ODBC +%% Remember to setup a database in the next section. +%% +%%{auth_method, odbc}. + +%% +%% Authentication using PAM +%% +%%{auth_method, pam}. +%%{pam_service, "pamservicename"}. + +%% +%% Authentication using LDAP +%% +%%{auth_method, ldap}. +%% + +%% List of LDAP servers: +%%{ldap_servers, ["localhost"]}. +%% +%% Encryption of connection to LDAP servers: +%%{ldap_encrypt, none}. +%%{ldap_encrypt, tls}. +%% +%% Port to connect to on LDAP servers: +%%{ldap_port, 389}. +%%{ldap_port, 636}. +%% +%% LDAP manager: +%%{ldap_rootdn, "dc=example,dc=com"}. +%% +%% Password of LDAP manager: +%%{ldap_password, "******"}. +%% +%% Search base of LDAP directory: +%%{ldap_base, "dc=example,dc=com"}. +%% +%% LDAP attribute that holds user ID: +%%{ldap_uids, [{"mail", "%u@mail.example.org"}]}. +%% +%% LDAP filter: +%%{ldap_filter, "(objectClass=shadowAccount)"}. + +%% +%% Anonymous login support: +%% auth_method: anonymous +%% anonymous_protocol: sasl_anon | login_anon | both +%% allow_multiple_connections: true | false +%% +%%{host_config, "public.example.org", [{auth_method, anonymous}, +%% {allow_multiple_connections, false}, +%% {anonymous_protocol, sasl_anon}]}. +%% +%% To use both anonymous and internal authentication: +%% +%%{host_config, "public.example.org", [{auth_method, [internal, anonymous]}]}. + + +%%%. ============== +%%%' DATABASE SETUP + +%% ejabberd by default uses the internal Mnesia database, +%% so you do not necessarily need this section. +%% This section provides configuration examples in case +%% you want to use other database backends. +%% Please consult the ejabberd Guide for details on database creation. + +%% +%% MySQL server: +%% +%% {odbc_server, {mysql, "localhost", 3306, "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {mysql, "server", 1234, "database", "username", "password"}}. + +%% +%% PostgreSQL server: +%% +%%{odbc_server, {pgsql, "server", "database", "username", "password"}}. +%% +%% If you want to specify the port: +%%{odbc_server, {pgsql, "server", 1234, "database", "username", "password"}}. +%% +%% If you use PostgreSQL, have a large database, and need a +%% faster but inexact replacement for "select count(*) from users" +%% +%%{pgsql_users_number_estimate, true}. + +%% +%% ODBC compatible or MSSQL server: +%% +%%{odbc_server, "DSN=ejabberd;UID=ejabberd;PWD=ejabberd"}. + +%% +%% Number of connections to open to the database for each virtual host +%% +%%{odbc_pool_size, 10}. + +%% +%% Interval to make a dummy SQL request to keep the connections to the +%% database alive. Specify in seconds: for example 28800 means 8 hours +%% +%%{odbc_keepalive_interval, undefined}. + + +%%%. =============== +%%%' TRAFFIC SHAPERS + +%% +%% The "normal" shaper limits traffic speed to 1000 B/s +%% +{shaper, normal, {maxrate, 1000}}. + +%% +%% The "fast" shaper limits traffic speed to 50000 B/s +%% +{shaper, fast, {maxrate, 50000}}. + +%% +%% This option specifies the maximum number of elements in the queue +%% of the FSM. Refer to the documentation for details. +%% +{max_fsm_queue, 1000}. + + +%%%. ==================== +%%%' ACCESS CONTROL LISTS + +%% +%% The 'admin' ACL grants administrative privileges to XMPP accounts. +%% You can put here as many accounts as you want. +%% +%{acl, admin, {user, "alice", "localhost"}}. +%{acl, admin, {user, "a", "localhost"}}. + +%% +%% Blocked users +%% +%%{acl, blocked, {user, "baduser", "example.org"}}. +%%{acl, blocked, {user, "test"}}. + +%% +%% Local users: don't modify this line. +%% +{acl, local, {user_regexp, ""}}. + +%% +%% More examples of ACLs +%% +%%{acl, jabberorg, {server, "jabber.org"}}. +%%{acl, aleksey, {user, "aleksey", "jabber.ru"}}. +%%{acl, test, {user_regexp, "^test"}}. +%%{acl, test, {user_glob, "test*"}}. + +%% +%% Define specific ACLs in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {acl, admin, {user, "bob-local", "localhost"}} +%% ] +%%}. + + +%%%. ============ +%%%' ACCESS RULES + +%% Maximum number of simultaneous sessions allowed for a single user: +{access, max_user_sessions, [{10, all}]}. + +%% Maximum number of offline messages that users can have: +{access, max_user_offline_messages, [{5000, admin}, {100, all}]}. + +%% This rule allows access only for local users: +{access, local, [{allow, local}]}. + +%% Only non-blocked users can use c2s connections: +{access, c2s, [{deny, blocked}, + {allow, all}]}. + +%% For C2S connections, all users except admins use the "normal" shaper +{access, c2s_shaper, [{none, admin}, + {normal, all}]}. + +%% All S2S connections use the "fast" shaper +{access, s2s_shaper, [{fast, all}]}. + +%% Admins of this server are also admins of the MUC service: +{access, muc_admin, [{allow, admin}]}. + +%% Only accounts of the local ejabberd server can create rooms: +{access, muc_create, [{allow, local}]}. + +%% All users are allowed to use the MUC service: +{access, muc, [{allow, all}]}. + +%% In-band registration allows registration of any possible username. +%% To disable in-band registration, replace 'allow' with 'deny'. +{access, register, [{allow, all}]}. + +%% By default the frequency of account registrations from the same IP +%% is limited to 1 account every 10 minutes. To disable, specify: infinity +{registration_timeout, infinity}. + +%% Default settings for MAM. +%% To set non-standard value, replace 'default' with 'allow' or 'deny'. +%% Only user can access his/her archive by default. +%% An online user can read room's archive by default. +%% Only an owner can change settings and purge messages by default. +%% Empty list (i.e. `[]`) means `[{deny, all}]`. +{access, mam_set_prefs, [{default, all}]}. +{access, mam_get_prefs, [{default, all}]}. +{access, mam_lookup_messages, [{default, all}]}. +{access, mam_purge_single_message, [{default, all}]}. +{access, mam_purge_multiple_messages, [{default, all}]}. + +%% 1 command of the specified type per second. +{shaper, mam_shaper, {maxrate, 1}}. +%% This shaper is primeraly for Mnesia overload protection during stress testing. +%% The limit is 1000 operations of each type per second. +{shaper, mam_global_shaper, {maxrate, 1000}}. + +{access, mam_set_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_get_prefs_shaper, [{mam_shaper, all}]}. +{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}. +{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}. +{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}. + +{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}. +{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}. + + +%% +%% Define specific Access Rules in a virtual host. +%% +%%{host_config, "localhost", +%% [ +%% {access, c2s, [{allow, admin}, {deny, all}]}, +%% {access, register, [{deny, all}]} +%% ] +%%}. + + +%%%. ================ +%%%' DEFAULT LANGUAGE + +%% +%% language: Default language used for server messages. +%% +{language, "en"}. + +%% +%% Set a different default language in a virtual host. +%% +%%{host_config, "localhost", +%% [{language, "ru"}] +%%}. + + +%%%. ======= +%%%' MODULES + +%% +%% Modules enabled in all ejabberd virtual hosts. +%% For list of possible modules options, check documentation. +%% If module comes in two versions, like mod_last and mod_last_odbc, +%% use only one of them. +%% +{modules, + [ + {mod_revproxy, [{routes, + [{"_", "_", "_", "http://localhost:1234"}] + }] + }, + %{mod_admin_extra, [{submods, [node, accounts, sessions, vcard, + % roster, last, private, stanza, stats]}]}, + {mod_adhoc, []}, + {mod_disco, []}, + {mod_last, []}, + {mod_stream_management, [ + % default 100 + % size of a buffer of unacked messages + % {buffer_max, 100} + + % default 1 - server sends the ack request after each stanza + % {ack_freq, 1} + + % default: 600 seconds + % {resume_timeout, 600} + ]}, + {mod_muc, [ + {host, "muc.@HOST@"}, + {access, muc}, + {access_create, muc_create} + ]}, + {mod_muc_log, + [ + {outdir, "/tmp/muclogs"}, + {access_log, muc} + ]}, + {mod_privacy, []}, + {mod_register, [ + %% + %% Set the minimum informational entropy for passwords. + %% + %%{password_strength, 32}, + + %% + %% After successful registration, the user receives + %% a message with this subject and body. + %% + {welcome_message, {""}}, + + %% + %% When a user registers, send a notification to + %% these XMPP accounts. + %% + %%{registration_watchers, ["admin1@example.org"]}, + + %% + %% Only clients in the server machine can register accounts + %% + {ip_access, [{allow, "127.0.0.0/8"}, + {deny, "0.0.0.0/0"}]}, + + %% + %% Local c2s or remote s2s users cannot register accounts + %% + %%{access_from, deny}, + + {access, register} + ]}, + {mod_roster, []}, + {mod_sic, []}, + {mod_vcard, [ {allow_return_all, true}, +{search_all_hosts, true} +%{matches, 1}, +%{search, true}, +%{host, directory.@HOST@} +]} + %% + %% Message Archive Management (MAM) for registered users. + %% + + %% A module for storing preferences in RDBMS (used by default). + %% Enable for private message archives. +% {mod_mam_odbc_prefs, [pm]}, + %% Enable for multiuser message archives. +% {mod_mam_odbc_prefs, [muc]}, + %% Enable for both private and multiuser message archives. +% {mod_mam_odbc_prefs, [pm, muc]}, + + %% A module for storing preferences in Mnesia (recommended). + %% This module will be called each time, as a message is routed. + %% That is why, Mnesia is better for this job. +% {mod_mam_mnesia_prefs, [pm, muc]}, + + %% Mnesia back-end with optimized writes and dirty synchronious writes. +% {mod_mam_mnesia_dirty_prefs, [pm, muc]}, + + %% A back-end for storing messages. + %% Synchronious writer (used by default). + %% This writer is easy to debug, but writing performance is low. +% {mod_mam_odbc_arch, [pm]}, + + %% Enable the module with a custom writer. +% {mod_mam_odbc_arch, [no_writer, pm]}, + + %% Asynchronious writer for RDBMS (recommended). + %% Messages will be grouped and inserted all at once. +% {mod_mam_odbc_async_writer, [pm]}, + + %% A pool of asynchronious writers (recommended). + %% Messages will be grouped together based on archive id. +% {mod_mam_odbc_async_pool_writer, [pm]}, + + %% A module for converting an archive id to an integer. + %% Extract information using ODBC. +% {mod_mam_odbc_user, [pm, muc]}, + + %% Cache information about users (recommended). + %% Requires mod_mam_odbc_user or alternative. +% {mod_mam_cache_user, [pm, muc]}, + + %% Enable MAM. +% {mod_mam, []}, + + + %% + %% Message Archive Management (MAM) for multi-user chats (MUC). + %% Enable XEP-0313 for "muc.@HOST@". + %% + + %% A back-end for storing messages (default for MUC). + %% Modules mod_mam_muc_* are optimized for MUC. + %% + %% Synchronious writer (used by default for MUC). + %% This module is easy to debug, but performance is low. +% {mod_mam_muc_odbc_arch, []}, +% {mod_mam_muc_odbc_arch, [no_writer]}, + + %% Asynchronious writer for RDBMS (recommended for MUC). + %% Messages will be grouped and inserted all at once. +% {mod_mam_muc_odbc_async_writer, []}, +% {mod_mam_muc_odbc_async_pool_writer, []}, + + %% Load mod_mam_odbc_user too. + + %% Enable MAM for MUC +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + + %% + %% MAM configuration examples + %% + + %% Only MUC, no user-defined preferences, good performance. +% {mod_mam_odbc_user, [muc]}, +% {mod_mam_cache_user, [muc]}, +% {mod_mam_muc_odbc_arch, [no_writer]}, +% {mod_mam_muc_odbc_async_pool_writer, []}, +% {mod_mam_muc, [{host, "muc.@HOST@"}]} + + %% Only archives for c2c messages, good performance. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_cache_user, [pm]}, +% {mod_mam_mnesia_dirty_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm, no_writer]}, +% {mod_mam_odbc_async_pool_writer, [pm]}, +% {mod_mam, []} + + %% Basic configuration for c2c messages, bad performance, easy to debug. +% {mod_mam_odbc_user, [pm]}, +% {mod_mam_odbc_prefs, [pm]}, +% {mod_mam_odbc_arch, [pm]}, +% {mod_mam, []} + + ]}. + + +%% +%% Enable modules with custom options in a specific virtual host +%% +%%{host_config, "localhost", +%% [{ {add, modules}, +%% [ +%% {mod_some_module, []} +%% ] +%% } +%% ]}. + +%%%. +%%%' + +%%% $Id$ + +%%% Local Variables: +%%% mode: erlang +%%% End: +%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker: +%%%. diff --git a/apps/ejabberd/test/xmpp_route_SUITE.erl b/apps/ejabberd/test/xmpp_route_SUITE.erl index 4f92cf7e18e..0be1eb67610 100644 --- a/apps/ejabberd/test/xmpp_route_SUITE.erl +++ b/apps/ejabberd/test/xmpp_route_SUITE.erl @@ -15,6 +15,9 @@ init_per_suite(C) -> application:ensure_all_started(lager), C. +end_per_suite(_C) -> + ok. + success_with_module_implementing_behaviour(_C) -> meck:new(xmpp_router_correct, [non_strict]), meck:expect(xmpp_router_correct, do_route, diff --git a/doc/advanced-configuration/Listener-modules.md b/doc/advanced-configuration/Listener-modules.md index 34992cd31c6..e2a89e34171 100644 --- a/doc/advanced-configuration/Listener-modules.md +++ b/doc/advanced-configuration/Listener-modules.md @@ -24,7 +24,7 @@ Handles pure XMPP connections, relies on `ejabberd_listener` for listening. It p Manages all HTTP-based services. Unlike `ejabberd_c2s`, it doesn't use `ejabberd_receiver` or `ejabberd_listener`. -Currently it is not possible to use different ports e.g. for BOSH and Websockets. + **Default port:** 5280 @@ -45,6 +45,32 @@ Currently it is not possible to use different ports e.g. for BOSH and Websockets `{"_", "/ws-xmpp", mod_websockets, []}`. * `mongoose_api` - REST API for accessing internal MongooseIM metrics. Please refer to [REST interface to metrics](../developers-guide/REST-interface-to-metrics.md) for more information. Default declaration: `{"localhost", "/api", mongoose_api, [{handlers, [mongoose_api_metrics]}]}`. +### mod_cowboy + +This modules provides additional routing layer on top of HTTP(s) or +WS(S) protocols. Example configuration looks like the following (add +this to ejabberd_cowboy modules list described above): + +```Erlang +{"_", "/[...]", mod_cowboy, [{http, mod_revproxy, + [{timeout, 5000}, + % time limit for upstream to respond + {body_length, 8000000}, + % maximum body size (may be infinity) + {custom_headers, [{<<"header">>,<<"value">>}]} + % list of extra headers that are send to upstream + ]}, + {ws, xmpp, mod_websockets} + ]}, +``` + +According to this configuration, all http requests will go through the +`mod_revproxy` module (see [mod_revproxy](../modules/mod_revproxy.md) +fro more details). +As for now, all websockets connections with the `Sec-WebSocket-Protocol: xmpp` +header, will go through the mod_websockets connection. +This is the MongooseIM's regular websocket connection handler. + ## ejabberd_s2s_in Handles incoming S2S connections. Relies on `ejabberd_listener` and `ejabberd_receiver` just like `ejabberd_c2s`. @@ -71,3 +97,5 @@ Interface for external [XMPP components](http://xmpp.org/extensions/xep-0114.htm * `host` ( tuple: `{host, Domain, [{password, "password here"}]}`, optional when `hosts` present) - Only allowed domain for components, protected by password. Must be set when `hosts` not present. * `shaper_rule` (atom, default: `fast`) - Connection shaper to use for incoming component traffic. * `service_check_from` (boolean, default: `true`) - Checks whether the server should verify the "from" field in stanzas from component + + diff --git a/rel/files/ejabberd.cfg b/rel/files/ejabberd.cfg index 9f09caf968a..aceceded58d 100755 --- a/rel/files/ejabberd.cfg +++ b/rel/files/ejabberd.cfg @@ -137,17 +137,6 @@ {max_connections, 1024}, {modules, [ - %% Example usage of mod_revproxy, please note that mod_revproxy - %% needs to be included in MODULES as well. - - %% {"_", "/[...]", mod_revproxy, [{timeout, 5000}, - %% % time limit for upstream to respond - %% {body_length, 8000000}, - %% % maximum body size (may be infinity) - %% {custom_headers, [{<<"header">>,<<"value">>}]} - %% % list of extra headers that are send to upstream - %% ]}, - {"_", "/http-bind", mod_bosh}, {"_", "/ws-xmpp", mod_websockets, [ %% Uncomment to enable connection dropping or/and server-side pings @@ -157,6 +146,29 @@ %{"_", "/static/[...]", cowboy_static, % {dir, "/var/www", [{mimetypes, cow_mimetypes, all}]} %}, + + %% Example usage of mod_revproxy + + %% {"_", "/[...]", mod_revproxy, [{timeout, 5000}, + %% % time limit for upstream to respond + %% {body_length, 8000000}, + %% % maximum body size (may be infinity) + %% {custom_headers, [{<<"header">>,<<"value">>}]} + %% % list of extra headers that are send to upstream + %% ]} + + %% Example usage of mod_cowboy + + %% {"_", "/[...]", mod_cowboy, [{http, mod_revproxy, + %% [{timeout, 5000}, + %% % time limit for upstream to respond + %% {body_length, 8000000}, + %% % maximum body size (may be infinity) + %% {custom_headers, [{<<"header">>,<<"value">>}]} + %% % list of extra headers that are send to upstream + %% ]}, + %% {ws, xmpp, mod_websockets} + %% ]} ]} ]},