Browse Source

Allow colon within path segments

Allow `cowboy_router:compile` to handle colon characters within
path segments, rather than exiting with `badarg`.  This is allowed
via RFC 7230 2.7 -> [RFC 3986 3.3](https://tools.ietf.org/html/rfc3986#section-3.3):

```
      segment       = *pchar
      segment-nz    = 1*pchar
      segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
                    ; non-zero-length segment without any colon ":"

      pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
```
Paul Fisher 9 years ago
parent
commit
82cd22a88b
2 changed files with 16 additions and 5 deletions
  1. 1 2
      src/cowboy_router.erl
  2. 15 3
      test/http_SUITE.erl

+ 1 - 2
src/cowboy_router.erl

@@ -101,12 +101,11 @@ compile_rules(<< S, Rest/bits >>, S, Segments, Rules, <<>>) ->
 	compile_rules(Rest, S, Segments, Rules, <<>>);
 compile_rules(<< S, Rest/bits >>, S, Segments, Rules, Acc) ->
 	compile_rules(Rest, S, [Acc|Segments], Rules, <<>>);
+%% Colon on path segment start is special, otherwise allow.
 compile_rules(<< $:, Rest/bits >>, S, Segments, Rules, <<>>) ->
 	{NameBin, Rest2} = compile_binding(Rest, S, <<>>),
 	Name = binary_to_atom(NameBin, utf8),
 	compile_rules(Rest2, S, Segments, Rules, Name);
-compile_rules(<< $:, _/bits >>, _, _, _, _) ->
-	error(badarg);
 compile_rules(<< $[, $., $., $., $], Rest/bits >>, S, Segments, Rules, Acc)
 		when Acc =:= <<>> ->
 	compile_rules(Rest, S, ['...'|Segments], Rules, Acc);

+ 15 - 3
test/http_SUITE.erl

@@ -35,12 +35,13 @@ all() ->
 		{group, http_compress},
 		{group, https_compress},
 		{group, parse_host},
-		{group, set_env}
+		{group, set_env},
+		{group, router_compile}
 	].
 
 groups() ->
 	Tests = ct_helper:all(?MODULE) -- [
-		parse_host, set_env_dispatch
+		parse_host, set_env_dispatch, path_allow_colon
 	],
 	[
 		{http, [], Tests}, %% @todo parallel
@@ -52,6 +53,9 @@ groups() ->
 		]},
 		{set_env, [], [
 			set_env_dispatch
+		]},
+		{router_compile, [], [
+			path_allow_colon
 		]}
 	].
 
@@ -85,8 +89,12 @@ init_per_group(set_env, Config) ->
 		env => #{dispatch => []}
 	}),
 	Port = ranch:get_port(set_env),
-	[{type, tcp}, {protocol, http}, {port, Port}, {opts, []}|Config].
+	[{type, tcp}, {protocol, http}, {port, Port}, {opts, []}|Config];
+init_per_group(router_compile, Config) ->
+	Config.
 
+end_per_group(router_compile, _) ->
+	ok;
 end_per_group(Name, _) ->
 	ok = cowboy:stop_listener(Name).
 
@@ -588,6 +596,10 @@ set_env_dispatch(Config) ->
 	{response, nofin, 200, _} = gun:await(ConnPid2, Ref2),
 	ok.
 
+path_allow_colon(_Config) ->
+	cowboy_router:compile([{'_', [{"/foo/bar:blah", http_handler, []}]}]),
+	ok.
+
 set_resp_body(Config) ->
 	ConnPid = gun_open(Config),
 	Ref = gun:get(ConnPid, "/set_resp/body"),