From bc39b433bb20c44690b75bf28539983517002268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Wed, 6 Dec 2017 14:05:30 +0100 Subject: Properly handle OPTIONS * requests Support for these was broken during the development of Cowboy 2.0. It is now fixed and better handled than it ever was. --- src/cowboy_req.erl | 6 ++++-- src/cowboy_router.erl | 4 ++++ test/handlers/asterisk_h.erl | 2 ++ test/rfc7230_SUITE.erl | 21 ++++++--------------- test/rfc7231_SUITE.erl | 24 ++++++++++++++++++++++-- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/src/cowboy_req.erl b/src/cowboy_req.erl index e38b76c..552f409 100644 --- a/src/cowboy_req.erl +++ b/src/cowboy_req.erl @@ -228,8 +228,10 @@ uri(#{scheme := Scheme0, host := Host0, port := Port0, end, Host = maps:get(host, Opts, Host0), Port = maps:get(port, Opts, Port0), - Path = maps:get(path, Opts, Path0), - Qs = maps:get(qs, Opts, Qs0), + {Path, Qs} = case maps:get(path, Opts, Path0) of + <<"*">> -> {<<>>, <<>>}; + P -> {P, maps:get(qs, Opts, Qs0)} + end, Fragment = maps:get(fragment, Opts, undefined), [uri_host(Scheme, Scheme0, Port, Host), uri_path(Path), uri_qs(Qs), uri_fragment(Fragment)]. diff --git a/src/cowboy_router.erl b/src/cowboy_router.erl index 9d25442..fb5c383 100644 --- a/src/cowboy_router.erl +++ b/src/cowboy_router.erl @@ -82,6 +82,8 @@ compile_paths([{PathMatch, Fields, Handler, Opts}|Tail], Acc) Fields, Handler, Opts}|Tail], Acc); compile_paths([{'_', Fields, Handler, Opts}|Tail], Acc) -> compile_paths(Tail, [{'_', Fields, Handler, Opts}] ++ Acc); +compile_paths([{<<"*">>, Fields, Handler, Opts}|Tail], Acc) -> + compile_paths(Tail, [{<<"*">>, Fields, Handler, Opts}|Acc]); compile_paths([{<< $/, PathMatch/bits >>, Fields, Handler, Opts}|Tail], Acc) -> PathRules = compile_rules(PathMatch, $/, [], [], <<>>), @@ -252,6 +254,8 @@ match_path([{'_', [], Handler, Opts}|_Tail], HostInfo, _, Bindings) -> {ok, Handler, Opts, Bindings, HostInfo, undefined}; match_path([{<<"*">>, _, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) -> {ok, Handler, Opts, Bindings, HostInfo, undefined}; +match_path([_|Tail], HostInfo, <<"*">>, Bindings) -> + match_path(Tail, HostInfo, <<"*">>, Bindings); match_path([{PathMatch, Fields, Handler, Opts}|Tail], HostInfo, Tokens, Bindings) when is_list(Tokens) -> case list_match(Tokens, PathMatch, Bindings) of diff --git a/test/handlers/asterisk_h.erl b/test/handlers/asterisk_h.erl index 56b8bcb..563b488 100644 --- a/test/handlers/asterisk_h.erl +++ b/test/handlers/asterisk_h.erl @@ -7,6 +7,8 @@ init(Req, Opts) -> echo(cowboy_req:header(<<"x-echo">>, Req), Req, Opts). +echo(undefined, Req, Opts) -> + {ok, cowboy_req:reply(200, Req), Opts}; echo(What, Req, Opts) -> F = binary_to_atom(What, latin1), Value = case cowboy_req:F(Req) of diff --git a/test/rfc7230_SUITE.erl b/test/rfc7230_SUITE.erl index 9175fa9..d680d75 100644 --- a/test/rfc7230_SUITE.erl +++ b/test/rfc7230_SUITE.erl @@ -41,9 +41,8 @@ init_routes(_) -> [ {"/echo/:key[/:arg]", echo_h, []}, {"/length/echo/:key", echo_h, []}, {"/resp/:key[/:arg]", resp_h, []}, - {"/send_message", send_message_h, []} -%% @todo Something is clearly wrong about routing * right now. -%% {"*", asterisk_h, []} + {"/send_message", send_message_h, []}, + {"*", asterisk_h, []} ]}, {"127.0.0.1", [{"/echo/:key", echo_h, []}]}, {"example.org", [{"/echo/:key", echo_h, []}]} @@ -603,20 +602,12 @@ asterisk_form_reject_if_not_options(Config) -> "\r\n"), {error, closed} = raw_recv(Client, 0, 1000). -asterisk_form_empty_path(Config) -> - doc("The path is empty when using asterisk-form. (RFC7230 5.5)"), - #{code := 200, body := <<>>} = do_raw(Config, +asterisk_form_empty_path_query(Config) -> + doc("The path and query components are empty when using asterisk-form. (RFC7230 5.5)"), + #{code := 200, body := <<"http://localhost">>} = do_raw(Config, "OPTIONS * HTTP/1.1\r\n" "Host: localhost\r\n" - "X-Echo: path\r\n" - "\r\n"). - -asterisk_form_empty_query(Config) -> - doc("The query is empty when using asterisk-form. (RFC7230 5.5)"), - #{code := 200, body := <<>>} = do_raw(Config, - "OPTIONS * HTTP/1.1\r\n" - "Host: localhost\r\n" - "X-Echo: query\r\n" + "X-Echo: uri\r\n" "\r\n"). %% Invalid request-target. diff --git a/test/rfc7231_SUITE.erl b/test/rfc7231_SUITE.erl index feb53b9..e0940ab 100644 --- a/test/rfc7231_SUITE.erl +++ b/test/rfc7231_SUITE.erl @@ -34,6 +34,7 @@ end_per_group(Name, _) -> init_dispatch(_) -> cowboy_router:compile([{"[...]", [ + {"*", asterisk_h, []}, {"/", hello_h, []}, {"/echo/:key", echo_h, []}, {"/resp/:key[/:arg]", resp_h, []} @@ -148,8 +149,27 @@ method_options(Config) -> {ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref), ok. -%method_options_asterisk(Config) -> -%method_options_content_length_0(Config) -> +method_options_asterisk(Config) -> + doc("The OPTIONS method is accepted with an asterisk. (RFC7231 4.3.7)"), + ConnPid = gun_open(Config), + Ref = gun:options(ConnPid, "*", [ + {<<"accept-encoding">>, <<"gzip">>}, + {<<"x-echo">>, <<"method">>} + ]), + {response, nofin, 200, _} = gun:await(ConnPid, Ref), + {ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref), + ok. + +method_options_content_length_0(Config) -> + doc("The OPTIONS method must set the content-length header " + "to 0 when no body is returned. (RFC7231 4.3.7)"), + ConnPid = gun_open(Config), + Ref = gun:options(ConnPid, "*", [ + {<<"accept-encoding">>, <<"gzip">>} + ]), + {response, fin, 200, Headers} = gun:await(ConnPid, Ref), + {_, <<"0">>} = lists:keyfind(<<"content-length">>, 1, Headers), + ok. method_trace(Config) -> doc("The TRACE method is currently not implemented. (RFC7231 4.3.8)"), -- cgit v1.2.3