Skip to content

Commit

Permalink
Websocket max stanza size (#1641)
Browse files Browse the repository at this point in the history
* fix default max_stanza_size in the docs

* Accept max_stanza_size in mod_websockets (#1640)
  • Loading branch information
igors authored and fenek committed Jan 15, 2018
1 parent 6021df8 commit ec13875
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 12 deletions.
6 changes: 4 additions & 2 deletions doc/advanced-configuration/Listener-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ You only need to declare running `ejabberd_c2s`, to have the other 2 modules sta
Please refer to the [OpenSSL documentation](http://www.openssl.org/docs/apps/ciphers.html) for the cipher string format.
* `access` (atom, default: `c2s`) - Access Rule to use for C2S connections.
* `c2s_shaper` (atom, default: `c2s_shaper`) - Connection shaper to use for incoming C2S stanzas.
* `max_stanza_size` (positive integer, default: 65536) - Maximum allowed incoming stanza size.
* `max_stanza_size` (positive integer, default: infinity) - Maximum allowed incoming stanza size.
**Warning:** this limit is checked **after** the input data parsing, so it does not apply to the input data size itself.
* `backlog` (positive integer, default 100) - overrides the default TCP backlog value
* `max_fsm_queue` (positive integer, the value of this option set global) - message queue limit to prevent resource exhaustion; overrides the global value of this option
Expand Down Expand Up @@ -74,6 +74,8 @@ Unlike `ejabberd_c2s`, it doesn't use `ejabberd_receiver` or `ejabberd_listener`
* `{timeout, Val}` - the time after which an inactive user is disconnected.
* `{ping_rate, Val}` - the Ping rate points to the time between pings sent by server.
By declaring this field you enable server-side pinging.
* `{max_stanza_size, Val}` (positive integer, default: infinity) - Maximum allowed incoming stanza size.
**Warning:** this limit is checked **after** the input data parsing, so it does not apply to the input data size itself.
* `{ejabberd_service, Params}` - this enables external component connections over WebSockets.
See the [ejabberd_service](#ejabberd_service) section for more details how to configure it.

Expand Down Expand Up @@ -142,7 +144,7 @@ Please refer to the [Advanced configuration](../Advanced-configuration.md) for m
### Configuration

* `shaper` (atom, default: `s2s_shaper`) - Connection shaper to use for incoming S2S data.
* `max_stanza_size` (positive integer, default: 131072) - Maximum allowed incoming stanza size.
* `max_stanza_size` (positive integer, default: infinity) - Maximum allowed incoming stanza size.
**Warning:** this limit is checked **after** input data parsing, so it does not apply to the input data size itself.
* `protocol_options` List of supported SSL protocols, default "no_sslv3".
It also accepts "no_tlsv1" and "no_tlsv1_1"
Expand Down
37 changes: 27 additions & 10 deletions src/mod_websockets.erl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@
open_tag :: stream | open | undefined,
parser :: exml_stream:parser() | undefined,
opts :: proplists:proplist() | undefined,
ping_rate :: integer() | none
ping_rate :: integer() | none,
max_stanza_size :: integer() | infinity
}).

-type socket() :: #websocket{}.
Expand Down Expand Up @@ -79,9 +80,10 @@ websocket_init(Transport, Req, Opts) ->
Req1 = cowboy_req:set_resp_header(<<"Sec-WebSocket-Protocol">>, <<"xmpp">>, Req),
Timeout = gen_mod:get_opt(timeout, Opts, infinity),
PingRate = gen_mod:get_opt(ping_rate, Opts, none),
MaxStanzaSize = gen_mod:get_opt(max_stanza_size, Opts, infinity),
?DEBUG("ping rate is ~p", [PingRate]),
maybe_send_ping_request(PingRate),
State = #ws_state{opts = Opts, ping_rate = PingRate},
State = #ws_state{opts = Opts, ping_rate = PingRate, max_stanza_size = MaxStanzaSize},
{ok, Req1, State, Timeout}.

% Called when a text message arrives.
Expand Down Expand Up @@ -142,17 +144,21 @@ websocket_terminate(_Reason, _Req, _State) ->
%%--------------------------------------------------------------------

handle_text(Text, Req, #ws_state{ parser = undefined } = State) ->
ParserOpts = get_parser_opts(Text),
ParserOpts = get_parser_opts(Text, State),
{ok, Parser} = exml_stream:new_parser(ParserOpts),
handle_text(Text, Req, State#ws_state{ parser = Parser });
handle_text(Text, Req, #ws_state{parser = Parser} = State) ->
{ok, NewParser, Elements} = exml_stream:parse(Parser, Text),
State1 = State#ws_state{ parser = NewParser },
case maybe_start_fsm(Elements, Req, State1) of
{ok, Req1, State2} ->
process_client_elements(Elements, Req1, State2);
{shutdown, _, _} = Shutdown ->
Shutdown
case exml_stream:parse(Parser, Text) of
{ok, NewParser, Elements} ->
State1 = State#ws_state{ parser = NewParser },
case maybe_start_fsm(Elements, Req, State1) of
{ok, Req1, State2} ->
process_client_elements(Elements, Req1, State2);
{shutdown, _, _} = Shutdown ->
Shutdown
end;
{error, Reason} ->
process_parse_error(Reason, Req, State)
end.

process_client_elements(Elements, Req, #ws_state{fsm_pid = FSM} = State) ->
Expand All @@ -161,6 +167,12 @@ process_client_elements(Elements, Req, #ws_state{fsm_pid = FSM} = State) ->
replace_stream_ns(Elem, State1), State1)) || Elem <- Elements1],
{ok, Req, State1}.

process_parse_error(Reason, Req, #ws_state{fsm_pid = undefined} = State) ->
{shutdown, Req, State};
process_parse_error(Reason, Req, #ws_state{fsm_pid = FSM} = State) ->
send_to_fsm(FSM, {xmlstreamerror, Reason}),
{ok, Req, State}.

send_to_fsm(FSM, #xmlel{} = Element) ->
send_to_fsm(FSM, {xmlstreamelement, Element});
send_to_fsm(FSM, StreamElement) ->
Expand Down Expand Up @@ -307,6 +319,11 @@ replace_stream_ns(Element, #ws_state{ open_tag = open }) ->
replace_stream_ns(Element, _State) ->
Element.

get_parser_opts(Text, #ws_state{ max_stanza_size = infinity } = State) ->
[{max_child_size, 0} | get_parser_opts(Text)];
get_parser_opts(Text, #ws_state{ max_stanza_size = MaxStanzaSize } = State) ->
[{max_child_size, MaxStanzaSize} | get_parser_opts(Text)].

get_parser_opts(<<"<open", _/binary>>) ->
[{infinite_stream, true}, {autoreset, true}]; % new-type WS
get_parser_opts(_) ->
Expand Down

0 comments on commit ec13875

Please sign in to comment.