Merge branch 'couchdb-3293'
Closes #226
diff --git a/src/couch_doc.erl b/src/couch_doc.erl
index af14038..a913eee 100644
--- a/src/couch_doc.erl
+++ b/src/couch_doc.erl
@@ -174,6 +174,14 @@
validate_docid(<<"_local/">>) ->
throw({illegal_docid, <<"Illegal document id `_local/`">>});
validate_docid(Id) when is_binary(Id) ->
+ MaxLen = case config:get("couchdb", "max_document_id_length", "infinity") of
+ "infinity" -> infinity;
+ IntegerVal -> list_to_integer(IntegerVal)
+ end,
+ case MaxLen > 0 andalso byte_size(Id) > MaxLen of
+ true -> throw({illegal_docid, <<"Document id is too long">>});
+ false -> ok
+ end,
case couch_util:validate_utf8(Id) of
false -> throw({illegal_docid, <<"Document id must be valid UTF-8">>});
true -> ok
diff --git a/test/couch_doc_json_tests.erl b/test/couch_doc_json_tests.erl
index ae4d73c..9003d06 100644
--- a/test/couch_doc_json_tests.erl
+++ b/test/couch_doc_json_tests.erl
@@ -18,11 +18,13 @@
setup() ->
mock(couch_log),
+ mock(config),
mock(couch_db_plugin),
ok.
teardown(_) ->
meck:unload(couch_log),
+ meck:unload(config),
meck:unload(couch_db_plugin),
ok.
@@ -33,6 +35,11 @@
mock(couch_log) ->
ok = meck:new(couch_log, [passthrough]),
ok = meck:expect(couch_log, debug, fun(_, _) -> ok end),
+ ok;
+mock(config) ->
+ meck:new(config, [passthrough]),
+ meck:expect(config, get, fun(_, _) -> undefined end),
+ meck:expect(config, get, fun(_, _, Default) -> Default end),
ok.
diff --git a/test/couch_doc_tests.erl b/test/couch_doc_tests.erl
index fce4ff7..d24cd67 100644
--- a/test/couch_doc_tests.erl
+++ b/test/couch_doc_tests.erl
@@ -29,8 +29,10 @@
ContentType = "multipart/related;boundary=multipart_related_boundary~~~~~~~~~~~~~~~~~~~~",
DataFun = fun() -> request(start) end,
+ mock_config_max_document_id_length(),
{ok, #doc{id = <<"doc0">>, atts = [_]}, _Fun, _Parser} =
couch_doc:doc_from_multi_part_stream(ContentType, DataFun),
+ meck:unload(config),
ok.
doc_to_multi_part_stream_test() ->
@@ -75,16 +77,19 @@
validate_docid_test_() ->
{setup,
fun() ->
+ mock_config_max_document_id_length(),
ok = meck:new(couch_db_plugin, [passthrough]),
meck:expect(couch_db_plugin, validate_docid, fun(_) -> false end)
end,
fun(_) ->
+ meck:unload(config),
meck:unload(couch_db_plugin)
end,
[
?_assertEqual(ok, couch_doc:validate_docid(<<"idx">>)),
?_assertEqual(ok, couch_doc:validate_docid(<<"_design/idx">>)),
?_assertEqual(ok, couch_doc:validate_docid(<<"_local/idx">>)),
+ ?_assertEqual(ok, couch_doc:validate_docid(large_id(1024))),
?_assertThrow({illegal_docid, _},
couch_doc:validate_docid(<<>>)),
?_assertThrow({illegal_docid, _},
@@ -96,10 +101,15 @@
?_assertThrow({illegal_docid, _},
couch_doc:validate_docid(<<"_design/">>)),
?_assertThrow({illegal_docid, _},
- couch_doc:validate_docid(<<"_local/">>))
+ couch_doc:validate_docid(<<"_local/">>)),
+ ?_assertThrow({illegal_docid, _},
+ couch_doc:validate_docid(large_id(1025)))
]
}.
+large_id(N) ->
+ << <<"x">> || _ <- lists:seq(1, N) >>.
+
request(start) ->
{ok, Doc} = file:read_file(?REQUEST_FIXTURE),
{Doc, fun() -> request(stop) end};
@@ -116,3 +126,11 @@
collected() ->
B = binary:replace(iolist_to_binary(get(data)), <<"\r\n">>, <<0>>, [global]),
binary:split(B, [<<0>>], [global]).
+
+mock_config_max_document_id_length() ->
+ ok = meck:new(config, [passthrough]),
+ meck:expect(config, get,
+ fun("couchdb", "max_document_id_length", "infinity") -> "1024";
+ (Key, Val, Default) -> meck:passthrough([Key, Val, Default])
+ end
+ ).