summaryrefslogtreecommitdiff
path: root/src/couchdb/couch_rep_httpc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couchdb/couch_rep_httpc.erl')
-rw-r--r--src/couchdb/couch_rep_httpc.erl91
1 files changed, 69 insertions, 22 deletions
diff --git a/src/couchdb/couch_rep_httpc.erl b/src/couchdb/couch_rep_httpc.erl
index 4944f554..ee46a15e 100644
--- a/src/couchdb/couch_rep_httpc.erl
+++ b/src/couchdb/couch_rep_httpc.erl
@@ -15,7 +15,8 @@
-include("../ibrowse/ibrowse.hrl").
-export([db_exists/1, db_exists/2, full_url/1, request/1, redirected_request/2,
- spawn_worker_process/1, spawn_link_worker_process/1]).
+ redirect_url/2, spawn_worker_process/1, spawn_link_worker_process/1]).
+-export([ssl_options/1]).
request(#http_db{} = Req) ->
do_request(Req).
@@ -34,7 +35,7 @@ do_request(Req) ->
qs = QS
} = Req,
Url = full_url(Req),
- Headers = case proplists:get_value(<<"oauth">>, Auth) of
+ Headers = case couch_util:get_value(<<"oauth">>, Auth) of
undefined ->
Headers0;
{OAuthProps} ->
@@ -46,7 +47,7 @@ do_request(Req) ->
nil ->
[];
_Else ->
- iolist_to_binary(?JSON_ENCODE(B))
+ iolist_to_binary(?JSON_ENCODE(B))
end,
Resp = case Conn of
nil ->
@@ -72,10 +73,11 @@ db_exists(Req, CanonicalUrl, CreateDB) ->
#http_db{
auth = Auth,
headers = Headers0,
+ options = Options,
url = Url
} = Req,
HeadersFun = fun(Method) ->
- case proplists:get_value(<<"oauth">>, Auth) of
+ case couch_util:get_value(<<"oauth">>, Auth) of
undefined ->
Headers0;
{OAuthProps} ->
@@ -84,25 +86,40 @@ db_exists(Req, CanonicalUrl, CreateDB) ->
end,
case CreateDB of
true ->
- catch ibrowse:send_req(Url, HeadersFun(put), put);
+ Headers = [{"Content-Length", 0} | HeadersFun(put)],
+ catch ibrowse:send_req(Url, Headers, put, [], Options);
_Else -> ok
end,
- case catch ibrowse:send_req(Url, HeadersFun(head), head) of
+ case catch ibrowse:send_req(Url, HeadersFun(head), head, [], Options) of
{ok, "200", _, _} ->
Req#http_db{url = CanonicalUrl};
{ok, "301", RespHeaders, _} ->
- MochiHeaders = mochiweb_headers:make(RespHeaders),
- RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
+ RedirectUrl = redirect_url(RespHeaders, Req#http_db.url),
db_exists(Req#http_db{url = RedirectUrl}, RedirectUrl);
{ok, "302", RespHeaders, _} ->
- MochiHeaders = mochiweb_headers:make(RespHeaders),
- RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
+ RedirectUrl = redirect_url(RespHeaders, Req#http_db.url),
db_exists(Req#http_db{url = RedirectUrl}, CanonicalUrl);
+ {ok, "401", _, _} ->
+ throw({unauthorized, ?l2b(Url)});
Error ->
?LOG_DEBUG("DB at ~s could not be found because ~p", [Url, Error]),
throw({db_not_found, ?l2b(Url)})
end.
+redirect_url(RespHeaders, OrigUrl) ->
+ MochiHeaders = mochiweb_headers:make(RespHeaders),
+ RedUrl = mochiweb_headers:get_value("Location", MochiHeaders),
+ {url, _, Base, Port, _, _, Path, Proto} = ibrowse_lib:parse_url(RedUrl),
+ {url, _, _, _, User, Passwd, _, _} = ibrowse_lib:parse_url(OrigUrl),
+ Creds = case is_list(User) andalso is_list(Passwd) of
+ true ->
+ User ++ ":" ++ Passwd ++ "@";
+ false ->
+ []
+ end,
+ atom_to_list(Proto) ++ "://" ++ Creds ++ Base ++ ":" ++
+ integer_to_list(Port) ++ Path.
+
full_url(#http_db{url=Url} = Req) when is_binary(Url) ->
full_url(Req#http_db{url = ?b2l(Url)});
@@ -115,7 +132,7 @@ full_url(Req) ->
resource = Resource,
qs = QS
} = Req,
- QStr = lists:map(fun({K,V}) -> io_lib:format("~s=~s",
+ QStr = lists:map(fun({K,V}) -> io_lib:format("~s=~s",
[couch_util:to_list(K), couch_util:to_list(V)]) end, QS),
lists:flatten([Url, Resource, "?", string:join(QStr, "&")]).
@@ -124,8 +141,7 @@ process_response({ok, Status, Headers, Body}, Req) ->
if Code =:= 200; Code =:= 201 ->
?JSON_DECODE(maybe_decompress(Headers, Body));
Code =:= 301; Code =:= 302 ->
- MochiHeaders = mochiweb_headers:make(Headers),
- RedirectUrl = mochiweb_headers:get_value("Location", MochiHeaders),
+ RedirectUrl = redirect_url(Headers, Req#http_db.url),
do_request(redirected_request(Req, RedirectUrl));
Code =:= 409 ->
throw(conflict);
@@ -156,7 +172,7 @@ process_response({error, Reason}, Req) ->
pause = Pause
} = Req,
ShortReason = case Reason of
- connection_closed ->
+ sel_conn_closed ->
connection_closed;
{'EXIT', {noproc, _}} ->
noproc;
@@ -178,7 +194,7 @@ process_response({error, Reason}, Req) ->
redirected_request(Req, RedirectUrl) ->
{Base, QStr, _} = mochiweb_util:urlsplit_path(RedirectUrl),
QS = mochiweb_util:parse_qs(QStr),
- Hdrs = case proplists:get_value(<<"oauth">>, Req#http_db.auth) of
+ Hdrs = case couch_util:get_value(<<"oauth">>, Req#http_db.auth) of
undefined ->
Req#http_db.headers;
_Else ->
@@ -192,8 +208,7 @@ spawn_worker_process(Req) ->
Pid.
spawn_link_worker_process(Req) ->
- Url = ibrowse_lib:parse_url(Req#http_db.url),
- {ok, Pid} = ibrowse_http_client:start_link(Url),
+ {ok, Pid} = ibrowse:spawn_link_worker_process(Req#http_db.url),
Pid.
maybe_decompress(Headers, Body) ->
@@ -209,11 +224,11 @@ oauth_header(Url, QS, Action, Props) ->
% erlang-oauth doesn't like iolists
QSL = [{couch_util:to_list(K), ?b2l(?l2b(couch_util:to_list(V)))} ||
{K,V} <- QS],
- ConsumerKey = ?b2l(proplists:get_value(<<"consumer_key">>, Props)),
- Token = ?b2l(proplists:get_value(<<"token">>, Props)),
- TokenSecret = ?b2l(proplists:get_value(<<"token_secret">>, Props)),
- ConsumerSecret = ?b2l(proplists:get_value(<<"consumer_secret">>, Props)),
- SignatureMethodStr = ?b2l(proplists:get_value(<<"signature_method">>, Props, <<"HMAC-SHA1">>)),
+ ConsumerKey = ?b2l(couch_util:get_value(<<"consumer_key">>, Props)),
+ Token = ?b2l(couch_util:get_value(<<"token">>, Props)),
+ TokenSecret = ?b2l(couch_util:get_value(<<"token_secret">>, Props)),
+ ConsumerSecret = ?b2l(couch_util:get_value(<<"consumer_secret">>, Props)),
+ SignatureMethodStr = ?b2l(couch_util:get_value(<<"signature_method">>, Props, <<"HMAC-SHA1">>)),
SignatureMethodAtom = case SignatureMethodStr of
"PLAINTEXT" ->
plaintext;
@@ -232,3 +247,35 @@ oauth_header(Url, QS, Action, Props) ->
Params = oauth:signed_params(Method, Url, QSL, Consumer, Token, TokenSecret)
-- QSL,
{"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}.
+
+ssl_options(#http_db{url = Url}) ->
+ case ibrowse_lib:parse_url(Url) of
+ #url{protocol = https} ->
+ Depth = list_to_integer(
+ couch_config:get("replicator", "ssl_certificate_max_depth", "3")
+ ),
+ SslOpts = [{depth, Depth} |
+ case couch_config:get("replicator", "verify_ssl_certificates") of
+ "true" ->
+ ssl_verify_options(true);
+ _ ->
+ ssl_verify_options(false)
+ end],
+ [{is_ssl, true}, {ssl_options, SslOpts}];
+ #url{protocol = http} ->
+ []
+ end.
+
+ssl_verify_options(Value) ->
+ ssl_verify_options(Value, erlang:system_info(otp_release)).
+
+ssl_verify_options(true, OTPVersion) when OTPVersion >= "R14" ->
+ CAFile = couch_config:get("replicator", "ssl_trusted_certificates_file"),
+ [{verify, verify_peer}, {cacertfile, CAFile}];
+ssl_verify_options(false, OTPVersion) when OTPVersion >= "R14" ->
+ [{verify, verify_none}];
+ssl_verify_options(true, _OTPVersion) ->
+ CAFile = couch_config:get("replicator", "ssl_trusted_certificates_file"),
+ [{verify, 2}, {cacertfile, CAFile}];
+ssl_verify_options(false, _OTPVersion) ->
+ [{verify, 0}].