0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-15 12:40:25 +00:00
This commit is contained in:
Loïc Hoguin 2013-01-29 19:16:45 +01:00
commit a59c5d6e91
3 changed files with 80 additions and 1 deletions

View file

@ -91,7 +91,8 @@ known_methods(Req, State=#state{method=Method}) ->
no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>; no_call when Method =:= <<"HEAD">>; Method =:= <<"GET">>;
Method =:= <<"POST">>; Method =:= <<"PUT">>; Method =:= <<"POST">>; Method =:= <<"PUT">>;
Method =:= <<"DELETE">>; Method =:= <<"TRACE">>; Method =:= <<"DELETE">>; Method =:= <<"TRACE">>;
Method =:= <<"CONNECT">>; Method =:= <<"OPTIONS">> -> Method =:= <<"CONNECT">>; Method =:= <<"OPTIONS">>;
Method =:= <<"PATCH">> ->
next(Req, State, fun uri_too_long/2); next(Req, State, fun uri_too_long/2);
no_call -> no_call ->
next(Req, State, 501); next(Req, State, 501);
@ -644,6 +645,8 @@ method(Req, State=#state{method= <<"POST">>}) ->
post_is_create(Req, State); post_is_create(Req, State);
method(Req, State=#state{method= <<"PUT">>}) -> method(Req, State=#state{method= <<"PUT">>}) ->
is_conflict(Req, State); is_conflict(Req, State);
method(Req, State=#state{method= <<"PATCH">>}) ->
patch_resource(Req, State);
method(Req, State=#state{method=Method}) method(Req, State=#state{method=Method})
when Method =:= <<"GET">>; Method =:= <<"HEAD">> -> when Method =:= <<"GET">>; Method =:= <<"HEAD">> ->
set_resp_body(Req, State); set_resp_body(Req, State);
@ -708,6 +711,9 @@ put_resource(Req, State) ->
%% may be different from the request path, and is stored as request metadata. %% may be different from the request path, and is stored as request metadata.
%% It is always defined past this point. It can be retrieved as demonstrated: %% It is always defined past this point. It can be retrieved as demonstrated:
%% {PutPath, Req2} = cowboy_req:meta(put_path, Req) %% {PutPath, Req2} = cowboy_req:meta(put_path, Req)
%%
%%content_types_accepted SHOULD return a different list
%% for each HTTP method.
put_resource(Req, State, OnTrue) -> put_resource(Req, State, OnTrue) ->
case call(Req, State, content_types_accepted) of case call(Req, State, content_types_accepted) of
no_call -> no_call ->
@ -722,6 +728,27 @@ put_resource(Req, State, OnTrue) ->
choose_content_type(Req3, State2, OnTrue, ContentType, CTA2) choose_content_type(Req3, State2, OnTrue, ContentType, CTA2)
end. end.
%% content_types_accepted should return a list of media types and their
%% associated callback functions in the same format as content_types_provided.
%%
%% The callback will then be called and is expected to process the content
%% pushed to the resource in the request body.
%%
%% content_types_accepted SHOULD return a different list
%% for each HTTP method.
patch_resource(Req, State) ->
case call(Req, State, content_types_accepted) of
no_call ->
respond(Req, State, 415);
{halt, Req2, HandlerState} ->
terminate(Req2, State#state{handler_state=HandlerState});
{CTM, Req2, HandlerState} ->
State2 = State#state{handler_state=HandlerState},
{ok, ContentType, Req3}
= cowboy_req:parse_header(<<"content-type">>, Req2),
choose_content_type(Req3, State2, 204, ContentType, CTM)
end.
%% The special content type '*' will always match. It can be used as a %% The special content type '*' will always match. It can be used as a
%% catch-all content type for accepting any kind of request content. %% catch-all content type for accepting any kind of request content.
%% Note that because it will always match, it should be the last of the %% Note that because it will always match, it should be the last of the

View file

@ -56,6 +56,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_patch/1]).
-export([rest_resource_etags/1]). -export([rest_resource_etags/1]).
-export([rest_resource_etags_if_none_match/1]). -export([rest_resource_etags_if_none_match/1]).
-export([set_resp_body/1]). -export([set_resp_body/1]).
@ -117,6 +118,7 @@ groups() ->
rest_missing_get_callbacks, rest_missing_get_callbacks,
rest_missing_put_callbacks, rest_missing_put_callbacks,
rest_nodelete, rest_nodelete,
rest_patch,
rest_resource_etags, rest_resource_etags,
rest_resource_etags_if_none_match, rest_resource_etags_if_none_match,
set_resp_body, set_resp_body,
@ -331,6 +333,7 @@ init_dispatch(Config) ->
{"/missing_get_callbacks", rest_missing_callbacks, []}, {"/missing_get_callbacks", rest_missing_callbacks, []},
{"/missing_put_callbacks", rest_missing_callbacks, []}, {"/missing_put_callbacks", rest_missing_callbacks, []},
{"/nodelete", rest_nodelete_resource, []}, {"/nodelete", rest_nodelete_resource, []},
{"/patch", rest_patch_resource, []},
{"/resetags", rest_resource_etags, []}, {"/resetags", rest_resource_etags, []},
{"/rest_expires", rest_expires, []}, {"/rest_expires", rest_expires, []},
{"/loop_timeout", http_handler_loop_timeout, []}, {"/loop_timeout", http_handler_loop_timeout, []},
@ -834,6 +837,21 @@ 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_patch(Config) ->
Tests = [
{204, [{<<"content-type">>, <<"text/plain">>}], <<"whatever">>},
{500, [{<<"content-type">>, <<"text/plain">>}], <<"false">>},
{400, [{<<"content-type">>, <<"text/plain">>}], <<"halt">>},
{415, [{<<"content-type">>, <<"application/json">>}], <<"bad_content_type">>}
],
Client = ?config(client, Config),
_ = [begin
{ok, Client2} = cowboy_client:request(<<"PATCH">>,
build_url("/patch", Config), Headers, Body, Client),
{ok, Status, _, _} = cowboy_client:response(Client2),
ok
end || {Status, Headers, Body} <- Tests].
rest_resource_get_etag(Config, Type) -> rest_resource_get_etag(Config, Type) ->
rest_resource_get_etag(Config, Type, []). rest_resource_get_etag(Config, Type, []).

View file

@ -0,0 +1,34 @@
-module(rest_patch_resource).
-export([init/3, allowed_methods/2, content_types_provided/2, get_text_plain/2,
content_types_accepted/2, patch_text_plain/2]).
init(_Transport, _Req, _Opts) ->
{upgrade, protocol, cowboy_rest}.
allowed_methods(Req, State) ->
{[<<"HEAD">>, <<"GET">>, <<"PATCH">>], Req, State}.
content_types_provided(Req, State) ->
{[{{<<"text">>, <<"plain">>, []}, get_text_plain}], Req, State}.
get_text_plain(Req, State) ->
{<<"This is REST!">>, Req, State}.
content_types_accepted(Req, State) ->
case cowboy_req:method(Req) of
{<<"PATCH">>, Req0} ->
{[{{<<"text">>, <<"plain">>, []}, patch_text_plain}], Req0, State};
{_, Req0} ->
{[], Req0, State}
end.
patch_text_plain(Req, State) ->
case cowboy_req:body(Req) of
{ok, <<"halt">>, Req0} ->
{ok, Req1} = cowboy_req:reply(400, Req0),
{halt, Req1, State};
{ok, <<"false">>, Req0} ->
{false, Req0, State};
{ok, _Body, Req0} ->
{true, Req0, State}
end.