mirror of
https://github.com/valitydev/woody_erlang.git
synced 2024-11-06 02:15:19 +00:00
Fixup TLS client options after resolving (#24)
This commit is contained in:
parent
2866b658ea
commit
aa8f9d0cd2
@ -124,7 +124,7 @@ send(Url, Body, Options, ResOpts, WoodyState) ->
|
|||||||
% MSPF-416: We resolve url host to an ip here to prevent
|
% MSPF-416: We resolve url host to an ip here to prevent
|
||||||
% reusing keep-alive connections to dead hosts
|
% reusing keep-alive connections to dead hosts
|
||||||
case woody_resolver:resolve_url(Url, WoodyState, ResOpts) of
|
case woody_resolver:resolve_url(Url, WoodyState, ResOpts) of
|
||||||
{ok, {OldUrl, NewUrl}} ->
|
{ok, {OldUrl, NewUrl}, _ConnectOpts} ->
|
||||||
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
||||||
Options1 = set_defaults(Options),
|
Options1 = set_defaults(Options),
|
||||||
Options2 = set_timeouts(Options1, Context),
|
Options2 = set_timeouts(Options1, Context),
|
||||||
|
@ -154,11 +154,16 @@ send_call(Buffer, #{url := Url} = Opts, WoodyState) ->
|
|||||||
% MSPF-416: We resolve url host to an ip here to prevent
|
% MSPF-416: We resolve url host to an ip here to prevent
|
||||||
% reusing keep-alive connections to dead hosts
|
% reusing keep-alive connections to dead hosts
|
||||||
case woody_resolver:resolve_url(Url, WoodyState, ResolverOpts) of
|
case woody_resolver:resolve_url(Url, WoodyState, ResolverOpts) of
|
||||||
{ok, {OldUrl, NewUrl}} ->
|
{ok, {OldUrl, NewUrl}, ConnectOpts} ->
|
||||||
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
Headers = add_host_header(OldUrl, make_woody_headers(Context)),
|
||||||
TransportOpts1 = set_defaults(TransportOpts),
|
TransportOpts1 = set_defaults(TransportOpts),
|
||||||
TransportOpts2 = set_timeouts(TransportOpts1, Context),
|
TransportOpts2 = set_timeouts(TransportOpts1, Context),
|
||||||
Result = hackney:request(post, NewUrl, Headers, Buffer, maps:to_list(TransportOpts2)),
|
% NOTE
|
||||||
|
% This is to ensure hackney won't try to resolve original hostname again in
|
||||||
|
% `set_tls_overrides/2`.
|
||||||
|
TransportOpts3 = append_connect_opts(TransportOpts2, ConnectOpts),
|
||||||
|
TransportOpts4 = set_tls_overrides(TransportOpts3, OldUrl),
|
||||||
|
Result = hackney:request(post, NewUrl, Headers, Buffer, maps:to_list(TransportOpts4)),
|
||||||
handle_response(Result, WoodyState);
|
handle_response(Result, WoodyState);
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
Error = {error, {resolve_failed, Reason}},
|
Error = {error, {resolve_failed, Reason}},
|
||||||
@ -203,6 +208,19 @@ calc_timeouts(Timeout) ->
|
|||||||
T
|
T
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
append_connect_opts(Options, ConnectOpts) ->
|
||||||
|
Options#{connect_options => maps:get(connect_options, Options, []) ++ ConnectOpts}.
|
||||||
|
|
||||||
|
set_tls_overrides(Options = #{ssl_options := _}, _OrigUrl) ->
|
||||||
|
Options;
|
||||||
|
set_tls_overrides(Options, #hackney_url{scheme = https, host = OrigHost}) ->
|
||||||
|
% NOTE
|
||||||
|
% Beware, we're abusing implementation details here.
|
||||||
|
SslOpts = hackney_connection:connect_options(hackney_ssl, OrigHost, maps:to_list(Options)),
|
||||||
|
Options#{ssl_options => SslOpts};
|
||||||
|
set_tls_overrides(Options, #hackney_url{scheme = _}) ->
|
||||||
|
Options.
|
||||||
|
|
||||||
-spec make_woody_headers(woody_context:ctx()) -> http_headers().
|
-spec make_woody_headers(woody_context:ctx()) -> http_headers().
|
||||||
make_woody_headers(Context) ->
|
make_woody_headers(Context) ->
|
||||||
add_optional_headers(Context, [
|
add_optional_headers(Context, [
|
||||||
|
@ -36,13 +36,13 @@
|
|||||||
%%
|
%%
|
||||||
|
|
||||||
-spec resolve_url(url(), woody_state:st()) ->
|
-spec resolve_url(url(), woody_state:st()) ->
|
||||||
{ok, resolve_result()}
|
{ok, resolve_result(), [gen_tcp:connect_option()]}
|
||||||
| {error, resolve_error()}.
|
| {error, resolve_error()}.
|
||||||
resolve_url(Url, WoodyState) ->
|
resolve_url(Url, WoodyState) ->
|
||||||
resolve_url(Url, WoodyState, #{}).
|
resolve_url(Url, WoodyState, #{}).
|
||||||
|
|
||||||
-spec resolve_url(url(), woody_state:st(), options()) ->
|
-spec resolve_url(url(), woody_state:st(), options()) ->
|
||||||
{ok, resolve_result()}
|
{ok, resolve_result(), [gen_tcp:connect_option()]}
|
||||||
| {error, resolve_error()}.
|
| {error, resolve_error()}.
|
||||||
resolve_url(Url, WoodyState, Opts) when is_list(Url) ->
|
resolve_url(Url, WoodyState, Opts) when is_list(Url) ->
|
||||||
resolve_url(unicode:characters_to_binary(Url), WoodyState, Opts);
|
resolve_url(unicode:characters_to_binary(Url), WoodyState, Opts);
|
||||||
@ -61,21 +61,28 @@ parse_url(Url) ->
|
|||||||
resolve_parsed_url(ParsedUrl = #hackney_url{}, WoodyState, Opts) ->
|
resolve_parsed_url(ParsedUrl = #hackney_url{}, WoodyState, Opts) ->
|
||||||
case inet:parse_address(ParsedUrl#hackney_url.host) of
|
case inet:parse_address(ParsedUrl#hackney_url.host) of
|
||||||
% url host is already an ip, move on
|
% url host is already an ip, move on
|
||||||
{ok, _} -> {ok, {ParsedUrl, ParsedUrl}};
|
{ok, IpAddr} ->
|
||||||
{error, _} -> do_resolve_url(ParsedUrl, WoodyState, Opts)
|
IpFamily =
|
||||||
|
case tuple_size(IpAddr) of
|
||||||
|
4 -> inet;
|
||||||
|
8 -> inet6
|
||||||
|
end,
|
||||||
|
{ok, {ParsedUrl, ParsedUrl}, [IpFamily]};
|
||||||
|
{error, _} ->
|
||||||
|
do_resolve_url(ParsedUrl, WoodyState, Opts)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
do_resolve_url(ParsedUrl, WoodyState, Opts) ->
|
do_resolve_url(ParsedUrl, WoodyState, Opts) ->
|
||||||
UnresolvedHost = ParsedUrl#hackney_url.host,
|
UnresolvedHost = ParsedUrl#hackney_url.host,
|
||||||
_ = log_event(?EV_CLIENT_RESOLVE_BEGIN, WoodyState, #{host => UnresolvedHost}),
|
_ = log_event(?EV_CLIENT_RESOLVE_BEGIN, WoodyState, #{host => UnresolvedHost}),
|
||||||
case lookup_host(UnresolvedHost, Opts) of
|
case lookup_host(UnresolvedHost, Opts) of
|
||||||
{ok, {IpAddr, _} = AddrInfo} ->
|
{ok, {IpAddr, IpFamily} = AddrInfo} ->
|
||||||
_ = log_event(?EV_CLIENT_RESOLVE_RESULT, WoodyState, #{
|
_ = log_event(?EV_CLIENT_RESOLVE_RESULT, WoodyState, #{
|
||||||
status => ok,
|
status => ok,
|
||||||
host => UnresolvedHost,
|
host => UnresolvedHost,
|
||||||
address => inet:ntoa(IpAddr)
|
address => inet:ntoa(IpAddr)
|
||||||
}),
|
}),
|
||||||
{ok, {ParsedUrl, replace_host(ParsedUrl, AddrInfo)}};
|
{ok, {ParsedUrl, replace_host(ParsedUrl, AddrInfo)}, [IpFamily]};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
_ = log_event(?EV_CLIENT_RESOLVE_RESULT, WoodyState, #{
|
_ = log_event(?EV_CLIENT_RESOLVE_RESULT, WoodyState, #{
|
||||||
status => error,
|
status => error,
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
-export([
|
-export([
|
||||||
client_wo_cert_test/1,
|
client_wo_cert_test/1,
|
||||||
valid_client_cert_test/1,
|
valid_client_cert_test/1,
|
||||||
invalid_client_cert_test/1
|
invalid_client_cert_test/1,
|
||||||
|
valid_cert_external_server/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
%% woody_server_thrift_handler callback
|
%% woody_server_thrift_handler callback
|
||||||
@ -58,7 +59,8 @@ all() ->
|
|||||||
[
|
[
|
||||||
{group, 'tlsv1.3'},
|
{group, 'tlsv1.3'},
|
||||||
{group, 'tlsv1.2'},
|
{group, 'tlsv1.2'},
|
||||||
{group, 'tlsv1.1'}
|
{group, 'tlsv1.1'},
|
||||||
|
valid_cert_external_server
|
||||||
].
|
].
|
||||||
|
|
||||||
-spec groups() -> [{group_name(), list(), [case_name()]}].
|
-spec groups() -> [{group_name(), list(), [case_name()]}].
|
||||||
@ -141,6 +143,30 @@ invalid_client_cert_test(C) ->
|
|||||||
{match, _} = re:run(Reason, <<"^{tls_alert,[\"\{]unknown[ _]ca.*$">>, [])
|
{match, _} = re:run(Reason, <<"^{tls_alert,[\"\{]unknown[ _]ca.*$">>, [])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec valid_cert_external_server(config()) -> _.
|
||||||
|
valid_cert_external_server(_C) ->
|
||||||
|
% NOTE
|
||||||
|
% This testcase needs internet connectivity.
|
||||||
|
% This testcase relies on correct TLS setup and implementation on example.org servers.
|
||||||
|
Url = <<"https://example.org/just-testing-tls-nevermind">>,
|
||||||
|
Context = woody_context:new(to_binary(?FUNCTION_NAME)),
|
||||||
|
Service = {?THRIFT_DEFS, 'Weapons'},
|
||||||
|
Options = #{
|
||||||
|
url => Url,
|
||||||
|
event_handler => {woody_ct_event_h, {client, external}}
|
||||||
|
},
|
||||||
|
try
|
||||||
|
_ = woody_client:call({Service, get_weapon, {<<"Example">>, <<>>}}, Options, Context),
|
||||||
|
error(unreachable)
|
||||||
|
catch
|
||||||
|
error:{woody_error, {
|
||||||
|
external,
|
||||||
|
result_unexpected,
|
||||||
|
<<"This server does not implement the woody protocol", _/binary>>
|
||||||
|
}} ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
-spec client_ssl_opts(atom()) -> [ssl:tls_client_option()].
|
-spec client_ssl_opts(atom()) -> [ssl:tls_client_option()].
|
||||||
client_ssl_opts('tlsv1.3') ->
|
client_ssl_opts('tlsv1.3') ->
|
||||||
% NOTE
|
% NOTE
|
||||||
|
@ -945,30 +945,30 @@ calls_with_cache(_) ->
|
|||||||
woody_resolver_inet(C) ->
|
woody_resolver_inet(C) ->
|
||||||
WoodyState = woody_state:new(client, woody_context:new(), ?MODULE),
|
WoodyState = woody_state:new(client, woody_context:new(), ?MODULE),
|
||||||
ok = inet_db:set_inet6(false),
|
ok = inet_db:set_inet6(false),
|
||||||
{ok, ?RESPONSE(http, <<"127.0.0.1">>, <<"127.0.0.1">>, <<"/test">>)} =
|
{ok, ?RESPONSE(http, <<"127.0.0.1">>, <<"127.0.0.1">>, <<"/test">>), [inet]} =
|
||||||
woody_resolver:resolve_url(<<"http://127.0.0.1/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"http://127.0.0.1/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(http, <<"localhost">>, <<"127.0.0.1:80">>, <<"/test">>)} =
|
{ok, ?RESPONSE(http, <<"localhost">>, <<"127.0.0.1:80">>, <<"/test">>), [inet]} =
|
||||||
woody_resolver:resolve_url(<<"http://localhost/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"http://localhost/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(http, <<"localhost">>, <<"127.0.0.1:80">>, <<"/test?q=a">>)} =
|
{ok, ?RESPONSE(http, <<"localhost">>, <<"127.0.0.1:80">>, <<"/test?q=a">>), [inet]} =
|
||||||
woody_resolver:resolve_url("http://localhost/test?q=a", WoodyState),
|
woody_resolver:resolve_url("http://localhost/test?q=a", WoodyState),
|
||||||
{ok, ?RESPONSE(https, <<"localhost:8080">>, <<"127.0.0.1:8080">>, <<"/test">>)} =
|
{ok, ?RESPONSE(https, <<"localhost:8080">>, <<"127.0.0.1:8080">>, <<"/test">>), [inet]} =
|
||||||
woody_resolver:resolve_url(<<"https://localhost:8080/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"https://localhost:8080/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(https, <<"localhost">>, <<"127.0.0.1:443">>, <<>>)} =
|
{ok, ?RESPONSE(https, <<"localhost">>, <<"127.0.0.1:443">>, <<>>), [inet]} =
|
||||||
woody_resolver:resolve_url(<<"https://localhost">>, WoodyState),
|
woody_resolver:resolve_url(<<"https://localhost">>, WoodyState),
|
||||||
ok = inet_db:set_inet6(?config(env_inet6, C)).
|
ok = inet_db:set_inet6(?config(env_inet6, C)).
|
||||||
|
|
||||||
woody_resolver_inet6(C) ->
|
woody_resolver_inet6(C) ->
|
||||||
WoodyState = woody_state:new(client, woody_context:new(), ?MODULE),
|
WoodyState = woody_state:new(client, woody_context:new(), ?MODULE),
|
||||||
ok = inet_db:set_inet6(true),
|
ok = inet_db:set_inet6(true),
|
||||||
{ok, ?RESPONSE(http, <<"[::1]">>, <<"[::1]">>, <<"/test">>)} =
|
{ok, ?RESPONSE(http, <<"[::1]">>, <<"[::1]">>, <<"/test">>), [inet6]} =
|
||||||
woody_resolver:resolve_url(<<"http://[::1]/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"http://[::1]/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(http, <<"localhost">>, <<"[::1]:80">>, <<"/test">>)} =
|
{ok, ?RESPONSE(http, <<"localhost">>, <<"[::1]:80">>, <<"/test">>), [inet6]} =
|
||||||
woody_resolver:resolve_url(<<"http://localhost/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"http://localhost/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(http, <<"localhost">>, <<"[::1]:80">>, <<"/test?q=a">>)} =
|
{ok, ?RESPONSE(http, <<"localhost">>, <<"[::1]:80">>, <<"/test?q=a">>), [inet6]} =
|
||||||
woody_resolver:resolve_url("http://localhost/test?q=a", WoodyState),
|
woody_resolver:resolve_url("http://localhost/test?q=a", WoodyState),
|
||||||
{ok, ?RESPONSE(https, <<"localhost:8080">>, <<"[::1]:8080">>, <<"/test">>)} =
|
{ok, ?RESPONSE(https, <<"localhost:8080">>, <<"[::1]:8080">>, <<"/test">>), [inet6]} =
|
||||||
woody_resolver:resolve_url(<<"https://localhost:8080/test">>, WoodyState),
|
woody_resolver:resolve_url(<<"https://localhost:8080/test">>, WoodyState),
|
||||||
{ok, ?RESPONSE(https, <<"localhost">>, <<"[::1]:443">>, <<>>)} =
|
{ok, ?RESPONSE(https, <<"localhost">>, <<"[::1]:443">>, <<>>), [inet6]} =
|
||||||
woody_resolver:resolve_url(<<"https://localhost">>, WoodyState),
|
woody_resolver:resolve_url(<<"https://localhost">>, WoodyState),
|
||||||
ok = inet_db:set_inet6(?config(env_inet6, C)).
|
ok = inet_db:set_inet6(?config(env_inet6, C)).
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user