aboutsummaryrefslogtreecommitdiffstats
path: root/src/gun_tcp.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2019-07-22 16:17:10 +0200
committerLoïc Hoguin <[email protected]>2019-07-22 16:17:10 +0200
commit516933f9dd2722329b3886c495d5242308958fe1 (patch)
tree60b16423184a4db405a65266c326b7944ce42256 /src/gun_tcp.erl
parent265ece680c53f77d1685434d0636216c94021497 (diff)
downloadgun-516933f9dd2722329b3886c495d5242308958fe1.tar.gz
gun-516933f9dd2722329b3886c495d5242308958fe1.tar.bz2
gun-516933f9dd2722329b3886c495d5242308958fe1.zip
Split domain lookup/connect/TLS handshake and add events
This changes the way we connect to servers entirely. We now have three states when connecting (domain_lookup, connect and tls_handshake when applicable) and as a result three corresponding timeout options. Each state has a start/end event associated and the event data was tweaked to best match each event. Since the TLS handshake is separate, the transport_opts option was also split into two: tcp_opts and tls_opts.
Diffstat (limited to 'src/gun_tcp.erl')
-rw-r--r--src/gun_tcp.erl73
1 files changed, 66 insertions, 7 deletions
diff --git a/src/gun_tcp.erl b/src/gun_tcp.erl
index 72e5681..2d091a0 100644
--- a/src/gun_tcp.erl
+++ b/src/gun_tcp.erl
@@ -16,23 +16,82 @@
-export([name/0]).
-export([messages/0]).
--export([connect/4]).
+-export([domain_lookup/4]).
+-export([connect/2]).
-export([send/2]).
-export([setopts/2]).
-export([sockname/1]).
-export([close/1]).
+-type lookup_info() :: #{
+ ip_addresses := [inet:ip_address()],
+ port := inet:port_number(),
+ tcp_module := module(),
+ tcp_opts := [gen_tcp:connect_option()] | [ssl:connect_option()]
+}.
+-export_type([lookup_info/0]).
+
name() -> tcp.
messages() -> {tcp, tcp_closed, tcp_error}.
--spec connect(inet:ip_address() | inet:hostname(),
- inet:port_number(), any(), timeout())
+%% The functions domain_lookup/4 and connect/2 are very similar
+%% to gen_tcp:connect/4 except the logic is split in order to
+%% be able to trigger events between the domain lookup step
+%% and the actual connect step.
+
+-spec domain_lookup(inet:ip_address() | inet:hostname(),
+ inet:port_number(), [gen_tcp:connect_option()] | [ssl:connect_option()], timeout())
+ -> {ok, lookup_info()} | {error, atom()}.
+domain_lookup(Address, Port0, Opts0, Timeout) ->
+ {Mod, Opts} = inet:tcp_module(Opts0, Address),
+ Timer = inet:start_timer(Timeout),
+ try Mod:getaddrs(Address, Timer) of
+ {ok, IPs} ->
+ case Mod:getserv(Port0) of
+ {ok, Port} ->
+ {ok, #{
+ ip_addresses => IPs,
+ port => Port,
+ tcp_module => Mod,
+ tcp_opts => Opts ++ [binary, {active, false}, {packet, raw}]
+ }};
+ Error ->
+ maybe_exit(Error)
+ end;
+ Error ->
+ maybe_exit(Error)
+ after
+ _ = inet:stop_timer(Timer)
+ end.
+
+-spec connect(lookup_info(), timeout())
-> {ok, inet:socket()} | {error, atom()}.
-connect(Host, Port, Opts, Timeout) when is_integer(Port) ->
- gen_tcp:connect(Host, Port,
- Opts ++ [binary, {active, false}, {packet, raw}],
- Timeout).
+connect(#{ip_addresses := IPs, port := Port, tcp_module := Mod, tcp_opts := Opts}, Timeout) ->
+ Timer = inet:start_timer(Timeout),
+ Res = try
+ try_connect(IPs, Port, Opts, Timer, Mod, {error, einval})
+ after
+ _ = inet:stop_timer(Timer)
+ end,
+ case Res of
+ {ok, S} -> {ok, S};
+ Error -> maybe_exit(Error)
+ end.
+
+try_connect([IP|IPs], Port, Opts, Timer, Mod, _) ->
+ Timeout = inet:timeout(Timer),
+ case Mod:connect(IP, Port, Opts, Timeout) of
+ {ok, S} -> {ok, S};
+ {error, einval} -> {error, einval};
+ {error, timeout} -> {error, timeout};
+ Error -> try_connect(IPs, Port, Opts, Timer, Mod, Error)
+ end;
+try_connect([], _, _, _, _, Error) ->
+ Error.
+
+maybe_exit({error, einval}) -> exit(badarg);
+maybe_exit(Error) -> Error.
-spec send(inet:socket(), iodata()) -> ok | {error, atom()}.
send(Socket, Packet) ->