0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

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.
This commit is contained in:
Loïc Hoguin 2017-12-06 14:05:30 +01:00
parent 2eb3e3f994
commit bc39b433bb
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
5 changed files with 38 additions and 19 deletions

View file

@ -228,8 +228,10 @@ uri(#{scheme := Scheme0, host := Host0, port := Port0,
end, end,
Host = maps:get(host, Opts, Host0), Host = maps:get(host, Opts, Host0),
Port = maps:get(port, Opts, Port0), Port = maps:get(port, Opts, Port0),
Path = maps:get(path, Opts, Path0), {Path, Qs} = case maps:get(path, Opts, Path0) of
Qs = maps:get(qs, Opts, Qs0), <<"*">> -> {<<>>, <<>>};
P -> {P, maps:get(qs, Opts, Qs0)}
end,
Fragment = maps:get(fragment, Opts, undefined), Fragment = maps:get(fragment, Opts, undefined),
[uri_host(Scheme, Scheme0, Port, Host), uri_path(Path), uri_qs(Qs), uri_fragment(Fragment)]. [uri_host(Scheme, Scheme0, Port, Host), uri_path(Path), uri_qs(Qs), uri_fragment(Fragment)].

View file

@ -82,6 +82,8 @@ compile_paths([{PathMatch, Fields, Handler, Opts}|Tail], Acc)
Fields, Handler, Opts}|Tail], Acc); Fields, Handler, Opts}|Tail], Acc);
compile_paths([{'_', Fields, Handler, Opts}|Tail], Acc) -> compile_paths([{'_', Fields, Handler, Opts}|Tail], Acc) ->
compile_paths(Tail, [{'_', Fields, Handler, Opts}] ++ 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], compile_paths([{<< $/, PathMatch/bits >>, Fields, Handler, Opts}|Tail],
Acc) -> Acc) ->
PathRules = compile_rules(PathMatch, $/, [], [], <<>>), PathRules = compile_rules(PathMatch, $/, [], [], <<>>),
@ -252,6 +254,8 @@ match_path([{'_', [], Handler, Opts}|_Tail], HostInfo, _, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined}; {ok, Handler, Opts, Bindings, HostInfo, undefined};
match_path([{<<"*">>, _, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) -> match_path([{<<"*">>, _, Handler, Opts}|_Tail], HostInfo, <<"*">>, Bindings) ->
{ok, Handler, Opts, Bindings, HostInfo, undefined}; {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, match_path([{PathMatch, Fields, Handler, Opts}|Tail], HostInfo, Tokens,
Bindings) when is_list(Tokens) -> Bindings) when is_list(Tokens) ->
case list_match(Tokens, PathMatch, Bindings) of case list_match(Tokens, PathMatch, Bindings) of

View file

@ -7,6 +7,8 @@
init(Req, Opts) -> init(Req, Opts) ->
echo(cowboy_req:header(<<"x-echo">>, Req), 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) -> echo(What, Req, Opts) ->
F = binary_to_atom(What, latin1), F = binary_to_atom(What, latin1),
Value = case cowboy_req:F(Req) of Value = case cowboy_req:F(Req) of

View file

@ -41,9 +41,8 @@ init_routes(_) -> [
{"/echo/:key[/:arg]", echo_h, []}, {"/echo/:key[/:arg]", echo_h, []},
{"/length/echo/:key", echo_h, []}, {"/length/echo/:key", echo_h, []},
{"/resp/:key[/:arg]", resp_h, []}, {"/resp/:key[/:arg]", resp_h, []},
{"/send_message", send_message_h, []} {"/send_message", send_message_h, []},
%% @todo Something is clearly wrong about routing * right now. {"*", asterisk_h, []}
%% {"*", asterisk_h, []}
]}, ]},
{"127.0.0.1", [{"/echo/:key", echo_h, []}]}, {"127.0.0.1", [{"/echo/:key", echo_h, []}]},
{"example.org", [{"/echo/:key", echo_h, []}]} {"example.org", [{"/echo/:key", echo_h, []}]}
@ -603,20 +602,12 @@ asterisk_form_reject_if_not_options(Config) ->
"\r\n"), "\r\n"),
{error, closed} = raw_recv(Client, 0, 1000). {error, closed} = raw_recv(Client, 0, 1000).
asterisk_form_empty_path(Config) -> asterisk_form_empty_path_query(Config) ->
doc("The path is empty when using asterisk-form. (RFC7230 5.5)"), doc("The path and query components are empty when using asterisk-form. (RFC7230 5.5)"),
#{code := 200, body := <<>>} = do_raw(Config, #{code := 200, body := <<"http://localhost">>} = do_raw(Config,
"OPTIONS * HTTP/1.1\r\n" "OPTIONS * HTTP/1.1\r\n"
"Host: localhost\r\n" "Host: localhost\r\n"
"X-Echo: path\r\n" "X-Echo: uri\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"
"\r\n"). "\r\n").
%% Invalid request-target. %% Invalid request-target.

View file

@ -34,6 +34,7 @@ end_per_group(Name, _) ->
init_dispatch(_) -> init_dispatch(_) ->
cowboy_router:compile([{"[...]", [ cowboy_router:compile([{"[...]", [
{"*", asterisk_h, []},
{"/", hello_h, []}, {"/", hello_h, []},
{"/echo/:key", echo_h, []}, {"/echo/:key", echo_h, []},
{"/resp/:key[/:arg]", resp_h, []} {"/resp/:key[/:arg]", resp_h, []}
@ -148,8 +149,27 @@ method_options(Config) ->
{ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref), {ok, <<"OPTIONS">>} = gun:await_body(ConnPid, Ref),
ok. ok.
%method_options_asterisk(Config) -> method_options_asterisk(Config) ->
%method_options_content_length_0(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) -> method_trace(Config) ->
doc("The TRACE method is currently not implemented. (RFC7231 4.3.8)"), doc("The TRACE method is currently not implemented. (RFC7231 4.3.8)"),