summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_httpd_show.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb/couch_httpd_show.erl')
-rw-r--r--src/couchdb/couch_httpd_show.erl181
1 files changed, 76 insertions, 105 deletions
diff --git a/src/couchdb/couch_httpd_show.erl b/src/couchdb/couch_httpd_show.erl
index 49dd88bc..ef5d41d1 100644
--- a/src/couchdb/couch_httpd_show.erl
+++ b/src/couchdb/couch_httpd_show.erl
@@ -117,47 +117,6 @@ send_view_list_response(Lang, ListSrc, ViewName, DesignId, Req, Db, Keys) ->
end
end.
-make_map_start_resp_fun(QueryServer, Db) ->
- fun(Req, CurrentEtag, TotalViewCount, Offset, _Acc) ->
- ExternalResp = couch_query_servers:render_list_head(QueryServer,
- Req, Db, TotalViewCount, Offset),
- JsonResp = apply_etag(ExternalResp, CurrentEtag),
- #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.
-
-make_map_send_row_fun(QueryServer, Req) ->
- fun(Resp, Db2, {{Key, DocId}, Value}, _IncludeDocs, RowFront) ->
- try
- JsonResp = couch_query_servers:render_list_row(QueryServer,
- Req, Db2, {{Key, DocId}, Value}),
- #extern_resp_args{
- stop = StopIter,
- data = RowBody
- } = couch_httpd_external:parse_external_response(JsonResp),
- case StopIter of
- true -> {stop, ""};
- _ ->
- Chunk = RowFront ++ binary_to_list(RowBody),
- case Chunk of
- [] -> ok;
- _ -> send_chunk(Resp, Chunk)
- end,
- {ok, ""}
- end
- catch
- throw:Error ->
- send_chunked_error(Resp, Error),
- throw({already_sent, Resp, Error})
- end
- end.
output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) ->
#view_query_args{
@@ -179,7 +138,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
{ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
- SendListRowFun = make_map_send_row_fun(QueryServer, Req),
+ SendListRowFun = make_map_send_row_fun(QueryServer),
FoldlFun = couch_httpd_view:make_view_fold_fun(Req, QueryArgs, CurrentEtag, Db, RowCount,
#view_fold_helper_funs{
@@ -189,7 +148,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
}),
FoldAccInit = {Limit, SkipCount, undefined, []},
{ok, FoldResult} = couch_view:fold(View, Start, Dir, FoldlFun, FoldAccInit),
- finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
+ finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
end);
output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) ->
@@ -210,7 +169,7 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
{ok, QueryServer} = couch_query_servers:start_view_list(Lang, ListSrc),
StartListRespFun = make_map_start_resp_fun(QueryServer, Db),
- SendListRowFun = make_map_send_row_fun(QueryServer, Req),
+ SendListRowFun = make_map_send_row_fun(QueryServer),
FoldAccInit = {Limit, SkipCount, undefined, []},
{ok, FoldResult} = lists:foldl(
@@ -226,49 +185,64 @@ output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Quer
}),
couch_view:fold(View, {Key, StartDocId}, Dir, FoldlFun, FoldAcc)
end, {ok, FoldAccInit}, Keys),
- finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
+ finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, RowCount)
end).
-make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag) ->
- fun(Req2, _Etag, _Acc) ->
- 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)}
+make_map_start_resp_fun(QueryServer, Db) ->
+ fun(Req, Etag, TotalRows, Offset, _Acc) ->
+ Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}]},
+ start_list_resp(QueryServer, Req, Db, Head, Etag)
end.
-make_reduce_send_row_fun(QueryServer, Req, Db) ->
- fun(Resp, {Key, Value}, RowFront) ->
- try
- 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),
- case StopIter of
- true -> {stop, ""};
- _ ->
- Chunk = RowFront ++ binary_to_list(RowBody),
- case Chunk of
- [] -> ok;
- _ -> send_chunk(Resp, Chunk)
- end,
- {ok, ""}
- end
- catch
- throw:Error ->
- send_chunked_error(Resp, Error),
- throw({already_sent, Resp, Error})
+make_reduce_start_resp_fun(QueryServer, _Req, Db, _CurrentEtag) ->
+ fun(Req2, Etag, _Acc) ->
+ start_list_resp(QueryServer, Req2, Db, {[]}, Etag)
+ end.
+
+start_list_resp(QueryServer, Req, Db, Head, Etag) ->
+ [<<"start">>,Chunks,JsonResp] = couch_query_servers:render_list_head(QueryServer,
+ Req, Db, Head),
+ JsonResp2 = apply_etag(JsonResp, Etag),
+ #extern_resp_args{
+ code = Code,
+ 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, ?b2l(?l2b(Chunks))}.
+
+make_map_send_row_fun(QueryServer) ->
+ fun(Resp, Db, Row, _IncludeDocs, RowFront) ->
+ send_list_row(Resp, QueryServer, Db, Row, RowFront)
+ end.
+
+make_reduce_send_row_fun(QueryServer, Db) ->
+ fun(Resp, Row, RowFront) ->
+ send_list_row(Resp, QueryServer, Db, Row, RowFront)
+ end.
+
+send_list_row(Resp, QueryServer, Db, Row, RowFront) ->
+ try
+ [Go,Chunks] = couch_query_servers:render_list_row(QueryServer, Db, Row),
+ Chunk = RowFront ++ ?b2l(?l2b(Chunks)),
+ send_non_empty_chunk(Resp, Chunk),
+ case Go of
+ <<"chunks">> ->
+ {ok, ""};
+ <<"end">> ->
+ {stop, stop}
end
+ catch
+ throw:Error ->
+ send_chunked_error(Resp, Error),
+ throw({already_sent, Resp, Error})
+ end.
+
+send_non_empty_chunk(Resp, Chunk) ->
+ case Chunk of
+ [] -> ok;
+ _ -> send_chunk(Resp, Chunk)
end.
output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil) ->
@@ -291,7 +265,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
CurrentEtag = couch_httpd_view:view_group_etag(Group, {Lang, ListSrc, Accept}),
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
- SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db),
+ SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
{ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
GroupLevel, QueryArgs, CurrentEtag,
@@ -303,7 +277,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
{ok, FoldResult} = couch_view:fold_reduce(View, Dir, {StartKey, StartDocId},
{EndKey, EndDocId}, GroupRowsFun, RespFun,
FoldAccInit),
- finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
+ finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
end);
output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, Keys) ->
@@ -325,7 +299,7 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
StartListRespFun = make_reduce_start_resp_fun(QueryServer, Req, Db, CurrentEtag),
- SendListRowFun = make_reduce_send_row_fun(QueryServer, Req, Db),
+ SendListRowFun = make_reduce_send_row_fun(QueryServer, Db),
{ok, GroupRowsFun, RespFun} = couch_httpd_view:make_reduce_fold_funs(Req,
GroupLevel, QueryArgs, CurrentEtag,
@@ -339,33 +313,30 @@ output_reduce_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, Q
couch_view:fold_reduce(View, Dir, {Key, StartDocId},
{Key, EndDocId}, GroupRowsFun, RespFun, FoldAcc)
end, {ok, FoldAccInit}, Keys),
- finish_list(Req, Db, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
+ finish_list(Req, QueryServer, CurrentEtag, FoldResult, StartListRespFun, null)
end).
-finish_list(Req, Db, QueryServer, Etag, FoldResult, StartListRespFun, TotalRows) ->
- {Resp, BeginBody} = case FoldResult of
+finish_list(Req, QueryServer, Etag, FoldResult, StartFun, TotalRows) ->
+ case FoldResult of
{_, _, undefined, _} ->
- {ok, Resp2, BeginBody2} = render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows),
- {Resp2, BeginBody2};
- {_, _, Resp0, _} ->
- {Resp0, ""}
- end,
- JsonTail = couch_query_servers:render_list_tail(QueryServer, Req, Db),
- #extern_resp_args{
- data = Tail
- } = couch_httpd_external:parse_external_response(JsonTail),
- Chunk = BeginBody ++ binary_to_list(Tail),
- case Chunk of
- [] -> ok;
- _ -> send_chunk(Resp, Chunk)
+ {ok, Resp, BeginBody} =
+ render_head_for_empty_list(StartFun, Req, Etag, TotalRows),
+ [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+ Chunk = BeginBody ++ ?b2l(?l2b(Chunks)),
+ send_non_empty_chunk(Resp, Chunk);
+ {_, _, Resp, stop} ->
+ ok;
+ {_, _, Resp, _} ->
+ [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+ send_non_empty_chunk(Resp, ?b2l(?l2b(Chunks)))
end,
send_chunk(Resp, []).
render_head_for_empty_list(StartListRespFun, Req, Etag, null) ->
- StartListRespFun(Req, Etag, []);
+ StartListRespFun(Req, Etag, []); % for reduce
render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows) ->
StartListRespFun(Req, Etag, TotalRows, null, []).
-
+
send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db) ->
% compute etag with no doc
Headers = MReq:get(headers),
@@ -373,7 +344,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, nil, #httpd{mochi_req=MReq}=Req, Db
Accept = proplists:get_value('Accept', Hlist),
CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept}),
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc,
+ [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc,
DocId, nil, Req, Db),
JsonResp = apply_etag(ExternalResp, CurrentEtag),
couch_httpd_external:send_external_response(Req, JsonResp)
@@ -387,7 +358,7 @@ send_doc_show_response(Lang, ShowSrc, DocId, #doc{revs=Revs}=Doc, #httpd{mochi_r
CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, Revs, Accept}),
% We know our etag now
couch_httpd:etag_respond(Req, CurrentEtag, fun() ->
- ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc,
+ [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang, ShowSrc,
DocId, Doc, Req, Db),
JsonResp = apply_etag(ExternalResp, CurrentEtag),
couch_httpd_external:send_external_response(Req, JsonResp)