Current section
Files
Jump to
Current section
Files
src/pgl@internal@socket.erl
-module(pgl@internal@socket).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pgl/internal/socket.gleam").
-export([new/0, host/2, port/2, timeout/2, ipv6/2, parameter/3, connect/1, start_ping/2, stop_ping/1, to_ssl/2, send/2, 'receive'/2, shutdown/1, supervised/1, factory/1]).
-export_type([internal_socket/0, builder/0, socket/0, state/0, msg/0, factory/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-opaque internal_socket() :: {tcp, neon@tcp:tcp()} | {ssl, neon@ssl:ssl()}.
-opaque builder() :: {builder, binary(), integer(), boolean(), integer()}.
-opaque socket() :: {socket,
gleam@erlang@process:subject(msg()),
binary(),
integer(),
gleam@dict:dict(binary(), binary())}.
-type state() :: {state,
internal_socket(),
gleam@erlang@process:subject(msg()),
integer(),
gleam@option:option(gleam@erlang@process:timer())}.
-opaque msg() :: {start_ping, integer()} |
stop_ping |
{ping, integer()} |
{ssl_upgrade,
gleam@erlang@process:subject({ok, nil} |
{error, pgl@internal:internal_error()}),
binary(),
boolean()} |
{send,
gleam@erlang@process:subject({ok, nil} |
{error, pgl@internal:socket_error()}),
bitstring()} |
{'receive',
gleam@erlang@process:subject({ok, bitstring()} |
{error, pgl@internal:socket_error()}),
integer(),
integer()} |
{shutdown,
gleam@erlang@process:subject({ok, nil} |
{error, pgl@internal:socket_error()})}.
-opaque factory() :: {factory,
gleam@erlang@process:name(gleam@otp@factory_supervisor:message(builder(), socket())),
builder()}.
-file("src/pgl/internal/socket.gleam", 60).
?DOC(false).
-spec new() -> builder().
new() ->
{builder, <<"127.0.0.1"/utf8>>, 5432, false, 1000}.
-file("src/pgl/internal/socket.gleam", 69).
?DOC(false).
-spec host(builder(), binary()) -> builder().
host(Builder, Host) ->
{builder,
Host,
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder)}.
-file("src/pgl/internal/socket.gleam", 73).
?DOC(false).
-spec port(builder(), integer()) -> builder().
port(Builder, Port) ->
{builder,
erlang:element(2, Builder),
Port,
erlang:element(4, Builder),
erlang:element(5, Builder)}.
-file("src/pgl/internal/socket.gleam", 77).
?DOC(false).
-spec timeout(builder(), integer()) -> builder().
timeout(Builder, Timeout) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
Timeout}.
-file("src/pgl/internal/socket.gleam", 81).
?DOC(false).
-spec ipv6(builder(), boolean()) -> builder().
ipv6(Builder, Ipv6) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
Ipv6,
erlang:element(5, Builder)}.
-file("src/pgl/internal/socket.gleam", 101).
?DOC(false).
-spec parameter(socket(), binary(), binary()) -> socket().
parameter(Sock, Key, Value) ->
Parameters = begin
_pipe = erlang:element(5, Sock),
gleam@dict:insert(_pipe, Key, Value)
end,
{socket,
erlang:element(2, Sock),
erlang:element(3, Sock),
erlang:element(4, Sock),
Parameters}.
-file("src/pgl/internal/socket.gleam", 106).
?DOC(false).
-spec connect(factory()) -> {ok, socket()} |
{error, gleam@otp@actor:start_error()}.
connect(Factory) ->
_pipe = gleam@otp@factory_supervisor:get_by_name(erlang:element(2, Factory)),
_pipe@1 = gleam@otp@factory_supervisor:start_child(
_pipe,
erlang:element(3, Factory)
),
gleam@result:map(_pipe@1, fun(Started) -> erlang:element(3, Started) end).
-file("src/pgl/internal/socket.gleam", 140).
?DOC(false).
-spec start_ping(socket(), integer()) -> nil.
start_ping(Socket, Interval) ->
gleam@erlang@process:send(erlang:element(2, Socket), {start_ping, Interval}).
-file("src/pgl/internal/socket.gleam", 144).
?DOC(false).
-spec stop_ping(socket()) -> nil.
stop_ping(Socket) ->
gleam@erlang@process:send(erlang:element(2, Socket), stop_ping).
-file("src/pgl/internal/socket.gleam", 148).
?DOC(false).
-spec to_ssl(socket(), boolean()) -> {ok, socket()} |
{error, pgl@internal:internal_error()}.
to_ssl(Socket, Verified) ->
_pipe = gleam@otp@actor:call(
erlang:element(2, Socket),
1000,
fun(_capture) ->
{ssl_upgrade, _capture, erlang:element(3, Socket), Verified}
end
),
gleam@result:replace(_pipe, Socket).
-file("src/pgl/internal/socket.gleam", 156).
?DOC(false).
-spec send(socket(), bitstring()) -> {ok, socket()} |
{error, pgl@internal:internal_error()}.
send(Socket, Payload) ->
_pipe = gleam@otp@actor:call(
erlang:element(2, Socket),
1000,
fun(_capture) -> {send, _capture, Payload} end
),
_pipe@1 = gleam@result:map_error(
_pipe,
fun(Kind) -> {socket_error, Kind, <<"Failed to send"/utf8>>} end
),
gleam@result:replace(_pipe@1, Socket).
-file("src/pgl/internal/socket.gleam", 167).
?DOC(false).
-spec 'receive'(socket(), integer()) -> {ok, bitstring()} |
{error, pgl@internal:internal_error()}.
'receive'(Conn, Length) ->
_pipe = gleam@otp@actor:call(
erlang:element(2, Conn),
erlang:element(4, Conn),
fun(_capture) ->
{'receive', _capture, Length, erlang:element(4, Conn)}
end
),
gleam@result:map_error(
_pipe,
fun(Kind) -> {socket_error, Kind, <<"Failed to receive"/utf8>>} end
).
-file("src/pgl/internal/socket.gleam", 177).
?DOC(false).
-spec shutdown(socket()) -> {ok, nil} | {error, pgl@internal:internal_error()}.
shutdown(Conn) ->
_pipe = gleam@otp@actor:call(
erlang:element(2, Conn),
1000,
fun(Field@0) -> {shutdown, Field@0} end
),
gleam@result:map_error(
_pipe,
fun(Kind) -> {socket_error, Kind, <<"Failed to shutdown"/utf8>>} end
).
-file("src/pgl/internal/socket.gleam", 359).
?DOC(false).
-spec tcp_error_to_socket_error(neon@tcp:tcp_error()) -> pgl@internal:socket_error().
tcp_error_to_socket_error(Error) ->
case Error of
closed ->
closed;
timeout ->
timeout;
system_limit ->
system_limit;
{posix, Code} ->
{posix, Code};
{tcp_error, Message} ->
{tcp_error, Message}
end.
-file("src/pgl/internal/socket.gleam", 335).
?DOC(false).
-spec tcp_connect(binary(), integer(), boolean()) -> {ok, internal_socket()} |
{error, binary()}.
tcp_connect(Host, Port, Ipv6) ->
_pipe = neon@net:port(Port),
_pipe@1 = gleam@result:replace_error(
_pipe,
{connect_error, <<"Invalid port"/utf8>>}
),
_pipe@8 = gleam@result:'try'(
_pipe@1,
fun(Port@1) ->
Ip_version = case Ipv6 of
true ->
ipv6;
false ->
ipv4
end,
_pipe@2 = Host,
_pipe@3 = neon@net:hostname(_pipe@2),
_pipe@4 = neon@tcp:new(_pipe@3, Port@1),
_pipe@5 = neon@tcp:ip_version(_pipe@4, Ip_version),
_pipe@6 = neon@tcp:connect(_pipe@5),
_pipe@7 = gleam@result:map_error(
_pipe@6,
fun tcp_error_to_socket_error/1
),
gleam@result:map(_pipe@7, fun(Field@0) -> {tcp, Field@0} end)
end
),
gleam@result:map_error(_pipe@8, fun pgl@internal:socket_error_to_string/1).
-file("src/pgl/internal/socket.gleam", 381).
?DOC(false).
-spec tls_alert_to_string(neon@ssl:tls_alert()) -> binary().
tls_alert_to_string(Alert) ->
case Alert of
close_notify ->
<<"close_notify"/utf8>>;
unexpected_message ->
<<"unexpected_message"/utf8>>;
bad_record_mac ->
<<"bad_record_mac"/utf8>>;
record_overflow ->
<<"record_overflow"/utf8>>;
handshake_failure ->
<<"handshake_failure"/utf8>>;
bad_certificate ->
<<"bad_certificate"/utf8>>;
unsupported_certificate ->
<<"unsupported_certificate"/utf8>>;
certificate_revoked ->
<<"certificate_revoked"/utf8>>;
certificate_expired ->
<<"certificate_expired"/utf8>>;
certificate_unknown ->
<<"certificate_unknown"/utf8>>;
illegal_parameter ->
<<"illegal_parameter"/utf8>>;
unknown_ca ->
<<"unknown_ca"/utf8>>;
access_denied ->
<<"access_denied"/utf8>>;
decode_error ->
<<"decode_error"/utf8>>;
decrypt_error ->
<<"decrypt_error"/utf8>>;
export_restriction ->
<<"export_restriction"/utf8>>;
protocol_version ->
<<"protocol_version"/utf8>>;
insufficient_security ->
<<"insufficient_security"/utf8>>;
internal_error ->
<<"internal_error"/utf8>>;
inappropriate_fallback ->
<<"inappropriate_fallback"/utf8>>;
user_canceled ->
<<"user_canceled"/utf8>>;
no_renegotiation ->
<<"no_renegotiation"/utf8>>;
unsupported_extension ->
<<"unsupported_extension"/utf8>>;
certificate_unobtainable ->
<<"certificate_unobtainable"/utf8>>;
unrecognized_name ->
<<"unrecognized_name"/utf8>>;
bad_certificate_status_response ->
<<"bad_certificate_status_response"/utf8>>;
bad_certificate_hash_value ->
<<"bad_certificate_hash_value"/utf8>>;
unknown_psk_identity ->
<<"unknown_psk_identity"/utf8>>;
no_application_protocol ->
<<"no_application_protocol"/utf8>>
end.
-file("src/pgl/internal/socket.gleam", 369).
?DOC(false).
-spec ssl_error_to_socket_error(neon@ssl:ssl_error()) -> pgl@internal:socket_error().
ssl_error_to_socket_error(Error) ->
case Error of
closed ->
closed;
timeout ->
timeout;
{posix, Code} ->
{posix, Code};
{tls_alert, Alert, Message} ->
{tls_alert,
<<<<(tls_alert_to_string(Alert))/binary, " | "/utf8>>/binary,
Message/binary>>};
{ssl_error, Message@1} ->
{ssl_sock_error, Message@1};
ssl_not_started ->
{ssl_sock_error, <<"SSL Not Started"/utf8>>}
end.
-file("src/pgl/internal/socket.gleam", 313).
?DOC(false).
-spec tcp_to_ssl(internal_socket(), binary(), boolean()) -> {ok,
internal_socket()} |
{error, pgl@internal:socket_error()}.
tcp_to_ssl(Socket, Host, Verified) ->
case Socket of
{tcp, Sock} ->
Verifier = case Verified of
true ->
fun neon@ssl:verify_peer/1;
false ->
fun neon@ssl:verify_none/1
end,
_pipe = neon@ssl:from_tcp(Sock, Host),
_pipe@1 = Verifier(_pipe),
_pipe@2 = neon@ssl:connect(_pipe@1),
_pipe@3 = gleam@result:map(
_pipe@2,
fun(Field@0) -> {ssl, Field@0} end
),
gleam@result:map_error(_pipe@3, fun ssl_error_to_socket_error/1);
_ ->
{ok, Socket}
end.
-file("src/pgl/internal/socket.gleam", 415).
?DOC(false).
-spec socket_send(internal_socket(), bitstring()) -> {ok, nil} |
{error, pgl@internal:socket_error()}.
socket_send(Socket, Payload) ->
case Socket of
{tcp, Sock} ->
_pipe = neon@tcp:send(Sock, Payload),
gleam@result:map_error(_pipe, fun tcp_error_to_socket_error/1);
{ssl, Sock@1} ->
_pipe@1 = neon@ssl:send(Sock@1, Payload),
gleam@result:map_error(_pipe@1, fun ssl_error_to_socket_error/1)
end.
-file("src/pgl/internal/socket.gleam", 427).
?DOC(false).
-spec socket_receive(internal_socket(), integer(), neon@net:timeout_()) -> {ok,
bitstring()} |
{error, pgl@internal:socket_error()}.
socket_receive(Socket, Length, Timeout) ->
case Socket of
{tcp, Sock} ->
_pipe = neon@tcp:'receive'(Sock, Length, Timeout),
gleam@result:map_error(_pipe, fun tcp_error_to_socket_error/1);
{ssl, Sock@1} ->
_pipe@1 = neon@ssl:'receive'(Sock@1, Length, Timeout),
gleam@result:map_error(_pipe@1, fun ssl_error_to_socket_error/1)
end.
-file("src/pgl/internal/socket.gleam", 209).
?DOC(false).
-spec receive_message(internal_socket(), integer()) -> {ok,
pgl@internal:message()} |
{error, pgl@internal:internal_error()}.
receive_message(Sock, Timeout) ->
_pipe = neon@net:timeout(Timeout),
_pipe@1 = gleam@result:replace_error(
_pipe,
{socket_error, timeout, <<""/utf8>>}
),
gleam@result:'try'(
_pipe@1,
fun(Timeout@1) -> _pipe@2 = socket_receive(Sock, 5, Timeout@1),
_pipe@3 = gleam@result:map_error(
_pipe@2,
fun(_capture) -> {socket_error, _capture, <<""/utf8>>} end
),
gleam@result:'try'(_pipe@3, fun(Data) -> case Data of
<<Code:8/bitstring, Size:32/integer>> ->
case Size - 4 of
0 ->
pgl@internal@decode:message(Code, <<>>);
Size1 ->
_pipe@4 = socket_receive(
Sock,
Size1,
Timeout@1
),
_pipe@5 = gleam@result:map_error(
_pipe@4,
fun(_capture@1) ->
{socket_error,
_capture@1,
<<""/utf8>>}
end
),
gleam@result:'try'(
_pipe@5,
fun(_capture@2) ->
pgl@internal@decode:message(
Code,
_capture@2
)
end
)
end;
_ ->
_pipe@6 = decoding_error,
_pipe@7 = {protocol_error,
_pipe@6,
<<"Unexpected data received"/utf8>>},
{error, _pipe@7}
end end) end
).
-file("src/pgl/internal/socket.gleam", 194).
?DOC(false).
-spec flush(
{ok, LQG} | {error, pgl@internal:internal_error()},
internal_socket(),
integer()
) -> {ok, LQG} | {error, pgl@internal:internal_error()}.
flush(Res, Sock, Timeout) ->
_pipe = receive_message(Sock, Timeout),
gleam@result:'try'(_pipe, fun(Msg) -> case Msg of
{parameter_status, _, _} ->
flush(Res, Sock, Timeout);
{ready_for_query, _} ->
Res;
_ ->
flush(Res, Sock, Timeout)
end end).
-file("src/pgl/internal/socket.gleam", 184).
?DOC(false).
-spec ping(internal_socket(), integer()) -> {ok, nil} |
{error, pgl@internal:internal_error()}.
ping(Sock, Timeout) ->
_pipe = pgl@internal@encode:sync(),
_pipe@1 = socket_send(Sock, _pipe),
_pipe@2 = gleam@result:map_error(
_pipe@1,
fun(_capture) -> {socket_error, _capture, <<""/utf8>>} end
),
flush(_pipe@2, Sock, Timeout).
-file("src/pgl/internal/socket.gleam", 442).
?DOC(false).
-spec socket_shutdown(internal_socket()) -> {ok, nil} |
{error, pgl@internal:socket_error()}.
socket_shutdown(Socket) ->
case Socket of
{tcp, Sock} ->
_pipe = neon@tcp:shutdown(Sock),
gleam@result:map_error(_pipe, fun tcp_error_to_socket_error/1);
{ssl, Sock@1} ->
_pipe@1 = neon@ssl:shutdown(Sock@1),
gleam@result:map_error(_pipe@1, fun ssl_error_to_socket_error/1)
end.
-file("src/pgl/internal/socket.gleam", 240).
?DOC(false).
-spec handle_message(state(), msg()) -> gleam@otp@actor:next(state(), msg()).
handle_message(State, Msg) ->
case Msg of
{start_ping, Interval} ->
Ping_timer = gleam@erlang@process:send_after(
erlang:element(3, State),
Interval,
{ping, Interval}
),
_pipe = {state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
{some, Ping_timer}},
gleam@otp@actor:continue(_pipe);
stop_ping ->
case erlang:element(5, State) of
{some, Timer} ->
gleam@erlang@process:cancel_timer(Timer);
none ->
timer_not_found
end,
_pipe@1 = {state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
none},
gleam@otp@actor:continue(_pipe@1);
{ping, Interval@1} ->
_ = ping(erlang:element(2, State), erlang:element(4, State)),
Ping_timer@1 = gleam@erlang@process:send_after(
erlang:element(3, State),
Interval@1,
{ping, Interval@1}
),
_pipe@2 = {state,
erlang:element(2, State),
erlang:element(3, State),
erlang:element(4, State),
{some, Ping_timer@1}},
gleam@otp@actor:continue(_pipe@2);
{ssl_upgrade, Client, Host, Verified} ->
_pipe@3 = case tcp_to_ssl(erlang:element(2, State), Host, Verified) of
{ok, Ssl} ->
gleam@otp@actor:send(Client, {ok, nil}),
{state,
Ssl,
erlang:element(3, State),
erlang:element(4, State),
erlang:element(5, State)};
{error, Err} ->
Err@1 = {socket_error,
Err,
<<"Failed to connect SSL"/utf8>>},
gleam@otp@actor:send(Client, {error, Err@1}),
State
end,
gleam@otp@actor:continue(_pipe@3);
{send, Client@1, Payload} ->
_pipe@4 = socket_send(erlang:element(2, State), Payload),
gleam@otp@actor:send(Client@1, _pipe@4),
gleam@otp@actor:continue(State);
{'receive', Client@2, Length, Timeout} ->
_pipe@5 = neon@net:timeout(Timeout),
_pipe@6 = gleam@result:map_error(
_pipe@5,
fun(_) -> {connect_error, <<"Invalid Port"/utf8>>} end
),
_pipe@7 = gleam@result:'try'(
_pipe@6,
fun(Timeout@1) ->
socket_receive(erlang:element(2, State), Length, Timeout@1)
end
),
gleam@otp@actor:send(Client@2, _pipe@7),
gleam@otp@actor:continue(State);
{shutdown, Client@3} ->
_ = socket_send(
erlang:element(2, State),
pgl@internal@encode:terminate()
),
_pipe@8 = socket_shutdown(erlang:element(2, State)),
gleam@otp@actor:send(Client@3, _pipe@8),
gleam@otp@actor:stop()
end.
-file("src/pgl/internal/socket.gleam", 120).
?DOC(false).
-spec start_socket(builder()) -> {ok, gleam@otp@actor:started(socket())} |
{error, gleam@otp@actor:start_error()}.
start_socket(Builder) ->
{builder, Host, Port, Ipv6, Timeout} = Builder,
_pipe@5 = gleam@otp@actor:new_with_initialiser(
1000,
fun(Subject) -> _pipe = tcp_connect(Host, Port, Ipv6),
gleam@result:map(
_pipe,
fun(Sock) ->
Selector = begin
_pipe@1 = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:select(_pipe@1, Subject)
end,
Socket = {socket, Subject, Host, Timeout, maps:new()},
_pipe@2 = {state, Sock, Subject, Timeout, none},
_pipe@3 = gleam@otp@actor:initialised(_pipe@2),
_pipe@4 = gleam@otp@actor:selecting(_pipe@3, Selector),
gleam@otp@actor:returning(_pipe@4, Socket)
end
) end
),
_pipe@6 = gleam@otp@actor:on_message(_pipe@5, fun handle_message/2),
gleam@otp@actor:start(_pipe@6).
-file("src/pgl/internal/socket.gleam", 112).
?DOC(false).
-spec supervised(factory()) -> gleam@otp@supervision:child_specification(gleam@otp@factory_supervisor:supervisor(builder(), socket())).
supervised(Factory) ->
_pipe = gleam@otp@factory_supervisor:worker_child(fun start_socket/1),
_pipe@1 = gleam@otp@factory_supervisor:named(
_pipe,
erlang:element(2, Factory)
),
gleam@otp@factory_supervisor:supervised(_pipe@1).
-file("src/pgl/internal/socket.gleam", 94).
?DOC(false).
-spec factory(builder()) -> factory().
factory(Builder) ->
_pipe = <<"pgl_sockets"/utf8>>,
_pipe@1 = gleam_erlang_ffi:new_name(_pipe),
{factory, _pipe@1, Builder}.