mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Add default operations for OPTIONS method in REST
It defaults to setting the Allow header to "HEAD, GET, OPTIONS".
This commit is contained in:
parent
d063511a0e
commit
b58a0549e1
4 changed files with 45 additions and 12 deletions
|
@ -77,7 +77,7 @@ empty column means there is no default value for this callback.
|
||||||
| moved_permanently | `false` |
|
| moved_permanently | `false` |
|
||||||
| moved_temporarily | `false` |
|
| moved_temporarily | `false` |
|
||||||
| multiple_choices | `false` |
|
| multiple_choices | `false` |
|
||||||
| options | |
|
| options | `ok` |
|
||||||
| previously_existed | `false` |
|
| previously_existed | `false` |
|
||||||
| resource_exists | `true` |
|
| resource_exists | `true` |
|
||||||
| service_available | `true` |
|
| service_available | `true` |
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
handler :: atom(),
|
handler :: atom(),
|
||||||
handler_state :: any(),
|
handler_state :: any(),
|
||||||
|
|
||||||
|
%% Allowed methods. Only used for OPTIONS requests.
|
||||||
|
allowed_methods :: [binary()],
|
||||||
|
|
||||||
%% Media type.
|
%% Media type.
|
||||||
content_types_p = [] ::
|
content_types_p = [] ::
|
||||||
[{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
|
[{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
|
||||||
|
@ -119,32 +122,43 @@ 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">> ->
|
||||||
next(Req, State, fun malformed_request/2);
|
next(Req, State, fun malformed_request/2);
|
||||||
|
no_call when Method =:= <<"OPTIONS">> ->
|
||||||
|
next(Req, State#state{allowed_methods=
|
||||||
|
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]},
|
||||||
|
fun malformed_request/2);
|
||||||
no_call ->
|
no_call ->
|
||||||
method_not_allowed(Req, State, [<<"GET">>, <<"HEAD">>]);
|
method_not_allowed(Req, State,
|
||||||
|
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]);
|
||||||
{halt, Req2, HandlerState} ->
|
{halt, Req2, HandlerState} ->
|
||||||
terminate(Req2, State#state{handler_state=HandlerState});
|
terminate(Req2, State#state{handler_state=HandlerState});
|
||||||
{List, Req2, HandlerState} ->
|
{List, Req2, HandlerState} ->
|
||||||
State2 = State#state{handler_state=HandlerState},
|
State2 = State#state{handler_state=HandlerState},
|
||||||
case lists:member(Method, List) of
|
case lists:member(Method, List) of
|
||||||
true -> next(Req2, State2, fun malformed_request/2);
|
true when Method =:= <<"OPTIONS">> ->
|
||||||
false -> method_not_allowed(Req2, State2, List)
|
next(Req2, State2#state{allowed_methods=
|
||||||
|
[<<"HEAD">>, <<"GET">>, <<"OPTIONS">>]},
|
||||||
|
fun malformed_request/2);
|
||||||
|
true ->
|
||||||
|
next(Req2, State2, fun malformed_request/2);
|
||||||
|
false ->
|
||||||
|
method_not_allowed(Req2, State2, List)
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
method_not_allowed(Req, State, Methods) ->
|
method_not_allowed(Req, State, Methods) ->
|
||||||
Req2 = cowboy_req:set_resp_header(
|
Req2 = cowboy_req:set_resp_header(
|
||||||
<<"allow">>, method_not_allowed_build(Methods, []), Req),
|
<<"allow">>, build_allow_header(Methods, []), Req),
|
||||||
respond(Req2, State, 405).
|
respond(Req2, State, 405).
|
||||||
|
|
||||||
method_not_allowed_build([], []) ->
|
build_allow_header([], []) ->
|
||||||
<<>>;
|
<<>>;
|
||||||
method_not_allowed_build([], [_Ignore|Acc]) ->
|
build_allow_header([], [_Ignore|Acc]) ->
|
||||||
lists:reverse(Acc);
|
lists:reverse(Acc);
|
||||||
method_not_allowed_build([Method|Tail], Acc) when is_atom(Method) ->
|
build_allow_header([Method|Tail], Acc) when is_atom(Method) ->
|
||||||
Method2 = list_to_binary(atom_to_list(Method)),
|
Method2 = list_to_binary(atom_to_list(Method)),
|
||||||
method_not_allowed_build(Tail, [<<", ">>, Method2|Acc]);
|
build_allow_header(Tail, [<<", ">>, Method2|Acc]);
|
||||||
method_not_allowed_build([Method|Tail], Acc) ->
|
build_allow_header([Method|Tail], Acc) ->
|
||||||
method_not_allowed_build(Tail, [<<", ">>, Method|Acc]).
|
build_allow_header(Tail, [<<", ">>, Method|Acc]).
|
||||||
|
|
||||||
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).
|
||||||
|
@ -180,8 +194,12 @@ 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{method= <<"OPTIONS">>}) ->
|
options(Req, State=#state{allowed_methods=Allow, method= <<"OPTIONS">>}) ->
|
||||||
case call(Req, State, options) of
|
case call(Req, State, options) of
|
||||||
|
no_call ->
|
||||||
|
Req2 = cowboy_req:set_resp_header(<<"allow">>,
|
||||||
|
build_allow_header(Allow, []), Req),
|
||||||
|
respond(Req2, State, 200);
|
||||||
{halt, Req2, HandlerState} ->
|
{halt, Req2, HandlerState} ->
|
||||||
terminate(Req2, State#state{handler_state=HandlerState});
|
terminate(Req2, State#state{handler_state=HandlerState});
|
||||||
{ok, Req2, HandlerState} ->
|
{ok, Req2, HandlerState} ->
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
-export([rest_missing_get_callbacks/1]).
|
-export([rest_missing_get_callbacks/1]).
|
||||||
-export([rest_missing_put_callbacks/1]).
|
-export([rest_missing_put_callbacks/1]).
|
||||||
-export([rest_nodelete/1]).
|
-export([rest_nodelete/1]).
|
||||||
|
-export([rest_options_default/1]).
|
||||||
-export([rest_param_all/1]).
|
-export([rest_param_all/1]).
|
||||||
-export([rest_patch/1]).
|
-export([rest_patch/1]).
|
||||||
-export([rest_resource_etags/1]).
|
-export([rest_resource_etags/1]).
|
||||||
|
@ -131,6 +132,7 @@ groups() ->
|
||||||
rest_missing_get_callbacks,
|
rest_missing_get_callbacks,
|
||||||
rest_missing_put_callbacks,
|
rest_missing_put_callbacks,
|
||||||
rest_nodelete,
|
rest_nodelete,
|
||||||
|
rest_options_default,
|
||||||
rest_param_all,
|
rest_param_all,
|
||||||
rest_patch,
|
rest_patch,
|
||||||
rest_resource_etags,
|
rest_resource_etags,
|
||||||
|
@ -367,6 +369,7 @@ init_dispatch(Config) ->
|
||||||
{"/patch", rest_patch_resource, []},
|
{"/patch", rest_patch_resource, []},
|
||||||
{"/resetags", rest_resource_etags, []},
|
{"/resetags", rest_resource_etags, []},
|
||||||
{"/rest_expires", rest_expires, []},
|
{"/rest_expires", rest_expires, []},
|
||||||
|
{"/rest_empty_resource", rest_empty_resource, []},
|
||||||
{"/loop_recv", http_handler_loop_recv, []},
|
{"/loop_recv", http_handler_loop_recv, []},
|
||||||
{"/loop_timeout", http_handler_loop_timeout, []},
|
{"/loop_timeout", http_handler_loop_timeout, []},
|
||||||
{"/", http_handler, []}
|
{"/", http_handler, []}
|
||||||
|
@ -967,6 +970,13 @@ rest_nodelete(Config) ->
|
||||||
build_url("/nodelete", Config), Client),
|
build_url("/nodelete", Config), Client),
|
||||||
{ok, 500, _, _} = cowboy_client:response(Client2).
|
{ok, 500, _, _} = cowboy_client:response(Client2).
|
||||||
|
|
||||||
|
rest_options_default(Config) ->
|
||||||
|
Client = ?config(client, Config),
|
||||||
|
{ok, Client2} = cowboy_client:request(<<"OPTIONS">>,
|
||||||
|
build_url("/rest_empty_resource", Config), Client),
|
||||||
|
{ok, 200, Headers, _} = cowboy_client:response(Client2),
|
||||||
|
{_, <<"HEAD, GET, OPTIONS">>} = lists:keyfind(<<"allow">>, 1, Headers).
|
||||||
|
|
||||||
rest_patch(Config) ->
|
rest_patch(Config) ->
|
||||||
Tests = [
|
Tests = [
|
||||||
{204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
|
{204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
|
||||||
|
|
5
test/rest_empty_resource.erl
Normal file
5
test/rest_empty_resource.erl
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
-module(rest_empty_resource).
|
||||||
|
-export([init/3]).
|
||||||
|
|
||||||
|
init(_Transport, _Req, _Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_rest}.
|
Loading…
Add table
Add a link
Reference in a new issue