C0 code coverage information

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

Name Total lines Lines of code Total coverage Code coverage
couch_rep_httpc ?? ?? ??
42% 
....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_rep_httpc).
...14 -include("couch_db.hrl").
...15 -include("../ibrowse/ibrowse.hrl").
...16 
...17 -export([db_exists/1, full_url/1, request/1, spawn_worker_process/1,
...18     spawn_link_worker_process/1]).
...19 
...20 request(Req) when is_record(Req, http_db) ->
...21     do_request(Req).
...22 
...23 do_request(#http_db{url=Url} = Req) when is_binary(Url) ->
...24     do_request(Req#http_db{url = ?b2l(Url)});
...25 
...26 do_request(Req) ->
...27     #http_db{
...28         auth = Auth,
...29         headers = Headers0,
...30         method = Method,
...31         body = B,
...32         options = Opts,
...33         conn = Conn
...34     } = Req,
...35     Url = full_url(Req),
...36     Headers = case proplists:get_value(<<"oauth">>, Auth) of
...37     undefined ->
...38         Headers0;
...39     {OAuthProps} ->
...40         [oauth_header(Url, Method, OAuthProps) | Headers0]
...41     end,
...42     Body = if B =:= nil -> []; true -> iolist_to_binary(?JSON_ENCODE(B)) end,
...43     Resp = case Conn of
...44     nil ->
...45         ibrowse:send_req(Url, Headers, Method, Body, Opts, infinity);
...46     _ ->
...47         ibrowse:send_req_direct(Conn, Url, Headers, Method, Body, Opts, infinity)
...48     end,
...49     process_response(Resp, Req).
...50 
...51 db_exists(Req) ->
...52     #http_db{
...53         url = Url,
...54         headers = Headers
...55     } = Req,
...56     case catch ibrowse:send_req(Url, Headers, head) of
...57     {ok, "200", _, _} ->
...58         true;
...59     {ok, "301", Headers, _} ->
...60         MochiHeaders = mochiweb_headers:make(Headers),
...61         RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
...62         db_exists(Req#http_db{url = RedirectUrl});
...63     Error ->
...64         ?LOG_DEBUG("DB at ~s could not be found because ~p", [Url, Error]),
...65         false
...66     end.
...67 
...68 full_url(#http_db{url=Url} = Req) when is_binary(Url) ->
...69     full_url(Req#http_db{url = ?b2l(Url)});
...70 
...71 full_url(#http_db{qs=[]} = Req) ->
...72     Req#http_db.url ++ Req#http_db.resource;
...73 
...74 full_url(Req) ->
...75     #http_db{
...76         url = Url,
...77         resource = Resource,
...78         qs = QS
...79     } = Req,
...80     QStr = lists:map(fun({K,V}) -> io_lib:format("~s=~s", 
...81         [couch_util:to_list(K), couch_util:to_list(V)]) end, QS),
...82     lists:flatten([Url, Resource, "?", string:join(QStr, "&")]).
...83 
...84 process_response({ok, Status, Headers, Body}, Req) ->
...85     Code = list_to_integer(Status),
...86     if Code =:= 200; Code =:= 201 ->
...87         ?JSON_DECODE(maybe_decompress(Headers, Body));
...88     Code =:= 301 ->
...89         MochiHeaders = mochiweb_headers:make(Headers),
...90         RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
...91         do_request(Req#http_db{url = RedirectUrl});
...92     Code >= 400, Code < 500 ->
...93         ?JSON_DECODE(maybe_decompress(Headers, Body));
...94     Code =:= 500; Code =:= 502 ->
...95         #http_db{pause = Pause, retries = Retries} = Req,
...96         ?LOG_INFO("retrying couch_rep_httpc request in ~p seconds " ++
...97             % "due to remote server error: ~s~s", [Pause/1000, Req#http_db.url,
...98             "due to remote server error: ~p Body ~s", [Pause, Code,
...99             Body]),
..100         timer:sleep(1000*Pause),
..101         do_request(Req#http_db{retries = Retries-1, pause = 2*Pause})
..102     end;
..103 
..104 process_response({ibrowse_req_id, Id}, _Req) ->
..105     {ibrowse_req_id, Id};
..106 
..107 process_response({error, _Reason}, #http_db{url=Url, retries=0}) ->
..108     ?LOG_ERROR("couch_rep_httpc request failed after 10 retries: ~s", [Url]),
..109     exit({http_request_failed, ?l2b(["failed to replicate ", Url])});
..110 process_response({error, Reason}, Req) ->
..111     #http_db{
..112         method = Method,
..113         retries = Retries,
..114         pause = Pause
..115     } = Req,
..116     ShortReason = case Reason of
..117     connection_closed ->
..118         connection_closed;
..119     {'EXIT', {noproc, _}} ->
..120         noproc;
..121     {'EXIT', {normal, _}} ->
..122         normal;
..123     Else ->
..124         Else
..125     end,
..126     ?LOG_DEBUG("retrying couch_rep_httpc ~p request in ~p seconds due to " ++
..127         "{error, ~p}", [Method, Pause, ShortReason]),
..128         % "{error}", [Method, Pause]),
..129     timer:sleep(1000*Pause),
..130     do_request(Req#http_db{retries = Retries-1, pause = 2*Pause}).
..131 
..132 spawn_worker_process(Req) ->
..133     Url = ibrowse_lib:parse_url(Req#http_db.url),
..134     {ok, Pid} = ibrowse:spawn_worker_process(Url#url.host, Url#url.port),
..135     Pid.
..136 
..137 spawn_link_worker_process(Req) ->
..138     Url = ibrowse_lib:parse_url(Req#http_db.url),
..139     {ok, Pid} = ibrowse:spawn_link_worker_process(Url#url.host, Url#url.port),
..140     Pid.
..141 
..142 maybe_decompress(Headers, Body) ->
..143     MochiHeaders = mochiweb_headers:make(Headers),
..144     case mochiweb_headers:get_value("Content-Encoding", MochiHeaders) of
..145     "gzip" ->
..146         zlib:gunzip(Body);
..147     _ ->
..148         Body
..149     end.
..150 
..151 oauth_header(Url, Action, Props) ->
..152     ConsumerKey = ?b2l(proplists:get_value(<<"consumer_key">>, Props)),
..153     Token = ?b2l(proplists:get_value(<<"token">>, Props)),
..154     TokenSecret = ?b2l(proplists:get_value(<<"token_secret">>, Props)),
..155     ConsumerSecret = ?b2l(proplists:get_value(<<"consumer_secret">>, Props)),
..156     Consumer = {ConsumerKey, ConsumerSecret, hmac_sha1},
..157     Method = case Action of
..158         get -> "GET";
..159         post -> "POST";
..160         put -> "PUT"
..161     end,
..162     Params = oauth:signed_params(Method, Url, [], Consumer, Token, TokenSecret),
..163     {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.

Generated using etap 0.3.4.