summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Kocoloski <kocolosk@apache.org>2009-08-28 01:10:42 +0000
committerAdam Kocoloski <kocolosk@apache.org>2009-08-28 01:10:42 +0000
commit4b900a07dd82c94d87f873cf0b2a7e0a77cbf7b1 (patch)
treea6644b931f5b22894894a37d0757c62bbc88cb84
parent5160a2e57ab54131f88682fb2f949570fd6da091 (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.js20
-rw-r--r--src/couchdb/couch_httpd_db.erl64
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`"})