diff options
author | Anthony Ramine <[email protected]> | 2011-05-09 14:31:06 +0200 |
---|---|---|
committer | Anthony Ramine <[email protected]> | 2011-05-09 15:14:38 +0200 |
commit | 0ca8f1364b857632030dbc3735ce789686361073 (patch) | |
tree | ba2af89911ea808263913e27ebf227f18a2ad29c | |
parent | 094f5c1735f92bb13b3b02440f24781d37a393cb (diff) | |
download | cowboy-0ca8f1364b857632030dbc3735ce789686361073.tar.gz cowboy-0ca8f1364b857632030dbc3735ce789686361073.tar.bz2 cowboy-0ca8f1364b857632030dbc3735ce789686361073.zip |
Implement path_info feature
The dispatcher now accepts '...' as the leading segment of Host and the
trailing segment of Path, this special atom matches any remaining path tail.
When given "cowboy.bugs.dev-extend.eu", host rule ['...', <<"dev-extend">>,
<<"eu">>] matches and fills host_info with [<<"cowboy">>, <<"bugs">>].
When given "/a/b/c/d", path rule [<<"a">>, <<"b">>, '...'] matches and fills
path_info with [<<"c">>, <<"d">>].
-rw-r--r-- | include/http.hrl | 3 | ||||
-rw-r--r-- | src/cowboy_dispatcher.erl | 84 | ||||
-rw-r--r-- | src/cowboy_http_protocol.erl | 6 | ||||
-rw-r--r-- | src/cowboy_http_req.erl | 16 |
4 files changed, 82 insertions, 27 deletions
diff --git a/include/http.hrl b/include/http.hrl index 20c63cb..7af6a7e 100644 --- a/include/http.hrl +++ b/include/http.hrl @@ -1,4 +1,5 @@ %% Copyright (c) 2011, Loïc Hoguin <[email protected]> +%% Copyright (c) 2011, Anthony Ramine <[email protected]> %% %% Permission to use, copy, modify, and/or distribute this software for any %% purpose with or without fee is hereby granted, provided that the above @@ -49,9 +50,11 @@ version = {1, 1} :: http_version(), peer = undefined :: undefined | {Address::ip_address(), Port::ip_port()}, host = undefined :: undefined | cowboy_dispatcher:path_tokens(), + host_info = undefined :: undefined | cowboy_dispatcher:path_tokens(), raw_host = undefined :: undefined | binary(), port = undefined :: undefined | ip_port(), path = undefined :: undefined | '*' | cowboy_dispatcher:path_tokens(), + path_info = undefined :: undefined | cowboy_dispatcher:path_tokens(), raw_path = undefined :: undefined | binary(), qs_vals = undefined :: undefined | list({Name::binary(), Value::binary() | true}), diff --git a/src/cowboy_dispatcher.erl b/src/cowboy_dispatcher.erl index c006185..67a7bf6 100644 --- a/src/cowboy_dispatcher.erl +++ b/src/cowboy_dispatcher.erl @@ -62,51 +62,59 @@ do_split_path(RawPath, Separator) -> -spec match(Host::path_tokens(), Path::path_tokens(), Dispatch::dispatch_rules()) - -> {ok, Handler::module(), Opts::term(), Binds::bindings()} + -> {ok, Handler::module(), Opts::term(), Binds::bindings(), + HostInfo::undefined | path_tokens(), + PathInfo::undefined | path_tokens()} | {error, notfound, host} | {error, notfound, path}. match(_Host, _Path, []) -> {error, notfound, host}; match(_Host, Path, [{'_', PathMatchs}|_Tail]) -> - match_path(Path, PathMatchs, []); + match_path(Path, PathMatchs, [], undefined); match(Host, Path, [{HostMatch, PathMatchs}|Tail]) -> case try_match(host, Host, HostMatch) of false -> match(Host, Path, Tail); - {true, HostBinds} -> - match_path(Path, PathMatchs, HostBinds) + {true, HostBinds, undefined} -> + match_path(Path, PathMatchs, HostBinds, undefined); + {true, HostBinds, HostInfo} -> + match_path(Path, PathMatchs, HostBinds, lists:reverse(HostInfo)) end. -spec match_path(Path::path_tokens(), list({Path::match_rule(), - Handler::module(), Opts::term()}), HostBinds::bindings()) - -> {ok, Handler::module(), Opts::term(), Binds::bindings()} + Handler::module(), Opts::term()}), HostBinds::bindings(), + HostInfo::undefined | path_tokens()) + -> {ok, Handler::module(), Opts::term(), Binds::bindings(), + HostInfo::undefined | path_tokens(), + PathInfo::undefined | path_tokens()} | {error, notfound, path}. -match_path(_Path, [], _HostBinds) -> +match_path(_Path, [], _HostBinds, _HostInfo) -> {error, notfound, path}; -match_path(_Path, [{'_', Handler, Opts}|_Tail], HostBinds) -> - {ok, Handler, Opts, HostBinds}; -match_path('*', [{'*', Handler, Opts}|_Tail], HostBinds) -> - {ok, Handler, Opts, HostBinds}; -match_path(Path, [{PathMatch, Handler, Opts}|Tail], HostBinds) -> +match_path(_Path, [{'_', Handler, Opts}|_Tail], HostBinds, HostInfo) -> + {ok, Handler, Opts, HostBinds, HostInfo, undefined}; +match_path('*', [{'*', Handler, Opts}|_Tail], HostBinds, HostInfo) -> + {ok, Handler, Opts, HostBinds, HostInfo, undefined}; +match_path(Path, [{PathMatch, Handler, Opts}|Tail], HostBinds, HostInfo) -> case try_match(path, Path, PathMatch) of false -> - match_path(Path, Tail, HostBinds); - {true, PathBinds} -> - {ok, Handler, Opts, HostBinds ++ PathBinds} + match_path(Path, Tail, HostBinds, HostInfo); + {true, PathBinds, PathInfo} -> + {ok, Handler, Opts, HostBinds ++ PathBinds, HostInfo, PathInfo} end. %% Internal. -spec try_match(Type::host | path, List::path_tokens(), Match::match_rule()) - -> {true, Binds::bindings()} | false. -try_match(_Type, List, Match) when length(List) =/= length(Match) -> - false; + -> {true, Binds::bindings(), ListInfo::undefined | path_tokens()} | false. try_match(host, List, Match) -> list_match(lists:reverse(List), lists:reverse(Match), []); try_match(path, List, Match) -> list_match(List, Match, []). -spec list_match(List::path_tokens(), Match::match_rule(), Binds::bindings()) - -> {true, Binds::bindings()} | false. + -> {true, Binds::bindings(), ListInfo::undefined | path_tokens()} | false. +%% Atom '...' matches any trailing path, stop right now. +list_match(List, ['...'], Binds) -> + {true, Binds, List}; %% Atom '_' matches anything, continue. list_match([_E|Tail], ['_'|TailMatch], Binds) -> list_match(Tail, TailMatch, Binds); @@ -116,12 +124,12 @@ list_match([E|Tail], [E|TailMatch], Binds) -> %% Bind E to the variable name V and continue. list_match([E|Tail], [V|TailMatch], Binds) when is_atom(V) -> list_match(Tail, TailMatch, [{V, E}|Binds]); -%% Values don't match, stop. -list_match([_E|_Tail], [_F|_TailMatch], _Binds) -> - false; %% Match complete. list_match([], [], Binds) -> - {true, Binds}. + {true, Binds, undefined}; +%% Values don't match, stop. +list_match(_List, _Match, _Binds) -> + false. %% Tests. @@ -229,6 +237,36 @@ match_test_() -> [{var, <<"fr">>}, {var, <<"987">>}]}} ], [{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() -> + {ok, Handler, Opts, Binds, undefined, undefined} = match(H, P, Dispatch) + end} || {H, P, {ok, Handler, Opts, Binds}} <- Tests]. + +match_info_test_() -> + Dispatch = [ + {[<<"www">>, <<"dev-extend">>, <<"eu">>], [ + {[<<"pathinfo">>, <<"is">>, <<"next">>, '...'], match_path, []} + ]}, + {['...', <<"dev-extend">>, <<"eu">>], [ + {'_', match_any, []} + ]} + ], + Tests = [ + {[<<"dev-extend">>, <<"eu">>], [], + {ok, match_any, [], [], [], undefined}}, + {[<<"bugs">>, <<"dev-extend">>, <<"eu">>], [], + {ok, match_any, [], [], [<<"bugs">>], undefined}}, + {[<<"cowboy">>, <<"bugs">>, <<"dev-extend">>, <<"eu">>], [], + {ok, match_any, [], [], [<<"cowboy">>, <<"bugs">>], undefined}}, + {[<<"www">>, <<"dev-extend">>, <<"eu">>], + [<<"pathinfo">>, <<"is">>, <<"next">>], + {ok, match_path, [], [], undefined, []}}, + {[<<"www">>, <<"dev-extend">>, <<"eu">>], + [<<"pathinfo">>, <<"is">>, <<"next">>, <<"path_info">>], + {ok, match_path, [], [], undefined, [<<"path_info">>]}}, + {[<<"www">>, <<"dev-extend">>, <<"eu">>], + [<<"pathinfo">>, <<"is">>, <<"next">>, <<"foo">>, <<"bar">>], + {ok, match_path, [], [], undefined, [<<"foo">>, <<"bar">>]}} + ], + [{lists:flatten(io_lib:format("~p, ~p", [H, P])), fun() -> R = match(H, P, Dispatch) end} || {H, P, R} <- Tests]. diff --git a/src/cowboy_http_protocol.erl b/src/cowboy_http_protocol.erl index 302df26..ed4d963 100644 --- a/src/cowboy_http_protocol.erl +++ b/src/cowboy_http_protocol.erl @@ -1,4 +1,5 @@ %% Copyright (c) 2011, Loïc Hoguin <[email protected]> +%% Copyright (c) 2011, Anthony Ramine <[email protected]> %% %% Permission to use, copy, modify, and/or distribute this software for any %% purpose with or without fee is hereby granted, provided that the above @@ -158,8 +159,9 @@ dispatch(Req=#http_req{host=Host, path=Path}, %% @todo We probably want to filter the Host and Path here to allow %% things like url rewriting. case cowboy_dispatcher:match(Host, Path, Dispatch) of - {ok, Handler, Opts, Binds} -> - parse_header(Req#http_req{bindings=Binds}, + {ok, Handler, Opts, Binds, HostInfo, PathInfo} -> + parse_header(Req#http_req{host_info=HostInfo, path_info=PathInfo, + bindings=Binds}, State#state{handler={Handler, Opts}}); {error, notfound, host} -> error_terminate(400, State); diff --git a/src/cowboy_http_req.erl b/src/cowboy_http_req.erl index 60f0b55..fad5aaf 100644 --- a/src/cowboy_http_req.erl +++ b/src/cowboy_http_req.erl @@ -17,8 +17,8 @@ -export([ method/1, version/1, peer/1, - host/1, raw_host/1, port/1, - path/1, raw_path/1, + host/1, host_info/1, raw_host/1, port/1, + path/1, path_info/1, raw_path/1, qs_val/2, qs_val/3, qs_vals/1, raw_qs/1, binding/2, binding/3, bindings/1, header/2, header/3, headers/1 @@ -59,6 +59,12 @@ peer(Req) -> host(Req) -> {Req#http_req.host, Req}. +-spec host_info(Req::#http_req{}) + -> {HostInfo::cowboy_dispatcher:path_tokens() | undefined, + Req::#http_req{}}. +host_info(Req) -> + {Req#http_req.host_info, Req}. + -spec raw_host(Req::#http_req{}) -> {RawHost::binary(), Req::#http_req{}}. raw_host(Req) -> {Req#http_req.raw_host, Req}. @@ -72,6 +78,12 @@ port(Req) -> path(Req) -> {Req#http_req.path, Req}. +-spec path_info(Req::#http_req{}) + -> {PathInfo::cowboy_dispatcher:path_tokens() | undefined, + Req::#http_req{}}. +path_info(Req) -> + {Req#http_req.path_info, Req}. + -spec raw_path(Req::#http_req{}) -> {RawPath::binary(), Req::#http_req{}}. raw_path(Req) -> {Req#http_req.raw_path, Req}. |