Packages
glisten
4.0.0
9.0.1
9.0.0
9.0.0-rc1
8.0.3
8.0.2
8.0.1
8.0.0
8.0.0-rc1
7.0.1
7.0.0
6.0.0
5.0.0
4.0.0
3.0.0
2.0.0
1.0.0
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
a shiny Gleam TCP/TLS server
Current section
Files
Jump to
Current section
Files
src/glisten.erl
-module(glisten).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([get_port/1, get_supervisor/1, get_client_info/1, send/2, handler/2, with_close/2, with_pool_size/2, with_http2/1, start_server/2, serve/2, start_ssl_server/4, serve_ssl/4]).
-export_type([start_error/0, message/1, ip_address/0, server_port/0, server/0, connection/1, handler/2]).
-type start_error() :: listener_closed |
listener_timeout |
acceptor_timeout |
{acceptor_failed, gleam@erlang@process:exit_reason()} |
{acceptor_crashed, gleam@dynamic:dynamic_()} |
{system_error, glisten@socket:socket_reason()}.
-type message(IMG) :: {packet, bitstring()} | {user, IMG}.
-type ip_address() :: {ip_v4, integer(), integer(), integer(), integer()} |
{ip_v6,
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()}.
-type server_port() :: {provided, integer()} | assigned.
-opaque server() :: {server,
server_port(),
glisten@socket:listen_socket(),
gleam@erlang@process:subject(gleam@otp@supervisor:message()),
glisten@transport:transport()}.
-type connection(IMH) :: {connection,
glisten@socket:socket(),
glisten@transport:transport(),
gleam@erlang@process:subject(glisten@internal@handler:message(IMH))}.
-opaque handler(IMI, IMJ) :: {handler,
fun((connection(IMI)) -> {IMJ,
gleam@option:option(gleam@erlang@process:selector(IMI))}),
fun((message(IMI), IMJ, connection(IMI)) -> gleam@otp@actor:next(message(IMI), IMJ)),
gleam@option:option(fun((IMJ) -> nil)),
integer(),
boolean()}.
-spec get_port(server()) -> {ok, integer()} | {error, nil}.
get_port(Server) ->
case erlang:element(2, Server) of
{provided, Value} ->
{ok, Value};
assigned ->
glisten@transport:port(
erlang:element(5, Server),
erlang:element(3, Server)
)
end.
-spec get_supervisor(server()) -> gleam@erlang@process:subject(gleam@otp@supervisor:message()).
get_supervisor(Server) ->
erlang:element(4, Server).
-spec get_client_info(connection(any())) -> {ok, {ip_address(), integer()}} |
{error, nil}.
get_client_info(Conn) ->
_pipe = glisten@transport:peername(
erlang:element(3, Conn),
erlang:element(2, Conn)
),
gleam@result:map(_pipe, fun(Pair) -> case Pair of
{{ip_v4, A, B, C, D}, Port} ->
{{ip_v4, A, B, C, D}, Port};
{{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H}, Port@1} ->
{{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H}, Port@1}
end end).
-spec send(connection(any()), gleam@bytes_builder:bytes_builder()) -> {ok, nil} |
{error, glisten@socket:socket_reason()}.
send(Conn, Msg) ->
glisten@transport:send(
erlang:element(3, Conn),
erlang:element(2, Conn),
Msg
).
-spec convert_on_init(
fun((connection(INN)) -> {INP,
gleam@option:option(gleam@erlang@process:selector(INN))})
) -> fun((glisten@internal@handler:connection(INN)) -> {INP,
gleam@option:option(gleam@erlang@process:selector(INN))}).
convert_on_init(On_init) ->
fun(Conn) ->
Connection = {connection,
erlang:element(3, Conn),
erlang:element(4, Conn),
erlang:element(5, Conn)},
On_init(Connection)
end.
-spec handler(
fun((connection(INV)) -> {INX,
gleam@option:option(gleam@erlang@process:selector(INV))}),
fun((message(INV), INX, connection(INV)) -> gleam@otp@actor:next(message(INV), INX))
) -> handler(INV, INX).
handler(On_init, Loop) ->
{handler, On_init, Loop, none, 10, false}.
-spec map_user_selector(gleam@erlang@process:selector(message(INC))) -> gleam@erlang@process:selector(glisten@internal@handler:loop_message(INC)).
map_user_selector(Selector) ->
gleam_erlang_ffi:map_selector(Selector, fun(Value) -> case Value of
{packet, Msg} ->
{packet, Msg};
{user, Msg@1} ->
{custom, Msg@1}
end end).
-spec convert_loop(
fun((message(INH), INI, connection(INH)) -> gleam@otp@actor:next(message(INH), INI))
) -> fun((glisten@internal@handler:loop_message(INH), INI, glisten@internal@handler:connection(INH)) -> gleam@otp@actor:next(glisten@internal@handler:loop_message(INH), INI)).
convert_loop(Loop) ->
fun(Msg, Data, Conn) ->
Conn@1 = {connection,
erlang:element(3, Conn),
erlang:element(4, Conn),
erlang:element(5, Conn)},
case Msg of
{packet, Msg@1} ->
case Loop({packet, Msg@1}, Data, Conn@1) of
{continue, Data@1, Selector} ->
{continue,
Data@1,
gleam@option:map(Selector, fun map_user_selector/1)};
{stop, Reason} ->
{stop, Reason}
end;
{custom, Msg@2} ->
case Loop({user, Msg@2}, Data, Conn@1) of
{continue, Data@2, Selector@1} ->
{continue,
Data@2,
gleam@option:map(
Selector@1,
fun map_user_selector/1
)};
{stop, Reason@1} ->
{stop, Reason@1}
end
end
end.
-spec with_close(handler(IOE, IOF), fun((IOF) -> nil)) -> handler(IOE, IOF).
with_close(Handler, On_close) ->
erlang:setelement(4, Handler, {some, On_close}).
-spec with_pool_size(handler(IOK, IOL), integer()) -> handler(IOK, IOL).
with_pool_size(Handler, Size) ->
erlang:setelement(5, Handler, Size).
-spec with_http2(handler(IOQ, IOR)) -> handler(IOQ, IOR).
with_http2(Handler) ->
erlang:setelement(6, Handler, true).
-spec start_server(handler(any(), any()), integer()) -> {ok, server()} |
{error, start_error()}.
start_server(Handler, Port) ->
_pipe = glisten@tcp:listen(Port, []),
_pipe@1 = gleam@result:map_error(_pipe, fun(Err) -> case Err of
closed ->
listener_closed;
timeout ->
listener_timeout;
Err@1 ->
{system_error, Err@1}
end end),
gleam@result:then(
_pipe@1,
fun(Socket) ->
_pipe@2 = {pool,
Socket,
convert_loop(erlang:element(3, Handler)),
erlang:element(5, Handler),
convert_on_init(erlang:element(2, Handler)),
erlang:element(4, Handler),
tcp},
_pipe@3 = glisten@internal@acceptor:start_pool(_pipe@2),
_pipe@4 = gleam@result:map_error(
_pipe@3,
fun(Err@2) -> case Err@2 of
init_timeout ->
acceptor_timeout;
{init_failed, Reason} ->
{acceptor_failed, Reason};
{init_crashed, Reason@1} ->
{acceptor_crashed, Reason@1}
end end
),
gleam@result:map(_pipe@4, fun(Pool) -> {server, case Port of
0 ->
assigned;
Val ->
{provided, Val}
end, Socket, Pool, tcp} end)
end
).
-spec serve(handler(any(), any()), integer()) -> {ok,
gleam@erlang@process:subject(gleam@otp@supervisor:message())} |
{error, start_error()}.
serve(Handler, Port) ->
_pipe = start_server(Handler, Port),
gleam@result:map(_pipe, fun get_supervisor/1).
-spec start_ssl_server(handler(any(), any()), integer(), binary(), binary()) -> {ok,
server()} |
{error, start_error()}.
start_ssl_server(Handler, Port, Certfile, Keyfile) ->
_assert_subject = glisten_ssl_ffi:start_ssl(),
{ok, _} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"glisten"/utf8>>,
function => <<"start_ssl_server"/utf8>>,
line => 304})
end,
Ssl_options = [{certfile, Certfile}, {keyfile, Keyfile}],
Protocol_options = case erlang:element(6, Handler) of
true ->
[{alpn_preferred_protocols, [<<"h2"/utf8>>, <<"http/1.1"/utf8>>]}];
false ->
[{alpn_preferred_protocols, [<<"http/1.1"/utf8>>]}]
end,
_pipe = glisten@ssl:listen(
Port,
gleam@list:concat([Ssl_options, Protocol_options])
),
_pipe@1 = gleam@result:map_error(_pipe, fun(Err) -> case Err of
closed ->
listener_closed;
timeout ->
listener_timeout;
Err@1 ->
{system_error, Err@1}
end end),
gleam@result:then(
_pipe@1,
fun(Socket) ->
_pipe@2 = {pool,
Socket,
convert_loop(erlang:element(3, Handler)),
erlang:element(5, Handler),
convert_on_init(erlang:element(2, Handler)),
erlang:element(4, Handler),
ssl},
_pipe@3 = glisten@internal@acceptor:start_pool(_pipe@2),
_pipe@4 = gleam@result:map_error(
_pipe@3,
fun(Err@2) -> case Err@2 of
init_timeout ->
acceptor_timeout;
{init_failed, Reason} ->
{acceptor_failed, Reason};
{init_crashed, Reason@1} ->
{acceptor_crashed, Reason@1}
end end
),
gleam@result:map(_pipe@4, fun(Pool) -> {server, case Port of
0 ->
assigned;
Val ->
{provided, Val}
end, Socket, Pool, ssl} end)
end
).
-spec serve_ssl(handler(any(), any()), integer(), binary(), binary()) -> {ok,
gleam@erlang@process:subject(gleam@otp@supervisor:message())} |
{error, start_error()}.
serve_ssl(Handler, Port, Certfile, Keyfile) ->
_pipe = start_ssl_server(Handler, Port, Certfile, Keyfile),
gleam@result:map(_pipe, fun get_supervisor/1).