Packages
mist
6.0.3
6.0.3
6.0.2
6.0.1
6.0.0
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
5.0.0-rc1
4.0.7
4.0.6
4.0.5
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
3.0.0
2.0.0
1.2.0
1.1.0
1.0.0
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
0.17.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
a misty Gleam web server
Current section
Files
Jump to
Current section
Files
src/mist.erl
-module(mist).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/mist.gleam").
-export([continue/1, with_selector/2, stop/0, stop_abnormal/1, ip_address_to_string/1, connection_info_to_string/1, get_connection_info/1, send_file/3, read_body/2, stream/1, new/1, port/2, read_request_body/3, after_start/2, bind/2, with_ipv6/1, with_tls/3, start/1, supervised/1, websocket/4, send_binary_frame/2, send_text_frame/2, event/1, event_id/2, event_name/2, event_retry/2, server_sent_events/4, send_event/2, chunk_continue/1, chunk_stop/0, chunk_stop_abnormal/1, send_chunk/2, chunked/4]).
-export_type([next/2, ip_address/0, connection_info/0, response_data/0, file_error/0, read_error/0, chunk/0, chunk_state/0, tls_options/0, builder/2, port_/0, websocket_message/1, s_s_e_connection/0, s_s_e_event/0, chunk_next/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque next(LWW, LWX) :: {continue,
LWW,
gleam@option:option(gleam@erlang@process:selector(LWX))} |
normal_stop |
{abnormal_stop, binary()}.
-type ip_address() :: {ip_v4, integer(), integer(), integer(), integer()} |
{ip_v6,
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer(),
integer()}.
-type connection_info() :: {connection_info, integer(), ip_address()}.
-type response_data() :: websocket |
{bytes, gleam@bytes_tree:bytes_tree()} |
chunked |
{file, mist@internal@file:file_descriptor(), integer(), integer()} |
server_sent_events.
-type file_error() :: is_dir | no_access | no_entry | unknown_file_error.
-type read_error() :: excess_body | malformed_body.
-type chunk() :: {chunk,
bitstring(),
fun((integer()) -> {ok, chunk()} | {error, read_error()})} |
done.
-type chunk_state() :: {chunk_state,
mist@internal@buffer:buffer(),
mist@internal@buffer:buffer(),
boolean()}.
-type tls_options() :: {cert_key_files, binary(), binary()}.
-opaque builder(LWY, LWZ) :: {builder,
integer(),
fun((gleam@http@request:request(LWY)) -> gleam@http@response:response(LWZ)),
fun((integer(), gleam@http:scheme(), ip_address()) -> nil),
binary(),
boolean(),
gleam@option:option(tls_options())}.
-type port_() :: assigned | {provided, integer()}.
-type websocket_message(LXA) :: {text, binary()} |
{binary, bitstring()} |
closed |
shutdown |
{custom, LXA}.
-opaque s_s_e_connection() :: {s_s_e_connection,
mist@internal@http:connection()}.
-opaque s_s_e_event() :: {s_s_e_event,
gleam@option:option(binary()),
gleam@option:option(binary()),
gleam@option:option(integer()),
gleam@string_tree:string_tree()}.
-type chunk_next(LXB) :: {chunk_continue, LXB} |
chunk_stop |
{chunk_abort, binary()}.
-file("src/mist.gleam", 53).
-spec continue(LXC) -> next(LXC, any()).
continue(State) ->
{continue, State, none}.
-file("src/mist.gleam", 57).
-spec with_selector(next(LXG, LXH), gleam@erlang@process:selector(LXH)) -> next(LXG, LXH).
with_selector(Next, Selector) ->
case Next of
{continue, State, _} ->
{continue, State, {some, Selector}};
_ ->
Next
end.
-file("src/mist.gleam", 67).
-spec stop() -> next(any(), any()).
stop() ->
normal_stop.
-file("src/mist.gleam", 71).
-spec stop_abnormal(binary()) -> next(any(), any()).
stop_abnormal(Reason) ->
{abnormal_stop, Reason}.
-file("src/mist.gleam", 75).
-spec convert_next(next(LXV, LXW)) -> mist@internal@next:next(LXV, LXW).
convert_next(Next) ->
case Next of
{continue, State, Selector} ->
{continue, State, Selector};
normal_stop ->
normal_stop;
{abnormal_stop, Reason} ->
{abnormal_stop, Reason}
end.
-file("src/mist.gleam", 99).
-spec to_mist_ip_address(glisten:ip_address()) -> ip_address().
to_mist_ip_address(Ip) ->
case Ip of
{ip_v4, A, B, C, D} ->
{ip_v4, A, B, C, D};
{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H} ->
{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H}
end.
-file("src/mist.gleam", 106).
-spec to_glisten_ip_address(ip_address()) -> glisten:ip_address().
to_glisten_ip_address(Ip) ->
case Ip of
{ip_v4, A, B, C, D} ->
{ip_v4, A, B, C, D};
{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H} ->
{ip_v6, A@1, B@1, C@1, D@1, E, F, G, H}
end.
-file("src/mist.gleam", 95).
?DOC(
" Convenience function for printing the `IpAddress` type. It will convert the\n"
" IPv6 loopback to the short-hand `::1`.\n"
).
-spec ip_address_to_string(ip_address()) -> binary().
ip_address_to_string(Address) ->
glisten:ip_address_to_string(to_glisten_ip_address(Address)).
-file("src/mist.gleam", 117).
-spec connection_info_to_string(connection_info()) -> binary().
connection_info_to_string(Connection_info) ->
case erlang:element(3, Connection_info) of
{ip_v6, A, B, C, D, E, F, G, H} ->
Blocks = begin
_pipe = [A, B, C, D, E, F, G, H],
_pipe@1 = gleam@list:map(_pipe, fun erlang:integer_to_binary/1),
gleam@string:join(_pipe@1, <<":"/utf8>>)
end,
<<<<<<"["/utf8, Blocks/binary>>/binary, "]:"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(2, Connection_info)))/binary>>;
{ip_v4, A@1, B@1, C@1, D@1} ->
Blocks@1 = begin
_pipe@2 = [A@1, B@1, C@1, D@1],
_pipe@3 = gleam@list:map(
_pipe@2,
fun erlang:integer_to_binary/1
),
gleam@string:join(_pipe@3, <<"."/utf8>>)
end,
<<<<Blocks@1/binary, ":"/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(2, Connection_info)))/binary>>
end.
-file("src/mist.gleam", 139).
?DOC(" Tries to get the IP address and port of a connected client.\n").
-spec get_connection_info(mist@internal@http:connection()) -> {ok,
connection_info()} |
{error, nil}.
get_connection_info(Conn) ->
_pipe = glisten@transport:peername(
erlang:element(4, Conn),
erlang:element(3, Conn)
),
gleam@result:map(
_pipe,
fun(Pair) ->
{connection_info,
erlang:element(2, Pair),
begin
_pipe@1 = erlang:element(1, Pair),
_pipe@2 = glisten:convert_ip_address(_pipe@1),
to_mist_ip_address(_pipe@2)
end}
end
).
-file("src/mist.gleam", 175).
-spec convert_file_errors(mist@internal@file:file_error()) -> file_error().
convert_file_errors(Err) ->
case Err of
is_dir ->
is_dir;
no_access ->
no_access;
no_entry ->
no_entry;
unknown_file_error ->
unknown_file_error
end.
-file("src/mist.gleam", 189).
?DOC(
" To respond with a file using Erlang's `sendfile`, use this function\n"
" with the specified offset and limit (optional). It will attempt to open the\n"
" file for reading, get its file size, and then send the file. If the read\n"
" errors, this will return the relevant `FileError`. Generally, this will be\n"
" more memory efficient than manually doing this process with `mist.Bytes`.\n"
).
-spec send_file(binary(), integer(), gleam@option:option(integer())) -> {ok,
response_data()} |
{error, file_error()}.
send_file(Path, Offset, Limit) ->
_pipe = Path,
_pipe@1 = gleam_stdlib:identity(_pipe),
_pipe@2 = mist@internal@file:stat(_pipe@1),
_pipe@3 = gleam@result:map_error(_pipe@2, fun convert_file_errors/1),
gleam@result:map(
_pipe@3,
fun(Stat) ->
Length = case Limit of
{some, Value} when (Value + Offset) > erlang:element(3, Stat) ->
erlang:element(3, Stat) - Offset;
{some, Value@1} ->
Value@1;
none ->
erlang:element(3, Stat) - Offset
end,
{file, erlang:element(2, Stat), Offset, Length}
end
).
-file("src/mist.gleam", 220).
?DOC(
" The request body is not pulled from the socket until requested. The\n"
" `content-length` header is used to determine whether the socket is read\n"
" from or not. The read may also fail, and a `ReadError` is raised.\n"
).
-spec read_body(
gleam@http@request:request(mist@internal@http:connection()),
integer()
) -> {ok, gleam@http@request:request(bitstring())} | {error, read_error()}.
read_body(Req, Max_body_limit) ->
_pipe = Req,
_pipe@1 = gleam@http@request:get_header(_pipe, <<"content-length"/utf8>>),
_pipe@2 = gleam@result:'try'(_pipe@1, fun gleam_stdlib:parse_int/1),
_pipe@3 = gleam@result:unwrap(_pipe@2, 0),
(fun(Content_length) -> case Content_length of
Value when Value =< Max_body_limit ->
_pipe@4 = mist@internal@http:read_body(Req),
gleam@result:replace_error(_pipe@4, malformed_body);
_ ->
{error, excess_body}
end end)(_pipe@3).
-file("src/mist.gleam", 249).
-spec do_stream(
gleam@http@request:request(mist@internal@http:connection()),
mist@internal@buffer:buffer()
) -> fun((integer()) -> {ok, chunk()} | {error, read_error()}).
do_stream(Req, Buffer) ->
fun(Size) ->
Socket = erlang:element(3, erlang:element(4, Req)),
Transport = erlang:element(4, erlang:element(4, Req)),
Byte_size = erlang:byte_size(erlang:element(3, Buffer)),
case {erlang:element(2, Buffer), Byte_size} of
{0, 0} ->
{ok, done};
{0, _} ->
{Data, Rest} = mist@internal@buffer:slice(Buffer, Size),
{ok,
{chunk,
Data,
do_stream(Req, mist@internal@buffer:new(Rest))}};
{_, Buffer_size} when Buffer_size >= Size ->
{Data@1, Rest@1} = mist@internal@buffer:slice(Buffer, Size),
New_buffer = {buffer, erlang:element(2, Buffer), Rest@1},
{ok, {chunk, Data@1, do_stream(Req, New_buffer)}};
{_, _} ->
_pipe = mist@internal@http:read_data(
Socket,
Transport,
mist@internal@buffer:empty(),
invalid_body
),
_pipe@1 = gleam@result:replace_error(_pipe, malformed_body),
gleam@result:map(
_pipe@1,
fun(Data@2) ->
Fetched_data = erlang:byte_size(Data@2),
New_buffer@1 = {buffer,
gleam@int:max(
0,
erlang:element(2, Buffer) - Fetched_data
),
gleam@bit_array:append(
erlang:element(3, Buffer),
Data@2
)},
{New_data, Rest@2} = mist@internal@buffer:slice(
New_buffer@1,
Size
),
{chunk,
New_data,
do_stream(
Req,
{buffer,
erlang:element(2, New_buffer@1),
Rest@2}
)}
end
)
end
end.
-file("src/mist.gleam", 314).
-spec fetch_chunks_until(
glisten@socket:socket(),
glisten@transport:transport(),
chunk_state(),
integer()
) -> {ok, {bitstring(), chunk_state()}} | {error, read_error()}.
fetch_chunks_until(Socket, Transport, State, Byte_size) ->
Data_size = erlang:byte_size(erlang:element(3, erlang:element(2, State))),
case {erlang:element(4, State), Data_size} of
{_, Size} when Size >= Byte_size ->
{Value, Rest} = mist@internal@buffer:slice(
erlang:element(2, State),
Byte_size
),
{ok,
{Value,
{chunk_state,
mist@internal@buffer:new(Rest),
erlang:element(3, State),
erlang:element(4, State)}}};
{true, _} ->
{ok,
{erlang:element(3, erlang:element(2, State)),
{chunk_state,
erlang:element(2, State),
erlang:element(3, State),
true}}};
{false, _} ->
case mist@internal@http:parse_chunk(
erlang:element(3, erlang:element(3, State))
) of
complete ->
Updated_state = {chunk_state,
erlang:element(2, State),
mist@internal@buffer:empty(),
true},
fetch_chunks_until(
Socket,
Transport,
Updated_state,
Byte_size
);
{chunk, <<>>, Next_buffer} ->
_pipe = mist@internal@http:read_data(
Socket,
Transport,
Next_buffer,
invalid_body
),
_pipe@1 = gleam@result:replace_error(_pipe, malformed_body),
gleam@result:'try'(
_pipe@1,
fun(New_data) ->
Updated_state@1 = {chunk_state,
erlang:element(2, State),
mist@internal@buffer:new(New_data),
erlang:element(4, State)},
fetch_chunks_until(
Socket,
Transport,
Updated_state@1,
Byte_size
)
end
);
{chunk, Data, Next_buffer@1} ->
Updated_state@2 = {chunk_state,
mist@internal@buffer:append(
erlang:element(2, State),
Data
),
Next_buffer@1,
erlang:element(4, State)},
fetch_chunks_until(
Socket,
Transport,
Updated_state@2,
Byte_size
)
end
end.
-file("src/mist.gleam", 294).
-spec do_stream_chunked(
gleam@http@request:request(mist@internal@http:connection()),
chunk_state()
) -> fun((integer()) -> {ok, chunk()} | {error, read_error()}).
do_stream_chunked(Req, State) ->
Socket = erlang:element(3, erlang:element(4, Req)),
Transport = erlang:element(4, erlang:element(4, Req)),
fun(Size) -> case fetch_chunks_until(Socket, Transport, State, Size) of
{ok, {Data, {chunk_state, _, _, true}}} ->
{ok, {chunk, Data, fun(_) -> {ok, done} end}};
{ok, {Data@1, State@1}} ->
{ok, {chunk, Data@1, do_stream_chunked(Req, State@1)}};
{error, _} ->
{error, malformed_body}
end end.
-file("src/mist.gleam", 366).
?DOC(
" Rather than explicitly reading either the whole body (optionally up to\n"
" `N` bytes), this function allows you to consume a stream of the request\n"
" body. Any errors reading the body will propagate out, or `Chunk`s will be\n"
" emitted. This provides a `consume` method to attempt to grab the next\n"
" `size` chunk from the socket.\n"
).
-spec stream(gleam@http@request:request(mist@internal@http:connection())) -> {ok,
fun((integer()) -> {ok, chunk()} | {error, read_error()})} |
{error, read_error()}.
stream(Req) ->
Continue = begin
_pipe = Req,
_pipe@1 = mist@internal@http:handle_continue(_pipe),
gleam@result:replace_error(_pipe@1, malformed_body)
end,
gleam@result:map(
Continue,
fun(_) ->
Is_chunked = case gleam@http@request:get_header(
Req,
<<"transfer-encoding"/utf8>>
) of
{ok, <<"chunked"/utf8>>} ->
true;
_ ->
false
end,
Data@1 = case erlang:element(2, erlang:element(4, Req)) of
{initial, Data} -> Data;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"stream"/utf8>>,
line => 381,
value => _assert_fail,
start => 11891,
'end' => 11936,
pattern_start => 11902,
pattern_end => 11920})
end,
case Is_chunked of
true ->
State = {chunk_state,
mist@internal@buffer:new(<<>>),
mist@internal@buffer:new(Data@1),
false},
do_stream_chunked(Req, State);
false ->
Content_length = begin
_pipe@2 = Req,
_pipe@3 = gleam@http@request:get_header(
_pipe@2,
<<"content-length"/utf8>>
),
_pipe@4 = gleam@result:'try'(
_pipe@3,
fun gleam_stdlib:parse_int/1
),
gleam@result:unwrap(_pipe@4, 0)
end,
Initial_size = erlang:byte_size(Data@1),
Buffer = {buffer,
gleam@int:max(0, Content_length - Initial_size),
Data@1},
do_stream(Req, Buffer)
end
end
).
-file("src/mist.gleam", 422).
?DOC(
" Create a new `mist` handler with a given function. The default port is\n"
" 4000.\n"
).
-spec new(
fun((gleam@http@request:request(LYX)) -> gleam@http@response:response(LYZ))
) -> builder(LYX, LYZ).
new(Handler) ->
{builder,
4000,
Handler,
fun(Port, Scheme, Interface) ->
Address = case Interface of
{ip_v6, _, _, _, _, _, _, _, _} ->
<<<<"["/utf8, (ip_address_to_string(Interface))/binary>>/binary,
"]"/utf8>>;
_ ->
ip_address_to_string(Interface)
end,
Message = <<<<<<<<<<"Listening on "/utf8,
(gleam@http:scheme_to_string(Scheme))/binary>>/binary,
"://"/utf8>>/binary,
Address/binary>>/binary,
":"/utf8>>/binary,
(erlang:integer_to_binary(Port))/binary>>,
gleam_stdlib:println(Message)
end,
<<"localhost"/utf8>>,
false,
none}.
-file("src/mist.gleam", 447).
?DOC(" Assign a different listening port to the service.\n").
-spec port(builder(LZD, LZE), integer()) -> builder(LZD, LZE).
port(Builder, Port) ->
{builder,
Port,
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder)}.
-file("src/mist.gleam", 454).
?DOC(
" This function allows for implicitly reading the body of requests up\n"
" to a given size. If the size is too large, or the read fails, the provided\n"
" `failure_response` will be sent back as the response.\n"
).
-spec read_request_body(
builder(bitstring(), LZJ),
integer(),
gleam@http@response:response(LZJ)
) -> builder(mist@internal@http:connection(), LZJ).
read_request_body(Builder, Bytes_limit, Failure_response) ->
Handler = fun(Request) -> case read_body(Request, Bytes_limit) of
{ok, Request@1} ->
(erlang:element(3, Builder))(Request@1);
{error, _} ->
Failure_response
end end,
{builder,
erlang:element(2, Builder),
Handler,
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder)}.
-file("src/mist.gleam", 470).
?DOC(
" Override the default function to be called after the service starts. The\n"
" default is to log a message with the listening port.\n"
).
-spec after_start(
builder(LZP, LZQ),
fun((integer(), gleam@http:scheme(), ip_address()) -> nil)
) -> builder(LZP, LZQ).
after_start(Builder, After_start) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
After_start,
erlang:element(5, Builder),
erlang:element(6, Builder),
erlang:element(7, Builder)}.
-file("src/mist.gleam", 481).
?DOC(
" Specify an interface to listen on. This is a string that can have the\n"
" following values: \"localhost\", a valid IPv4 address (i.e. \"127.0.0.1\"), or\n"
" a valid IPv6 address (i.e. \"::1\"). An invalid value will cause the\n"
" application to crash.\n"
).
-spec bind(builder(LZV, LZW), binary()) -> builder(LZV, LZW).
bind(Builder, Interface) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
Interface,
erlang:element(6, Builder),
erlang:element(7, Builder)}.
-file("src/mist.gleam", 490).
?DOC(
" By default, `mist` will listen on `localhost` over IPv4. If you specify an\n"
" IPv4 address to bind to, it will still only serve over IPv4. Calling this\n"
" function will listen on both IPv4 and IPv6 for the given interface. If it is\n"
" not supported, your application will crash. If you provide an IPv6 address\n"
" to `mist.bind`, this function will have no effect.\n"
).
-spec with_ipv6(builder(MAB, MAC)) -> builder(MAB, MAC).
with_ipv6(Builder) ->
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
true,
erlang:element(7, Builder)}.
-file("src/mist.gleam", 495).
?DOC(" Use HTTPS with the provided certificate and key files.\n").
-spec with_tls(builder(MAH, MAI), binary(), binary()) -> builder(MAH, MAI).
with_tls(Builder, Cert, Key) ->
Certfile = mist_ffi:file_open(gleam_stdlib:identity(Cert)),
Keyfile = mist_ffi:file_open(gleam_stdlib:identity(Key)),
_ = case {Certfile, Keyfile} of
{{error, _}, {error, _}} ->
erlang:error(#{gleam_error => panic,
message => <<"Certificate and key file not found"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"with_tls"/utf8>>,
line => 504});
{{ok, _}, {error, _}} ->
erlang:error(#{gleam_error => panic,
message => <<"Key file not found"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"with_tls"/utf8>>,
line => 505});
{{error, _}, {ok, _}} ->
erlang:error(#{gleam_error => panic,
message => <<"Certificate file not found"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"with_tls"/utf8>>,
line => 506});
{{ok, _}, {ok, _}} ->
nil
end,
{builder,
erlang:element(2, Builder),
erlang:element(3, Builder),
erlang:element(4, Builder),
erlang:element(5, Builder),
erlang:element(6, Builder),
{some, {cert_key_files, Cert, Key}}}.
-file("src/mist.gleam", 513).
-spec convert_body_types(gleam@http@response:response(response_data())) -> gleam@http@response:response(mist@internal@http:response_data()).
convert_body_types(Resp) ->
New_body = case erlang:element(4, Resp) of
websocket ->
websocket;
{bytes, Data} ->
{bytes, Data};
{file, Descriptor, Offset, Length} ->
{file, Descriptor, Offset, Length};
chunked ->
chunked;
server_sent_events ->
server_sent_events
end,
gleam@http@response:set_body(Resp, New_body).
-file("src/mist.gleam", 532).
?DOC(" Start a `mist` service with the provided builder.\n").
-spec start(builder(mist@internal@http:connection(), response_data())) -> {ok,
gleam@otp@actor:started(gleam@otp@static_supervisor:supervisor())} |
{error, gleam@otp@actor:start_error()}.
start(Builder) ->
Listener_name = gleam_erlang_ffi:new_name(<<"glisten_listener"/utf8>>),
Factory_name = gleam_erlang_ffi:new_name(<<"mist_factory_supervisor"/utf8>>),
_pipe = gleam@otp@static_supervisor:new(one_for_one),
_pipe@10 = gleam@otp@static_supervisor:add(
_pipe,
gleam@otp@supervision:supervisor(
fun() ->
_pipe@1 = fun(Req) ->
convert_body_types((erlang:element(3, Builder))(Req))
end,
_pipe@2 = mist@internal@handler:with_func(_pipe@1, Factory_name),
_pipe@3 = glisten:new(fun mist@internal@handler:init/1, _pipe@2),
_pipe@4 = glisten:bind(_pipe@3, erlang:element(5, Builder)),
_pipe@5 = (fun(Handler) -> case erlang:element(6, Builder) of
true ->
glisten:with_ipv6(Handler);
false ->
Handler
end end)(_pipe@4),
_pipe@7 = (fun(Handler@1) -> case erlang:element(7, Builder) of
{some, {cert_key_files, Certfile, Keyfile}} ->
_pipe@6 = Handler@1,
glisten:with_tls(_pipe@6, Certfile, Keyfile);
_ ->
Handler@1
end end)(_pipe@5),
_pipe@8 = glisten:with_listener_name(_pipe@7, Listener_name),
_pipe@9 = glisten:start(_pipe@8, erlang:element(2, Builder)),
gleam@result:map(
_pipe@9,
fun(Server) ->
Info = glisten:get_server_info(Listener_name, 5000),
Ip_address = to_mist_ip_address(erlang:element(3, Info)),
Scheme = case gleam@option:is_some(
erlang:element(7, Builder)
) of
true ->
https;
false ->
http
end,
(erlang:element(4, Builder))(
erlang:element(2, Info),
Scheme,
Ip_address
),
Server
end
)
end
)
),
_pipe@14 = gleam@otp@static_supervisor:add(
_pipe@10,
gleam@otp@supervision:supervisor(
fun() ->
_pipe@11 = gleam@otp@factory_supervisor:worker_child(
fun(Start) -> Start() end
),
_pipe@12 = gleam@otp@factory_supervisor:named(
_pipe@11,
Factory_name
),
_pipe@13 = gleam@otp@factory_supervisor:restart_strategy(
_pipe@12,
temporary
),
gleam@otp@factory_supervisor:start(_pipe@13)
end
)
),
gleam@otp@static_supervisor:start(_pipe@14).
-file("src/mist.gleam", 585).
?DOC(" Start the `mist` supervisor as a child of a supervision tree.\n").
-spec supervised(builder(mist@internal@http:connection(), response_data())) -> gleam@otp@supervision:child_specification(gleam@otp@static_supervisor:supervisor()).
supervised(Builder) ->
gleam@otp@supervision:supervisor(fun() -> start(Builder) end).
-file("src/mist.gleam", 600).
-spec internal_to_public_ws_message(
mist@internal@websocket:handler_message(MAX)
) -> {ok, websocket_message(MAX)} | {error, nil}.
internal_to_public_ws_message(Msg) ->
case Msg of
{internal, {data, {text_frame, Data}}} ->
_pipe = Data,
_pipe@1 = gleam@bit_array:to_string(_pipe),
gleam@result:map(_pipe@1, fun(Field@0) -> {text, Field@0} end);
{internal, {data, {binary_frame, Data@1}}} ->
{ok, {binary, Data@1}};
{user, Msg@1} ->
{ok, {custom, Msg@1}};
_ ->
{error, nil}
end.
-file("src/mist.gleam", 625).
?DOC(
" Upgrade a request to handle websockets. If the request is\n"
" malformed, or the websocket process fails to initialize, an empty\n"
" 400 response will be sent to the client.\n"
"\n"
" The `on_init` method will be called when the actual WebSocket process\n"
" is started, and the return value is the initial state and an optional\n"
" selector for receiving user messages.\n"
"\n"
" The `on_close` method is called when the WebSocket process shuts down\n"
" for any reason, valid or otherwise.\n"
).
-spec websocket(
gleam@http@request:request(mist@internal@http:connection()),
fun((MBD, websocket_message(MBE), mist@internal@websocket:websocket_connection()) -> next(MBD, MBE)),
fun((mist@internal@websocket:websocket_connection()) -> {MBD,
gleam@option:option(gleam@erlang@process:selector(MBE))}),
fun((MBD) -> nil)
) -> gleam@http@response:response(response_data()).
websocket(Request, Handler, On_init, On_close) ->
Handler@1 = fun(State, Message, Connection) -> _pipe = Message,
_pipe@1 = internal_to_public_ws_message(_pipe),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(_capture) -> Handler(State, _capture, Connection) end
),
_pipe@3 = gleam@result:unwrap(_pipe@2, continue(State)),
convert_next(_pipe@3) end,
Extensions = begin
_pipe@4 = Request,
_pipe@5 = gleam@http@request:get_header(
_pipe@4,
<<"sec-websocket-extensions"/utf8>>
),
_pipe@6 = gleam@result:map(
_pipe@5,
fun(Header) -> gleam@string:split(Header, <<";"/utf8>>) end
),
gleam@result:unwrap(_pipe@6, [])
end,
Socket = erlang:element(3, erlang:element(4, Request)),
Transport = erlang:element(4, erlang:element(4, Request)),
case mist@internal@http:upgrade(Socket, Transport, Extensions, Request) of
{ok, _} ->
Start = fun() ->
mist@internal@websocket:initialize_connection(
On_init,
On_close,
Handler@1,
Socket,
Transport,
Extensions
)
end,
Factory_supervisor = gleam@otp@factory_supervisor:get_by_name(
erlang:element(5, erlang:element(4, Request))
),
case gleam@otp@factory_supervisor:start_child(
Factory_supervisor,
Start
) of
{ok, Started} ->
case glisten@transport:controlling_process(
Transport,
Socket,
erlang:element(3, Started)
) of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"websocket"/utf8>>,
line => 664,
value => _assert_fail,
start => 20644,
'end' => 20737,
pattern_start => 20655,
pattern_end => 20660})
end,
mist@internal@websocket:set_active(Transport, Socket),
_pipe@7 = gleam@http@response:new(200),
gleam@http@response:set_body(_pipe@7, websocket);
{error, Start_error} ->
Msg = case Start_error of
init_timeout ->
<<"init timed out"/utf8>>;
{init_failed, Reason} ->
<<"init failed: "/utf8, Reason/binary>>;
{init_exited, normal} ->
<<"init exited normally"/utf8>>;
{init_exited, killed} ->
<<"init killed"/utf8>>;
{init_exited, {abnormal, _}} ->
<<"init exited abnormally"/utf8>>
end,
logging:log(
error,
<<"Failed to start WebSocket process: "/utf8,
Msg/binary>>
),
_pipe@8 = gleam@http@response:new(400),
gleam@http@response:set_body(
_pipe@8,
{bytes, gleam@bytes_tree:new()}
)
end;
{error, _} ->
_pipe@9 = gleam@http@response:new(400),
gleam@http@response:set_body(
_pipe@9,
{bytes, gleam@bytes_tree:new()}
)
end.
-file("src/mist.gleam", 698).
?DOC(" Sends a binary frame across the websocket.\n").
-spec send_binary_frame(
mist@internal@websocket:websocket_connection(),
bitstring()
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
send_binary_frame(Connection, Frame) ->
Binary_frame = exception_ffi:rescue(
fun() ->
gramps@websocket:encode_binary_frame(
Frame,
erlang:element(4, Connection),
none
)
end
),
case Binary_frame of
{ok, Binary_frame@1} ->
glisten@transport:send(
erlang:element(3, Connection),
erlang:element(2, Connection),
Binary_frame@1
);
{error, _} ->
logging:log(
error,
<<"Cannot send messages from a different process than the WebSocket"/utf8>>
),
erlang:error(#{gleam_error => panic,
message => <<"Exiting due to sending WebSocket message from non-owning process"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"send_binary_frame"/utf8>>,
line => 715})
end.
-file("src/mist.gleam", 721).
?DOC(" Sends a text frame across the websocket.\n").
-spec send_text_frame(mist@internal@websocket:websocket_connection(), binary()) -> {ok,
nil} |
{error, glisten@socket:socket_reason()}.
send_text_frame(Connection, Frame) ->
Text_frame = exception_ffi:rescue(
fun() ->
gramps@websocket:encode_text_frame(
Frame,
erlang:element(4, Connection),
none
)
end
),
case Text_frame of
{ok, Text_frame@1} ->
glisten@transport:send(
erlang:element(3, Connection),
erlang:element(2, Connection),
Text_frame@1
);
{error, _} ->
logging:log(
error,
<<"Cannot send messages from a different process than the WebSocket"/utf8>>
),
erlang:error(#{gleam_error => panic,
message => <<"Exiting due to sending WebSocket message from non-owning process"/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"send_text_frame"/utf8>>,
line => 738})
end.
-file("src/mist.gleam", 764).
-spec event(gleam@string_tree:string_tree()) -> s_s_e_event().
event(Data) ->
{s_s_e_event, none, none, none, Data}.
-file("src/mist.gleam", 769).
-spec event_id(s_s_e_event(), binary()) -> s_s_e_event().
event_id(Event, Id) ->
{s_s_e_event,
{some, Id},
erlang:element(3, Event),
erlang:element(4, Event),
erlang:element(5, Event)}.
-file("src/mist.gleam", 774).
-spec event_name(s_s_e_event(), binary()) -> s_s_e_event().
event_name(Event, Name) ->
{s_s_e_event,
erlang:element(2, Event),
{some, Name},
erlang:element(4, Event),
erlang:element(5, Event)}.
-file("src/mist.gleam", 779).
-spec event_retry(s_s_e_event(), integer()) -> s_s_e_event().
event_retry(Event, Retry) ->
{s_s_e_event,
erlang:element(2, Event),
erlang:element(3, Event),
{some, Retry},
erlang:element(5, Event)}.
-file("src/mist.gleam", 792).
?DOC(
" Sets up the connection for server-sent events. The initial response provided\n"
" here will have its headers included in the SSE setup. The body is discarded.\n"
" The `init` and `loop` parameters follow the same shape as the\n"
" `gleam/otp/actor` module.\n"
"\n"
" NOTE: There is no proper way within the spec for the server to \"close\" the\n"
" SSE connection. There are ways around it.\n"
"\n"
" See: `examples/eventz` for a sample usage.\n"
).
-spec server_sent_events(
gleam@http@request:request(mist@internal@http:connection()),
gleam@http@response:response(any()),
fun((gleam@erlang@process:subject(MBS)) -> MBU),
fun((MBU, MBS, s_s_e_connection()) -> gleam@otp@actor:next(MBU, MBS))
) -> gleam@http@response:response(response_data()).
server_sent_events(Req, Resp, Init, Loop) ->
With_default_headers = begin
_pipe = Resp,
_pipe@1 = gleam@http@response:set_header(
_pipe,
<<"content-type"/utf8>>,
<<"text/event-stream"/utf8>>
),
_pipe@2 = gleam@http@response:set_header(
_pipe@1,
<<"cache-control"/utf8>>,
<<"no-cache"/utf8>>
),
gleam@http@response:set_header(
_pipe@2,
<<"connection"/utf8>>,
<<"keep-alive"/utf8>>
)
end,
case glisten@transport:send(
erlang:element(4, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
mist@internal@encoder:response_builder(
200,
erlang:element(3, With_default_headers),
<<"1.1"/utf8>>
)
) of
{ok, _} ->
Start = fun() ->
_pipe@8 = gleam@otp@actor:new_with_initialiser(
1000,
fun(Subj) -> _pipe@3 = Init(Subj),
_pipe@4 = gleam@otp@actor:initialised(_pipe@3),
_pipe@5 = gleam@otp@actor:returning(
_pipe@4,
erlang:self()
),
_pipe@7 = gleam@otp@actor:selecting(
_pipe@5,
begin
_pipe@6 = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:select(_pipe@6, Subj)
end
),
{ok, _pipe@7} end
),
_pipe@9 = gleam@otp@actor:on_message(
_pipe@8,
fun(State, Message) ->
Loop(
State,
Message,
{s_s_e_connection, erlang:element(4, Req)}
)
end
),
_pipe@10 = gleam@otp@actor:start(_pipe@9),
gleam@result:map(
_pipe@10,
fun(Started) ->
Pid = erlang:element(3, Started),
{started, Pid, Pid}
end
)
end,
Factory_supervisor = gleam@otp@factory_supervisor:get_by_name(
erlang:element(5, erlang:element(4, Req))
),
case gleam@otp@factory_supervisor:start_child(
Factory_supervisor,
Start
) of
{ok, Started@1} ->
case glisten@transport:controlling_process(
erlang:element(4, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
erlang:element(3, Started@1)
) of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"server_sent_events"/utf8>>,
line => 832,
value => _assert_fail,
start => 26099,
'end' => 26270,
pattern_start => 26110,
pattern_end => 26118})
end,
_pipe@11 = gleam@http@response:new(200),
gleam@http@response:set_body(_pipe@11, server_sent_events);
{error, _} ->
logging:log(error, <<"Failed to start SSE process"/utf8>>),
_pipe@12 = gleam@http@response:new(400),
gleam@http@response:set_body(
_pipe@12,
{bytes, gleam@bytes_tree:new()}
)
end;
{error, _} ->
_pipe@13 = gleam@http@response:new(400),
gleam@http@response:set_body(
_pipe@13,
{bytes, gleam@bytes_tree:new()}
)
end.
-file("src/mist.gleam", 859).
-spec send_event(s_s_e_connection(), s_s_e_event()) -> {ok, nil} | {error, nil}.
send_event(Conn, Event) ->
{s_s_e_connection, Conn@1} = Conn,
Id@1 = begin
_pipe = erlang:element(2, Event),
_pipe@1 = gleam@option:map(
_pipe,
fun(Id) -> <<<<"id: "/utf8, Id/binary>>/binary, "\n"/utf8>> end
),
gleam@option:unwrap(_pipe@1, <<""/utf8>>)
end,
Event_name = begin
_pipe@2 = erlang:element(3, Event),
_pipe@3 = gleam@option:map(
_pipe@2,
fun(Name) ->
<<<<"event: "/utf8, Name/binary>>/binary, "\n"/utf8>>
end
),
gleam@option:unwrap(_pipe@3, <<""/utf8>>)
end,
Retry@1 = begin
_pipe@4 = erlang:element(4, Event),
_pipe@5 = gleam@option:map(
_pipe@4,
fun(Retry) ->
<<<<"retry: "/utf8, (erlang:integer_to_binary(Retry))/binary>>/binary,
"\n"/utf8>>
end
),
gleam@option:unwrap(_pipe@5, <<""/utf8>>)
end,
Data = begin
_pipe@6 = erlang:element(5, Event),
_pipe@7 = gleam@string_tree:split(_pipe@6, <<"\n"/utf8>>),
_pipe@8 = gleam@list:map(
_pipe@7,
fun(Row) -> gleam@string_tree:prepend(Row, <<"data: "/utf8>>) end
),
gleam@string_tree:join(_pipe@8, <<"\n"/utf8>>)
end,
Message = begin
_pipe@9 = Data,
_pipe@10 = gleam@string_tree:prepend(_pipe@9, Event_name),
_pipe@11 = gleam@string_tree:prepend(_pipe@10, Id@1),
_pipe@12 = gleam@string_tree:prepend(_pipe@11, Retry@1),
_pipe@13 = gleam@string_tree:append(_pipe@12, <<"\n\n"/utf8>>),
gleam_stdlib:wrap_list(_pipe@13)
end,
_pipe@14 = glisten@transport:send(
erlang:element(4, Conn@1),
erlang:element(3, Conn@1),
Message
),
_pipe@15 = gleam@result:replace(_pipe@14, nil),
gleam@result:replace_error(_pipe@15, nil).
-file("src/mist.gleam", 975).
-spec chunk_continue(MCK) -> chunk_next(MCK).
chunk_continue(State) ->
{chunk_continue, State}.
-file("src/mist.gleam", 979).
-spec chunk_stop() -> chunk_next(any()).
chunk_stop() ->
chunk_stop.
-file("src/mist.gleam", 983).
-spec chunk_stop_abnormal(binary()) -> chunk_next(any()).
chunk_stop_abnormal(Reason) ->
{chunk_abort, Reason}.
-file("src/mist.gleam", 991).
-spec int_to_hex(integer()) -> binary().
int_to_hex(Int) ->
erlang:integer_to_list(Int, 16).
-file("src/mist.gleam", 961).
-spec send_chunk(mist@internal@http:connection(), bitstring()) -> {ok, nil} |
{error, nil}.
send_chunk(Connection, Data) ->
Size = erlang:byte_size(Data),
Encoded = begin
_pipe = Size,
_pipe@1 = int_to_hex(_pipe),
_pipe@2 = gleam_stdlib:wrap_list(_pipe@1),
_pipe@3 = gleam@bytes_tree:append_string(_pipe@2, <<"\r\n"/utf8>>),
_pipe@4 = gleam@bytes_tree:append(_pipe@3, Data),
gleam@bytes_tree:append_string(_pipe@4, <<"\r\n"/utf8>>)
end,
_pipe@5 = glisten@transport:send(
erlang:element(4, Connection),
erlang:element(3, Connection),
Encoded
),
gleam@result:replace_error(_pipe@5, nil).
-file("src/mist.gleam", 898).
-spec chunked(
gleam@http@request:request(mist@internal@http:connection()),
gleam@http@response:response(any()),
fun((gleam@erlang@process:subject(MCD)) -> MCF),
fun((MCF, MCD, mist@internal@http:connection()) -> chunk_next(MCF))
) -> gleam@http@response:response(response_data()).
chunked(Req, Response, Init, Loop) ->
Start = fun() ->
_pipe@5 = gleam@otp@actor:new_with_initialiser(
1000,
fun(Subj) -> _pipe = Init(Subj),
_pipe@1 = gleam@otp@actor:initialised(_pipe),
_pipe@2 = gleam@otp@actor:returning(_pipe@1, erlang:self()),
_pipe@4 = gleam@otp@actor:selecting(
_pipe@2,
begin
_pipe@3 = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:select(_pipe@3, Subj)
end
),
{ok, _pipe@4} end
),
_pipe@6 = gleam@otp@actor:on_message(
_pipe@5,
fun(State, Message) ->
case Loop(State, Message, erlang:element(4, Req)) of
{chunk_continue, State@1} ->
gleam@otp@actor:continue(State@1);
chunk_stop ->
_ = case send_chunk(erlang:element(4, Req), <<>>) of
{ok, _} ->
nil;
{error, _} ->
logging:log(
debug,
<<"Failed to send final chunk"/utf8>>
)
end,
gleam@otp@actor:stop();
{chunk_abort, Reason} ->
gleam@otp@actor:stop_abnormal(Reason)
end
end
),
_pipe@7 = gleam@otp@actor:start(_pipe@6),
gleam@result:map(
_pipe@7,
fun(Started) ->
{started,
erlang:element(3, Started),
erlang:element(3, Started)}
end
)
end,
Headers = [{<<"transfer-encoding"/utf8>>, <<"chunked"/utf8>>} |
erlang:element(3, Response)],
Initial_payload = mist@internal@encoder:response_builder(
erlang:element(2, Response),
Headers,
mist@internal@http:version_to_string(http11)
),
case glisten@transport:send(
erlang:element(4, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
Initial_payload
) of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"chunked"/utf8>>,
line => 939,
value => _assert_fail,
start => 29275,
'end' => 29369,
pattern_start => 29286,
pattern_end => 29294})
end,
Factory_supervisor = gleam@otp@factory_supervisor:get_by_name(
erlang:element(5, erlang:element(4, Req))
),
case gleam@otp@factory_supervisor:start_child(Factory_supervisor, Start) of
{ok, Started@1} ->
case glisten@transport:controlling_process(
erlang:element(4, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
erlang:element(3, Started@1)
) of
{ok, _} -> nil;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"mist"/utf8>>,
function => <<"chunked"/utf8>>,
line => 946,
value => _assert_fail@1,
start => 29525,
'end' => 29683,
pattern_start => 29536,
pattern_end => 29551})
end,
_pipe@8 = gleam@http@response:new(200),
gleam@http@response:set_body(_pipe@8, chunked);
{error, _} ->
logging:log(
error,
<<"Failed to start chunked response process"/utf8>>
),
_pipe@9 = gleam@http@response:new(400),
gleam@http@response:set_body(
_pipe@9,
{bytes, gleam@bytes_tree:new()}
)
end.