Skip to content

Commit

Permalink
supply etags for reduce view lists
Browse files Browse the repository at this point in the history
git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@748754 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
jchris committed Feb 28, 2009
1 parent 1cb18dc commit 0c19800
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 48 deletions.
20 changes: 19 additions & 1 deletion share/www/script/test/list_views.js
Expand Up @@ -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: {
Expand Down
99 changes: 52 additions & 47 deletions src/couchdb/couch_httpd_show.erl
Expand Up @@ -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").

Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down

0 comments on commit 0c19800

Please sign in to comment.