Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #49 from fdmanana/req_accepts_content_types

Add mochiweb_request:accepted_content_types/1
  • Loading branch information...
commit 5d21379b4efdeb7eeba88ed0d943a42b99bb6aab 2 parents a85a6e2 + a2e3bb5
@etrepum etrepum authored
Showing with 123 additions and 7 deletions.
  1. +76 −7 src/mochiweb_request.erl
  2. +47 −0 src/mochiweb_request_tests.erl
View
83 src/mochiweb_request.erl
@@ -21,7 +21,7 @@
-export([parse_cookie/0, get_cookie_value/1]).
-export([serve_file/2, serve_file/3]).
-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_PATH, mochiweb_request_path).
@@ -729,12 +729,7 @@ accepted_encodings(SupportedEncodings) ->
%%
accepts_content_type(ContentType1) ->
ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
- AcceptHeader = case get_header_value("Accept") of
- undefined ->
- "*/*";
- Value ->
- Value
- end,
+ AcceptHeader = accept_header(),
case mochiweb_util:parse_qvalues(AcceptHeader) of
invalid_qvalue_string ->
bad_accept_header;
@@ -755,6 +750,80 @@ accepts_content_type(ContentType1) ->
(not lists:member({SuperType, 0.0}, QList))
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
%%
View
47 src/mochiweb_request_tests.erl
@@ -102,4 +102,51 @@ accepted_encodings_test() ->
?assertEqual([],
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.
Please sign in to comment.
Something went wrong with that request. Please try again.