Skip to content

Commit

Permalink
Feature: add functions to add + remove dispatch routes
Browse files Browse the repository at this point in the history
  • Loading branch information
artman41 committed Nov 1, 2022
1 parent 30ee75c commit 3218156
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PROJECT = cowboy
PROJECT_DESCRIPTION = Small, fast, modern HTTP server.
PROJECT_VERSION = 2.9.0
PROJECT_VERSION = 2.9.1
PROJECT_REGISTERED = cowboy_clock

# Options.
Expand Down
55 changes: 55 additions & 0 deletions src/cowboy.erl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
-export([stop_listener/1]).
-export([set_env/3]).

-export([add_routes/2]).
-export([remove_routes/2]).

%% Internal.
-export([log/2]).
-export([log/4]).
Expand Down Expand Up @@ -70,6 +73,18 @@ ensure_connection_type(TransOpts) ->
stop_listener(Ref) ->
ranch:stop_listener(Ref).

-spec add_routes(ranch:ref(), cowboy_router:dispatch_rules()) -> ok.
add_routes(Ref, NewDispatch) ->
Opts = #{env := Env0 = #{dispatch := Dispatch}} = ranch:get_protocol_options(Ref),
Env1 = Env0#{dispatch => add_routes_(NewDispatch, Dispatch)},
ok = ranch:set_protocol_options(Ref, Opts#{env => Env1}).

-spec remove_routes(ranch:ref(), cowboy_router:dispatch_rules()) -> ok.
remove_routes(Ref, NewDispatch) ->
Opts = #{env := Env0 = #{dispatch := Dispatch}} = ranch:get_protocol_options(Ref),
Env1 = Env0#{dispatch => remove_routes_(NewDispatch, Dispatch)},
ok = ranch:set_protocol_options(Ref, Opts#{env => Env1}).

-spec set_env(ranch:ref(), atom(), any()) -> ok.
set_env(Ref, Name, Value) ->
Opts = ranch:get_protocol_options(Ref),
Expand Down Expand Up @@ -103,3 +118,43 @@ log(Level, Format, Args, _) ->
debug -> info_msg
end,
error_logger:Function(Format, Args).

add_routes_([], Dispatch) ->
Dispatch;
add_routes_(Dispatch, []) ->
Dispatch;
add_routes_([Route0 = {HostMatch, CowboyFields, Paths}|Tail], Dispatch0) ->
{Route1, Dispatch1} =
case lists:keytake(HostMatch, 1, Dispatch0) of
{value, {HostMatch, ExistingCowboyFields, ExistingPaths}, Dispatch} ->
Route = {HostMatch, lists:flatten(CowboyFields, ExistingCowboyFields), lists:flatten(Paths, ExistingPaths)},
{Route, Dispatch};
false ->
{Route0, Dispatch0}
end,
[Route1 | add_routes_(Tail, Dispatch1)].

remove_routes_([], Dispatch) ->
Dispatch;
remove_routes_(_Dispatch, []) ->
[];
remove_routes_([Route0 = {HostMatch, CowboyFields, Paths}|Tail], Dispatch0) ->
{Route1, Dispatch1} =
case lists:keytake(HostMatch, 1, Dispatch0) of
{value, {HostMatch, ExistingCowboyFields, ExistingPaths}, Dispatch} ->
case ExistingPaths -- Paths of
[] ->
{undefined, Dispatch};
NewPaths ->
Route = {HostMatch, ExistingCowboyFields -- CowboyFields, NewPaths},
{Route, Dispatch}
end;
false ->
{Route0, Dispatch0}
end,
case Route1 of
undefined ->
add_routes_(Tail, Dispatch1);
_ ->
[Route1 | add_routes_(Tail, Dispatch1)]
end.
71 changes: 69 additions & 2 deletions test/misc_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
-import(cowboy_test, [gun_open/1]).

all() ->
[{group, app}, {group, set_env}|cowboy_test:common_all()].
[{group, app}, {group, set_env}, {group, routes}|cowboy_test:common_all()].

groups() ->
Common = ct_helper:all(?MODULE)
-- [restart_gracefully, set_env, set_env_missing],
[
{app, [], [restart_gracefully]},
{set_env, [parallel], [set_env, set_env_missing]}
{set_env, [parallel], [set_env, set_env_missing]},
{routes, [parallel], [add_route, remove_route]}
|cowboy_test:common_groups(Common)].

init_per_group(Name=app, Config) ->
Expand All @@ -37,11 +38,15 @@ init_per_group(Name=app, Config) ->
}, Config);
init_per_group(set_env, Config) ->
Config;
init_per_group(routes, Config) ->
Config;
init_per_group(Name, Config) ->
cowboy_test:init_common_groups(Name, Config, ?MODULE).

end_per_group(set_env, _) ->
ok;
end_per_group(routes, _) ->
ok;
end_per_group(Name, _) ->
cowboy:stop_listener(Name).

Expand Down Expand Up @@ -119,3 +124,65 @@ set_env_missing(Config0) ->
after
cowboy:stop_listener(?FUNCTION_NAME)
end.

add_route(Config0) ->
doc("Live add a route."),
Dispatch =
cowboy_router:compile([{'_', [
{"/", hello_h, []}
]}]),
Config = cowboy_test:init_http(?FUNCTION_NAME, #{
env => #{
dispatch => Dispatch
}
}, Config0),
try
ConnPid1 = gun_open(Config),
Ref1 = gun:get(ConnPid1, "/"),
{response, _, 200, _} = gun:await(ConnPid1, Ref1),
ConnPid2 = gun_open(Config),
Ref2 = gun:get(ConnPid2, "/new"),
{response, _, 404, _} = gun:await(ConnPid2, Ref2),
cowboy:add_routes(?FUNCTION_NAME, cowboy_router:compile([{'_', [
{"/new", hello_h, []}
]}])),
%% Only new connections get the updated environment.
ConnPid3 = gun_open(Config),
Ref3 = gun:get(ConnPid3, "/new"),
{response, _, 200, _} = gun:await(ConnPid3, Ref3)
after
cowboy:stop_listener(?FUNCTION_NAME)
end.

remove_route(Config0) ->
doc("Live add a route."),
Dispatch =
cowboy_router:compile([{'_', [
{"/", hello_h, []},
{"/new", hello_h, []}
]}]),
Config = cowboy_test:init_http(?FUNCTION_NAME, #{
env => #{
dispatch => Dispatch
}
}, Config0),
try
ConnPid1 = gun_open(Config),
Ref1 = gun:get(ConnPid1, "/"),
{response, _, 200, _} = gun:await(ConnPid1, Ref1),
ConnPid2 = gun_open(Config),
Ref2 = gun:get(ConnPid2, "/new"),
{response, _, 200, _} = gun:await(ConnPid2, Ref2),
cowboy:remove_routes(?FUNCTION_NAME, cowboy_router:compile([{'_', [
{"/new", hello_h, []}
]}])),
%% Only new connections get the updated environment.
ConnPid3 = gun_open(Config),
Ref3 = gun:get(ConnPid3, "/"),
{response, _, 200, _} = gun:await(ConnPid3, Ref3),
ConnPid4 = gun_open(Config),
Ref4 = gun:get(ConnPid4, "/new"),
{response, _, 404, _} = gun:await(ConnPid4, Ref4)
after
cowboy:stop_listener(?FUNCTION_NAME)
end.

0 comments on commit 3218156

Please sign in to comment.