Packages
mist
0.13.1
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@internal@websocket.erl
-module(mist@internal@websocket).
-compile([no_auto_import, nowarn_unused_vars]).
-export([frame_from_message/3, frame_to_bit_builder/1, to_text_frame/1, to_binary_frame/1, initialize_connection/5]).
-export_type([data_frame/0, control_frame/0, frame/0, valid_message/1, websocket_message/1, websocket_connection/0]).
-type data_frame() :: {text_frame, integer(), bitstring()} |
{binary_frame, integer(), bitstring()}.
-type control_frame() :: {close_frame, integer(), bitstring()} |
{ping_frame, integer(), bitstring()} |
{pong_frame, integer(), bitstring()}.
-type frame() :: {data, data_frame()} | {control, control_frame()}.
-type valid_message(ITX) :: {internal, frame()} | socket_closed | {user, ITX}.
-type websocket_message(ITY) :: {valid, valid_message(ITY)} | invalid.
-type websocket_connection() :: {websocket_connection,
glisten@socket:socket(),
glisten@socket@transport:transport()}.
-spec unmask_data(bitstring(), list(bitstring()), integer(), bitstring()) -> bitstring().
unmask_data(Data, Masks, Index, Resp) ->
case Data of
<<>> ->
Resp;
<<Masked:8/bitstring, Rest/bitstring>> ->
_assert_subject = gleam@list:at(Masks, Index rem 4),
{ok, Mask_value} = 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 => <<"mist/internal/websocket"/utf8>>,
function => <<"unmask_data"/utf8>>,
line => 48})
end,
Unmasked = crypto:exor(Mask_value, Masked),
unmask_data(
Rest,
Masks,
Index + 1,
<<Resp/bitstring, Unmasked/bitstring>>
)
end.
-spec frame_from_message(
glisten@socket:socket(),
glisten@socket@transport:transport(),
bitstring()
) -> {ok, frame()} | {error, nil}.
frame_from_message(Socket, Transport, Message) ->
<<_:1, Rest/bitstring>> = case Message of
<<_:1, _/bitstring>> -> Message;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 65})
end,
<<_:3, Rest@1/bitstring>> = case Rest of
<<_:3, _/bitstring>> -> Rest;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 66})
end,
<<Opcode:4/integer, Rest@2/bitstring>> = case Rest@1 of
<<_:4/integer, _/bitstring>> -> Rest@1;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 67})
end,
<<1:1, Rest@3/bitstring>> = case Rest@2 of
<<1:1, _/bitstring>> -> Rest@2;
_assert_fail@3 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@3,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 69})
end,
<<Payload_length:7/integer, Rest@4/bitstring>> = case Rest@3 of
<<_:7/integer, _/bitstring>> -> Rest@3;
_assert_fail@4 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@4,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 70})
end,
{Payload_length@1, Rest@7} = case Payload_length of
126 ->
<<Length:16/integer, Rest@5/bitstring>> = case Rest@4 of
<<_:16/integer, _/bitstring>> -> Rest@4;
_assert_fail@5 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@5,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 73})
end,
{Length, Rest@5};
127 ->
<<Length@1:64/integer, Rest@6/bitstring>> = case Rest@4 of
<<_:64/integer, _/bitstring>> -> Rest@4;
_assert_fail@6 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@6,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 77})
end,
{Length@1, Rest@6};
_ ->
{Payload_length, Rest@4}
end,
<<Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest@8/bitstring>> = case Rest@7 of
<<_:8/bitstring,
_:8/bitstring,
_:8/bitstring,
_:8/bitstring,
_/bitstring>> -> Rest@7;
_assert_fail@7 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@7,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 82})
end,
_pipe@5 = case Payload_length@1 - gleam@bit_string:byte_size(Rest@8) of
0 ->
{ok, unmask_data(Rest@8, [Mask1, Mask2, Mask3, Mask4], 0, <<>>)};
Need ->
_pipe = Need,
_pipe@1 = (erlang:element(9, Transport))(Socket, _pipe),
_pipe@2 = gleam@result:replace_error(_pipe@1, nil),
gleam@result:map(_pipe@2, fun(Needed) -> _pipe@3 = Rest@8,
_pipe@4 = gleam@bit_string:append(_pipe@3, Needed),
unmask_data(_pipe@4, [Mask1, Mask2, Mask3, Mask4], 0, <<>>) end)
end,
gleam@result:map(_pipe@5, fun(Data) -> case Opcode of
1 ->
{data, {text_frame, Payload_length@1, Data}};
2 ->
{data, {binary_frame, Payload_length@1, Data}};
8 ->
{control, {close_frame, Payload_length@1, Data}};
9 ->
{control, {ping_frame, Payload_length@1, Data}};
10 ->
{control, {pong_frame, Payload_length@1, Data}}
end end).
-spec make_frame(integer(), integer(), bitstring()) -> gleam@bit_builder:bit_builder().
make_frame(Opcode, Length, Payload) ->
Length_section = case Length of
Length@1 when Length@1 > 65535 ->
<<127:7, Length@1:64/integer>>;
Length@2 when Length@2 >= 126 ->
<<126:7, Length@2:16/integer>>;
_ ->
<<Length:7>>
end,
_pipe = <<1:1,
0:3,
Opcode:4,
0:1,
Length_section/bitstring,
Payload/bitstring>>,
gleam@bit_builder:from_bit_string(_pipe).
-spec frame_to_bit_builder(frame()) -> gleam@bit_builder:bit_builder().
frame_to_bit_builder(Frame) ->
case Frame of
{data, {text_frame, Payload_length, Payload}} ->
make_frame(1, Payload_length, Payload);
{control, {close_frame, Payload_length@1, Payload@1}} ->
make_frame(8, Payload_length@1, Payload@1);
{data, {binary_frame, Payload_length@2, Payload@2}} ->
make_frame(2, Payload_length@2, Payload@2);
{control, {pong_frame, Payload_length@3, Payload@3}} ->
make_frame(10, Payload_length@3, Payload@3);
{control, {ping_frame, Payload_length@4, Payload@4}} ->
make_frame(9, Payload_length@4, Payload@4)
end.
-spec to_text_frame(bitstring()) -> gleam@bit_builder:bit_builder().
to_text_frame(Data) ->
Size = gleam@bit_string:byte_size(Data),
frame_to_bit_builder({data, {text_frame, Size, Data}}).
-spec to_binary_frame(bitstring()) -> gleam@bit_builder:bit_builder().
to_binary_frame(Data) ->
Size = gleam@bit_string:byte_size(Data),
frame_to_bit_builder({data, {binary_frame, Size, Data}}).
-spec initialize_connection(
IUG,
gleam@option:option(gleam@erlang@process:selector(IUH)),
fun((IUG, websocket_connection(), valid_message(IUH)) -> gleam@otp@actor:next(IUG)),
glisten@socket:socket(),
glisten@socket@transport:transport()
) -> {ok, gleam@erlang@process:subject(websocket_message(IUH))} | {error, nil}.
initialize_connection(Initial_state, User_selector, Handler, Socket, Transport) ->
Connection = {websocket_connection, Socket, Transport},
_pipe@23 = gleam@otp@actor:start_spec(
{spec,
fun() ->
Selector@1 = begin
_pipe = gleam_erlang_ffi:new_selector(),
_pipe@7 = gleam@erlang@process:selecting_record3(
_pipe,
erlang:binary_to_atom(<<"tcp"/utf8>>),
fun(_, Data) -> _pipe@1 = Data,
_pipe@2 = gleam@dynamic:bit_string(_pipe@1),
_pipe@3 = gleam@result:replace_error(_pipe@2, nil),
_pipe@4 = gleam@result:then(
_pipe@3,
fun(_capture) ->
frame_from_message(
Socket,
Transport,
_capture
)
end
),
_pipe@5 = gleam@result:map(
_pipe@4,
fun(Field@0) -> {internal, Field@0} end
),
_pipe@6 = gleam@result:map(
_pipe@5,
fun(Field@0) -> {valid, Field@0} end
),
gleam@result:unwrap(_pipe@6, invalid) end
),
_pipe@14 = gleam@erlang@process:selecting_record3(
_pipe@7,
erlang:binary_to_atom(<<"ssl"/utf8>>),
fun(_, Data@1) -> _pipe@8 = Data@1,
_pipe@9 = gleam@dynamic:bit_string(_pipe@8),
_pipe@10 = gleam@result:replace_error(_pipe@9, nil),
_pipe@11 = gleam@result:then(
_pipe@10,
fun(_capture@1) ->
frame_from_message(
Socket,
Transport,
_capture@1
)
end
),
_pipe@12 = gleam@result:map(
_pipe@11,
fun(Field@0) -> {internal, Field@0} end
),
_pipe@13 = gleam@result:map(
_pipe@12,
fun(Field@0) -> {valid, Field@0} end
),
gleam@result:unwrap(_pipe@13, invalid) end
),
_pipe@15 = gleam@erlang@process:selecting_record2(
_pipe@14,
erlang:binary_to_atom(<<"ssl_closed"/utf8>>),
fun(_) -> {valid, socket_closed} end
),
_pipe@16 = gleam@erlang@process:selecting_record2(
_pipe@15,
erlang:binary_to_atom(<<"tcp_closed"/utf8>>),
fun(_) -> {valid, socket_closed} end
),
(fun(Selector) -> case User_selector of
{some, User_selector@1} ->
_pipe@17 = User_selector@1,
_pipe@18 = gleam_erlang_ffi:map_selector(
_pipe@17,
fun(Field@0) -> {user, Field@0} end
),
_pipe@19 = gleam_erlang_ffi:map_selector(
_pipe@18,
fun(Field@0) -> {valid, Field@0} end
),
gleam_erlang_ffi:merge_selector(
_pipe@19,
Selector
);
_ ->
Selector
end end)(_pipe@16)
end,
{ready, Initial_state, Selector@1}
end,
500,
fun(Msg, State) -> case Msg of
{valid, {internal, {control, {close_frame, _, _}} = Frame}} ->
_assert_subject = (erlang:element(
11,
erlang:element(3, Connection)
))(
erlang:element(2, Connection),
frame_to_bit_builder(Frame)
),
{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 => <<"mist/internal/websocket"/utf8>>,
function => <<"initialize_connection"/utf8>>,
line => 231})
end,
{stop, normal};
{valid,
{internal, {control, {ping_frame, Length, Payload}}}} ->
_pipe@20 = (erlang:element(
11,
erlang:element(3, Connection)
))(
erlang:element(2, Connection),
frame_to_bit_builder(
{control, {pong_frame, Length, Payload}}
)
),
_pipe@21 = gleam@result:map(
_pipe@20,
fun(_) -> {continue, State} end
),
gleam@result:unwrap(
_pipe@21,
{stop,
{abnormal, <<"Failed to send pong frame"/utf8>>}}
);
invalid ->
mist@internal@logger:error(
{<<"Received a malformed Websocket frame"/utf8>>}
),
{continue, State};
{valid, Msg@1} ->
_pipe@22 = gleam_erlang_ffi:rescue(
fun() -> Handler(State, Connection, Msg@1) end
),
gleam@result:lazy_unwrap(
_pipe@22,
fun() ->
mist@internal@logger:error(
<<"Caught error in websocket handler"/utf8>>
),
{stop,
{abnormal, <<"Websocket terminated"/utf8>>}}
end
)
end end}
),
_pipe@24 = gleam@result:replace_error(_pipe@23, nil),
_pipe@25 = gleam@result:map(
_pipe@24,
fun(Subj) ->
Websocket_pid = gleam@erlang@process:subject_owner(Subj),
_assert_subject@1 = (erlang:element(
5,
erlang:element(3, Connection)
))(erlang:element(2, Connection), Websocket_pid),
{ok, _} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"initialize_connection"/utf8>>,
line => 265})
end,
_assert_subject@2 = (erlang:element(
12,
erlang:element(3, Connection)
))(erlang:element(2, Connection), [{active_mode, active}]),
{ok, _} = case _assert_subject@2 of
{ok, _} -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"mist/internal/websocket"/utf8>>,
function => <<"initialize_connection"/utf8>>,
line => 267})
end,
Subj
end
),
gleam@result:replace_error(_pipe@25, nil).