From 7ed60ffb9b667d75199b688362f92db2d25da4e3 Mon Sep 17 00:00:00 2001 From: Marcelino Alberdi Pereira Date: Fri, 16 Jan 2026 23:02:03 +0100 Subject: [PATCH 1/4] feat(#21): Convert to library application Closes #21. --- README.md | 27 ++++++++------ rebar.config | 2 +- src/nrte.app.src | 5 ++- src/nrte.erl | 42 +++++++++++++++------- src/nrte_auth.erl | 7 ++-- src/nrte_conf.erl | 22 +++++++----- src/nrte_eventsource.erl | 2 +- src/nrte_rest.erl | 2 +- src/nrte_sup.erl | 33 ------------------ src/nrte_websocket.erl | 2 +- test/nrte_eventsource_SUITE.erl | 37 ++++++++++++-------- test/nrte_rest_SUITE.erl | 62 ++++++++++++++++++--------------- test/nrte_websocket_SUITE.erl | 41 ++++++++++++++-------- 13 files changed, 153 insertions(+), 131 deletions(-) delete mode 100644 src/nrte_sup.erl diff --git a/README.md b/README.md index 8ab2bd1..0092a80 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # nrte -An OTP application to send and receive real time events over HTTP connections. +An OTP library to send and receive real time events over HTTP connections. ## Status [![GitHub branch checks state](https://github.com/nomasystems/nrte/actions/workflows/ci.yml/badge.svg)](https://github.com/nomasystems/nrte/actions/workflows/ci.yml) @@ -19,6 +19,11 @@ In your `rebar.config` file, add the dependency: ]}. ``` +And start listening to incoming connections: +```erl +nrte:start_listening(nrte_example, #{} = Opts). +``` + Then, you can connect either via WebSocket or server-sent events to start receiving messages for the given topics. For example, in javascript via the `WebSocket` interface: @@ -38,24 +43,26 @@ curl -X POST -d 'my custom message' 'localhost:2080/message?topics=topic1' ## Configuration -`nrte` defaults to the following configuration values: +All the keys in the `nrte:start_listening/1` parameter are optional and default to the following values: ```erl -[ - {auth_type, {always_allow, all}}, - {data_template, {<<"{{topic}};{{message}}">>}}, - {port, 2080}, - {serve_priv_dir, false} -] +#{ + auth_type => {always_allow, all}}, + name => nrte_listener, + port => 2080, + serve_priv_dir => false +} ``` * `auth_type`: see [authentication](#authentication) for details. -* `data_template`: a template for sending the data through the http connections. Both `{{topic}}` and `{{message}}` are optional and will be replaced with the actual values. +* `name`: a unique name to be registered as the server. * `port`: TCP port that serves the different endpoints. * `serve_priv_dir`: whether to include the [priv dir](/priv/) in the server or not. +It's also possible to define a global `data_template` with `application:set_env(nrte, data_template, Template)`. Defaults to `{data_tempate, {<<"{{topic}};{{message}}">>}}` and any appearance of `{{topic}}` and `{{message}}` are replaced with the actual values. + ## Authentication -By default nrte doesn't authenticate its users, but this can be changed by setting the `auth_type` configuration parameter to a tuple `{auth_mod, Mod}`. In that case, when a request arrives, `nrte` will call `Mod:nrte_auth(Headers)` and use the returned `nrte_auth_value()` to allow or deny access to the resource. See the [`nrte_auth` module](/src/nrte_auth.erl) for the behaviour to implement. +By default nrte doesn't authenticate its users, but this can be changed by setting the `auth_type` option to a tuple `{auth_mod, Mod}`. In that case, when a request arrives, `nrte` will call `Mod:nrte_auth(Headers)` and use the returned `nrte_auth_value()` to allow or deny access to the resource. See the [`nrte_auth` module](/src/nrte_auth.erl) for the behaviour to implement. Some examples: ```erl diff --git a/rebar.config b/rebar.config index eac0441..e89cc9e 100644 --- a/rebar.config +++ b/rebar.config @@ -38,4 +38,4 @@ {cover_enabled, true}. {cover_opts, [verbose]}. -{xref_ignores, []}. +{xref_ignores, [nrte]}. diff --git a/src/nrte.app.src b/src/nrte.app.src index 7725ca3..c64744c 100644 --- a/src/nrte.app.src +++ b/src/nrte.app.src @@ -1,8 +1,7 @@ {application, nrte, [ - {description, "An OTP application to send and receive real time events over HTTP connections"}, - {vsn, "0.1.0"}, + {description, "An OTP libary to send and receive real time events over HTTP connections"}, + {vsn, "1.0.0"}, {registered, []}, {applications, [kernel, stdlib, cowboy, ebus, njson]}, - {mod, {nrte, []}}, {env, []} ]}. diff --git a/src/nrte.erl b/src/nrte.erl index f7bcbe4..bedd284 100644 --- a/src/nrte.erl +++ b/src/nrte.erl @@ -12,36 +12,52 @@ %% See the License for the specific language governing permissions and %% limitations under the License -module(nrte). --behaviour(application). -%%% APPLICATION EXPORTS --export([start/2, stop/1]). +%%% START/STOP SERVER EXPORTS +-export([start_listening/0, start_listening/1, stop_listening/0, stop_listening/1]). %%% EXTERNAL EXPORTS -export([publish/2, subscribe/1]). +%%% TYPES +-type opts() :: #{ + auth_type => {always_allow, nrte_auth:nrte_auth_value()} | {auth_mod, atom()}, + name => atom(), + port => pos_integer(), + serve_priv_dir => boolean() +}. + %%%----------------------------------------------------------------------------- %%% APPLICATION EXPORTS %%%----------------------------------------------------------------------------- -start(_, _) -> +-spec start_listening() -> {ok, pid()} | {error, any()}. +start_listening() -> + start_listening(#{}). + +-spec start_listening(opts()) -> {ok, pid()} | {error, any()}. +start_listening(Opts) -> BaseRoutes = [ - {"/eventsource", nrte_eventsource, []}, - {"/websocket", nrte_websocket, #{}}, - {"/[...]", nrte_rest, []} + {"/eventsource", nrte_eventsource, Opts}, + {"/websocket", nrte_websocket, Opts}, + {"/[...]", nrte_rest, Opts} ], Routes = - case nrte_conf:serve_priv_dir() of + case nrte_conf:serve_priv_dir(Opts) of true -> [{"/priv/[...]", cowboy_static, {priv_dir, nrte, ""}} | BaseRoutes]; _ -> BaseRoutes end, Dispatch = cowboy_router:compile([{'_', Routes}]), - cowboy:start_clear(nrte_listener, [{port, nrte_conf:port()}], #{ + cowboy:start_clear(nrte_conf:name(Opts), [{port, nrte_conf:port(Opts)}], #{ enable_connect_protocol => true, env => #{dispatch => Dispatch} - }), - nrte_sup:start_link(). + }). + +-spec stop_listening() -> ok. +stop_listening() -> + stop_listening(nrte_conf:name(#{})). -stop(_) -> - cowboy:stop_listener(nrte_listener), +-spec stop_listening(atom()) -> ok. +stop_listening(Name) -> + cowboy:stop_listener(Name), ok. %%%----------------------------------------------------------------------------- diff --git a/src/nrte_auth.erl b/src/nrte_auth.erl index bf4bc8c..246b9d3 100644 --- a/src/nrte_auth.erl +++ b/src/nrte_auth.erl @@ -14,18 +14,19 @@ -module(nrte_auth). -type nrte_auth_value() :: unauthorized | all | none | [RegExp :: iodata()]. +-export_type([nrte_auth_value/0]). -callback nrte_auth(Headers :: #{binary() => binary()}) -> nrte_auth_value() | #{allowed_publications | allowed_subscriptions => nrte_auth_value()}. %%% EXTERNAL EXPORTS --export([authorization/2]). +-export([authorization/3]). %%%----------------------------------------------------------------------------- %%% EXTERNAL EXPORTS %%%----------------------------------------------------------------------------- -authorization(Req, Type) -> +authorization(Req, Opts, Type) -> AuthValue = - case nrte_conf:auth_type() of + case nrte_conf:auth_type(Opts) of {always_allow, Value} -> Value; {auth_mod, Mod} -> Mod:nrte_auth(maps:get(headers, Req)) end, diff --git a/src/nrte_conf.erl b/src/nrte_conf.erl index a69b081..747d869 100644 --- a/src/nrte_conf.erl +++ b/src/nrte_conf.erl @@ -15,23 +15,27 @@ %%% EXTERNAL EXPORTS -export([ - auth_type/0, + auth_type/1, data_template/0, - port/0, - serve_priv_dir/0 + name/1, + port/1, + serve_priv_dir/1 ]). %%%----------------------------------------------------------------------------- %%% EXTERNAL EXPORTS %%%----------------------------------------------------------------------------- -auth_type() -> - application:get_env(nrte, auth_type, {always_allow, all}). +auth_type(Opts) -> + maps:get(auth_type, Opts, {always_allow, all}). data_template() -> application:get_env(nrte, data_template, <<"{{topic}};{{message}}">>). -port() -> - application:get_env(nrte, port, 2080). +name(Opts) -> + maps:get(name, Opts, nrte_listener). -serve_priv_dir() -> - application:get_env(nrte, serve_priv_dir, false). +port(Opts) -> + maps:get(port, Opts, 2080). + +serve_priv_dir(Opts) -> + maps:get(serve_priv_dir, Opts, false). diff --git a/src/nrte_eventsource.erl b/src/nrte_eventsource.erl index 6e135e9..33823b0 100644 --- a/src/nrte_eventsource.erl +++ b/src/nrte_eventsource.erl @@ -24,7 +24,7 @@ %%% COWBOY LOOP EXPORTS %%%----------------------------------------------------------------------------- init(Req, Opts) -> - case nrte_auth:authorization(Req, subscribe) of + case nrte_auth:authorization(Req, Opts, subscribe) of {authorized, TopicList} -> nrte:subscribe(TopicList), nrte_publications:subscription_init_link_terminate(<<"eventsource">>, TopicList), diff --git a/src/nrte_rest.erl b/src/nrte_rest.erl index 4ed9455..3bb4333 100644 --- a/src/nrte_rest.erl +++ b/src/nrte_rest.erl @@ -21,7 +21,7 @@ %%% COWBOY HANDLER EXPORTS %%%----------------------------------------------------------------------------- init(Req, Opts) -> - case nrte_auth:authorization(Req, publish) of + case nrte_auth:authorization(Req, Opts, publish) of {authorized, TopicList} -> init_authorized(Req, Opts, TopicList); {unauthorized, Req2} -> diff --git a/src/nrte_sup.erl b/src/nrte_sup.erl deleted file mode 100644 index 4b7faef..0000000 --- a/src/nrte_sup.erl +++ /dev/null @@ -1,33 +0,0 @@ -%%% Copyright 2023 Nomasystems, S.L. http://www.nomasystems.com -%% -%% 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(nrte_sup). --behaviour(supervisor). - -%%% START/STOP EXPORTS --export([start_link/0]). - -%%% SUPERVISOR EXPORTS --export([init/1]). - -%%%----------------------------------------------------------------------------- -%%% START/STOP EXPORTS -%%%----------------------------------------------------------------------------- -start_link() -> - supervisor:start_link({local, ?MODULE}, ?MODULE, []). - -%%%----------------------------------------------------------------------------- -%%% SUPERVISOR EXPORTS -%%%----------------------------------------------------------------------------- -init(_) -> - {ok, {#{}, []}}. diff --git a/src/nrte_websocket.erl b/src/nrte_websocket.erl index 98c998c..6a2235f 100644 --- a/src/nrte_websocket.erl +++ b/src/nrte_websocket.erl @@ -21,7 +21,7 @@ %%% COWBOY WEBSOCKET EXPORTS %%%----------------------------------------------------------------------------- init(Req, Opts) -> - case nrte_auth:authorization(Req, subscribe) of + case nrte_auth:authorization(Req, Opts, subscribe) of {authorized, TopicList} -> {cowboy_websocket, Req, Opts#{topic_list => TopicList}}; {unauthorized, Req2} -> diff --git a/test/nrte_eventsource_SUITE.erl b/test/nrte_eventsource_SUITE.erl index c93ce37..31dd4bb 100644 --- a/test/nrte_eventsource_SUITE.erl +++ b/test/nrte_eventsource_SUITE.erl @@ -38,12 +38,15 @@ suite() -> %%% INIT SUITE EXPORTS %%%----------------------------------------------------------------------------- init_per_suite(Conf) -> - nct_util:setup_suite(Conf). + Conf2 = nct_util:setup_suite(Conf), + {ok, _} = nrte:start_listening(), + Conf2. %%%----------------------------------------------------------------------------- %%% END SUITE EXPORTS %%%----------------------------------------------------------------------------- end_per_suite(Conf) -> + ok = nrte:stop_listening(), nct_util:teardown_suite(Conf). %%%----------------------------------------------------------------------------- @@ -60,28 +63,31 @@ connect_authorized_pattern() -> [{userdata, [{doc, "Tests connecting to the event source with an authorized pattern"}]}]. connect_authorized_pattern(_Conf) -> - application:set_env(nrte, auth_type, {auth_mod, ?MODULE}), - {ok, {Pid, _StreamRef}} = connect_es("auth-allowed"), - application:unset_env(nrte, auth_type), - ok = gun:close(Pid). + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {auth_mod, ?MODULE}}), + {ok, {Pid, _StreamRef}} = connect_es("auth-allowed", Port), + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). connect_forbidden_pattern() -> [{userdata, [{doc, "Tests being forbidden from connecting to the event source"}]}]. connect_forbidden_pattern(_Conf) -> - application:set_env(nrte, auth_type, {auth_mod, ?MODULE}), - forbidden = connect_es("auth-allowed;noauth-noallowed"), - application:unset_env(nrte, auth_type), - ok. + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {auth_mod, ?MODULE}}), + forbidden = connect_es("auth-allowed;noauth-noallowed", Port), + ok = nrte:stop_listening(Name). connect_unauthorized() -> [{userdata, [{doc, "Tests unauthorized connecting to the event source"}]}]. connect_unauthorized(_Conf) -> - application:set_env(nrte, auth_type, {always_allow, unauthorized}), - unauthorized = connect_es("topic1;topic2;topic3"), - application:unset_env(nrte, auth_type), - ok. + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{ + name => Name, port => Port, auth_type => {always_allow, unauthorized} + }), + unauthorized = connect_es("topic1;topic2;topic3", Port), + ok = nrte:stop_listening(Name). receive_subscription_init() -> [{userdata, [{doc, "Tests receiving the special subscription init message"}]}]. @@ -129,8 +135,11 @@ nrte_auth(_) -> #{allowed_subscriptions => [<<"auth.*">>]}. connect_es(Topics) -> + connect_es(Topics, 2080). + +connect_es(Topics, Port) -> Protocol = http2, - {ok, Pid} = gun:open("localhost", 2080, #{ + {ok, Pid} = gun:open("localhost", Port, #{ protocols => [Protocol], http2_opts => #{content_handlers => [gun_sse_h, gun_data_h]} }), diff --git a/test/nrte_rest_SUITE.erl b/test/nrte_rest_SUITE.erl index 7863f1b..487af1e 100644 --- a/test/nrte_rest_SUITE.erl +++ b/test/nrte_rest_SUITE.erl @@ -46,12 +46,15 @@ suite() -> %%% INIT SUITE EXPORTS %%%----------------------------------------------------------------------------- init_per_suite(Conf) -> - nct_util:setup_suite(Conf). + Conf2 = nct_util:setup_suite(Conf), + {ok, _} = nrte:start_listening(), + Conf2. %%%----------------------------------------------------------------------------- %%% END SUITE EXPORTS %%%----------------------------------------------------------------------------- end_per_suite(Conf) -> + ok = nrte:stop_listening(), nct_util:teardown_suite(Conf). %%%----------------------------------------------------------------------------- @@ -61,17 +64,14 @@ get_priv_dir() -> [{userdata, [{doc, "Tests getting a file from the priv dir"}]}]. get_priv_dir(_Conf) -> - application:set_env(nrte, serve_priv_dir, true), - application:stop(nrte), - application:start(nrte), - {ok, Pid} = gun:open("localhost", 2080), + {Name, Port} = {nrte_rest_priv_dir, 3080}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, serve_priv_dir => true}), + {ok, Pid} = gun:open("localhost", Port), {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/priv/tester.html"), {response, _, 200, _} = gun:await(Pid, StreamRef, 1000), - application:unset_env(nrte, serve_priv_dir), - application:stop(nrte), - application:start(nrte), - ok. + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). get_priv_dir_404() -> [{userdata, [{doc, "Tests not getting a file from the priv dir with the default conf"}]}]. @@ -81,31 +81,35 @@ get_priv_dir_404(_Conf) -> {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/priv/tester.html"), {response, _, 404, _} = gun:await(Pid, StreamRef, 1000), - ok. + ok = gun:close(Pid). get_unauthorized() -> [{userdata, [{doc, "Tests getting an unauthorized path"}]}]. get_unauthorized(_Conf) -> - application:set_env(nrte, auth_type, {always_allow, unauthorized}), - {ok, Pid} = gun:open("localhost", 2080), + {Name, Port} = {nrte_rest_unauthorized, 3081}, + {ok, _} = nrte:start_listening(#{ + name => Name, port => Port, auth_type => {always_allow, unauthorized} + }), + {ok, Pid} = gun:open("localhost", Port), {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/auth"), {response, fin, 401, _Headers} = gun:await(Pid, StreamRef, 1000), - application:unset_env(nrte, auth_type), - ok. + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). get_unauthorized_via_auth_mod() -> [{userdata, [{doc, "Tests getting an unauthorized path with auth mod"}]}]. get_unauthorized_via_auth_mod(_Conf) -> - application:set_env(nrte, auth_type, {auth_mod, ?MODULE}), - {ok, Pid} = gun:open("localhost", 2080), + {Name, Port} = {nrte_rest_auth_mod, 3082}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {auth_mod, ?MODULE}}), + {ok, Pid} = gun:open("localhost", Port), {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/auth"), {response, fin, 401, _Headers} = gun:await(Pid, StreamRef, 1000), - application:unset_env(nrte, auth_type), - ok. + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). get_undocumented_path() -> [{userdata, [{doc, "Tests getting an undocumented path"}]}]. @@ -115,19 +119,20 @@ get_undocumented_path(_Conf) -> {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/undocumented"), {response, fin, 404, _Headers} = gun:await(Pid, StreamRef, 1000), - ok. + ok = gun:close(Pid). get_undocumented_path_via_auth_mod() -> [{userdata, [{doc, "Tests getting an undocumented path with auth mod"}]}]. get_undocumented_path_via_auth_mod(_Conf) -> - application:set_env(nrte, auth_type, {auth_mod, ?MODULE}), - {ok, Pid} = gun:open("localhost", 2080), + {Name, Port} = {nrte_rest_auth_mod, 3083}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {auth_mod, ?MODULE}}), + {ok, Pid} = gun:open("localhost", Port), {ok, http} = gun:await_up(Pid), StreamRef = gun:get(Pid, "/undocumented", [{<<"authorization">>, <<"some">>}]), {response, fin, 404, _Headers} = gun:await(Pid, StreamRef, 1000), - application:unset_env(nrte, auth_type), - ok. + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). post_empty_body() -> [{userdata, [{doc, "Tests posting an empty body"}]}]. @@ -149,21 +154,22 @@ post_no_topic(_Conf) -> {ok, http} = gun:await_up(Pid), StreamRef = gun:post(Pid, "/message", #{}, ?TEXT), {response, _, 400, _Headers} = gun:await(Pid, StreamRef, 1000), - ok. + ok = gun:close(Pid). post_topic_all_authorized() -> [{userdata, [{doc, "Tests posting a topic message with all publications auth"}]}]. post_topic_all_authorized(_Conf) -> - application:set_env(nrte, auth_type, {always_allow, all}), + {Name, Port} = {nrte_rest_allow_all, 3084}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {always_allow, all}}), ok = nrte:subscribe([<<"topic">>]), - {ok, Pid} = gun:open("localhost", 2080), + {ok, Pid} = gun:open("localhost", Port), {ok, http} = gun:await_up(Pid), StreamRef = gun:post(Pid, "/message?topics=topic", ?HEADERS, ?TEXT), {response, fin, 200, _Headers} = gun:await(Pid, StreamRef, 1000), ok = nrte_receive(<<"topic">>, ?TEXT), - application:unset_env(nrte, auth_type), - ok = gun:close(Pid). + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). post_topic_message() -> [{userdata, [{doc, "Tests posting a topic message"}]}]. diff --git a/test/nrte_websocket_SUITE.erl b/test/nrte_websocket_SUITE.erl index 2b0607b..606c625 100644 --- a/test/nrte_websocket_SUITE.erl +++ b/test/nrte_websocket_SUITE.erl @@ -44,12 +44,15 @@ suite() -> %%% INIT SUITE EXPORTS %%%----------------------------------------------------------------------------- init_per_suite(Conf) -> - nct_util:setup_suite(Conf). + Conf2 = nct_util:setup_suite(Conf), + {ok, _} = nrte:start_listening(), + Conf2. %%%----------------------------------------------------------------------------- %%% END SUITE EXPORTS %%%----------------------------------------------------------------------------- end_per_suite(Conf) -> + ok = nrte:stop_listening(), nct_util:teardown_suite(Conf). %%%----------------------------------------------------------------------------- @@ -66,28 +69,35 @@ connect_authorized_explicitly() -> [{userdata, [{doc, "Tests connecting to the websocket with explicit authorization"}]}]. connect_authorized_explicitly(_Conf) -> - application:set_env(nrte, auth_type, {auth_mod, ?MODULE}), - {ok, {Pid, _StreamRef}} = connect_ws(), - application:unset_env(nrte, auth_type), - ok = gun:close(Pid). + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{name => Name, port => Port, auth_type => {auth_mod, ?MODULE}}), + {ok, {Pid, _StreamRef}} = connect_ws(Port), + ok = gun:close(Pid), + ok = nrte:stop_listening(Name). connect_forbidden_subscription() -> [{userdata, [{doc, "Tests connecting to the websocket without subscription authorization"}]}]. connect_forbidden_subscription(_Conf) -> - application:set_env(nrte, auth_type, {always_allow, #{allowed_publications => all}}), - forbidden = connect_ws(), - application:unset_env(nrte, auth_type), - ok. + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{ + name => Name, + port => Port, + auth_type => {always_allow, #{allowed_publications => all}} + }), + forbidden = connect_ws(Port), + ok = nrte:stop_listening(Name). connect_unauthorized() -> [{userdata, [{doc, "Tests connecting to the websocket without authorization"}]}]. connect_unauthorized(_Conf) -> - application:set_env(nrte, auth_type, {always_allow, unauthorized}), - unauthorized = connect_ws(), - application:unset_env(nrte, auth_type), - ok. + {Name, Port} = {nrte_eventsource_connect_auth, 2081}, + {ok, _} = nrte:start_listening(#{ + name => Name, port => Port, auth_type => {always_allow, unauthorized} + }), + unauthorized = connect_ws(Port), + ok = nrte:stop_listening(Name). coverage_completion() -> [{userdata, [{doc, "Ensure coverage completion by sending a direct info message"}]}]. @@ -157,8 +167,11 @@ send_unsupported_text(_Conf) -> %%% INTERNAL FUNCTIONS %%%----------------------------------------------------------------------------- connect_ws() -> + connect_ws(2080). + +connect_ws(Port) -> Protocol = http2, - {ok, Pid} = gun:open("localhost", 2080, #{ + {ok, Pid} = gun:open("localhost", Port, #{ ws_opts => #{silence_pings => false}, protocols => [Protocol], http2_opts => #{notify_settings_changed => true} From 0bae807c828df6ecbaf4a6ea76c86ff1b0eb213a Mon Sep 17 00:00:00 2001 From: Marcelino Alberdi Pereira Date: Mon, 19 Jan 2026 10:21:30 +0100 Subject: [PATCH 2/4] Update README.md Co-authored-by: josecuevas-noma --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0092a80..9cb67ef 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ In your `rebar.config` file, add the dependency: And start listening to incoming connections: ```erl -nrte:start_listening(nrte_example, #{} = Opts). +nrte:start_listening(#{} = Opts). ``` Then, you can connect either via WebSocket or server-sent events to start receiving messages for the given topics. From c7aa1eced7e6dba9969dcb7caf7736819a5e0dc3 Mon Sep 17 00:00:00 2001 From: Marcelino Alberdi Pereira Date: Mon, 19 Jan 2026 11:24:31 +0100 Subject: [PATCH 3/4] fix: Add cowlib and ranch to the list of applications --- src/nrte.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nrte.app.src b/src/nrte.app.src index c64744c..3207bb2 100644 --- a/src/nrte.app.src +++ b/src/nrte.app.src @@ -2,6 +2,6 @@ {description, "An OTP libary to send and receive real time events over HTTP connections"}, {vsn, "1.0.0"}, {registered, []}, - {applications, [kernel, stdlib, cowboy, ebus, njson]}, + {applications, [kernel, stdlib, cowlib, ranch, cowboy, ebus, njson]}, {env, []} ]}. From 4deba839298209fa61c17942d8c1d8e1e1e07f35 Mon Sep 17 00:00:00 2001 From: Marcelino Alberdi Pereira Date: Mon, 19 Jan 2026 11:34:13 +0100 Subject: [PATCH 4/4] Revert "fix: Add cowlib and ranch to the list of applications" This reverts commit c7aa1eced7e6dba9969dcb7caf7736819a5e0dc3. --- src/nrte.app.src | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nrte.app.src b/src/nrte.app.src index 3207bb2..c64744c 100644 --- a/src/nrte.app.src +++ b/src/nrte.app.src @@ -2,6 +2,6 @@ {description, "An OTP libary to send and receive real time events over HTTP connections"}, {vsn, "1.0.0"}, {registered, []}, - {applications, [kernel, stdlib, cowlib, ranch, cowboy, ebus, njson]}, + {applications, [kernel, stdlib, cowboy, ebus, njson]}, {env, []} ]}.