You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by ei...@apache.org on 2021/02/17 20:50:55 UTC

[couchdb] 02/02: Read and validate JSON payload on POST to _changes

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

eiri pushed a commit to branch 3087-read-body-on-post-to-changes
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 525716f1ce5f349da683363c73222179887b34a9
Author: Eric Avdey <ei...@eiri.ca>
AuthorDate: Wed Feb 17 16:50:02 2021 -0400

    Read and validate JSON payload on POST to _changes
---
 src/chttpd/src/chttpd_db.erl      |  8 +++++-
 test/elixir/test/changes_test.exs | 58 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl
index 876371f..27a564b 100644
--- a/src/chttpd/src/chttpd_db.erl
+++ b/src/chttpd/src/chttpd_db.erl
@@ -85,7 +85,13 @@ handle_request(#httpd{path_parts=[DbName|RestParts],method=Method}=Req)->
 
 handle_changes_req(#httpd{method='POST'}=Req, Db) ->
     chttpd:validate_ctype(Req, "application/json"),
-    handle_changes_req1(Req, Db);
+    case chttpd:body_length(Req) of
+        0 ->
+            handle_changes_req1(Req, Db);
+        _ ->
+            {JsonProps} = chttpd:json_body_obj(Req),
+            handle_changes_req1(Req#httpd{req_body = {JsonProps}}, Db)
+    end;
 handle_changes_req(#httpd{method='GET'}=Req, Db) ->
     handle_changes_req1(Req, Db);
 handle_changes_req(#httpd{path_parts=[_,<<"_changes">>]}=Req, _Db) ->
diff --git a/test/elixir/test/changes_test.exs b/test/elixir/test/changes_test.exs
index 5bb376b..fe73291 100644
--- a/test/elixir/test/changes_test.exs
+++ b/test/elixir/test/changes_test.exs
@@ -228,6 +228,64 @@ defmodule ChangesTest do
   end
 
   @tag :with_db
+  test "changes filtering on custom filter", context do
+    db_name = context[:db_name]
+    create_filters_view(db_name)
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop")
+    assert Enum.empty?(resp.body["results"]), "db must be empty"
+
+    {:ok, doc_resp} = create_doc(db_name, %{bop: "foom"})
+    rev = doc_resp.body["rev"]
+    create_doc(db_name, %{bop: false})
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop")
+    assert length(resp.body["results"]) == 1
+    change_rev = get_change_rev_at(resp.body["results"], 0)
+    assert change_rev == rev
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop",
+      body: %{doc_ids: ["doc1", "doc3", "doc4"]},
+      headers: ["Content-Type": "application/json"]
+    )
+    assert length(resp.body["results"]) == 1
+    change_rev = get_change_rev_at(resp.body["results"], 0)
+    assert change_rev == rev
+  end
+
+  @tag :with_db
+  test "changes fail on invalid payload", context do
+    db_name = context[:db_name]
+    create_filters_view(db_name)
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop",
+      body: "[\"doc1\"]",
+      headers: ["Content-Type": "application/json"]
+    )
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "Request body must be a JSON object"
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop",
+      body: "{\"doc_ids\": [\"doc1\",",
+      headers: ["Content-Type": "application/json"]
+    )
+    assert resp.status_code == 400
+    assert resp.body["error"] == "bad_request"
+    assert resp.body["reason"] == "invalid UTF-8 JSON"
+
+    set_config({"httpd", "max_http_request_size", "16"})
+
+    resp = Couch.post("/#{db_name}/_changes?filter=changes_filter/bop",
+      body: %{doc_ids: ["doc1", "doc3", "doc4"]},
+      headers: ["Content-Type": "application/json"]
+    )
+    assert resp.status_code == 413
+    assert resp.body["error"] == "too_large"
+    assert resp.body["reason"] == "the request entity is too large"
+  end
+
+  @tag :with_db
   test "COUCHDB-1037-empty result for ?limit=1&filter=foo/bar in some cases",
        context do
     db_name = context[:db_name]