mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Rewrite the dispatcher to take a list of host each having a list of paths.
* Makes more sense to parse the host only once instead of for each path. * Allows proper handling of: If the host is not a valid host on the server, the response MUST be a 400 (Bad Request) error.
This commit is contained in:
parent
ebe638165e
commit
2c52a30b0a
4 changed files with 54 additions and 28 deletions
|
@ -49,8 +49,8 @@ Code speaks more than words:
|
||||||
|
|
||||||
application:start(cowboy),
|
application:start(cowboy),
|
||||||
Dispatch = [
|
Dispatch = [
|
||||||
%% Host, Path, Handler, Opts
|
%% {Host, list({Path, Handler, Opts})}
|
||||||
{'_', '_', my_handler, []}
|
{'_', [{'_', my_handler, []}]}
|
||||||
],
|
],
|
||||||
%% NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
|
%% NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
|
||||||
cowboy_listener_sup:start_link(100,
|
cowboy_listener_sup:start_link(100,
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
-type path_tokens() :: list(string()).
|
-type path_tokens() :: list(string()).
|
||||||
-type match() :: '_' | list(string() | '_' | atom()).
|
-type match() :: '_' | list(string() | '_' | atom()).
|
||||||
|
|
||||||
-type dispatch_rules() :: {Host::match(), Path::match(),
|
-type dispatch_rules() :: {Host::match(), list({Path::match(),
|
||||||
Handler::module(), Opts::term()}.
|
Handler::module(), Opts::term()})}.
|
||||||
-type dispatch() :: list(dispatch_rules()).
|
-type dispatch() :: list(dispatch_rules()).
|
||||||
|
|
||||||
-type http_method() :: 'OPTIONS' | 'GET' | 'HEAD'
|
-type http_method() :: 'OPTIONS' | 'GET' | 'HEAD'
|
||||||
|
|
|
@ -35,18 +35,34 @@ split_path(Path) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec match(Host::path_tokens(), Path::path_tokens(), Dispatch::dispatch())
|
-spec match(Host::path_tokens(), Path::path_tokens(), Dispatch::dispatch())
|
||||||
-> {ok, Handler::module(), Opts::term(), Binds::bindings()} | {error, notfound}.
|
-> {ok, Handler::module(), Opts::term(), Binds::bindings()}
|
||||||
|
| {error, notfound, host} | {error, notfound, path}.
|
||||||
match(_Host, _Path, []) ->
|
match(_Host, _Path, []) ->
|
||||||
{error, notfound};
|
{error, notfound, host};
|
||||||
match(_Host, _Path, [{'_', '_', Handler, Opts}|_Tail]) ->
|
match(_Host, Path, [{'_', PathMatchs}|_Tail]) ->
|
||||||
{ok, Handler, Opts, []};
|
match_path(Path, PathMatchs, []);
|
||||||
match(Host, Path, [{HostMatch, PathMatch, Handler, Opts}|Tail]) ->
|
match(Host, Path, [{HostMatch, PathMatchs}|Tail]) ->
|
||||||
case try_match(host, Host, HostMatch) of
|
case try_match(host, Host, HostMatch) of
|
||||||
false -> match(Host, Path, Tail);
|
false ->
|
||||||
{true, HostBinds} -> case try_match(path, Path, PathMatch) of
|
match(Host, Path, Tail);
|
||||||
false -> match(Host, Path, Tail);
|
{true, HostBinds} ->
|
||||||
{true, PathBinds} -> {ok, Handler, Opts, HostBinds ++ PathBinds}
|
match_path(Path, PathMatchs, HostBinds)
|
||||||
end
|
end.
|
||||||
|
|
||||||
|
-spec match_path(Path::path_tokens(), list({Path::match(),
|
||||||
|
Handler::module(), Opts::term()}), HostBinds::bindings())
|
||||||
|
-> {ok, Handler::module(), Opts::term(), Binds::bindings()}
|
||||||
|
| {error, notfound, path}.
|
||||||
|
match_path(_Path, [], _HostBinds) ->
|
||||||
|
{error, notfound, path};
|
||||||
|
match_path(_Path, [{'_', Handler, Opts}|_Tail], HostBinds) ->
|
||||||
|
{ok, Handler, Opts, HostBinds};
|
||||||
|
match_path(Path, [{PathMatch, Handler, Opts}|Tail], HostBinds) ->
|
||||||
|
case try_match(path, Path, PathMatch) of
|
||||||
|
false ->
|
||||||
|
match_path(Path, Tail, HostBinds);
|
||||||
|
{true, PathBinds} ->
|
||||||
|
{ok, Handler, Opts, HostBinds ++ PathBinds}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Internal.
|
%% Internal.
|
||||||
|
@ -116,16 +132,24 @@ split_path_test_() ->
|
||||||
|
|
||||||
match_test_() ->
|
match_test_() ->
|
||||||
Dispatch = [
|
Dispatch = [
|
||||||
{["www", '_', "dev-extend", "eu"], ["users", '_', "mails"],
|
{["www", '_', "dev-extend", "eu"], [
|
||||||
match_any_subdomain_users, []},
|
{["users", '_', "mails"], match_any_subdomain_users, []}
|
||||||
{["dev-extend", "eu"], ["users", id, "friends"],
|
]},
|
||||||
match_extend_users_friends, []},
|
{["dev-extend", "eu"], [
|
||||||
{["dev-extend", var], ["threads", var],
|
{["users", id, "friends"], match_extend_users_friends, []},
|
||||||
match_duplicate_vars, [we, {expect, two}, var, here]},
|
{'_', match_extend, []}
|
||||||
{["dev-extend", "eu"], '_', match_extend, []},
|
]},
|
||||||
{["erlang", ext], '_', match_erlang_ext, []},
|
{["dev-extend", var], [
|
||||||
{'_', ["users", id, "friends"], match_users_friends, []},
|
{["threads", var], match_duplicate_vars,
|
||||||
{'_', '_', match_any, []}
|
[we, {expect, two}, var, here]}
|
||||||
|
]},
|
||||||
|
{["erlang", ext], [
|
||||||
|
{'_', match_erlang_ext, []}
|
||||||
|
]},
|
||||||
|
{'_', [
|
||||||
|
{["users", id, "friends"], match_users_friends, []},
|
||||||
|
{'_', match_any, []}
|
||||||
|
]}
|
||||||
],
|
],
|
||||||
%% {Host, Path, Result}
|
%% {Host, Path, Result}
|
||||||
Tests = [
|
Tests = [
|
||||||
|
@ -135,16 +159,16 @@ match_test_() ->
|
||||||
{["www", "dev-extend", "eu"], ["users", "42", "mails"],
|
{["www", "dev-extend", "eu"], ["users", "42", "mails"],
|
||||||
{ok, match_any, [], []}},
|
{ok, match_any, [], []}},
|
||||||
{["www", "any", "dev-extend", "eu"], ["not_users", "42", "mails"],
|
{["www", "any", "dev-extend", "eu"], ["not_users", "42", "mails"],
|
||||||
{ok, match_any, [], []}},
|
{error, notfound, path}},
|
||||||
{["dev-extend", "eu"], [], {ok, match_extend, [], []}},
|
{["dev-extend", "eu"], [], {ok, match_extend, [], []}},
|
||||||
{["dev-extend", "eu"], ["users", "42", "friends"],
|
{["dev-extend", "eu"], ["users", "42", "friends"],
|
||||||
{ok, match_extend_users_friends, [], [{id, "42"}]}},
|
{ok, match_extend_users_friends, [], [{id, "42"}]}},
|
||||||
{["erlang", "fr"], '_', {ok, match_erlang_ext, [], [{ext, "fr"}]}},
|
{["erlang", "fr"], '_', {ok, match_erlang_ext, [], [{ext, "fr"}]}},
|
||||||
{["any"], ["users", "444", "friends"],
|
{["any"], ["users", "444", "friends"],
|
||||||
{ok, match_users_friends, [], [{id, "444"}]}},
|
{ok, match_users_friends, [], [{id, "444"}]}},
|
||||||
{["dev-extend", "eu"], ["threads", "987"],
|
{["dev-extend", "fr"], ["threads", "987"],
|
||||||
{ok, match_duplicate_vars, [we, {expect, two}, var, here],
|
{ok, match_duplicate_vars, [we, {expect, two}, var, here],
|
||||||
[{var, "eu"}, {var, "987"}]}}
|
[{var, "fr"}, {var, "987"}]}}
|
||||||
],
|
],
|
||||||
[{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() ->
|
[{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() ->
|
||||||
R = match(H, P, Dispatch)
|
R = match(H, P, Dispatch)
|
||||||
|
|
|
@ -99,7 +99,9 @@ header({http_header, _I, 'Host', _R, Value}, Req=#http_req{path=Path,
|
||||||
wait_header(Req#http_req{host=Host, bindings=Binds,
|
wait_header(Req#http_req{host=Host, bindings=Binds,
|
||||||
headers=[{'Host', Value2}|Req#http_req.headers]},
|
headers=[{'Host', Value2}|Req#http_req.headers]},
|
||||||
State#state{handler={Handler, Opts}});
|
State#state{handler={Handler, Opts}});
|
||||||
{error, notfound} ->
|
{error, notfound, host} ->
|
||||||
|
error_terminate(400, State);
|
||||||
|
{error, notfound, path} ->
|
||||||
error_terminate(404, State)
|
error_terminate(404, State)
|
||||||
end;
|
end;
|
||||||
%% Ignore Host headers if we already have it.
|
%% Ignore Host headers if we already have it.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue