Merge branch 'couchdb-2992'
Closes #235
diff --git a/src/couch_doc.erl b/src/couch_doc.erl
index a913eee..381ad4b 100644
--- a/src/couch_doc.erl
+++ b/src/couch_doc.erl
@@ -13,7 +13,7 @@
-module(couch_doc).
-export([to_doc_info/1,to_doc_info_path/1,parse_rev/1,parse_revs/1,rev_to_str/1,revs_to_strs/1]).
--export([from_json_obj/1,to_json_obj/2,has_stubs/1, merge_stubs/2]).
+-export([from_json_obj/1, from_json_obj_validate/1, to_json_obj/2,has_stubs/1, merge_stubs/2]).
-export([validate_docid/1, get_validate_doc_fun/1]).
-export([doc_from_multi_part_stream/2, doc_from_multi_part_stream/3]).
-export([doc_to_multi_part_stream/5, len_doc_to_multi_part_stream/4]).
@@ -124,6 +124,16 @@
++ to_json_attachments(Doc#doc.atts, Options)
}.
+from_json_obj_validate(EJson) ->
+ MaxSize = config:get_integer("couchdb", "max_document_size", 4294967296),
+ Doc = from_json_obj(EJson),
+ case erlang:external_size(Doc#doc.body) =< MaxSize of
+ true ->
+ Doc;
+ false ->
+ throw({request_entity_too_large, Doc#doc.id})
+ end.
+
from_json_obj({Props}) ->
transfer_fields(Props, #doc{body=[]});
@@ -414,7 +424,7 @@
{{started_open_doc_revs, NewRef}, Parser, _ParserRef} ->
restart_open_doc_revs(Parser, Ref, NewRef);
{{doc_bytes, Ref, DocBytes}, Parser, ParserRef} ->
- Doc = from_json_obj(?JSON_DECODE(DocBytes)),
+ Doc = from_json_obj_validate(?JSON_DECODE(DocBytes)),
erlang:put(mochiweb_request_recv, true),
% we'll send the Parser process ID to the remote nodes so they can
% retrieve their own copies of the attachment data
diff --git a/src/couch_httpd_db.erl b/src/couch_httpd_db.erl
index 3793a06..e1af1bf 100644
--- a/src/couch_httpd_db.erl
+++ b/src/couch_httpd_db.erl
@@ -256,7 +256,7 @@
db_req(#httpd{method='POST',path_parts=[_DbName]}=Req, Db) ->
couch_httpd:validate_ctype(Req, "application/json"),
- Doc = couch_doc:from_json_obj(couch_httpd:json_body(Req)),
+ Doc = couch_doc:from_json_obj_validate(couch_httpd:json_body(Req)),
validate_attachment_names(Doc),
Doc2 = case Doc#doc.id of
<<"">> ->
@@ -319,7 +319,7 @@
true ->
Docs = lists:map(
fun({ObjProps} = JsonObj) ->
- Doc = couch_doc:from_json_obj(JsonObj),
+ Doc = couch_doc:from_json_obj_validate(JsonObj),
validate_attachment_names(Doc),
Id = case Doc#doc.id of
<<>> -> couch_uuids:new();
@@ -353,7 +353,7 @@
end;
false ->
Docs = lists:map(fun(JsonObj) ->
- Doc = couch_doc:from_json_obj(JsonObj),
+ Doc = couch_doc:from_json_obj_validate(JsonObj),
validate_attachment_names(Doc),
Doc
end, DocsArray),
@@ -809,7 +809,7 @@
end,
Doc#doc{id=DocId, revs=Revs2};
couch_doc_from_req(Req, DocId, Json) ->
- couch_doc_from_req(Req, DocId, couch_doc:from_json_obj(Json)).
+ couch_doc_from_req(Req, DocId, couch_doc:from_json_obj_validate(Json)).
% Useful for debugging
% couch_doc_open(Db, DocId) ->
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index 9003d06..ce099d1 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -38,6 +38,8 @@
ok;
mock(config) ->
meck:new(config, [passthrough]),
+ meck:expect(config, get_integer,
+ fun("couchdb", "max_document_size", 4294967296) -> 1024 end),
meck:expect(config, get, fun(_, _) -> undefined end),
meck:expect(config, get, fun(_, _, Default) -> Default end),
ok.
@@ -165,7 +167,7 @@
],
lists:map(
fun({EJson, Expect, Msg}) ->
- {Msg, ?_assertMatch(Expect, couch_doc:from_json_obj(EJson))}
+ {Msg, ?_assertMatch(Expect, couch_doc:from_json_obj_validate(EJson))}
end,
Cases).
@@ -230,16 +232,29 @@
{[{<<"_something">>, 5}]},
{doc_validation, <<"Bad special document member: _something">>},
"Underscore prefix fields are reserved."
+ },
+ {
+ fun() ->
+ {[
+ {<<"_id">>, <<"large_doc">>},
+ {<<"x">> , << <<"x">> || _ <- lists:seq(1,1025) >>}
+ ]}
+ end,
+ {request_entity_too_large, <<"large_doc">>},
+ "Document too large."
}
],
lists:map(fun
+ ({Fun, Expect, Msg}) when is_function(Fun, 0) ->
+ Error = (catch couch_doc:from_json_obj_validate(Fun())),
+ {Msg, ?_assertMatch(Expect, Error)};
({EJson, Expect, Msg}) ->
- Error = (catch couch_doc:from_json_obj(EJson)),
+ Error = (catch couch_doc:from_json_obj_validate(EJson)),
{Msg, ?_assertMatch(Expect, Error)};
({EJson, Msg}) ->
try
- couch_doc:from_json_obj(EJson),
+ couch_doc:from_json_obj_validate(EJson),
{"Conversion failed to raise an exception", ?_assert(false)}
catch
_:_ -> {Msg, ?_assert(true)}