Browse Source

Router: properly handle path segments

The path segments . and .. are now removed according to the
rules found in RFC3986.

The path segments are now percent-decoded using the correct
algorithm (the one in RFC3986 and not the "query string" one).
Loïc Hoguin 9 years ago
parent
commit
c9f5603650
1 changed files with 23 additions and 2 deletions
  1. 23 2
      src/cowboy_router.erl

+ 23 - 2
src/cowboy_router.erl

@@ -322,9 +322,9 @@ split_path(Path, Acc) ->
 	try
 	try
 		case binary:match(Path, <<"/">>) of
 		case binary:match(Path, <<"/">>) of
 			nomatch when Path =:= <<>> ->
 			nomatch when Path =:= <<>> ->
-				lists:reverse([cow_qs:urldecode(S) || S <- Acc]);
+				remove_dot_segments(lists:reverse([cow_uri:urldecode(S) || S <- Acc]), []);
 			nomatch ->
 			nomatch ->
-				lists:reverse([cow_qs:urldecode(S) || S <- [Path|Acc]]);
+				remove_dot_segments(lists:reverse([cow_uri:urldecode(S) || S <- [Path|Acc]]), []);
 			{Pos, _} ->
 			{Pos, _} ->
 				<< Segment:Pos/binary, _:8, Rest/bits >> = Path,
 				<< Segment:Pos/binary, _:8, Rest/bits >> = Path,
 				split_path(Rest, [Segment|Acc])
 				split_path(Rest, [Segment|Acc])
@@ -334,6 +334,27 @@ split_path(Path, Acc) ->
 			badrequest
 			badrequest
 	end.
 	end.
 
 
+remove_dot_segments([], Acc) ->
+	lists:reverse(Acc);
+remove_dot_segments([<<".">>|Segments], Acc) ->
+	remove_dot_segments(Segments, Acc);
+remove_dot_segments([<<"..">>|Segments], Acc=[]) ->
+	remove_dot_segments(Segments, Acc);
+remove_dot_segments([<<"..">>|Segments], [_|Acc]) ->
+	remove_dot_segments(Segments, Acc);
+remove_dot_segments([S|Segments], Acc) ->
+	remove_dot_segments(Segments, [S|Acc]).
+
+-ifdef(TEST).
+remove_dot_segments_test_() ->
+	Tests = [
+		{[<<"a">>, <<"b">>, <<"c">>, <<".">>, <<"..">>, <<"..">>, <<"g">>], [<<"a">>, <<"g">>]},
+		{[<<"mid">>, <<"content=5">>, <<"..">>, <<"6">>], [<<"mid">>, <<"6">>]},
+		{[<<"..">>, <<"a">>], [<<"a">>]}
+	],
+	[fun() -> R = remove_dot_segments(S, []) end || {S, R} <- Tests].
+-endif.
+
 -spec list_match(tokens(), dispatch_match(), bindings())
 -spec list_match(tokens(), dispatch_match(), bindings())
 	-> {true, bindings(), undefined | tokens()} | false.
 	-> {true, bindings(), undefined | tokens()} | false.
 %% Atom '...' matches any trailing path, stop right now.
 %% Atom '...' matches any trailing path, stop right now.