You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/02/12 07:21:57 UTC

[25/50] [abbrv] mochiweb commit: updated refs/heads/import-master to 3a54dbf

Allow storing attachments in compressed form. Closes COUCHDB-583. Thanks Filipe Manana

git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@904650 13f79535-47bb-0310-9956-ffa450edef68


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

Branch: refs/heads/import-master
Commit: ed7cd3d3936f79333f6ed2b7f3a542c0b0b883c8
Parents: 72d0597
Author: John Christopher Anderson <jc...@apache.org>
Authored: Fri Jan 29 22:43:33 2010 +0000
Committer: John Christopher Anderson <jc...@apache.org>
Committed: Fri Jan 29 22:43:33 2010 +0000

----------------------------------------------------------------------
 mochiweb_request.erl |  41 +++++++
 mochiweb_util.erl    | 282 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 323 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ed7cd3d3/mochiweb_request.erl
----------------------------------------------------------------------
diff --git a/mochiweb_request.erl b/mochiweb_request.erl
index fc296f4..5d7af26 100644
--- a/mochiweb_request.erl
+++ b/mochiweb_request.erl
@@ -20,6 +20,7 @@
 -export([should_close/0, cleanup/0]).
 -export([parse_cookie/0, get_cookie_value/1]).
 -export([serve_file/2, serve_file/3]).
+-export([accepted_encodings/1]).
 -export([test/0]).
 
 -define(SAVE_QS, mochiweb_request_qs).
@@ -730,6 +731,46 @@ parse_range_request(RawRange) when is_list(RawRange) ->
             fail
     end.
 
+%% @spec accepted_encodings([encoding()]) -> [encoding()] | error()
+%% @type encoding() -> string()
+%% @type error() -> bad_accept_encoding_value
+%%
+%% @doc Returns a list of encodings accepted by a request. Encodings that are
+%%      not supported by the server will not be included in the return list.
+%%      This list is computed from the "Accept-Encoding" header and
+%%      its elements are ordered, descendingly, according to their Q values.
+%%
+%%      Section 14.3 of the RFC 2616 (HTTP 1.1) describes the "Accept-Encoding"
+%%      header and the process of determining which server supported encodings
+%%      can be used for encoding the body for the request's response.
+%%
+%%      Examples
+%%
+%%      1) For a missing "Accept-Encoding" header:
+%%         accepted_encodings(["gzip", "identity"]) -> ["identity"]
+%%
+%%      2) For an "Accept-Encoding" header with value "gzip, deflate":
+%%         accepted_encodings(["gzip", "identity"]) -> ["gzip", "identity"]
+%%
+%%      3) For an "Accept-Encoding" header with value "gzip;q=0.5, deflate":
+%%         accepted_encodings(["gzip", "deflate", "identity"]) ->
+%%            ["deflate", "gzip", "identity"]
+%%
+accepted_encodings(SupportedEncodings) ->
+    AcceptEncodingHeader = case get_header_value("Accept-Encoding") of
+        undefined ->
+            "";
+        Value ->
+            Value
+    end,
+    case mochiweb_util:parse_qvalues(AcceptEncodingHeader) of
+        invalid_qvalue_string ->
+            bad_accept_encoding_value;
+        QList ->
+            mochiweb_util:pick_accepted_encodings(
+                QList, SupportedEncodings, "identity"
+            )
+    end.
 
 test() ->
     ok = test_range(),

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ed7cd3d3/mochiweb_util.erl
----------------------------------------------------------------------
diff --git a/mochiweb_util.erl b/mochiweb_util.erl
index 73cacea..d8fc89d 100644
--- a/mochiweb_util.erl
+++ b/mochiweb_util.erl
@@ -12,6 +12,7 @@
 -export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]).
 -export([record_to_proplist/2, record_to_proplist/3]).
 -export([safe_relative_path/1, partition/2]).
+-export([parse_qvalues/1, pick_accepted_encodings/3]).
 -export([test/0]).
 
 -define(PERCENT, 37).  % $\%
@@ -435,6 +436,136 @@ shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse
 shell_quote([C | Rest], Acc) ->
     shell_quote(Rest, [C | Acc]).
 
+%% @spec parse_qvalues(string()) -> [qvalue()] | error()
+%% @type qvalue() -> {element(), q()}
+%% @type element() -> string()
+%% @type q() -> 0.0 .. 1.0
+%% @type error() -> invalid_qvalue_string
+%%
+%% @doc Parses a list (given as a string) of elements with Q values associated
+%%      to them. Elements are separated by commas and each element is separated
+%%      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.
+%%
+%%      Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1).
+%%
+%%      Example:
+%%
+%%      parse_qvalues("gzip; q=0.5, deflate, identity;q=0.0") ->
+%%          [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}]
+%%
+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
+            end,
+            string:tokens(string:to_lower(QValuesStr), ",")
+        )
+    catch
+        _Type:_Error ->
+            invalid_qvalue_string
+    end.
+
+%% @spec pick_accepted_encodings(qvalues(), [encoding()], encoding()) ->
+%%    [encoding()]
+%% @type qvalues() -> [ {encoding(), q()} ]
+%% @type encoding() -> string()
+%% @type q() -> 0.0 .. 1.0
+%%
+%% @doc Determines which encodings specified in the given Q values list are
+%%      valid according to a list of supported encodings and a default encoding.
+%%
+%%      The returned list of encodings is sorted, descendingly, according to the
+%%      Q values of the given list. The last element of this list is the given
+%%      default encoding unless this encoding is explicitily or implicitily
+%%      marked with a Q value of 0.0 in the given Q values list.
+%%      Note: encodings with the same Q value are kept in the same order as
+%%            found in the input Q values list.
+%%
+%%      This encoding picking process is described in section 14.3 of the
+%%      RFC 2616 (HTTP 1.1).
+%%
+%%      Example:
+%%
+%%      pick_accepted_encodings(
+%%          [{"gzip", 0.5}, {"deflate", 1.0}],
+%%          ["gzip", "identity"],
+%%          "identity"
+%%      ) ->
+%%          ["gzip", "identity"]
+%%
+pick_accepted_encodings(AcceptedEncs, SupportedEncs, DefaultEnc) ->
+    SortedQList = lists:reverse(
+        lists:sort(fun({_, Q1}, {_, Q2}) -> Q1 < Q2 end, AcceptedEncs)
+    ),
+    {Accepted, Refused} = lists:foldr(
+        fun({E, Q}, {A, R}) ->
+            case Q > 0.0 of
+                true ->
+                    {[E | A], R};
+                false ->
+                    {A, [E | R]}
+            end
+        end,
+        {[], []},
+        SortedQList
+    ),
+    Refused1 = lists:foldr(
+        fun(Enc, Acc) ->
+            case Enc of
+                "*" ->
+                    lists:subtract(SupportedEncs, Accepted) ++ Acc;
+                _ ->
+                    [Enc | Acc]
+            end
+        end,
+        [],
+        Refused
+    ),
+    Accepted1 = lists:foldr(
+        fun(Enc, Acc) ->
+            case Enc of
+                "*" ->
+                    lists:subtract(SupportedEncs, Accepted ++ Refused1) ++ Acc;
+                _ ->
+                    [Enc | Acc]
+            end
+        end,
+        [],
+        Accepted
+    ),
+    Accepted2 = case lists:member(DefaultEnc, Accepted1) of
+        true ->
+            Accepted1;
+        false ->
+            Accepted1 ++ [DefaultEnc]
+    end,
+    [E || E <- Accepted2, lists:member(E, SupportedEncs),
+        not lists:member(E, Refused1)].
+
 test() ->
     test_join(),
     test_quote_plus(),
@@ -453,6 +584,8 @@ test() ->
     test_cmd_string(),
     test_partition(),
     test_safe_relative_path(),
+    test_parse_qvalues(),
+    test_pick_accepted_encodings(),
     ok.
 
 test_shell_quote() ->
@@ -575,3 +708,152 @@ test_safe_relative_path() ->
     undefined = safe_relative_path("foo/../.."),
     undefined = safe_relative_path("foo//"),
     ok.
+
+test_parse_qvalues() ->
+    [] = parse_qvalues(""),
+    [{"identity", 0.0}] = parse_qvalues("identity;q=0"),
+    [{"identity", 0.0}] = parse_qvalues("identity ;q=0"),
+    [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "),
+    [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"),
+    [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"),
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "gzip,deflate,identity;q=0.0"
+    ),
+    [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "deflate,gzip,identity;q=0.0"
+    ),
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] =
+        parse_qvalues("gzip,deflate,gzip,identity;q=0"),
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "gzip, deflate , identity; q=0.0"
+    ),
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "gzip; q=1, deflate;q=1.0, identity;q=0.0"
+    ),
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "gzip; q=0.5, deflate;q=1.0, identity;q=0"
+    ),
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+        "gzip; q=0.5, deflate , identity;q=0.0"
+    ),
+    [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues(
+        "gzip; q=0.5, deflate;q=0.8, identity;q=0.0"
+    ),
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues(
+        "gzip; q=0.5,deflate,identity"
+    ),
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}, {"identity", 1.0}] =
+        parse_qvalues("gzip; q=0.5,deflate,identity, identity "),
+    invalid_qvalue_string = parse_qvalues("gzip; q=1.1, deflate"),
+    invalid_qvalue_string = parse_qvalues("gzip; q=0.5, deflate;q=2"),
+    invalid_qvalue_string = parse_qvalues("gzip, deflate;q=AB"),
+    invalid_qvalue_string = parse_qvalues("gzip; q=2.1, deflate"),
+    ok.
+
+test_pick_accepted_encodings() ->
+    ["identity"] = pick_accepted_encodings(
+        [],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 1.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["identity"] = pick_accepted_encodings(
+        [{"gzip", 0.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"deflate", 1.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 0.5}, {"deflate", 1.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["identity"] = pick_accepted_encodings(
+        [{"gzip", 0.0}, {"deflate", 0.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["gzip"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
+        ["gzip", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate", "identity"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"deflate", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["deflate", "gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 0.2}, {"deflate", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["deflate", "deflate", "gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 0.2}, {"deflate", 1.0}, {"deflate", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["deflate", "gzip", "gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 0.2}, {"deflate", 1.0}, {"gzip", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate", "gzip", "identity"] = pick_accepted_encodings(
+        [{"gzip", 0.2}, {"deflate", 0.9}, {"gzip", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    [] = pick_accepted_encodings(
+        [{"*", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate", "identity"] = pick_accepted_encodings(
+        [{"*", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate", "identity"] = pick_accepted_encodings(
+        [{"*", 0.6}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"*", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "deflate"] = pick_accepted_encodings(
+        [{"gzip", 1.0}, {"deflate", 0.6}, {"*", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["deflate", "gzip"] = pick_accepted_encodings(
+        [{"gzip", 0.5}, {"deflate", 1.0}, {"*", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "identity"] = pick_accepted_encodings(
+        [{"deflate", 0.0}, {"*", 1.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ["gzip", "identity"] = pick_accepted_encodings(
+        [{"*", 1.0}, {"deflate", 0.0}],
+        ["gzip", "deflate", "identity"],
+        "identity"
+    ),
+    ok.