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 | ?? | ?? | ?? |
|
....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.