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

cowboy_rest: Always set the Allow header

Not just on 405 responses or OPTIONS requests.
This commit is contained in:
Loïc Hoguin 2025-02-10 18:41:11 +01:00
parent 58402b4162
commit f316a65906
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
2 changed files with 11 additions and 30 deletions

View file

@ -246,9 +246,6 @@
handler :: atom(), handler :: atom(),
handler_state :: any(), handler_state :: any(),
%% Allowed methods. Only used for OPTIONS requests.
allowed_methods :: [binary()] | undefined,
%% Media type. %% Media type.
content_types_p = [] :: content_types_p = [] ::
[{binary() | {binary(), binary(), [{binary(), binary()}] | '*'}, [{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
@ -327,37 +324,26 @@ uri_too_long(Req, State) ->
%% allowed_methods/2 should return a list of binary methods. %% allowed_methods/2 should return a list of binary methods.
allowed_methods(Req, State=#state{method=Method}) -> allowed_methods(Req, State=#state{method=Method}) ->
case call(Req, State, allowed_methods) of case call(Req, State, allowed_methods) of
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">> -> no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>; Method =:= <<"OPTIONS">> ->
malformed_request(Req, State); Req2 = cowboy_req:set_resp_header(<<"allow">>, <<"HEAD, GET, OPTIONS">>, Req),
no_call when Method =:= <<"OPTIONS">> -> malformed_request(Req2, State);
malformed_request(Req, State#state{allowed_methods=
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]});
no_call -> no_call ->
method_not_allowed(Req, State, Req2 = cowboy_req:set_resp_header(<<"allow">>, <<"HEAD, GET, OPTIONS">>, Req),
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]); respond(Req2, State, 405);
{stop, Req2, State2} -> {stop, Req2, State2} ->
terminate(Req2, State2); terminate(Req2, State2);
{Switch, Req2, State2} when element(1, Switch) =:= switch_handler -> {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->
switch_handler(Switch, Req2, State2); switch_handler(Switch, Req2, State2);
{List, Req2, State2} -> {List, Req2, State2} ->
Req3 = cowboy_req:set_resp_header(<<"allow">>, cow_http_hd:allow(List), Req2),
case lists:member(Method, List) of case lists:member(Method, List) of
true when Method =:= <<"OPTIONS">> ->
malformed_request(Req2, State2#state{allowed_methods=List});
true -> true ->
malformed_request(Req2, State2); malformed_request(Req3, State2);
false -> false ->
method_not_allowed(Req2, State2, List) respond(Req3, State2, 405)
end end
end. end.
method_not_allowed(Req, State, []) ->
Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
respond(Req2, State, 405);
method_not_allowed(Req, State, Methods) ->
<< ", ", Allow/binary >> = << << ", ", M/binary >> || M <- Methods >>,
Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
respond(Req2, State, 405).
malformed_request(Req, State) -> malformed_request(Req, State) ->
expect(Req, State, malformed_request, false, fun is_authorized/2, 400). expect(Req, State, malformed_request, false, fun is_authorized/2, 400).
@ -411,16 +397,10 @@ valid_entity_length(Req, State) ->
%% If you need to add additional headers to the response at this point, %% If you need to add additional headers to the response at this point,
%% you should do it directly in the options/2 call using set_resp_headers. %% you should do it directly in the options/2 call using set_resp_headers.
options(Req, State=#state{allowed_methods=Methods, method= <<"OPTIONS">>}) -> options(Req, State=#state{method= <<"OPTIONS">>}) ->
case call(Req, State, options) of case call(Req, State, options) of
no_call when Methods =:= [] ->
Req2 = cowboy_req:set_resp_header(<<"allow">>, <<>>, Req),
respond(Req2, State, 200);
no_call -> no_call ->
<< ", ", Allow/binary >> respond(Req, State, 200);
= << << ", ", M/binary >> || M <- Methods >>,
Req2 = cowboy_req:set_resp_header(<<"allow">>, Allow, Req),
respond(Req2, State, 200);
{stop, Req2, State2} -> {stop, Req2, State2} ->
terminate(Req2, State2); terminate(Req2, State2);
{Switch, Req2, State2} when element(1, Switch) =:= switch_handler -> {Switch, Req2, State2} when element(1, Switch) =:= switch_handler ->

View file

@ -817,6 +817,7 @@ provide_callback(Config) ->
]), ]),
{response, nofin, 200, Headers} = gun:await(ConnPid, Ref), {response, nofin, 200, Headers} = gun:await(ConnPid, Ref),
{_, <<"text/plain">>} = lists:keyfind(<<"content-type">>, 1, Headers), {_, <<"text/plain">>} = lists:keyfind(<<"content-type">>, 1, Headers),
{_, <<"HEAD, GET, OPTIONS">>} = lists:keyfind(<<"allow">>, 1, Headers),
{ok, <<"This is REST!">>} = gun:await_body(ConnPid, Ref), {ok, <<"This is REST!">>} = gun:await_body(ConnPid, Ref),
ok. ok.