Browse Source

Merge branch 'param_all' of git://github.com/nevar/cowboy

Loïc Hoguin 12 years ago
parent
commit
7d13391d39
3 changed files with 108 additions and 8 deletions
  1. 30 8
      src/cowboy_rest.erl
  2. 42 0
      test/http_SUITE.erl
  3. 36 0
      test/rest_param_all.erl

+ 30 - 8
src/cowboy_rest.erl

@@ -33,9 +33,11 @@
 
 
 	%% Media type.
 	%% Media type.
 	content_types_p = [] ::
 	content_types_p = [] ::
-		[{binary() | {binary(), binary(), [{binary(), binary()}]}, atom()}],
+		[{binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
+			atom()}],
 	content_type_a :: undefined
 	content_type_a :: undefined
-		| {binary() | {binary(), binary(), [{binary(), binary()}]}, atom()},
+		| {binary() | {binary(), binary(), [{binary(), binary()}] | '*'},
+			atom()},
 
 
 	%% Language.
 	%% Language.
 	languages_p = [] :: [binary()],
 	languages_p = [] :: [binary()],
@@ -286,6 +288,12 @@ match_media_type(Req, State, Accept,
 match_media_type(Req, State, Accept, [_Any|Tail], MediaType) ->
 match_media_type(Req, State, Accept, [_Any|Tail], MediaType) ->
 	match_media_type(Req, State, Accept, Tail, MediaType).
 	match_media_type(Req, State, Accept, Tail, MediaType).
 
 
+match_media_type_params(Req, State, _Accept,
+		[Provided = {{TP, STP, '*'}, _Fun}|_Tail],
+		{{_TA, _STA, Params_A}, _QA, _APA}) ->
+	PMT = {TP, STP, Params_A},
+	languages_provided(cowboy_req:set_meta(media_type, PMT, Req),
+		State#state{content_type_a=Provided});
 match_media_type_params(Req, State, Accept,
 match_media_type_params(Req, State, Accept,
 		[Provided = {PMT = {_TP, _STP, Params_P}, _Fun}|Tail],
 		[Provided = {PMT = {_TP, _STP, Params_P}, _Fun}|Tail],
 		MediaType = {{_TA, _STA, Params_A}, _QA, _APA}) ->
 		MediaType = {{_TA, _STA, Params_A}, _QA, _APA}) ->
@@ -426,6 +434,8 @@ set_content_type(Req, State=#state{
 	Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
 	Req2 = cowboy_req:set_resp_header(<<"content-type">>, ContentType2, Req),
 	encodings_provided(cowboy_req:set_meta(charset, Charset, Req2), State).
 	encodings_provided(cowboy_req:set_meta(charset, Charset, Req2), State).
 
 
+set_content_type_build_params('*', []) ->
+	<<>>;
 set_content_type_build_params([], []) ->
 set_content_type_build_params([], []) ->
 	<<>>;
 	<<>>;
 set_content_type_build_params([], Acc) ->
 set_content_type_build_params([], Acc) ->
@@ -855,10 +865,24 @@ patch_resource(Req, State) ->
 %% list of content types, otherwise it'll shadow the ones following.
 %% list of content types, otherwise it'll shadow the ones following.
 choose_content_type(Req, State, _OnTrue, _ContentType, []) ->
 choose_content_type(Req, State, _OnTrue, _ContentType, []) ->
 	respond(Req, State, 415);
 	respond(Req, State, 415);
-choose_content_type(Req,
+choose_content_type(Req, State, OnTrue, ContentType, [{Accepted, Fun}|_Tail])
+		when Accepted =:= '*'; Accepted =:= ContentType ->
+	process_content_type(Req, State, OnTrue, Fun);
+%% The special parameter '*' will always match any kind of content type
+%% parameters.
+%% Note that because it will always match, it should be the last of the
+%% list for specific content type, otherwise it'll shadow the ones following.
+choose_content_type(Req, State, OnTrue,
+		{Type, SubType, Param},
+		[{{Type, SubType, AcceptedParam}, Fun}|_Tail])
+		when AcceptedParam =:= '*'; AcceptedParam =:= Param ->
+	process_content_type(Req, State, OnTrue, Fun);
+choose_content_type(Req, State, OnTrue, ContentType, [_Any|Tail]) ->
+	choose_content_type(Req, State, OnTrue, ContentType, Tail).
+
+process_content_type(Req,
 		State=#state{handler=Handler, handler_state=HandlerState},
 		State=#state{handler=Handler, handler_state=HandlerState},
-		OnTrue, ContentType, [{Accepted, Fun}|_Tail])
-		when Accepted =:= '*' orelse Accepted =:= ContentType ->
+		OnTrue, Fun) ->
 	case call(Req, State, Fun) of
 	case call(Req, State, Fun) of
 		no_call ->
 		no_call ->
 			error_logger:error_msg(
 			error_logger:error_msg(
@@ -875,9 +899,7 @@ choose_content_type(Req,
 		{false, Req2, HandlerState2} ->
 		{false, Req2, HandlerState2} ->
 			State2 = State#state{handler_state=HandlerState2},
 			State2 = State#state{handler_state=HandlerState2},
 			respond(Req2, State2, 422)
 			respond(Req2, State2, 422)
-	end;
-choose_content_type(Req, State, OnTrue, ContentType, [_Any|Tail]) ->
-	choose_content_type(Req, State, OnTrue, ContentType, Tail).
+	end.
 
 
 %% Whether we created a new resource, either through PUT or POST.
 %% Whether we created a new resource, either through PUT or POST.
 %% This is easily testable because we would have set the Location
 %% This is easily testable because we would have set the Location

+ 42 - 0
test/http_SUITE.erl

@@ -58,6 +58,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_param_all/1]).
 -export([rest_patch/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]).
@@ -124,6 +125,7 @@ groups() ->
 		rest_missing_get_callbacks,
 		rest_missing_get_callbacks,
 		rest_missing_put_callbacks,
 		rest_missing_put_callbacks,
 		rest_nodelete,
 		rest_nodelete,
+		rest_param_all,
 		rest_patch,
 		rest_patch,
 		rest_resource_etags,
 		rest_resource_etags,
 		rest_resource_etags_if_none_match,
 		rest_resource_etags_if_none_match,
@@ -346,6 +348,7 @@ init_dispatch(Config) ->
 				 {file, <<"test_file.css">>}]},
 				 {file, <<"test_file.css">>}]},
 			{"/multipart", http_handler_multipart, []},
 			{"/multipart", http_handler_multipart, []},
 			{"/echo/body", http_handler_echo_body, []},
 			{"/echo/body", http_handler_echo_body, []},
+			{"/param_all", rest_param_all, []},
 			{"/bad_accept", rest_simple_resource, []},
 			{"/bad_accept", rest_simple_resource, []},
 			{"/simple", rest_simple_resource, []},
 			{"/simple", rest_simple_resource, []},
 			{"/forbidden_post", rest_forbidden_resource, [true]},
 			{"/forbidden_post", rest_forbidden_resource, [true]},
@@ -790,6 +793,45 @@ pipeline_long_polling(Config) ->
 	{ok, 102, _, Client5} = cowboy_client:response(Client4),
 	{ok, 102, _, Client5} = cowboy_client:response(Client4),
 	{error, closed} = cowboy_client:response(Client5).
 	{error, closed} = cowboy_client:response(Client5).
 
 
+rest_param_all(Config) ->
+	Client = ?config(client, Config),
+	URL = build_url("/param_all", Config),
+	% Accept without param
+	{ok, Client2} = cowboy_client:request(<<"GET">>, URL,
+		[{<<"accept">>, <<"text/plain">>}], Client),
+	Client3 = check_response(Client2, <<"[]">>),
+	% Accept with param
+	{ok, Client4} = cowboy_client:request(<<"GET">>, URL,
+		[{<<"accept">>, <<"text/plain;level=1">>}], Client3),
+	Client5 = check_response(Client4, <<"level=1">>),
+	% Accept with param and quality
+	{ok, Client6} = cowboy_client:request(<<"GET">>, URL,
+		[{<<"accept">>,
+			<<"text/plain;level=1;q=0.8, text/plain;level=2;q=0.5">>}],
+		Client5),
+	Client7 = check_response(Client6, <<"level=1">>),
+	{ok, Client8} = cowboy_client:request(<<"GET">>, URL,
+		[{<<"accept">>,
+			<<"text/plain;level=1;q=0.5, text/plain;level=2;q=0.8">>}],
+		Client7),
+	Client9 = check_response(Client8, <<"level=2">>),
+	% Without Accept
+	{ok, Client10} = cowboy_client:request(<<"GET">>, URL, [], Client9),
+	Client11 = check_response(Client10, <<"'*'">>),
+	% Content-Type without param
+	{ok, Client12} = cowboy_client:request(<<"PUT">>, URL,
+		[{<<"content-type">>, <<"text/plain">>}], Client11),
+	{ok, 204, _, Client13} = cowboy_client:response(Client12),
+	% Content-Type with param
+	{ok, Client14} = cowboy_client:request(<<"PUT">>, URL,
+		[{<<"content-type">>, <<"text/plain; charset=utf-8">>}], Client13),
+	{ok, 204, _, _} = cowboy_client:response(Client14).
+
+check_response(Client, Body) ->
+	{ok, 200, _, Client2} = cowboy_client:response(Client),
+	{ok, Body, Client3} = cowboy_client:response_body(Client2),
+	Client3.
+
 rest_bad_accept(Config) ->
 rest_bad_accept(Config) ->
 	Client = ?config(client, Config),
 	Client = ?config(client, Config),
 	{ok, Client2} = cowboy_client:request(<<"GET">>,
 	{ok, Client2} = cowboy_client:request(<<"GET">>,

+ 36 - 0
test/rest_param_all.erl

@@ -0,0 +1,36 @@
+-module(rest_param_all).
+
+-export([init/3]).
+-export([allowed_methods/2]).
+-export([content_types_provided/2]).
+-export([get_text_plain/2]).
+-export([content_types_accepted/2]).
+-export([put_text_plain/2]).
+
+init(_Transport, _Req, _Opts) ->
+	{upgrade, protocol, cowboy_rest}.
+
+allowed_methods(Req, State) ->
+	{[<<"GET">>, <<"PUT">>], Req, State}.
+
+content_types_provided(Req, State) ->
+	{[{{<<"text">>, <<"plain">>, '*'}, get_text_plain}], Req, State}.
+
+get_text_plain(Req, State) ->
+	{{_, _, Param}, Req2} =
+		cowboy_req:meta(media_type, Req, {{<<"text">>, <<"plain">>}, []}),
+	Body = if
+	Param == '*' ->
+		<<"'*'">>;
+	Param == [] ->
+		<<"[]">>;
+	Param /= [] ->
+		iolist_to_binary([[Key, $=, Value] || {Key, Value} <- Param])
+	end,
+	{Body, Req2, State}.
+
+content_types_accepted(Req, State) ->
+	{[{{<<"text">>, <<"plain">>, '*'}, put_text_plain}], Req, State}.
+
+put_text_plain(Req, State) ->
+	{true, Req, State}.