From ebac05f686b56791511cb9b599dfb5a742dcfc96 Mon Sep 17 00:00:00 2001 From: Adam Kocoloski Date: Mon, 25 Oct 2010 15:46:05 -0400 Subject: use get-deps to pull down individual cloudant projects --- apps/chttpd/src/chttpd_show.erl | 311 ---------------------------------------- 1 file changed, 311 deletions(-) delete mode 100644 apps/chttpd/src/chttpd_show.erl (limited to 'apps/chttpd/src/chttpd_show.erl') diff --git a/apps/chttpd/src/chttpd_show.erl b/apps/chttpd/src/chttpd_show.erl deleted file mode 100644 index 0aba2835..00000000 --- a/apps/chttpd/src/chttpd_show.erl +++ /dev/null @@ -1,311 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(chttpd_show). - --export([handle_doc_show_req/3, handle_doc_update_req/3, handle_view_list_req/3]). - --include_lib("couch/include/couch_db.hrl"). - --import(chttpd, - [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2, - start_json_response/2,send_chunk/2,last_chunk/1,send_chunked_error/2, - start_chunked_response/3, send_error/4]). - --record(lacc, { - req, - resp = nil, - qserver, - lname, - db, - etag -}). - -% /db/_design/foo/_show/bar/docid -% show converts a json doc to a response of any content-type. -% it looks up the doc an then passes it to the query server. -% then it sends the response from the query server to the http client. - -maybe_open_doc(Db, DocId) -> - case fabric:open_doc(Db, DocId, [conflicts]) of - {ok, Doc} -> - Doc; - {not_found, _} -> - nil - end. - -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName, DocId] - }=Req, Db, DDoc) -> - - % open the doc - Doc = maybe_open_doc(Db, DocId), - - % we don't handle revs here b/c they are an internal api - % returns 404 if there is no doc with DocId - handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId); - -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName, DocId|Rest] - }=Req, Db, DDoc) -> - - DocParts = [DocId|Rest], - DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")), - - % open the doc - Doc = maybe_open_doc(Db, DocId1), - - % we don't handle revs here b/c they are an internal api - % pass 404 docs to the show function - handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId1); - -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName] - }=Req, Db, DDoc) -> - % with no docid the doc is nil - handle_doc_show(Req, Db, DDoc, ShowName, nil); - -handle_doc_show_req(Req, _Db, _DDoc) -> - send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>). - -handle_doc_show(Req, Db, DDoc, ShowName, Doc) -> - handle_doc_show(Req, Db, DDoc, ShowName, Doc, null). - -handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId) -> - % get responder for ddoc/showname - CurrentEtag = show_etag(Req, Doc, DDoc, []), - chttpd:etag_respond(Req, CurrentEtag, fun() -> - JsonReq = chttpd_external:json_req_obj(Req, Db, DocId), - JsonDoc = couch_query_servers:json_doc(Doc), - [<<"resp">>, ExternalResp] = - couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName], - [JsonDoc, JsonReq]), - JsonResp = apply_etag(ExternalResp, CurrentEtag), - chttpd_external:send_external_response(Req, JsonResp) - end). - - -show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) -> - Accept = chttpd:header_value(Req, "Accept"), - DocPart = case Doc of - nil -> nil; - Doc -> chttpd:doc_etag(Doc) - end, - couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept, - UserCtx#user_ctx.roles, More}). - -% /db/_design/foo/update/bar/docid -% updates a doc based on a request -% handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) -> -% % anything but GET -% send_method_not_allowed(Req, "POST,PUT,DELETE,ETC"); - -handle_doc_update_req(#httpd{ - path_parts=[_, _, _, _, UpdateName, DocId] - }=Req, Db, DDoc) -> - Doc = maybe_open_doc(Db, DocId), - send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId); - -handle_doc_update_req(#httpd{ - path_parts=[_, _, _, _, UpdateName] - }=Req, Db, DDoc) -> - send_doc_update_response(Req, Db, DDoc, UpdateName, nil, null); - -handle_doc_update_req(Req, _Db, _DDoc) -> - send_error(Req, 404, <<"update_error">>, <<"Invalid path.">>). - -send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) -> - JsonReq = chttpd_external:json_req_obj(Req, Db, DocId), - JsonDoc = couch_query_servers:json_doc(Doc), - Cmd = [<<"updates">>, UpdateName], - case couch_query_servers:ddoc_prompt(DDoc, Cmd, [JsonDoc, JsonReq]) of - [<<"up">>, {NewJsonDoc}, JsonResp] -> - case chttpd:header_value(Req, "X-Couch-Full-Commit", "false") of - "true" -> - Options = [full_commit, {user_ctx, Req#httpd.user_ctx}]; - _ -> - Options = [{user_ctx, Req#httpd.user_ctx}] - end, - NewDoc = couch_doc:from_json_obj({NewJsonDoc}), - Code = 201, - {ok, _NewRev} = fabric:update_doc(Db, NewDoc, Options); - [<<"up">>, _Other, JsonResp] -> - Code = 200 - end, - JsonResp2 = json_apply_field({<<"code">>, Code}, JsonResp), - % todo set location field - chttpd_external:send_external_response(Req, JsonResp2). - - -% view-list request with view and list from same design doc. -handle_view_list_req(#httpd{method='GET', - path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) -> - handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, nil); - -% view-list request with view and list from different design docs. -handle_view_list_req(#httpd{method='GET', - path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) -> - handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, nil); - -handle_view_list_req(#httpd{method='GET'}=Req, _Db, _DDoc) -> - send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); - -handle_view_list_req(#httpd{method='POST', - path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) -> - ReqBody = couch_httpd:body(Req), - {Props2} = ?JSON_DECODE(ReqBody), - Keys = proplists:get_value(<<"keys">>, Props2, nil), - handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, - {DesignName, ViewName}, Keys); - -handle_view_list_req(#httpd{method='POST', - path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) -> - ReqBody = couch_httpd:body(Req), - {Props2} = ?JSON_DECODE(ReqBody), - Keys = proplists:get_value(<<"keys">>, Props2, nil), - handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, - {DesignName, ViewName}, Keys); - -handle_view_list_req(#httpd{method='POST'}=Req, _Db, _DDoc) -> - send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); - -handle_view_list_req(Req, _Db, _DDoc) -> - send_method_not_allowed(Req, "GET,POST,HEAD"). - -handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) -> - {ok, VDoc} = fabric:open_doc(Db, <<"_design/", ViewDesignName/binary>>, []), - Group = couch_view_group:design_doc_to_view_group(VDoc), - IsReduce = chttpd_view:get_reduce_type(Req), - ViewType = chttpd_view:extract_view_type(ViewName, Group#group.views, - IsReduce), - QueryArgs = chttpd_view:parse_view_params(Req, Keys, ViewType), - CB = fun list_callback/2, - Etag = couch_uuids:new(), - chttpd:etag_respond(Req, Etag, fun() -> - couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) -> - Acc0 = #lacc{ - lname = LName, - req = Req, - qserver = QServer, - db = Db, - etag = Etag - }, - fabric:query_view(Db, VDoc, ViewName, CB, Acc0, QueryArgs) - end) - end). - -list_callback({total_and_offset, Total, Offset}, #lacc{resp=nil} = Acc) -> - start_list_resp({[{<<"total_rows">>, Total}, {<<"offset">>, Offset}]}, Acc); -list_callback({total_and_offset, _, _}, Acc) -> - % a sorted=false view where the message came in late. Ignore. - {ok, Acc}; -list_callback({row, Row}, #lacc{resp=nil} = Acc) -> - % first row of a reduce view, or a sorted=false view - {ok, NewAcc} = start_list_resp({[]}, Acc), - send_list_row(Row, NewAcc); -list_callback({row, Row}, Acc) -> - send_list_row(Row, Acc); -list_callback(complete, Acc) -> - #lacc{qserver = {Proc, _}, resp = Resp0} = Acc, - if Resp0 =:= nil -> - {ok, #lacc{resp = Resp}} = start_list_resp({[]}, Acc); - true -> - Resp = Resp0 - end, - [<<"end">>, Chunk] = couch_query_servers:proc_prompt(Proc, [<<"list_end">>]), - send_non_empty_chunk(Resp, Chunk), - couch_httpd:last_chunk(Resp), - {ok, Resp}; -list_callback({error, Reason}, {_, Resp}) -> - chttpd:send_chunked_error(Resp, {error, Reason}). - -start_list_resp(Head, Acc) -> - #lacc{ - req = Req, - db = Db, - qserver = QServer, - lname = LName, - etag = Etag - } = Acc, - - % use a separate process because we're already in a receive loop, and - % json_req_obj calls fabric:get_db_info() - spawn_monitor(fun() -> exit(chttpd_external:json_req_obj(Req, Db)) end), - receive {'DOWN', _, _, _, JsonReq} -> ok end, - - [<<"start">>,Chunk,JsonResp] = couch_query_servers:ddoc_proc_prompt(QServer, - [<<"lists">>, LName], [Head, JsonReq]), - 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), - send_non_empty_chunk(Resp, Chunk), - {ok, Acc#lacc{resp=Resp}}. - -send_list_row(Row, #lacc{qserver = {Proc, _}, resp = Resp} = Acc) -> - try couch_query_servers:proc_prompt(Proc, [<<"list_row">>, Row]) of - [<<"chunks">>, Chunk] -> - send_non_empty_chunk(Resp, Chunk), - {ok, Acc}; - [<<"end">>, Chunk] -> - send_non_empty_chunk(Resp, Chunk), - couch_httpd:last_chunk(Resp), - {stop, Resp} - catch Error -> - chttpd:send_chunked_error(Resp, Error), - {stop, Resp} - end. - -send_non_empty_chunk(_, []) -> - ok; -send_non_empty_chunk(Resp, Chunk) -> - send_chunk(Resp, Chunk). - -% Maybe this is in the proplists API -% todo move to couch_util -json_apply_field(H, {L}) -> - json_apply_field(H, L, []). -json_apply_field({Key, NewValue}, [{Key, _OldVal} | Headers], Acc) -> - % drop matching keys - json_apply_field({Key, NewValue}, Headers, Acc); -json_apply_field({Key, NewValue}, [{OtherKey, OtherVal} | Headers], Acc) -> - % something else is next, leave it alone. - json_apply_field({Key, NewValue}, Headers, [{OtherKey, OtherVal} | Acc]); -json_apply_field({Key, NewValue}, [], Acc) -> - % end of list, add ours - {[{Key, NewValue}|Acc]}. - -apply_etag({ExternalResponse}, CurrentEtag) -> - % Here we embark on the delicate task of replacing or creating the - % headers on the JsonResponse object. We need to control the Etag and - % Vary headers. If the external function controls the Etag, we'd have to - % run it to check for a match, which sort of defeats the purpose. - case couch_util:get_value(<<"headers">>, ExternalResponse, nil) of - nil -> - % no JSON headers - % add our Etag and Vary headers to the response - {[{<<"headers">>, {[{<<"Etag">>, CurrentEtag}, {<<"Vary">>, <<"Accept">>}]}} | ExternalResponse]}; - JsonHeaders -> - {[case Field of - {<<"headers">>, JsonHeaders} -> % add our headers - JsonHeadersEtagged = json_apply_field({<<"Etag">>, CurrentEtag}, JsonHeaders), - JsonHeadersVaried = json_apply_field({<<"Vary">>, <<"Accept">>}, JsonHeadersEtagged), - {<<"headers">>, JsonHeadersVaried}; - _ -> % skip non-header fields - Field - end || Field <- ExternalResponse]} - end. - -- cgit v1.2.3