aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2011-03-19 17:42:03 +0100
committerLoïc Hoguin <[email protected]>2011-03-19 17:42:03 +0100
commit2c52a30b0a209cfb0814a42b814271b168ddb2e2 (patch)
tree5991a067ba529f2c7a5dbc70b6334dbecff7a18e
parentebe638165e5a5285ae595c9e0ae07b3dd919881b (diff)
downloadcowboy-2c52a30b0a209cfb0814a42b814271b168ddb2e2.tar.gz
cowboy-2c52a30b0a209cfb0814a42b814271b168ddb2e2.tar.bz2
cowboy-2c52a30b0a209cfb0814a42b814271b168ddb2e2.zip
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.
-rw-r--r--README.md4
-rw-r--r--include/types.hrl4
-rw-r--r--src/cowboy_dispatcher.erl70
-rw-r--r--src/cowboy_http_protocol.erl4
4 files changed, 54 insertions, 28 deletions
diff --git a/README.md b/README.md
index 2390abb..b08e36c 100644
--- a/README.md
+++ b/README.md
@@ -49,8 +49,8 @@ Code speaks more than words:
application:start(cowboy),
Dispatch = [
- %% Host, Path, Handler, Opts
- {'_', '_', my_handler, []}
+ %% {Host, list({Path, Handler, Opts})}
+ {'_', [{'_', my_handler, []}]}
],
%% NbAcceptors, Transport, TransOpts, Protocol, ProtoOpts
cowboy_listener_sup:start_link(100,
diff --git a/include/types.hrl b/include/types.hrl
index faee24e..17882cc 100644
--- a/include/types.hrl
+++ b/include/types.hrl
@@ -28,8 +28,8 @@
-type path_tokens() :: list(string()).
-type match() :: '_' | list(string() | '_' | atom()).
--type dispatch_rules() :: {Host::match(), Path::match(),
- Handler::module(), Opts::term()}.
+-type dispatch_rules() :: {Host::match(), list({Path::match(),
+ Handler::module(), Opts::term()})}.
-type dispatch() :: list(dispatch_rules()).
-type http_method() :: 'OPTIONS' | 'GET' | 'HEAD'
diff --git a/src/cowboy_dispatcher.erl b/src/cowboy_dispatcher.erl
index 3e9a7e0..755a398 100644
--- a/src/cowboy_dispatcher.erl
+++ b/src/cowboy_dispatcher.erl
@@ -35,18 +35,34 @@ split_path(Path) ->
end.
-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, []) ->
- {error, notfound};
-match(_Host, _Path, [{'_', '_', Handler, Opts}|_Tail]) ->
- {ok, Handler, Opts, []};
-match(Host, Path, [{HostMatch, PathMatch, Handler, Opts}|Tail]) ->
+ {error, notfound, host};
+match(_Host, Path, [{'_', PathMatchs}|_Tail]) ->
+ match_path(Path, PathMatchs, []);
+match(Host, Path, [{HostMatch, PathMatchs}|Tail]) ->
case try_match(host, Host, HostMatch) of
- false -> match(Host, Path, Tail);
- {true, HostBinds} -> case try_match(path, Path, PathMatch) of
- false -> match(Host, Path, Tail);
- {true, PathBinds} -> {ok, Handler, Opts, HostBinds ++ PathBinds}
- end
+ false ->
+ match(Host, Path, Tail);
+ {true, HostBinds} ->
+ match_path(Path, PathMatchs, HostBinds)
+ 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.
%% Internal.
@@ -116,16 +132,24 @@ split_path_test_() ->
match_test_() ->
Dispatch = [
- {["www", '_', "dev-extend", "eu"], ["users", '_', "mails"],
- match_any_subdomain_users, []},
- {["dev-extend", "eu"], ["users", id, "friends"],
- match_extend_users_friends, []},
- {["dev-extend", var], ["threads", var],
- match_duplicate_vars, [we, {expect, two}, var, here]},
- {["dev-extend", "eu"], '_', match_extend, []},
- {["erlang", ext], '_', match_erlang_ext, []},
- {'_', ["users", id, "friends"], match_users_friends, []},
- {'_', '_', match_any, []}
+ {["www", '_', "dev-extend", "eu"], [
+ {["users", '_', "mails"], match_any_subdomain_users, []}
+ ]},
+ {["dev-extend", "eu"], [
+ {["users", id, "friends"], match_extend_users_friends, []},
+ {'_', match_extend, []}
+ ]},
+ {["dev-extend", var], [
+ {["threads", var], match_duplicate_vars,
+ [we, {expect, two}, var, here]}
+ ]},
+ {["erlang", ext], [
+ {'_', match_erlang_ext, []}
+ ]},
+ {'_', [
+ {["users", id, "friends"], match_users_friends, []},
+ {'_', match_any, []}
+ ]}
],
%% {Host, Path, Result}
Tests = [
@@ -135,16 +159,16 @@ match_test_() ->
{["www", "dev-extend", "eu"], ["users", "42", "mails"],
{ok, match_any, [], []}},
{["www", "any", "dev-extend", "eu"], ["not_users", "42", "mails"],
- {ok, match_any, [], []}},
+ {error, notfound, path}},
{["dev-extend", "eu"], [], {ok, match_extend, [], []}},
{["dev-extend", "eu"], ["users", "42", "friends"],
{ok, match_extend_users_friends, [], [{id, "42"}]}},
{["erlang", "fr"], '_', {ok, match_erlang_ext, [], [{ext, "fr"}]}},
{["any"], ["users", "444", "friends"],
{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],
- [{var, "eu"}, {var, "987"}]}}
+ [{var, "fr"}, {var, "987"}]}}
],
[{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() ->
R = match(H, P, Dispatch)
diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl
index 0ea2cd9..82cf733 100644
--- a/src/cowboy_http_protocol.erl
+++ b/src/cowboy_http_protocol.erl
@@ -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,
headers=[{'Host', Value2}|Req#http_req.headers]},
State#state{handler={Handler, Opts}});
- {error, notfound} ->
+ {error, notfound, host} ->
+ error_terminate(400, State);
+ {error, notfound, path} ->
error_terminate(404, State)
end;
%% Ignore Host headers if we already have it.