mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 12:20:24 +00:00
Feature: add functions to add + remove dispatch routes
This commit is contained in:
parent
30ee75cea1
commit
f68ded10b8
3 changed files with 125 additions and 3 deletions
2
Makefile
2
Makefile
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
PROJECT = cowboy
|
PROJECT = cowboy
|
||||||
PROJECT_DESCRIPTION = Small, fast, modern HTTP server.
|
PROJECT_DESCRIPTION = Small, fast, modern HTTP server.
|
||||||
PROJECT_VERSION = 2.9.0
|
PROJECT_VERSION = 2.9.1
|
||||||
PROJECT_REGISTERED = cowboy_clock
|
PROJECT_REGISTERED = cowboy_clock
|
||||||
|
|
||||||
# Options.
|
# Options.
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
-export([stop_listener/1]).
|
-export([stop_listener/1]).
|
||||||
-export([set_env/3]).
|
-export([set_env/3]).
|
||||||
|
|
||||||
|
-export([add_routes/2]).
|
||||||
|
-export([remove_routes/2]).
|
||||||
|
|
||||||
%% Internal.
|
%% Internal.
|
||||||
-export([log/2]).
|
-export([log/2]).
|
||||||
-export([log/4]).
|
-export([log/4]).
|
||||||
|
@ -70,6 +73,18 @@ ensure_connection_type(TransOpts) ->
|
||||||
stop_listener(Ref) ->
|
stop_listener(Ref) ->
|
||||||
ranch: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.
|
-spec set_env(ranch:ref(), atom(), any()) -> ok.
|
||||||
set_env(Ref, Name, Value) ->
|
set_env(Ref, Name, Value) ->
|
||||||
Opts = ranch:get_protocol_options(Ref),
|
Opts = ranch:get_protocol_options(Ref),
|
||||||
|
@ -103,3 +118,43 @@ log(Level, Format, Args, _) ->
|
||||||
debug -> info_msg
|
debug -> info_msg
|
||||||
end,
|
end,
|
||||||
error_logger:Function(Format, Args).
|
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.
|
|
@ -21,14 +21,15 @@
|
||||||
-import(cowboy_test, [gun_open/1]).
|
-import(cowboy_test, [gun_open/1]).
|
||||||
|
|
||||||
all() ->
|
all() ->
|
||||||
[{group, app}, {group, set_env}|cowboy_test:common_all()].
|
[{group, app}, {group, set_env}, {group, routes}|cowboy_test:common_all()].
|
||||||
|
|
||||||
groups() ->
|
groups() ->
|
||||||
Common = ct_helper:all(?MODULE)
|
Common = ct_helper:all(?MODULE)
|
||||||
-- [restart_gracefully, set_env, set_env_missing],
|
-- [restart_gracefully, set_env, set_env_missing],
|
||||||
[
|
[
|
||||||
{app, [], [restart_gracefully]},
|
{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)].
|
|cowboy_test:common_groups(Common)].
|
||||||
|
|
||||||
init_per_group(Name=app, Config) ->
|
init_per_group(Name=app, Config) ->
|
||||||
|
@ -37,11 +38,15 @@ init_per_group(Name=app, Config) ->
|
||||||
}, Config);
|
}, Config);
|
||||||
init_per_group(set_env, Config) ->
|
init_per_group(set_env, Config) ->
|
||||||
Config;
|
Config;
|
||||||
|
init_per_group(routes, Config) ->
|
||||||
|
Config;
|
||||||
init_per_group(Name, Config) ->
|
init_per_group(Name, Config) ->
|
||||||
cowboy_test:init_common_groups(Name, Config, ?MODULE).
|
cowboy_test:init_common_groups(Name, Config, ?MODULE).
|
||||||
|
|
||||||
end_per_group(set_env, _) ->
|
end_per_group(set_env, _) ->
|
||||||
ok;
|
ok;
|
||||||
|
end_per_group(routes, _) ->
|
||||||
|
ok;
|
||||||
end_per_group(Name, _) ->
|
end_per_group(Name, _) ->
|
||||||
cowboy:stop_listener(Name).
|
cowboy:stop_listener(Name).
|
||||||
|
|
||||||
|
@ -119,3 +124,65 @@ set_env_missing(Config0) ->
|
||||||
after
|
after
|
||||||
cowboy:stop_listener(?FUNCTION_NAME)
|
cowboy:stop_listener(?FUNCTION_NAME)
|
||||||
end.
|
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 remove 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.
|
Loading…
Add table
Add a link
Reference in a new issue