From 0c198005f6a84d948e11354912bb6178f3f852eb Mon Sep 17 00:00:00 2001 From: John Christopher Anderson Date: Sat, 28 Feb 2009 00:04:10 +0000 Subject: supply etags for reduce view lists git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@748754 13f79535-47bb-0310-9956-ffa450edef68 --- share/www/script/test/list_views.js | 20 +++++++- src/couchdb/couch_httpd_show.erl | 99 +++++++++++++++++++------------------ 2 files changed, 71 insertions(+), 48 deletions(-) diff --git a/share/www/script/test/list_views.js b/share/www/script/test/list_views.js index a25eed30..5469efd2 100644 --- a/share/www/script/test/list_views.js +++ b/share/www/script/test/list_views.js @@ -193,10 +193,28 @@ couchTests.list_views = function(debug) { T(/Key: 1/.test(xhr.responseText)); // when there is a reduce present, and used - var xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true"); + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true"); T(xhr.status == 200); T(/Key: 1/.test(xhr.responseText)); + // there should be etags on reduce as well + var etag = xhr.getResponseHeader("etag"); + T(etag, "Etags should be served with reduce lists"); + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", { + headers: {"if-none-match": etag} + }); + T(xhr.status == 304); + + // verify the etags expire correctly + var docs = makeDocs(11, 12); + var saveResult = db.bulkSave(docs); + T(saveResult.ok); + + xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/simpleForm/withReduce?group=true", { + headers: {"if-none-match": etag} + }); + T(xhr.status == 200); + // with accept headers for HTML xhr = CouchDB.request("GET", "/test_suite_db/_list/lists/acceptSwitch/basicView", { headers: { diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl index a6c795c6..09484a3c 100644 --- a/src/couchdb/couch_httpd_show.erl +++ b/src/couchdb/couch_httpd_show.erl @@ -56,6 +56,9 @@ handle_view_list_req(#httpd{method='GET',path_parts=[_, _, DesignName, ListName, ListSrc = get_nested_json_value({Props}, [<<"lists">>, ListName]), send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db); +handle_view_list_req(#httpd{method='GET'}=Req, _Db) -> + send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); + handle_view_list_req(Req, _Db) -> send_method_not_allowed(Req, "GET,HEAD"). @@ -71,7 +74,6 @@ get_nested_json_value(_NotJSONObj, _) -> throw({not_found, json_mismatch}). send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db) -> - % TODO add etags when we get view etags #view_query_args{ stale = Stale, reduce = Reduce @@ -124,7 +126,6 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer headers = ExtHeaders } = couch_httpd_external:parse_external_response(JsonResp), JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), - % TODO use the Etag {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), {ok, Resp, binary_to_list(BeginBody)} end, @@ -181,53 +182,56 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q Hlist = mochiweb_headers:to_list(Headers), Accept = proplists:get_value('Accept', Hlist), CurrentEtag = couch_httpd_view:view_group_etag(Group, {Lang, ListSrc, Accept}), - - StartListRespFun = fun(Req2, _Etag, _, _) -> - JsonResp = couch_query_servers:render_reduce_head(QueryServer, - Req2, Db), - #extern_resp_args{ - code = Code, - data = BeginBody, - ctype = CType, - headers = ExtHeaders - } = couch_httpd_external:parse_external_response(JsonResp), - JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), - {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), - {ok, Resp, binary_to_list(BeginBody)} - end, - - SendListRowFun = fun(Resp, {Key, Value}, RowFront) -> - JsonResp = couch_query_servers:render_reduce_row(QueryServer, - Req, Db, {Key, Value}), - #extern_resp_args{ - stop = StopIter, - data = RowBody - } = couch_httpd_external:parse_external_response(JsonResp), - RowFront2 = case RowFront of - nil -> []; - _ -> RowFront + couch_httpd:etag_respond(Req, CurrentEtag, fun() -> + StartListRespFun = fun(Req2, _Etag, _, _) -> + JsonResp = couch_query_servers:render_reduce_head(QueryServer, + Req2, Db), + JsonResp2 = apply_etag(JsonResp, CurrentEtag), + #extern_resp_args{ + code = Code, + data = BeginBody, + ctype = CType, + headers = ExtHeaders + } = couch_httpd_external:parse_external_response(JsonResp2), + JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders), + {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders), + {ok, Resp, binary_to_list(BeginBody)} end, - case StopIter of - true -> stop; - _ -> - Chunk = RowFront2 ++ binary_to_list(RowBody), - case Chunk of - [] -> {ok, Resp}; - _ -> send_chunk(Resp, Chunk) + + SendListRowFun = fun(Resp, {Key, Value}, RowFront) -> + JsonResp = couch_query_servers:render_reduce_row(QueryServer, + Req, Db, {Key, Value}), + #extern_resp_args{ + stop = StopIter, + data = RowBody + } = couch_httpd_external:parse_external_response(JsonResp), + RowFront2 = case RowFront of + nil -> []; + _ -> RowFront + end, + case StopIter of + true -> stop; + _ -> + Chunk = RowFront2 ++ binary_to_list(RowBody), + case Chunk of + [] -> {ok, Resp}; + _ -> send_chunk(Resp, Chunk) + end end - end - end, + end, - {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, GroupLevel, QueryArgs, CurrentEtag, - #reduce_fold_helper_funs{ - start_response = StartListRespFun, - send_row = SendListRowFun - }), - FoldAccInit = {Limit, SkipCount, undefined, []}, - FoldResult = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId}, - {EndKey, EndDocId}, GroupRowsFun, RespFun, - FoldAccInit), - finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null). + {ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req, + GroupLevel, QueryArgs, CurrentEtag, + #reduce_fold_helper_funs{ + start_response = StartListRespFun, + send_row = SendListRowFun + }), + FoldAccInit = {Limit, SkipCount, undefined, []}, + FoldResult = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId}, + {EndKey, EndDocId}, GroupRowsFun, RespFun, + FoldAccInit), + finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null) + end). finish_list(Req, Db, QueryServer, Etag, FoldResult, StartListRespFun, TotalRows) -> case FoldResult of @@ -266,7 +270,8 @@ send_doc_show_response(Lang, ShowSrc, nil, #httpd{mochi_req=MReq}=Req, Db) -> couch_httpd_external:send_external_response(Req, JsonResp) end); -send_doc_show_response(Lang, ShowSrc, #doc{revs=[DocRev|_]}=Doc, #httpd{mochi_req=MReq}=Req, Db) -> +send_doc_show_response(Lang, ShowSrc, #doc{revs=[DocRev|_]}=Doc, + #httpd{mochi_req=MReq}=Req, Db) -> % calculate the etag Headers = MReq:get(headers), Hlist = mochiweb_headers:to_list(Headers), -- cgit v1.2.3