Skip to content

Commit

Permalink
Merge pull request #3636 from esl/http-handler-rework
Browse files Browse the repository at this point in the history
Rework HTTP handler configuration
  • Loading branch information
Premwoik authored Apr 27, 2022
2 parents 2023f3e + eeda654 commit 0d956f2
Show file tree
Hide file tree
Showing 25 changed files with 524 additions and 583 deletions.
7 changes: 4 additions & 3 deletions big_tests/tests/domain_rest_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,10 @@ listener_opts(Params) ->
transport => config([listen, http, transport], #{num_acceptors => 10})}).

domain_handler(Params) ->
{"localhost", "/api", mongoose_domain_handler, handler_opts(Params)}.
maps:merge(#{host => "localhost", path => "/api", module => mongoose_domain_handler},
handler_opts(Params)).

handler_opts(#{skip_auth := true}) ->
[];
#{};
handler_opts(_Params) ->
[{password, <<"secret">>}, {username, <<"admin">>}].
#{password => <<"secret">>, username => <<"admin">>}.
46 changes: 20 additions & 26 deletions big_tests/tests/rest_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -277,36 +277,30 @@ start_admin_listener(Creds) ->
NewOpts = insert_creds(Opts, Creds),
rpc(mim(), mongoose_listener, start_listener, [NewOpts]).

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

inject_creds_to_opts(PathOpts, any) ->
lists:keydelete(auth, 1, PathOpts);
inject_creds_to_opts(PathOpts, Creds) ->
case lists:keymember(auth, 1, PathOpts) of
true ->
lists:keyreplace(auth, 1, PathOpts, {auth, Creds});
false ->
lists:append(PathOpts, [{auth, Creds}])
end.
insert_creds(Opts = #{handlers := Handlers}, Creds) ->
NewHandlers = [inject_creds_to_opts(Handler, Creds) || Handler <- Handlers],
Opts#{handlers := NewHandlers}.

inject_creds_to_opts(Handler = #{module := mongoose_api_admin}, Creds) ->
case Creds of
{UserName, Password} ->
Handler#{username => UserName, password => Password};
any ->
maps:without([username, password], Handler)
end;
inject_creds_to_opts(Handler, _Creds) ->
Handler.

% @doc Checks whether a config for a port is an admin or client one.
% This is determined based on modules used. If there is any mongoose_api_admin module used,
% it is admin config. If not and there is at least one mongoose_api_client* module used,
% it's clients.
is_roles_config(#{module := ejabberd_cowboy, handlers := Modules}, admin) ->
lists:any(fun({_, _Path, Mod, _Args}) -> Mod == mongoose_api_admin; (_) -> false end, Modules);
is_roles_config(#{module := ejabberd_cowboy, handlers := Modules}, client) ->
ModulesTokens = lists:map(fun({_, _Path, Mod, _}) -> string:tokens(atom_to_list(Mod), "_");
(_) -> []
end, Modules),
lists:any(fun(["mongoose", "client", "api" | _T]) -> true; (_) -> false end, ModulesTokens);
% This is determined based on handler modules used.
is_roles_config(#{module := ejabberd_cowboy, handlers := Handlers}, Role) ->
RoleModule = role_to_module(Role),
lists:any(fun(#{module := Module}) -> Module =:= RoleModule end, Handlers);
is_roles_config(_, _) -> false.

role_to_module(admin) -> mongoose_api_admin;
role_to_module(client) -> mongoose_client_api.

mapfromlist(L) ->
Nl = lists:map(fun({K, {V}}) when is_list(V) ->
{binary_to_atom(K, utf8), mapfromlist(V)};
Expand Down
84 changes: 30 additions & 54 deletions doc/configuration/listen.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,16 +420,16 @@ Recommended port number: 5280 for BOSH/WS.
There are the following options for each of the HTTP listeners:

### `listen.http.handlers`
* **Syntax:** each handler is specified in a subsection starting with `[[listen.http.handlers.type]]` where `type` is one of the allowed handler types, handling different connection types, e.g.
* **Syntax:** each handler is specified in a subsection starting with `[[listen.http.handlers.type]]` where `type` is one of the allowed handler types, handling different connection types:

* `mod_bosh` - for [BOSH](https://xmpp.org/extensions/xep-0124.html) connections,
* `mod_websockets` - for [WebSocket](https://tools.ietf.org/html/rfc6455) connections,
* `mongoose_api_*`, `mongoose_client_api_*`, ... - for REST API.
* `mongoose_api_admin`, `mongoose_api_client`(obsolete), `mongoose_client_api`, `mongoose_domain_handler`, `mongoose_api` - for REST API.

These types are described below in more detail.
The double-bracket syntax is used because there can be multiple handlers of a given type, so for each type there is a TOML array of one or more tables (subsections).

* **Default:** there is no default, all handlers need to be specified explicitly.
* **Default:** `[]` - no handlers enabled, all of them need to be specified explicitly.
* **Example:** two handlers, one for BOSH and one for WebSockets
```toml
[[listen.http.handlers.mod_bosh]]
Expand Down Expand Up @@ -459,10 +459,12 @@ Path for this handler.

### Handler types: BOSH - `mod_bosh`

The recommended configuration is shown in [Example 1](#example-1-bosh-and-ws) below.
To handle incoming BOSH traffic you need to configure the `mod_bosh` module in the `modules` section as well.

### Handler types: WebSockets - `mod_websockets`

The recommended configuration is shown in [Example 1](#example-1-bosh-and-ws) below.
Websocket connections as defined in [RFC 7395](https://tools.ietf.org/html/rfc7395).
You can pass the following optional parameters:

Expand Down Expand Up @@ -490,7 +492,7 @@ Maximum allowed incoming stanza size.
This limit is checked **after** the input data parsing, so it does not apply to the input data size itself.

#### `listen.http.handlers.mod_websockets.service`
* **Syntax:** an array of `listen.service.*` options
* **Syntax:** a table of `listen.service.*` options
* **Default:** not set
* **Example:**

Expand All @@ -506,6 +508,7 @@ See the [service](#xmpp-components-listenservice) listener section for details.

### Handler types: REST API - Admin - `mongoose_api_admin`

The recommended configuration is shown in [Example 2](#example-2-admin-api) below.
For more information about the API, see the [REST interface](../rest-api/Administration-backend.md) documentation.
The following options are supported for this handler:

Expand All @@ -523,19 +526,32 @@ When set, enables authentication for the admin API, otherwise it is disabled. Re

Required to enable authentication for the admin API.

### Handler types: REST API - Client

To enable the REST API for clients, several handlers need to be added:

* `mongoose_client_api_*` - handles individual API endpoints. You can add and remove these to enable particular functionality.
* `lasse_handler` - provides the [SSE handler](https://github.com/inaka/lasse) which is required for the client HTTP API, should not be changed.
* `cowboy_*` - hosts the Swagger web-based documentation, should not be changed, but can be removed to disable the API docs.
### Handler types: REST API - Client - `mongoose_client_api`

The recommended configuration is shown in [Example 3](#example-3-client-api) below.
Please refer to [REST interface](../rest-api/Client-frontend.md) documentation for more information.
The following options are supported for this handler:

#### `listen.http.handlers.mongoose_client_api.handlers`
* **Syntax:** array of strings - Erlang modules
* **Default:** all API handler modules enabled
* **Example:** `handlers = ["mongoose_client_api_messages", "mongoose_client_api_sse"]`

The client API consists of several modules, each of them implementing a subset of the functionality.
By default all modules are enabled, so you don't need to change this option.
For a list of allowed modules, you need to consult the [source code](https://github.com/esl/MongooseIM/blob/master/src/mongoose_client_api/mongoose_client_api.erl).

#### `listen.http.handlers.mongoose_client_api.docs`
* **Syntax:** boolean
* **Default:** `true`
* **Example:** `docs = "false"`

The Swagger documentation of the client API is hosted at the `/api-docs` path.
You can disable the hosted documentation by setting this option to `false`.

### Handler types: REST API - Domain management - `mongoose_domain_handler`

The recommended configuration is shown in [Example 4](#example-4-domain-api) below.
This handler enables dynamic domain management for different host types.
For more information about the API, see the [REST interface](../rest-api/Dynamic-domains.md) documentation.
The following options are supported for this handler:
Expand All @@ -562,7 +578,7 @@ The following option is required:

#### `listen.http.handlers.mongoose_api.handlers`
* **Syntax:** array of strings - Erlang modules
* **Default:** not set, this is a mandatory option for this handler
* **Default:** all API handler modules enabled
* **Example:** `handlers = ["mongoose_api_metrics"]`

### Transport options
Expand Down Expand Up @@ -720,49 +736,9 @@ REST API for clients.
transport.max_connections = 1024
protocol.compress = true

[[listen.http.handlers.lasse_handler]]
host = "_"
path = "/api/sse"
module = "mongoose_client_api_sse"

[[listen.http.handlers.mongoose_client_api_messages]]
[[listen.http.handlers.mongoose_client_api]]
host = "_"
path = "/api/messages/[:with]"

[[listen.http.handlers.mongoose_client_api_contacts]]
host = "_"
path = "/api/contacts/[:jid]"

[[listen.http.handlers.mongoose_client_api_rooms]]
host = "_"
path = "/api/rooms/[:id]"

[[listen.http.handlers.mongoose_client_api_rooms_config]]
host = "_"
path = "/api/rooms/[:id]/config"

[[listen.http.handlers.mongoose_client_api_rooms_users]]
host = "_"
path = "/api/rooms/:id/users/[:user]"

[[listen.http.handlers.mongoose_client_api_rooms_messages]]
host = "_"
path = "/api/rooms/[:id]/messages"

[[listen.http.handlers.cowboy_swagger_redirect_handler]]
host = "_"
path = "/api-docs"

[[listen.http.handlers.cowboy_swagger_json_handler]]
host = "_"
path = "/api-docs/swagger.json"

[[listen.http.handlers.cowboy_static]]
host = "_"
path = "/api-docs/[...]"
type = "priv_dir"
app = "cowboy_swagger"
content_path = "swagger"
path = "/api"
```

#### Example 4. Domain API
Expand Down
4 changes: 4 additions & 0 deletions doc/migrations/5.0.0_5.1.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

The configuration format has slightly changed and you might need to amend `mongooseim.toml`.

### Section `listen`

There is a new, simplified configuration format for `mongoose_client_api`. You need to change the `listen` section unless you have disabled the client API in your configuration file. Consult the [option description](../configuration/listen.md#handler-types-rest-api-client-mongoose_client_api) and the [example configuration](http://localhost:8000/configuration/listen/#example-3-client-api) for details.

### Section `acl`

The implicit check for user's domain in patterns is now configurable and the default behaviour (previously undocumented) is more consistent - the check is always performed unless disabled with `match = "all"`.
Expand Down
46 changes: 3 additions & 43 deletions rel/files/mongooseim.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
[[listen.http.handlers.mongoose_api_admin]]
host = "localhost"
path = "/api"

[[listen.http.handlers.mongoose_domain_handler]]
host = "localhost"
path = "/api"
Expand All @@ -74,49 +75,9 @@
{{{https_config}}}
{{/https_config}}

[[listen.http.handlers.lasse_handler]]
host = "_"
path = "/api/sse"
module = "mongoose_client_api_sse"

[[listen.http.handlers.mongoose_client_api_messages]]
host = "_"
path = "/api/messages/[:with]"

[[listen.http.handlers.mongoose_client_api_contacts]]
host = "_"
path = "/api/contacts/[:jid]"

[[listen.http.handlers.mongoose_client_api_rooms]]
host = "_"
path = "/api/rooms/[:id]"

[[listen.http.handlers.mongoose_client_api_rooms_config]]
host = "_"
path = "/api/rooms/[:id]/config"

[[listen.http.handlers.mongoose_client_api_rooms_users]]
host = "_"
path = "/api/rooms/:id/users/[:user]"

[[listen.http.handlers.mongoose_client_api_rooms_messages]]
host = "_"
path = "/api/rooms/[:id]/messages"

[[listen.http.handlers.cowboy_swagger_redirect_handler]]
[[listen.http.handlers.mongoose_client_api]]
host = "_"
path = "/api-docs"

[[listen.http.handlers.cowboy_swagger_json_handler]]
host = "_"
path = "/api-docs/swagger.json"

[[listen.http.handlers.cowboy_static]]
host = "_"
path = "/api-docs/[...]"
type = "priv_dir"
app = "cowboy_swagger"
content_path = "swagger"
path = "/api"

[[listen.http]]
{{#http_api_old_endpoint}}
Expand All @@ -128,7 +89,6 @@
[[listen.http.handlers.mongoose_api]]
host = "localhost"
path = "/api"
handlers = ["mongoose_api_metrics", "mongoose_api_users"]

[[listen.c2s]]
port = {{{c2s_port}}}
Expand Down
3 changes: 3 additions & 0 deletions src/config/mongoose_config_parser_toml.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

-export([parse_file/1]).

%% Utilities for section manipulation
-export([process/3]).

-ifdef(TEST).
-export([process/1,
extract_errors/1]).
Expand Down
Loading

0 comments on commit 0d956f2

Please sign in to comment.