Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xmpp services #4442

Merged
merged 9 commits into from
Jan 2, 2025
158 changes: 75 additions & 83 deletions big_tests/tests/component_SUITE.erl

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions big_tests/tests/component_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ component_start_stream(Conn = #client{props = Props}, []) ->

#xmlstreamstart{attrs = Attrs} = StreamStartRep,
Id = proplists:get_value(<<"id">>, Attrs),
From = proplists:get_value(<<"from">>, Attrs),
From =:= ComponentHost orelse throw({from_does_not_correspond_to_connection, Server, From}),

{Conn#client{props = [{sid, Id} | Props]}, []}.

Expand Down
6 changes: 1 addition & 5 deletions big_tests/tests/tcp_listener_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
%%--------------------------------------------------------------------

all() ->
[s2s_inet_sockname_returns_error,
service_inet_sockname_returns_error].
[s2s_inet_sockname_returns_error].

suite() ->
require_rpc_nodes([mim]).
Expand Down Expand Up @@ -69,9 +68,6 @@ tcp_listener_helper_code() ->
s2s_inet_sockname_returns_error(_Config) ->
inet_sockname_returns_error(incoming_s2s_port).

service_inet_sockname_returns_error(_Config) ->
inet_sockname_returns_error(service_port).

%% Checks that the listener does not crash if inet:sockname/1 call returns an error
inet_sockname_returns_error(PortName) ->
Port = ct:get_config({hosts, mim, PortName}),
Expand Down
33 changes: 23 additions & 10 deletions doc/listeners/listen-components.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@ According to [XEP-0114: Jabber Component Protocol](http://xmpp.org/extensions/xe

The following options are supported for each component listener under `listen.service` subsection:

### `listen.service.max_connections`
* **Syntax:** positive integer or the string `"infinity"`
* **Default:** `"infinity"`
* **Example:** `max_connections = 10000`

Maximum number of open connections. This is a *soft limit* according to the [Ranch](https://ninenines.eu/docs/en/ranch/2.1/manual/ranch) documentation.

### `listen.service.reuse_port`
* **Syntax:** boolean
* **Default:** `false`
* **Example:** `reuse_port = true`

Enables linux support for `SO_REUSEPORT`, see [Stack Overflow](https://stackoverflow.com/questions/14388706/how-do-so-reuseaddr-and-so-reuseport-differ) for more details.

### `listen.service.state_timeout`
* **Syntax:** non-negative integer or the string `"infinity"`
* **Default:** `5000`
* **Example:** `state_timeout = 10_000`

Timeout value (in milliseconds) used by the component state machine when waiting for the connecting client to respond during stream negotiation. After the timeout the server responds with the `connection-timeout` stream error and closes the connection.

### `listen.service.access`
* **Syntax:** string, rule name or `"all"`
* **Default:** `"all"`
Expand All @@ -26,13 +47,12 @@ Determines who is allowed to send data to external components. By default, the r

The external component needs to authenticate with this password to connect.

### `listen.service.shaper_rule`
### `listen.service.shaper`
* **Syntax:** string, name of the shaper
* **Default:** `"none"`
* **Example:** `shaper = "component_shaper"`

The traffic shaper used to limit the XMPP traffic to prevent the server from being flooded with incoming data.
Contrary to the C2S and S2S shapers, here the shaper name directly references the shaper that needs to be defined in the [`shaper`](../configuration/shaper.md) section.

### `listen.service.check_from`
* **Syntax:** boolean
Expand Down Expand Up @@ -71,13 +91,6 @@ By default, when a component tries to connect and a registration conflict occurs
It makes implementing the reconnection logic difficult, because the old connection would not allow any other connections.
By setting this option to `kick_old`, we drop any old connections registered at the same host before accepting new ones.

### `listen.service.max_fsm_queue`
* **Syntax:** positive integer
* **Default:** not set - no limit
* **Example:** `max_fsm_queue = 1000`

Message queue limit to prevent resource exhaustion; overrides the value set in the [`general`](../configuration/general.md#generalmax_fsm_queue) section.

## Custom extension to the protocol

In order to register a component for all virtual hosts served by the server (see [`hosts`](../configuration/general.md#generalhosts) in the [`general`](../configuration/general.md) section), the component must add the attribute `is_subdomain="true"` to the opening stream element.
Expand All @@ -95,7 +108,7 @@ The shaper named `fast` needs to be defined in the [`shaper`](../configuration/s
[[listen.service]]
port = 8888
access = "all"
shaper_rule = "fast"
shaper = "fast"
ip_address = "127.0.0.1"
password = "secret"
```
1 change: 1 addition & 0 deletions include/mongoose_ns.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
-define(MONGOOSEIM_NS_HRL, true).

-define(NS_CLIENT, <<"jabber:client">>).
-define(NS_COMPONENT_ACCEPT, <<"jabber:component:accept">>).
-define(NS_CONFERENCE, <<"jabber:x:conference">>).
-define(NS_DISCO_ITEMS, <<"http://jabber.org/protocol/disco#items">>).
-define(NS_DISCO_INFO, <<"http://jabber.org/protocol/disco#info">>).
Expand Down
6 changes: 3 additions & 3 deletions rel/mim1.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,23 @@
"[[listen.service]]
port = {{ service_port }}
access = \"all\"
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\"

[[listen.service]]
port = {{ kicking_service_port }}
access = \"all\"
conflict_behaviour = \"kick_old\"
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\"

[[listen.service]]
port = {{ hidden_service_port }}
access = \"all\"
hidden_components = true
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\""}.

Expand Down
2 changes: 1 addition & 1 deletion rel/mim2.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"[[listen.service]]
port = {{ service_port }}
access = \"all\"
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\""}.

Expand Down
2 changes: 1 addition & 1 deletion rel/prod.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{listen_service, "[[listen.service]]
port = 8888
access = \"all\"
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\""}.

Expand Down
2 changes: 1 addition & 1 deletion rel/reg1.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
{listen_service, "[[listen.service]]
port = {{ service_port }}
access = \"all\"
shaper_rule = \"fast\"
shaper = \"fast\"
ip_address = \"127.0.0.1\"
password = \"secret\""}.

Expand Down
27 changes: 1 addition & 26 deletions src/c2s/mongoose_c2s_listener.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ instrumentation(_Opts) ->
-spec start_listener(options()) -> ok.
start_listener(#{module := Module} = Opts) ->
HostTypes = ?ALL_HOST_TYPES,
TransportOpts = prepare_socket_opts(Opts),
TransportOpts = mongoose_listener:prepare_socket_opts(Opts),
ListenerId = mongoose_listener_config:listener_id(Opts),
maybe_add_access_check(HostTypes, Opts, ListenerId),
ChildSpec = ranch:child_spec(ListenerId, ranch_tcp, TransportOpts, Module, Opts),
Expand Down Expand Up @@ -73,28 +73,3 @@ maybe_add_access_check(HostTypes, _, ListenerId) ->
#{listener_id => ListenerId}, 10}
|| HostType <- HostTypes ],
gen_hook:add_handlers(AclHooks).

prepare_socket_opts(#{port := Port,
ip_version := IPVersion,
ip_tuple := IPTuple,
backlog := Backlog,
num_acceptors := NumAcceptors,
max_connections := MaxConnections,
reuse_port := ReusePort}) ->
SocketOpts = [{nodelay, true},
{keepalive, true},
{ip, IPTuple},
{port, Port},
{backlog, Backlog},
mongoose_listener_config:address_family(IPVersion)
| maybe_reuseport(ReusePort)],
#{max_connections => MaxConnections,
num_acceptors => NumAcceptors,
num_listen_sockets => num_listen_sockets(ReusePort),
socket_opts => SocketOpts}.

maybe_reuseport(false) -> [];
maybe_reuseport(true) -> [{raw, 1, 15, <<1:32/native>>}].

num_listen_sockets(false) -> 1;
num_listen_sockets(true) -> erlang:system_info(schedulers_online).
10 changes: 10 additions & 0 deletions src/component/mongoose_component.erl
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
-module(mongoose_component).

-behaviour(mongoose_packet_handler).

%% API
-export([has_component/1,
dirty_get_all_components/1,
Expand All @@ -10,6 +12,7 @@

-export([start/0, stop/0]).
-export([node_cleanup/3]).
-export([process_packet/5]).

-include("mongoose.hrl").
-include("external_component.hrl").
Expand Down Expand Up @@ -45,6 +48,13 @@ stop() ->
hooks() ->
[{node_cleanup, global, fun ?MODULE:node_cleanup/3, #{}, 90}].

-spec process_packet(
mongoose_acc:t(), jid:jid(), jid:jid(), exml:element(), #{pid := pid()}) ->
mongoose_acc:t().
process_packet(Acc, _From, _To, _El, #{pid := Pid}) ->
mongoose_component_connection:route(Pid, Acc),
Acc.

-spec register_components(Domain :: [domain()],
Node :: node(),
Handler :: mongoose_packet_handler:t(),
Expand Down
Loading