You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ga...@apache.org on 2017/08/09 13:23:34 UTC

[couchdb] branch master updated: Add Prefer: return=minimal support (#605)

This is an automated email from the ASF dual-hosted git repository.

garren pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/couchdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 52da19b  Add Prefer: return=minimal support (#605)
52da19b is described below

commit 52da19b9c0f75e0afbb3267b8766343c270fdcf7
Author: garren smith <ga...@gmail.com>
AuthorDate: Wed Aug 9 15:23:32 2017 +0200

    Add Prefer: return=minimal support (#605)
    
    Use the Prefer: return=minimal header options from
    [rfc7240](https://tools.ietf.org/html/rfc7240) to reduce the
    number of headers in the response. The header is configurable via the config
---
 rel/overlay/etc/default.ini                   |   3 +
 src/chttpd/src/chttpd.erl                     |   5 +-
 src/chttpd/src/chttpd_prefer_header.erl       |  54 +++++++++++++
 src/chttpd/test/chttpd_prefer_header_test.erl | 109 ++++++++++++++++++++++++++
 src/couch/src/couch_httpd.erl                 |   6 +-
 5 files changed, 173 insertions(+), 4 deletions(-)

diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini
index d840ef8..30b2efa 100644
--- a/rel/overlay/etc/default.ini
+++ b/rel/overlay/etc/default.ini
@@ -57,6 +57,9 @@ backlog = 512
 docroot = {{fauxton_root}}
 socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
 require_valid_user = false
+; List of headers that will be kept when the header Prefer: return=minimal is included in a request.
+; If Server header is left out, Mochiweb will add its own one in.
+prefer_minimal = Cache-Control, Content-Length, Content-Range, Content-Type, ETag, Server, Transfer-Encoding, Vary
 
 [database_compaction]
 ; larger buffer sizes can originate smaller files
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl
index cfefb78..178209b 100644
--- a/src/chttpd/src/chttpd.erl
+++ b/src/chttpd/src/chttpd.erl
@@ -1115,8 +1115,9 @@ basic_headers(Req, Headers0) ->
     Headers = Headers0
         ++ server_header()
         ++ couch_httpd_auth:cookie_auth_header(Req, Headers0),
-    Headers1 = chttpd_xframe_options:header(Req, Headers),
-    chttpd_cors:headers(Req, Headers1).
+    Headers1 = chttpd_cors:headers(Req, Headers),
+	Headers2 = chttpd_xframe_options:header(Req, Headers1),
+    chttpd_prefer_header:maybe_return_minimal(Req, Headers2).
 
 handle_response(Req0, Code0, Headers0, Args0, Type) ->
     {ok, {Req1, Code1, Headers1, Args1}} =
diff --git a/src/chttpd/src/chttpd_prefer_header.erl b/src/chttpd/src/chttpd_prefer_header.erl
new file mode 100644
index 0000000..f550e80
--- /dev/null
+++ b/src/chttpd/src/chttpd_prefer_header.erl
@@ -0,0 +1,54 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(chttpd_prefer_header).
+
+
+-export([
+    maybe_return_minimal/2
+]).
+
+
+-include_lib("couch/include/couch_db.hrl").
+
+
+maybe_return_minimal(#httpd{mochi_req = MochiReq}, Headers) ->
+    case get_prefer_header(MochiReq) of
+        "return=minimal" -> 
+            filter_headers(Headers, get_header_list());
+        _ -> 
+            Headers
+    end.
+
+
+get_prefer_header(Req) ->
+    case Req:get_header_value("Prefer") of
+        Value when is_list(Value) ->
+            string:to_lower(Value);
+        undefined -> 
+            undefined
+    end.
+
+
+filter_headers(Headers, IncludeList) ->
+    lists:filter(fun({HeaderName, _}) -> 
+        lists:member(HeaderName, IncludeList)
+    end, Headers).
+
+
+get_header_list() ->
+    SectionStr = config:get("chttpd", "prefer_minimal", ""),
+    split_list(SectionStr).
+
+
+split_list(S) ->
+    re:split(S, "\\s*,\\s*", [trim, {return, list}]). 
diff --git a/src/chttpd/test/chttpd_prefer_header_test.erl b/src/chttpd/test/chttpd_prefer_header_test.erl
new file mode 100644
index 0000000..a8a5b3d
--- /dev/null
+++ b/src/chttpd/test/chttpd_prefer_header_test.erl
@@ -0,0 +1,109 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(chttpd_prefer_header_test).
+-include_lib("couch/include/couch_db.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+
+mock_request(ExcludeHeader) ->
+    Headers = mochiweb_headers:make(ExcludeHeader),
+    MochiReq = mochiweb_request:new(nil, 'GET', "/", {1, 1}, Headers),
+    MochiReq:cleanup(),
+    #httpd{mochi_req = MochiReq}.
+
+
+default_headers() ->
+    [
+        {"Cache-Control","must-revalidate"},
+        {"Content-Type","application/json"},
+        {"Content-Length", "100"},
+        {"ETag","\"12343\""},
+        {"X-Couch-Request-ID","7bd1adab86"},
+        {"X-CouchDB-Body-Time","0"},
+        {"Vary", "Accept-Encoding"},
+        {"Server","CouchDB/2.1.0-f1a1d7f1c (Erlang OTP/19)"}
+    ].
+
+
+minimal_options_headers() ->
+    [
+        {"Cache-Control","must-revalidate"},
+        {"Content-Type","application/json"},
+        {"Content-Length", "100"},
+        {"ETag","\"12343\""},
+        {"Vary", "Accept-Encoding"},
+        {"Server","CouchDB/2.1.0-f1a1d7f1c (Erlang OTP/19)"}
+    ].
+
+
+default_no_exclude_header_test() ->
+    Headers = chttpd_prefer_header:maybe_return_minimal(
+        mock_request([]), 
+        default_headers()
+        ),
+    ?assertEqual(default_headers(), Headers).
+
+
+unsupported_exclude_header_test() ->
+    Req = mock_request([{"prefer", "Wrong"}]),
+    Headers = chttpd_prefer_header:maybe_return_minimal(Req, default_headers()),
+    ?assertEqual(default_headers(), Headers).
+
+
+empty_header_test() ->
+    Req = mock_request([{"prefer", ""}]),
+    Headers = chttpd_prefer_header:maybe_return_minimal(Req, default_headers()),
+    ?assertEqual(default_headers(), Headers).
+
+setup() ->
+    ok = meck:new(config),
+    ok = meck:expect(config, get, fun("chttpd", "prefer_minimal",  _) -> 
+        "Cache-Control, Content-Length, Content-Type, ETag, Server, Vary"
+    end),
+    ok.
+
+
+teardown(_) ->
+    meck:unload(config).
+
+
+exclude_headers_test_() ->
+     {
+         "Test Prefer headers",
+         {
+             foreach, fun setup/0, fun teardown/1,
+             [
+                 fun minimal_options/1,
+                 fun minimal_options_check_header_case/1,
+                 fun minimal_options_check_header_value_case/1
+             ]
+         }
+     }.
+
+
+minimal_options(_) ->
+    Req = mock_request([{"Prefer", "return=minimal"}]),
+    Headers = chttpd_prefer_header:maybe_return_minimal(Req, default_headers()),
+    ?_assertEqual(minimal_options_headers(), Headers).
+
+
+minimal_options_check_header_case(_) ->
+    Req = mock_request([{"prefer", "return=minimal"}]),
+    Headers = chttpd_prefer_header:maybe_return_minimal(Req, default_headers()),
+    ?_assertEqual(minimal_options_headers(), Headers).
+
+
+minimal_options_check_header_value_case(_) ->
+    Req = mock_request([{"prefer", "RETURN=MINIMAL"}]),
+    Headers = chttpd_prefer_header:maybe_return_minimal(Req, default_headers()),
+    ?_assertEqual(minimal_options_headers(), Headers).
\ No newline at end of file
diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl
index 95b7074..faaf080 100644
--- a/src/couch/src/couch_httpd.erl
+++ b/src/couch/src/couch_httpd.erl
@@ -755,7 +755,8 @@ send_response_no_cors(#httpd{mochi_req=MochiReq}=Req, Code, Headers, Body) ->
     Headers1 = http_1_0_keep_alive(MochiReq, Headers),
     Headers2 = basic_headers_no_cors(Req, Headers1),
     Headers3 = chttpd_xframe_options:header(Req, Headers2),
-    Resp = handle_response(Req, Code, Headers3, Body, respond),
+	Headers4 = chttpd_prefer_header:maybe_return_minimal(Req, Headers3),
+    Resp = handle_response(Req, Code, Headers4, Body, respond),
     log_response(Code, Body),
     {ok, Resp}.
 
@@ -1134,7 +1135,8 @@ validate_bind_address(Address) ->
 
 add_headers(Req, Headers0) ->
     Headers = basic_headers(Req, Headers0),
-    http_1_0_keep_alive(Req, Headers).
+    Headers1 = http_1_0_keep_alive(Req, Headers),
+    chttpd_prefer_header:maybe_return_minimal(Req, Headers1).
 
 basic_headers(Req, Headers0) ->
     Headers1 = basic_headers_no_cors(Req, Headers0),

-- 
To stop receiving notification emails like this one, please contact
['"commits@couchdb.apache.org" <co...@couchdb.apache.org>'].