summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/www/script/test/list_views.js20
-rw-r--r--src/couchdb/couch_httpd_show.erl99
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),