Skip to content

Commit

Permalink
Merge pull request #49 from fdmanana/req_accepts_content_types
Browse files Browse the repository at this point in the history
Add mochiweb_request:accepted_content_types/1
  • Loading branch information
etrepum committed Jul 28, 2011
2 parents a85a6e2 + a2e3bb5 commit 5d21379
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 7 deletions.
83 changes: 76 additions & 7 deletions src/mochiweb_request.erl
Expand Up @@ -21,7 +21,7 @@
-export([parse_cookie/0, get_cookie_value/1]). -export([parse_cookie/0, get_cookie_value/1]).
-export([serve_file/2, serve_file/3]). -export([serve_file/2, serve_file/3]).
-export([accepted_encodings/1]). -export([accepted_encodings/1]).
-export([accepts_content_type/1]). -export([accepts_content_type/1, accepted_content_types/1]).


-define(SAVE_QS, mochiweb_request_qs). -define(SAVE_QS, mochiweb_request_qs).
-define(SAVE_PATH, mochiweb_request_path). -define(SAVE_PATH, mochiweb_request_path).
Expand Down Expand Up @@ -729,12 +729,7 @@ accepted_encodings(SupportedEncodings) ->
%% %%
accepts_content_type(ContentType1) -> accepts_content_type(ContentType1) ->
ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]), ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
AcceptHeader = case get_header_value("Accept") of AcceptHeader = accept_header(),
undefined ->
"*/*";
Value ->
Value
end,
case mochiweb_util:parse_qvalues(AcceptHeader) of case mochiweb_util:parse_qvalues(AcceptHeader) of
invalid_qvalue_string -> invalid_qvalue_string ->
bad_accept_header; bad_accept_header;
Expand All @@ -755,6 +750,80 @@ accepts_content_type(ContentType1) ->
(not lists:member({SuperType, 0.0}, QList)) (not lists:member({SuperType, 0.0}, QList))
end. end.


%% @spec accepted_content_types([string() | binary()]) -> [string()] | bad_accept_header
%%
%% @doc Filters which of the given media types this request accepts. This filtering
%% is performed by analyzing the "Accept" header. The returned list is sorted
%% according to the preferences specified in the "Accept" header (higher Q values
%% first). If two or more types have the same preference (Q value), they're order
%% in the returned list is the same as they're order in the input list.
%%
%% Examples
%%
%% 1) For a missing "Accept" header:
%% accepted_content_types(["text/html", "application/json"]) ->
%% ["text/html", "application/json"]
%%
%% 2) For an "Accept" header with value "text/html, application/*":
%% accepted_content_types(["application/json", "text/html"]) ->
%% ["application/json", "text/html"]
%%
%% 3) For an "Accept" header with value "text/html, */*; q=0.0":
%% accepted_content_types(["text/html", "application/json"]) ->
%% ["text/html"]
%%
%% 4) For an "Accept" header with value "text/html; q=0.5, */*; q=0.1":
%% accepts_content_types(["application/json", "text/html"]) ->
%% ["text/html", "application/json"]
%%
accepted_content_types(Types1) ->
Types = lists:map(
fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end,
Types1),
AcceptHeader = accept_header(),
case mochiweb_util:parse_qvalues(AcceptHeader) of
invalid_qvalue_string ->
bad_accept_header;
QList ->
TypesQ = lists:foldr(
fun(T, Acc) ->
case proplists:get_value(T, QList) of
undefined ->
[MainType, _SubType] = string:tokens(T, "/"),
case proplists:get_value(MainType ++ "/*", QList) of
undefined ->
case proplists:get_value("*/*", QList) of
Q when is_float(Q), Q > 0.0 ->
[{Q, T} | Acc];
_ ->
Acc
end;
Q when Q > 0.0 ->
[{Q, T} | Acc];
_ ->
Acc
end;
Q when Q > 0.0 ->
[{Q, T} | Acc];
_ ->
Acc
end
end,
[], Types),
% Note: Stable sort. If 2 types have the same Q value we leave them in the
% same order as in the input list.
SortFun = fun({Q1, _}, {Q2, _}) -> Q1 >= Q2 end,
[Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)]
end.

accept_header() ->
case get_header_value("Accept") of
undefined ->
"*/*";
Value ->
Value
end.

%% %%
%% Tests %% Tests
%% %%
Expand Down
47 changes: 47 additions & 0 deletions src/mochiweb_request_tests.erl
Expand Up @@ -102,4 +102,51 @@ accepted_encodings_test() ->
?assertEqual([], ?assertEqual([],
Req8:accepted_encodings(["gzip", "identity"])). Req8:accepted_encodings(["gzip", "identity"])).


accepted_content_types_test() ->
Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/html"}])),
?assertEqual(["text/html"],
Req1:accepted_content_types(["text/html", "application/json"])),

Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/html, */*;q=0"}])),
?assertEqual(["text/html"],
Req2:accepted_content_types(["text/html", "application/json"])),

Req3 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/*, */*;q=0"}])),
?assertEqual(["text/html"],
Req3:accepted_content_types(["text/html", "application/json"])),

Req4 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/*;q=0.8, */*;q=0.5"}])),
?assertEqual(["text/html", "application/json"],
Req4:accepted_content_types(["application/json", "text/html"])),

Req5 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/*;q=0.8, */*;q=0.5"}])),
?assertEqual(["text/html", "application/json"],
Req5:accepted_content_types(["text/html", "application/json"])),

Req6 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/*;q=0.5, */*;q=0.5"}])),
?assertEqual(["application/json", "text/html"],
Req6:accepted_content_types(["application/json", "text/html"])),

Req7 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make(
[{"Accept", "text/html;q=0.5, application/json;q=0.5"}])),
?assertEqual(["application/json", "text/html"],
Req7:accepted_content_types(["application/json", "text/html"])),

Req8 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/html"}])),
?assertEqual([],
Req8:accepted_content_types(["application/json"])),

Req9 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
mochiweb_headers:make([{"Accept", "text/*;q=0.9, text/html;q=0.5, */*;q=0.7"}])),
?assertEqual(["application/json", "text/html"],
Req9:accepted_content_types(["text/html", "application/json"])).

-endif. -endif.

0 comments on commit 5d21379

Please sign in to comment.