Skip to content

Commit

Permalink
Merge branch 'master' of github.com:benoitc/hackney
Browse files Browse the repository at this point in the history
  • Loading branch information
benoitc committed Jul 23, 2015
2 parents 51702ce + 9602008 commit e8d3b09
Show file tree
Hide file tree
Showing 12 changed files with 8,076 additions and 5,153 deletions.
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,18 @@ dialyzer:
#
CA_BUNDLE_BIN=./support/mk-ca-bundle.pl
CA_BUNDLE=ca-bundle.crt
CA_SRC=src/hackney_connect/hackney_cacerts.erl.src
CA_OUT=src/hackney_connect/hackney_cacerts.erl

mkcert:
$(CA_BUNDLE_BIN) -p ALL:ALL -l -u
$(CA_BUNDLE_BIN)
@cat $(CA_SRC) \
| head -n `grep -n "%% GENERATED" $(CA_SRC) | cut -d : -f 1` \
> $(CA_OUT)
@cat $(CA_BUNDLE) >> $(CA_OUT)
@cat $(CA_SRC) \
| tail -n +`grep -n "%% GENERATED" $(CA_SRC) | cut -d : -f 1` \
>> $(CA_OUT)
mv $(CA_BUNDLE) priv/


Expand Down
1 change: 1 addition & 0 deletions include/hackney.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
expect = false,
async = false,
with_body = false,
max_body,
stream_to,
send_fun=nil,
body_state=waiting,
Expand Down
9,088 changes: 3,971 additions & 5,117 deletions priv/ca-bundle.crt

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion src/hackney_client/hackney.erl
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ request(Method, URL, Headers, Body) ->
%%
%% <li>`with_body': when this option is passed the body is returned
%% directly. The response is `{ok, Status, Headers, Body}'</li>
%% <li>`max_body': sets maximum allowed size of the body if
%% with_body is true</li>
%% <li>`async': receive the response asynchronously
%% The function return {ok, StreamRef}.
%% When {async, once} is used the response will be received only once. To
Expand Down Expand Up @@ -961,7 +963,10 @@ reply_response(Error, State) ->


reply_with_body(Status, Headers, State) ->
Reply = hackney_response:body(State),
Reply = case State#client.max_body of
undefined -> hackney_response:body(State);
MaxBody -> hackney_response:body(MaxBody, State)
end,
case reply(Reply, State) of
{ok, Body} ->
{ok, Status, Headers, Body};
Expand Down Expand Up @@ -998,6 +1003,8 @@ parse_options([{with_body, WithBody} | Rest], State) ->
parse_options(Rest, State#client{with_body=WithBody});
parse_options([with_body | Rest], State) ->
parse_options(Rest, State#client{with_body=true});
parse_options([{max_body, MaxBody} | Rest], State) ->
parse_options(Rest, State#client{max_body=MaxBody});
parse_options([_ | Rest], State) ->
parse_options(Rest, State).

Expand Down
3,997 changes: 3,997 additions & 0 deletions src/hackney_connect/hackney_cacerts.erl

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions src/hackney_connect/hackney_cacerts.erl.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-module(hackney_cacerts).

-export([cacerts/0]).

cacerts() ->
<<"
%% GENERATED
">>.
98 changes: 69 additions & 29 deletions src/hackney_connect/hackney_connect.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@

-include("hackney.hrl").
-include_lib("../hackney_app/hackney_internal.hrl").

-define(SSL_SECURE_VERSION, {5, 3, 6}).
-include_lib("public_key/include/OTP-PUB-KEY.hrl").

connect(Transport, Host, Port) ->
connect(Transport, Host, Port, []).
Expand Down Expand Up @@ -63,6 +62,7 @@ create_connection(Transport, Host, Port, Options, Dynamic)
Async = proplists:get_value(async, Options, false),
StreamTo = proplists:get_value(stream_to, Options, false),
WithBody = proplists:get_value(with_body, Options, false),
MaxBody = proplists:get_value(max_body, Options),

%% get mod metrics
Mod = hackney_util:mod_metrics(),
Expand All @@ -82,6 +82,7 @@ create_connection(Transport, Host, Port, Options, Dynamic)
force_redirect=ForceRedirect,
async=Async,
with_body=WithBody,
max_body=MaxBody,
stream_to=StreamTo,
buffer = <<>>},
%% if we use a pool then checkout the connection from the pool, else
Expand Down Expand Up @@ -279,48 +280,87 @@ check_mod_metrics(State) ->
State#client{mod_metrics=hackney_util:mod_metrics()}.

ssl_opts(Host, Options) ->
CACertFile = filename:join(hackney_util:privdir(), "ca-bundle.crt"),
case proplists:get_value(ssl_options, Options) of
undefined ->
Insecure = proplists:get_value(insecure, Options),
UseSecureSsl = use_secure_ssl(),
UseSecureSsl = check_ssl_version(),
CACerts = cacerts(),

case {Insecure, UseSecureSsl} of
{true, _} ->
[{verify, verify_none},
{reuse_sessions, true}];
{_, true} ->
[{cacertfile, CACertFile},
{server_name_indication, Host},
{verify_fun, {fun ssl_verify_hostname:verify_fun/3,
[{check_hostname, Host}]}},
{verify, verify_peer},
{depth, 2}];
VerifyFun = {fun ssl_verify_hostname:verify_fun/3,
[{check_hostname, Host}]},
[{verify, verify_peer},
{depth, 99},
{cacerts, CACerts},
{partial_chain, fun partial_chain/1},
{verify_fun, VerifyFun}];
{_, _} ->
CACertFile = filename:join(hackney_util:privdir(),
"ca-bundle.crt"),
[{cacertfile, CACertFile },
[{cacerts, CACerts},
{verify, verify_peer}, {depth, 2}]
end;
SSLOpts ->
SSLOpts
end.

ssl_version() ->
case application:get_env(hackney, ssl_version) of
{ok, Version} -> Version;
undefined ->
{ok, Vsn} = application:get_key(ssl, vsn),
Parsed = [list_to_integer(V) || V <- string:tokens(Vsn, ".")],
Version = case Parsed of
[Major] -> [Major, 0, 0];
[Major, Minor] -> [Major, Minor];
[Major, Minor, Patch] -> [Major, Minor, Patch];
[Major, Minor, Patch | _] -> [Major, Minor, Patch]
end,
application:set_env(hackney, ssl_version, Version),
Version
%% code from rebar3 undert BSD license
partial_chain(Certs) ->
Certs1 = [{Cert, public_key:pkix_decode_cert(Cert, otp)} || Cert <- Certs],
CACerts = cacerts(),
CACerts1 = [public_key:pkix_decode_cert(Cert, otp) || Cert <- CACerts],


case find(fun({_, Cert}) ->
check_cert(CACerts1, Cert)
end, Certs1) of
{ok, Trusted} ->
{trusted_ca, element(1, Trusted)};
_ ->
unknown_ca
end.

use_secure_ssl() ->
ssl_version() >= ?SSL_SECURE_VERSION.
extract_public_key_info(Cert) ->
((Cert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subjectPublicKeyInfo).

cacerts() ->
Pems = public_key:pem_decode(hackney_cacerts:cacerts()),
[Der || {'Certificate', Der, _} <- Pems].

check_cert(CACerts, Cert) ->
lists:any(fun(CACert) ->
extract_public_key_info(CACert) == extract_public_key_info(Cert)
end, CACerts).

check_ssl_version() ->
case application:get_key(ssl, vsn) of
{ok, Vsn} ->
parse_vsn(Vsn) >= {5, 3, 6};
_ ->
false
end.

parse_vsn(Vsn) ->
version_pad(string:tokens(Vsn, ".")).

version_pad([Major]) ->
{list_to_integer(Major), 0, 0};
version_pad([Major, Minor]) ->
{list_to_integer(Major), list_to_integer(Minor), 0};
version_pad([Major, Minor, Patch]) ->
{list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)};
version_pad([Major, Minor, Patch | _]) ->
{list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}.

-spec find(fun(), list()) -> {ok, term()} | error.
find(Fun, [Head|Tail]) when is_function(Fun) ->
case Fun(Head) of
true ->
{ok, Head};
false ->
find(Fun, Tail)
end;
find(_Fun, []) ->
error.
2 changes: 1 addition & 1 deletion src/hackney_connect/hackney_ssl_transport.erl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ connect(Host, Port, Opts, Timeout) when is_list(Host), is_integer(Port),
fail_if_no_peer_cert, hibernate_after, ip, key, keyfile,
linger, next_protocols_advertised, nodelay, password, raw,
reuse_session, reuse_sessions, secure_renegotiate,
send_timeout, send_timeout_close, verify,
send_timeout, send_timeout_close, verify, partial_chain,
verify_fun, inet6, versions, server_name_indication,
depth, reuseaddr],
BaseOpts = [binary, {active, false}, {packet, raw}],
Expand Down
2 changes: 1 addition & 1 deletion src/hackney_lib/hackney_http.erl
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ read_chunk(Data, Size) ->
case Data of
<<Chunk:Size/binary, "\r\n", Rest/binary>> ->
{ok, Chunk, Rest};
<<_Chunk:Size/binary, _Rest/binary>> when size(_Rest) >= 2 ->
<<_Chunk:Size/binary, Rest/binary>> when byte_size(Rest) >= 2 ->
{error, poorly_formatted_chunked_size};
_ ->
eof
Expand Down
4 changes: 2 additions & 2 deletions src/hackney_lib/hackney_multipart.erl
Original file line number Diff line number Diff line change
Expand Up @@ -266,10 +266,10 @@ mp_data_header({Name, Len, {Disposition, Params}, ExtraHeaders}, Boundary) ->
%% internal functions
%%
unique(Size) -> unique(Size, <<>>).
unique(Size, Acc) when size(Acc) == Size -> Acc;
unique(0, Acc) -> Acc;
unique(Size, Acc) ->
Random = $a + random:uniform($z - $a),
unique(Size, <<Acc/binary, Random>>).
unique(Size - 1, <<Acc/binary, Random>>).

decode_form1(eof, [[]|Acc]) ->
{ok, lists:reverse(Acc)};
Expand Down
2 changes: 1 addition & 1 deletion support/mk-ca-bundle.pl
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
'http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
);

$opt_d = 'nss';
$opt_d = 'release';

# If the OpenSSL commandline is not in search path you can configure it here!
my $openssl = 'openssl';
Expand Down
7 changes: 7 additions & 0 deletions test/hackney_integration_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ http_requests_test_() ->
fun stop/1,
fun(ok) ->
{inparallel, [get_request(),
request_with_body(),
head_request(),
no_content_response(),
not_modified_response(),
Expand All @@ -33,6 +34,12 @@ get_request() ->
{ok, StatusCode, _, _} = hackney:request(get, URL, [], <<>>, []),
?_assertEqual(200, StatusCode).

request_with_body() ->
URL = <<"http://localhost:8000/robots.txt">>,
ExpectedBody = <<"User-agent: *\nDisallow: /deny\n">>,
{ok, 200, _, Body} = hackney:request(get, URL, [], <<>>, [{with_body, true}]),
?_assertEqual(ExpectedBody, Body).

head_request() ->
URL = <<"http://localhost:8000/get">>,
{ok, StatusCode, _} = hackney:request(head, URL, [], <<>>, []),
Expand Down

0 comments on commit e8d3b09

Please sign in to comment.