couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject [29/50] git commit: Added mochiweb_request:accept_content_type/1 function. This function allows a caller to know if a request accepts a given media type. Examples:
Date Fri, 17 Jan 2014 22:53:06 GMT
Added mochiweb_request:accept_content_type/1 function.
This function allows a caller to know if a request accepts a given media type.
Examples:

      1) For a missing "Accept" header:
         accepts_content_type("application/json") -> true

      2) For an "Accept" header with value "text/plain, application/*":
         accepts_content_type("application/json") -> true

      3) For an "Accept" header with value "text/plain, */*; q=0.0":
         accepts_content_type("application/json") -> false

      4) For an "Accept" header with value "text/plain; q=0.5, */*; q=0.1":
         accepts_content_type("application/json") -> true

      5) For an "Accept" header with value "text/*; q=0.0, */*":
         accepts_content_type("text/plain") -> false


Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/6e0c6598
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/6e0c6598
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/6e0c6598

Branch: refs/heads/import
Commit: 6e0c659823f84bbd2b1644a837b26aa0986946c3
Parents: 54aa7e0
Author: Filipe David Manana <fdmanana@gmail.com>
Authored: Mon Nov 8 14:26:10 2010 +0000
Committer: Filipe David Manana <fdmanana@gmail.com>
Committed: Mon Nov 8 14:26:10 2010 +0000

----------------------------------------------------------------------
 src/mochiweb_request.erl       | 53 ++++++++++++++++++++++++++++
 src/mochiweb_request_tests.erl | 63 +++++++++++++++++++++++++++++++++
 src/mochiweb_util.erl          | 70 +++++++++++++++++++++++++------------
 3 files changed, 163 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/6e0c6598/src/mochiweb_request.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_request.erl b/src/mochiweb_request.erl
index 0ee2a19..1bdc7b7 100644
--- a/src/mochiweb_request.erl
+++ b/src/mochiweb_request.erl
@@ -21,6 +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]).
 
 -define(SAVE_QS, mochiweb_request_qs).
 -define(SAVE_PATH, mochiweb_request_path).
@@ -708,6 +709,58 @@ accepted_encodings(SupportedEncodings) ->
             )
     end.
 
+%% @spec accepts_content_type(string() | binary()) -> boolean() | bad_accept_header
+%%
+%% @doc Determines whether a request accepts a given media type by analyzing its
+%%      its "Accept" header.
+%%
+%%      Examples
+%%
+%%      1) For a missing "Accept" header:
+%%         accepts_content_type("application/json") -> true
+%%
+%%      2) For an "Accept" header with value "text/plain, application/*":
+%%         accepts_content_type("application/json") -> true
+%%
+%%      3) For an "Accept" header with value "text/plain, */*; q=0.0":
+%%         accepts_content_type("application/json") -> false
+%%
+%%      4) For an "Accept" header with value "text/plain; q=0.5, */*; q=0.1":
+%%         accepts_content_type("application/json") -> true
+%%
+%%      5) For an "Accept" header with value "text/*; q=0.0, */*":
+%%         accepts_content_type("text/plain") -> false
+%%
+accepts_content_type(ContentType) when is_binary(ContentType) ->
+    accepts_content_type(binary_to_list(ContentType));
+accepts_content_type(ContentType1) ->
+    ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
+    AcceptHeader = case get_header_value("Accept") of
+        undefined ->
+            "*/*";
+        Value ->
+            Value
+    end,
+    case mochiweb_util:parse_qvalues(AcceptHeader) of
+        invalid_qvalue_string ->
+            bad_accept_header;
+        QList ->
+            [MainType, _SubType] = string:tokens(ContentType, "/"),
+            SuperType = MainType ++ "/*",
+            lists:any(
+                fun({"*/*", Q}) when Q > 0.0 ->
+                        true;
+                    ({Type, Q}) when Q > 0.0 ->
+                        Type =:= ContentType orelse Type =:= SuperType;
+                    (_) ->
+                        false
+                end,
+                QList
+            ) andalso
+            (not lists:member({ContentType, 0.0}, QList)) andalso
+            (not lists:member({SuperType, 0.0}, QList))
+    end.
+
 %%
 %% Tests
 %%

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/6e0c6598/src/mochiweb_request_tests.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_request_tests.erl b/src/mochiweb_request_tests.erl
new file mode 100644
index 0000000..b61a583
--- /dev/null
+++ b/src/mochiweb_request_tests.erl
@@ -0,0 +1,63 @@
+-module(mochiweb_request_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+-ifdef(TEST).
+
+accepts_content_type_test() ->
+    Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "multipart/related"}])),
+    ?assertEqual(true, Req1:accepts_content_type("multipart/related")),
+
+    Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html"}])),
+    ?assertEqual(false, Req2:accepts_content_type("multipart/related")),
+
+    Req3 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*"}])),
+    ?assertEqual(true, Req3:accepts_content_type("multipart/related")),
+
+    Req4 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*; q=0.0"}])),
+    ?assertEqual(false, Req4:accepts_content_type("multipart/related")),
+
+    Req5 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*; q=0"}])),
+    ?assertEqual(false, Req5:accepts_content_type("multipart/related")),
+
+    Req6 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, */*; q=0.0"}])),
+    ?assertEqual(false, Req6:accepts_content_type("multipart/related")),
+
+    Req7 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "multipart/*; q=0.0, */*"}])),
+    ?assertEqual(false, Req7:accepts_content_type("multipart/related")),
+
+    Req8 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "*/*; q=0.0, multipart/*"}])),
+    ?assertEqual(true, Req8:accepts_content_type("multipart/related")),
+
+    Req9 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "*/*; q=0.0, multipart/related"}])),
+    ?assertEqual(true, Req9:accepts_content_type("multipart/related")),
+
+    Req10 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1"}])),
+    ?assertEqual(true, Req10:accepts_content_type("text/html;level=1")),
+
+    Req11 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1, text/html"}])),
+    ?assertEqual(true, Req11:accepts_content_type("text/html")),
+
+    Req12 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1; q=0.0, text/html"}])),
+    ?assertEqual(false, Req12:accepts_content_type("text/html;level=1")),
+
+    Req13 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1; q=0.0, text/html"}])),
+    ?assertEqual(false, Req13:accepts_content_type("text/html; level=1")),
+
+    Req14 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html;level=1;q=0.1, text/html"}])),
+    ?assertEqual(true, Req14:accepts_content_type("text/html; level=1")).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/6e0c6598/src/mochiweb_util.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb_util.erl b/src/mochiweb_util.erl
index d1cc59d..a22d993 100644
--- a/src/mochiweb_util.erl
+++ b/src/mochiweb_util.erl
@@ -414,7 +414,8 @@ shell_quote([C | Rest], Acc) ->
     shell_quote(Rest, [C | Acc]).
 
 %% @spec parse_qvalues(string()) -> [qvalue()] | invalid_qvalue_string
-%% @type qvalue() = {encoding(), float()}.
+%% @type qvalue() = {media_type() | encoding() , float()}.
+%% @type media_type() = string().
 %% @type encoding() = string().
 %%
 %% @doc Parses a list (given as a string) of elements with Q values associated
@@ -422,7 +423,7 @@ shell_quote([C | Rest], Acc) ->
 %%      from its Q value by a semicolon. Q values are optional but when missing
 %%      the value of an element is considered as 1.0. A Q value is always in the
 %%      range [0.0, 1.0]. A Q value list is used for example as the value of the
-%%      HTTP "Accept-Encoding" header.
+%%      HTTP "Accept" and "Accept-Encoding" headers.
 %%
 %%      Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1).
 %%
@@ -433,29 +434,12 @@ shell_quote([C | Rest], Acc) ->
 %%
 parse_qvalues(QValuesStr) ->
     try
-        {ok, Re} = re:compile("^\\s*q\\s*=\\s*((?:0|1)(?:\\.\\d{1,3})?)\\s*$"),
         lists:map(
             fun(Pair) ->
-                case string:tokens(Pair, ";") of
-                    [Enc] ->
-                        {string:strip(Enc), 1.0};
-                    [Enc, QStr] ->
-                        case re:run(QStr, Re, [{capture, [1], list}]) of
-                            {match, [Q]} ->
-                                QVal = case Q of
-                                    "0" ->
-                                        0.0;
-                                    "1" ->
-                                        1.0;
-                                    Else ->
-                                        list_to_float(Else)
-                                end,
-                                case QVal < 0.0 orelse QVal > 1.0 of
-                                    false ->
-                                        {string:strip(Enc), QVal}
-                                end
-                        end
-                end
+                [Type | Params] = string:tokens(Pair, ";"),
+                NormParams = normalize_media_params(Params),
+                {Q, NonQParams} = extract_q(NormParams),
+                {string:join([string:strip(Type) | NonQParams], ";"), Q}
             end,
             string:tokens(string:to_lower(QValuesStr), ",")
         )
@@ -464,6 +448,46 @@ parse_qvalues(QValuesStr) ->
             invalid_qvalue_string
     end.
 
+normalize_media_params(Params) ->
+    {ok, Re} = re:compile("\\s"),
+    normalize_media_params(Re, Params, []).
+
+normalize_media_params(_Re, [], Acc) ->
+    lists:reverse(Acc);
+normalize_media_params(Re, [Param | Rest], Acc) ->
+    NormParam = re:replace(Param, Re, "", [global, {return, list}]),
+    normalize_media_params(Re, Rest, [NormParam | Acc]).
+
+extract_q(NormParams) ->
+    {ok, KVRe} = re:compile("^([^=]+)=([^=]+)$"),
+    {ok, QRe} = re:compile("^((?:0|1)(?:\\.\\d{1,3})?)$"),
+    extract_q(KVRe, QRe, NormParams, []).
+
+extract_q(_KVRe, _QRe, [], Acc) ->
+    {1.0, lists:reverse(Acc)};
+extract_q(KVRe, QRe, [Param | Rest], Acc) ->
+    case re:run(Param, KVRe, [{capture, [1, 2], list}]) of
+        {match, [Name, Value]} ->
+            case Name of
+            "q" ->
+                {match, [Q]} = re:run(Value, QRe, [{capture, [1], list}]),
+                QVal = case Q of
+                    "0" ->
+                        0.0;
+                    "1" ->
+                        1.0;
+                    Else ->
+                        list_to_float(Else)
+                end,
+                case QVal < 0.0 orelse QVal > 1.0 of
+                false ->
+                    {QVal, lists:reverse(Acc) ++ Rest}
+                end;
+            _ ->
+                extract_q(KVRe, QRe, Rest, [Param | Acc])
+            end
+    end.
+
 %% @spec pick_accepted_encodings([qvalue()], [encoding()], encoding()) ->
 %%    [encoding()]
 %%


Mime
View raw message