-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 <> -> 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}.