Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Allow limiting length of document ID
Previously it was not possibly to define a maxum document ID size. That meant
large document ID would hit various limitations and corner cases. For example,
large document IDs could be inserted via a _bulk_docs endpoint but then trying
to insert the same document via a single HTTP method like PUT would fail
because of a limitation in Mochiweb's HTTP parser.

Let operators specify a maxium document ID length via the

```
couchdb.max_document_id_length = infinity | Integer
```

configuration.

The default value of `infinity` keeps the current behavior where document
ID length is not checked.

COUCHDB-3293
  • Loading branch information
nickva committed Feb 9, 2017
1 parent bbbd532 commit 2a263f84db62e4849a41322b92588c6893169198
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 1 deletion.
@@ -174,6 +174,14 @@ validate_docid(<<"_design/">>) ->
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
@@ -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_db_plugin) ->
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.


@@ -29,8 +29,10 @@ doc_from_multi_part_stream_test() ->
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 @@ len_doc_to_multi_part_stream_test() ->
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 @@ validate_docid_test_() ->
?_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 @@ send(Data, Acc) ->
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
).

0 comments on commit 2a263f8

Please sign in to comment.