diff options
| author | John Christopher Anderson <jchris@apache.org> | 2009-02-28 00:04:10 +0000 | 
|---|---|---|
| committer | John Christopher Anderson <jchris@apache.org> | 2009-02-28 00:04:10 +0000 | 
| commit | 0c198005f6a84d948e11354912bb6178f3f852eb (patch) | |
| tree | 2b6a39ac5415328979f7ad62b775d73f613e14ee | |
| parent | 1cb18dc3db07b5a8e795c96e9e566eb621becc89 (diff) | |
supply etags for reduce view lists
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@748754 13f79535-47bb-0310-9956-ffa450edef68
| -rw-r--r-- | share/www/script/test/list_views.js | 20 | ||||
| -rw-r--r-- | 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), | 
