diff options
author | Adam Kocoloski <kocolosk@apache.org> | 2009-08-28 01:10:42 +0000 |
---|---|---|
committer | Adam Kocoloski <kocolosk@apache.org> | 2009-08-28 01:10:42 +0000 |
commit | 4b900a07dd82c94d87f873cf0b2a7e0a77cbf7b1 (patch) | |
tree | a6644b931f5b22894894a37d0757c62bbc88cb84 | |
parent | 5160a2e57ab54131f88682fb2f949570fd6da091 (diff) |
guard against non-existing filters in _changes
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@808716 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r-- | share/www/script/test/changes.js | 20 | ||||
-rw-r--r-- | src/couchdb/couch_httpd_db.erl | 64 |
2 files changed, 57 insertions, 27 deletions
diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js index 1adfaa1b..314a52b5 100644 --- a/share/www/script/test/changes.js +++ b/share/www/script/test/changes.js @@ -188,7 +188,25 @@ couchTests.changes = function(debug) { req = CouchDB.request("GET", "/test_suite_db/_changes?filter=changes_filter/dynamic&field=bop"); resp = JSON.parse(req.responseText); T(resp.results.length == 1); - + + // error conditions + + // non-existing design doc + var req = CouchDB.request("GET", + "/test_suite_db/_changes?filter=nothingtosee/bop"); + TEquals(400, req.status, "should return 400 for non existant design doc"); + + // non-existing filter + var req = CouchDB.request("GET", + "/test_suite_db/_changes?filter=changes_filter/movealong"); + TEquals(400, req.status, "should return 400 for non existant filter fun"); + + // both + var req = CouchDB.request("GET", + "/test_suite_db/_changes?filter=nothingtosee/movealong"); + TEquals(400, req.status, + "should return 400 for non existant design doc and filter fun"); + // changes get all_docs style with deleted docs var doc = {a:1}; db.save(doc); diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 8bcacfdc..1a18c4ba 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -72,6 +72,7 @@ start_sending_changes(Resp, _Else) -> send_chunk(Resp, "{\"results\":[\n"). handle_changes_req(#httpd{method='GET',path_parts=[DbName|_]}=Req, Db) -> + {FilterFun, EndFilterFun} = make_filter_funs(Req, Db), StartSeq = list_to_integer(couch_httpd:qs_value(Req, "since", "0")), {ok, Resp} = start_json_response(Req, 200), ResponseType = couch_httpd:qs_value(Req, "feed", "normal"), @@ -88,14 +89,16 @@ handle_changes_req(#httpd{method='GET',path_parts=[DbName|_]}=Req, Db) -> couch_stats_collector:track_process_count(Self, {httpd, clients_requesting_changes}), try - keep_sending_changes(Req, Resp, Db, StartSeq, <<"">>, Timeout, TimeoutFun, ResponseType) + keep_sending_changes(Req, Resp, Db, StartSeq, <<"">>, Timeout, + TimeoutFun, ResponseType, FilterFun, EndFilterFun) after couch_db_update_notifier:stop(Notify), get_rest_db_updated() % clean out any remaining update messages end; true -> {ok, {LastSeq, _Prepend, _, _, _}} = - send_changes(Req, Resp, Db, StartSeq, <<"">>, "normal"), + send_changes(Req, Resp, Db, StartSeq, <<"">>, "normal", + FilterFun, EndFilterFun), end_sending_changes(Resp, LastSeq, ResponseType) end; @@ -125,9 +128,9 @@ end_sending_changes(Resp, EndSeq, _Else) -> end_json_response(Resp). keep_sending_changes(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Resp, - Db, StartSeq, Prepend, Timeout, TimeoutFun, ResponseType) -> + Db, StartSeq, Prepend, Timeout, TimeoutFun, ResponseType, Filter, End) -> {ok, {EndSeq, Prepend2, _, _, _}} = send_changes(Req, Resp, Db, StartSeq, - Prepend, ResponseType), + Prepend, ResponseType, Filter, End), couch_db:close(Db), if EndSeq > StartSeq, ResponseType == "longpoll" -> @@ -136,7 +139,8 @@ keep_sending_changes(#httpd{user_ctx=UserCtx,path_parts=[DbName|_]}=Req, Resp, case wait_db_updated(Timeout, TimeoutFun) of updated -> {ok, Db2} = couch_db:open(DbName, [{user_ctx, UserCtx}]), - keep_sending_changes(Req, Resp, Db2, EndSeq, Prepend2, Timeout, TimeoutFun, ResponseType); + keep_sending_changes(Req, Resp, Db2, EndSeq, Prepend2, Timeout, + TimeoutFun, ResponseType, Filter, End); stop -> end_sending_changes(Resp, EndSeq, ResponseType) end @@ -167,15 +171,14 @@ changes_enumerator(DocInfos, {_, Prepend, FilterFun, Resp, _}) -> {ok, {Seq, <<",\n">>, FilterFun, Resp, nil}} end. -send_changes(Req, Resp, Db, StartSeq, Prepend, ResponseType) -> +send_changes(Req, Resp, Db, StartSeq, Prepend, ResponseType, FilterFun, End) -> Style = list_to_existing_atom( couch_httpd:qs_value(Req, "style", "main_only")), - {FilterFun, EndFilterFun} = make_filter_funs(Req, Db), try couch_db:changes_since(Db, Style, StartSeq, fun changes_enumerator/2, {StartSeq, Prepend, FilterFun, Resp, ResponseType}) after - EndFilterFun() + End() end. make_filter_funs(Req, Db) -> @@ -189,24 +192,33 @@ make_filter_funs(Req, Db) -> fun() -> ok end}; [DName, FName] -> DesignId = <<"_design/", DName/binary>>, - #doc{body={Props}} = couch_httpd_db:couch_doc_open(Db, DesignId, nil, []), - Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>), - FilterSrc = couch_util:get_nested_json_value({Props}, [<<"filters">>, FName]), - {ok, Pid} = couch_query_servers:start_filter(Lang, FilterSrc), - FilterFun = fun(DInfo = #doc_info{revs=[#rev_info{rev=Rev}|_]}) -> - {ok, Doc} = couch_db:open_doc(Db, DInfo, [deleted]), - {ok, Pass} = couch_query_servers:filter_doc(Pid, Doc, Req, Db), - case Pass of - true -> - {[{rev, couch_doc:rev_to_str(Rev)}]}; - false -> - null - end - end, - EndFilterFun = fun() -> - couch_query_servers:end_filter(Pid) - end, - {FilterFun, EndFilterFun}; + case couch_db:open_doc(Db, DesignId) of + {ok, #doc{body={Props}}} -> + FilterSrc = try couch_util:get_nested_json_value({Props}, + [<<"filters">>, FName]) + catch + throw:{not_found, _} -> + throw({bad_request, "invalid filter function"}) + end, + Lang = proplists:get_value(<<"language">>, Props, <<"javascript">>), + {ok, Pid} = couch_query_servers:start_filter(Lang, FilterSrc), + FilterFun = fun(DInfo = #doc_info{revs=[#rev_info{rev=Rev}|_]}) -> + {ok, Doc} = couch_db:open_doc(Db, DInfo, [deleted]), + {ok, Pass} = couch_query_servers:filter_doc(Pid, Doc, Req, Db), + case Pass of + true -> + {[{rev, couch_doc:rev_to_str(Rev)}]}; + false -> + null + end + end, + EndFilterFun = fun() -> + couch_query_servers:end_filter(Pid) + end, + {FilterFun, EndFilterFun}; + _Error -> + throw({bad_request, "invalid design doc"}) + end; _Else -> throw({bad_request, "filter parameter must be of the form `designname/filtername`"}) |