diff options
Diffstat (limited to 'src/gun_tcp.erl')
-rw-r--r-- | src/gun_tcp.erl | 73 |
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) -> |