Packages

A TCP library for Gleam.

Current section

Files

Jump to
tcpea src tcpea@client.erl
Raw

src/tcpea@client.erl

-module(tcpea@client).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([new/2, timeout/2, ip_version/2, send/2, 'receive'/2, shutdown/1, connect/1]).
-spec new(binary(), integer()) -> tcpea@client@types:tcpea_options().
new(Host, Port) ->
{tcpea_options, Host, Port, 1000, transparent}.
-spec timeout(tcpea@client@types:tcpea_options(), integer()) -> tcpea@client@types:tcpea_options().
timeout(Options, Timeout) ->
erlang:setelement(4, Options, Timeout).
-spec ip_version(
tcpea@client@types:tcpea_options(),
tcpea@client@types:ip_version()
) -> tcpea@client@types:tcpea_options().
ip_version(Options, Ip_version) ->
erlang:setelement(5, Options, Ip_version).
-spec send(tcpea@client@types:socket(), bitstring()) -> {ok, nil} |
{error, tcpea@client@types:error()}.
send(Socket, Packet) ->
tcpea_ffi:send(Socket, Packet).
-spec 'receive'(tcpea@client@types:socket(), integer()) -> {ok, bitstring()} |
{error, tcpea@client@types:error()}.
'receive'(Socket, Timeout) ->
gen_tcp:recv(Socket, 0, Timeout).
-spec shutdown(tcpea@client@types:socket()) -> {ok, nil} |
{error, tcpea@client@types:error()}.
shutdown(Socket) ->
tcpea_ffi:shutdown(Socket, read_write).
-spec happy_eyeballs_select(
gleam@erlang@process:selector({ok, tcpea@client@types:socket()} |
{error, tcpea@client@types:error()}),
integer()
) -> {ok, tcpea@client@types:socket()} | {error, tcpea@client@types:error()}.
happy_eyeballs_select(Selector, Attempts) ->
case gleam_erlang_ffi:select(Selector) of
{error, Err} ->
case Attempts of
1 ->
{error, Err};
_ ->
happy_eyeballs_select(Selector, Attempts - 1)
end;
A ->
A
end.
-spec connect_single_stack(tcpea@client@types:tcpea_options()) -> {ok,
tcpea@client@types:socket()} |
{error, tcpea@client@types:error()}.
connect_single_stack(Options) ->
tcpea_ffi:connect(
erlang:element(2, Options),
erlang:element(3, Options),
[{active, false}, {mode, binary}, case erlang:element(5, Options) of
ipv4 ->
inet;
ipv6 ->
inet6;
_ ->
erlang:error(#{gleam_error => panic,
message => <<"unreachable"/utf8>>,
module => <<"tcpea/client"/utf8>>,
function => <<"connect_single_stack"/utf8>>,
line => 118})
end],
erlang:element(4, Options)
).
-spec happy_eyeballs_attempt(
gleam@erlang@process:subject({ok, tcpea@client@types:socket()} |
{error, tcpea@client@types:error()}),
gleam@erlang@process:pid_(),
tcpea@client@types:tcpea_options()
) -> nil.
happy_eyeballs_attempt(Subject, Pid, Options) ->
gleam@erlang@process:send(Subject, case connect_single_stack(Options) of
{ok, Socket} ->
_assert_subject = tcpea_ffi:controlling_process(Socket, Pid),
{ok, nil} = case _assert_subject of
{ok, nil} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"tcpea/client"/utf8>>,
function => <<"happy_eyeballs_attempt"/utf8>>,
line => 90})
end,
{ok, Socket};
A ->
A
end).
-spec connect_happy_eyeballs(tcpea@client@types:tcpea_options()) -> {ok,
tcpea@client@types:socket()} |
{error, tcpea@client@types:error()}.
connect_happy_eyeballs(Options) ->
Subject = gleam@erlang@process:new_subject(),
Pid = erlang:self(),
gleam@erlang@process:start(
fun() ->
happy_eyeballs_attempt(
Subject,
Pid,
erlang:setelement(5, Options, ipv6)
)
end,
true
),
gleam@erlang@process:start(
fun() ->
gleam_erlang_ffi:sleep(250),
happy_eyeballs_attempt(
Subject,
Pid,
erlang:setelement(5, Options, ipv4)
)
end,
true
),
_pipe = gleam_erlang_ffi:new_selector(),
_pipe@1 = gleam@erlang@process:selecting(
_pipe,
Subject,
fun(Res) -> Res end
),
happy_eyeballs_select(_pipe@1, 2).
-spec connect(tcpea@client@types:tcpea_options()) -> {ok,
tcpea@client@types:socket()} |
{error, tcpea@client@types:error()}.
connect(Options) ->
case erlang:element(5, Options) of
transparent ->
connect_happy_eyeballs(Options);
_ ->
connect_single_stack(Options)
end.