Kind of like https://github.com/yandex/inet64_tcp but adapts to some of the changes made to inet_tcp
and inet_tcp6
modules in the last eight years.
def deps do
[
{:happy_tcp, github: "ruslandoga/happy_tcp"}
]
end
iex> default_tcp_module = :inet_db.tcp_module()
#==> :inet_tcp
iex> :inet_db.set_tcp_module(:happy_tcp)
#==> :ok
# try out some of the hosts from http:https://dual.tlund.se
# try ipv4-only host (has only A records)
iex> {:ok, socket} = :gen_tcp.connect(~c"ipv4.tlund.se", 80, active: false)
#==> {:ok, #Port<0.666>}
iex> :inet.peername(socket)
#==> {:ok, {{193, 15, 228, 195}, 80}}
iex> :gen_tcp.close(socket)
#==> :ok
# now try ipv6-only host (has only AAAA recods)
#
# note that this one would only work if you have ipv6 connection to the internet
# you can check it with some other tool like ping6
#
# $ ping6 ipv6.tlund.se
#
iex> {:ok, socket} = :gen_tcp.connect(~c"ipv6.tlund.se", 80, active: false)
#==> {:ok, #Port<0.666>}
#===> or {:error, :ehostunreach} if you don't have IPv6
iex> :inet.peername(socket)
#==> {:ok, {{10752, 2049, 15, 0, 0, 0, 0, 405}, 80}}
iex> :gen_tcp.close(socket)
#==> :ok
# now try dual-stack host (has both A and AAAA records)
#
# this should prefer ipv6 if it's avaible on your machine
iex> {:ok, socket} = :gen_tcp.connect(~c"dual.tlund.se", 80, active: false)
#==> {:ok, #Port<0.666>}
iex> :inet.peername(socket)
#==> {:ok, {{10752, 2049, 15, 0, 0, 0, 0, 405}, 80}}
#==> or {:ok, {{193, 15, 228, 195}, 80}} if you don't have IPv6
iex> :gen_tcp.close(socket)
#==> :ok
# now let's try TLS connections just in case (ensure :ssl app is loaded and started)
iex> {:ok, socket} = :ssl.connect(~c"google.com", 443, [:binary, cacerts: :public_key.cacerts_get])
#==> {:ok,
#==> {:sslsocket, {:gen_tcp, #Port<0.666>, :tls_connection, :undefined},
#==> [#PID<0.667.0>, #PID<0.666.0>]}}
iex> :ssl.peername(socket)
#==> {:ok, {{9220, 26624, 16387, 3074, 0, 0, 0, 101}, 443}}
#==> or {:ok, {{172, 217, 26, 78}, 443}} if you don't have IPv6
iex> :ssl.close(socket)
#==> :ok
# testing done, we can now put everything back the way it was before
iex> :inet_db.set_tcp_module(default_tcp_module)
#==> :ok
When resolving domains, the A and AAAA results are interspersed:
iex> :happy_tcp.getaddrs(~c"google.com")
#==> {:ok,
#==> [
#==> {9220, 26624, 16387, 3074, 0, 0, 0, 139},
#==> {74, 125, 24, 139},
#==> {9220, 26624, 16387, 3074, 0, 0, 0, 102},
#==> {74, 125, 24, 138},
#==> {9220, 26624, 16387, 3074, 0, 0, 0, 100},
#==> {74, 125, 24, 102},
#==> {9220, 26624, 16387, 3074, 0, 0, 0, 101},
#==> {74, 125, 24, 100},
#==> {74, 125, 24, 113},
#==> {74, 125, 24, 101}
#==> ]}
- actually implement happy eyeballs
- can replace / inject a separate gen_tcp module the same way using
inet:gen_tcp_module
or can return a list of list from:happy_tcp.getaddrs
:) - instead of connecting to addrs sequentially, do the happy eyeballs thing and connect to ipv6, give it 300ms, and if by that time it's not connected, start ipv4 connection, then wait to whoever returns first, if none, go ahead through the ips
- "deep down" (gen_tcp -> inet_tcp -> prim_inet) gen_tcp.connect becomes async so we can fire off a couple of them and wait for the first one and close (or cancel) the others
- if gen_tcp_socket backend is used, we can start multiple gen_statems
- can replace / inject a separate gen_tcp module the same way using
- ensure socket works too