C0 code coverage information

Generated on 2009-08-09 21:22:56 with etap 0.3.4.

Name Total lines Lines of code Total coverage Code coverage
couch_httpd_external ?? ?? ??
0% 
....1 % Licensed under the Apache License, Version 2.0 (the "License"); you may not
....2 % use this file except in compliance with the License. You may obtain a copy of
....3 % the License at
....4 %
....5 %   http://www.apache.org/licenses/LICENSE-2.0
....6 %
....7 % Unless required by applicable law or agreed to in writing, software
....8 % distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
....9 % WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
...10 % License for the specific language governing permissions and limitations under
...11 % the License.
...12 
...13 -module(couch_httpd_external).
...14 
...15 -export([handle_external_req/2, handle_external_req/3]).
...16 -export([send_external_response/2, json_req_obj/2]).
...17 -export([default_or_content_type/2, parse_external_response/1]).
...18 
...19 -import(couch_httpd,[send_error/4]).
...20 
...21 -include("couch_db.hrl").
...22 
...23 % handle_external_req/2
...24 % for the old type of config usage:
...25 % _external = {couch_httpd_external, handle_external_req}
...26 % with urls like
...27 % /db/_external/action/design/name
...28 handle_external_req(#httpd{
...29                         path_parts=[_DbName, _External, UrlName | _Path]
...30                     }=HttpReq, Db) ->
...31     process_external_req(HttpReq, Db, UrlName);
...32 handle_external_req(#httpd{path_parts=[_, _]}=Req, _Db) ->
...33     send_error(Req, 404, <<"external_server_error">>, <<"No server name specified.">>);
...34 handle_external_req(Req, _) ->
...35     send_error(Req, 404, <<"external_server_error">>, <<"Broken assumption">>).
...36 
...37 % handle_external_req/3
...38 % for this type of config usage:
...39 % _action = {couch_httpd_external, handle_external_req, <<"action">>}
...40 % with urls like
...41 % /db/_action/design/name
...42 handle_external_req(HttpReq, Db, Name) ->
...43     process_external_req(HttpReq, Db, Name).
...44 
...45 process_external_req(HttpReq, Db, Name) ->
...46 
...47     Response = couch_external_manager:execute(binary_to_list(Name),
...48         json_req_obj(HttpReq, Db)),
...49 
...50     case Response of
...51     {unknown_external_server, Msg} ->
...52         send_error(HttpReq, 404, <<"external_server_error">>, Msg);
...53     _ ->
...54         send_external_response(HttpReq, Response)
...55     end.
...56 
...57 json_req_obj(#httpd{mochi_req=Req,
...58                method=Verb,
...59                path_parts=Path,
...60                req_body=ReqBody,
...61                user_ctx=#user_ctx{name=UserName, roles=UserRoles}
...62             }, Db) ->
...63     Body = case ReqBody of
...64         undefined -> Req:recv_body();
...65         Else -> Else
...66     end,
...67     ParsedForm = case Req:get_primary_header_value("content-type") of
...68         "application/x-www-form-urlencoded" ++ _ ->
...69             mochiweb_util:parse_qs(Body);
...70         _ ->
...71             []
...72     end,
...73     UserCtx = {[{<<"name">>, UserName}, {<<"roles">>, UserRoles}]},
...74     Headers = Req:get(headers),
...75     Hlist = mochiweb_headers:to_list(Headers),
...76     {ok, Info} = couch_db:get_db_info(Db),
...77     % add headers...
...78     {[{<<"info">>, {Info}},
...79         {<<"verb">>, Verb},
...80         {<<"path">>, Path},
...81         {<<"query">>, to_json_terms(Req:parse_qs())},
...82         {<<"headers">>, to_json_terms(Hlist)},
...83         {<<"body">>, Body},
...84         {<<"form">>, to_json_terms(ParsedForm)},
...85         {<<"cookie">>, to_json_terms(Req:parse_cookie())},
...86         {<<"userCtx">>, UserCtx}]}.
...87 
...88 to_json_terms(Data) ->
...89     to_json_terms(Data, []).
...90 to_json_terms([], Acc) ->
...91     {lists:reverse(Acc)};
...92 to_json_terms([{Key, Value} | Rest], Acc) when is_atom(Key) ->
...93     to_json_terms(Rest, [{list_to_binary(atom_to_list(Key)), list_to_binary(Value)} | Acc]);
...94 to_json_terms([{Key, Value} | Rest], Acc) ->
...95     to_json_terms(Rest, [{list_to_binary(Key), list_to_binary(Value)} | Acc]).
...96 
...97 
...98 send_external_response(#httpd{mochi_req=MochiReq}, Response) ->
...99     #extern_resp_args{
..100         code = Code,
..101         data = Data,
..102         ctype = CType,
..103         headers = Headers
..104     } = parse_external_response(Response),
..105     Resp = MochiReq:respond({Code,
..106         default_or_content_type(CType, Headers ++ couch_httpd:server_header()), Data}),
..107     {ok, Resp}.
..108 
..109 parse_external_response({Response}) ->
..110     lists:foldl(fun({Key,Value}, Args) ->
..111         case {Key, Value} of
..112             {"", _} ->
..113                 Args;
..114             {<<"code">>, Value} ->
..115                 Args#extern_resp_args{code=Value};
..116             {<<"stop">>, true} ->
..117                 Args#extern_resp_args{stop=true};
..118             {<<"json">>, Value} ->
..119                 Args#extern_resp_args{
..120                     data=?JSON_ENCODE(Value),
..121                     ctype="application/json"};
..122             {<<"body">>, Value} ->
..123                 Args#extern_resp_args{data=Value, ctype="text/html; charset=utf-8"};
..124             {<<"base64">>, Value} ->
..125                 Args#extern_resp_args{
..126                     data=couch_util:decodeBase64(Value),
..127                     ctype="application/binary"
..128                 };
..129             {<<"headers">>, {Headers}} ->
..130                 NewHeaders = lists:map(fun({Header, HVal}) ->
..131                     {binary_to_list(Header), binary_to_list(HVal)}
..132                 end, Headers),
..133                 Args#extern_resp_args{headers=NewHeaders};
..134             _ -> % unknown key
..135                 Msg = lists:flatten(io_lib:format("Invalid data from external server: ~p", [{Key, Value}])),
..136                 throw({external_response_error, Msg})
..137             end
..138         end, #extern_resp_args{}, Response).
..139 
..140 default_or_content_type(DefaultContentType, Headers) ->
..141     {ContentType, OtherHeaders} = lists:partition(
..142         fun({HeaderName, _}) ->
..143             HeaderName == "Content-Type"
..144         end, Headers),
..145 
..146     % XXX: What happens if we were passed multiple content types? We add another?
..147     case ContentType of
..148         [{"Content-Type", SetContentType}] ->
..149             TrueContentType = SetContentType;
..150         _Else ->
..151             TrueContentType = DefaultContentType
..152     end,
..153 
..154     HeadersWithContentType = lists:append(OtherHeaders, [{"Content-Type", TrueContentType}]),
..155     HeadersWithContentType.

Generated using etap 0.3.4.