Packages
mist
0.8.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@websocket.erl
-module(mist@websocket).
-compile(no_auto_import).
-export([frame_from_message/2, frame_to_bit_builder/1, crypto_hash/2, base64_encode/1, parse_key/1, send/2, echo_handler/2, with_handler/1, on_init/2, on_close/2]).
-export_type([message/0, frame/0, sha_hash/0, websocket_handler/0]).
-type message() :: {binary_message, bitstring()} | {text_message, binary()}.
-type frame() :: {close_frame, integer(), bitstring()} |
{text_frame, integer(), bitstring()} |
{binary_frame, integer(), bitstring()} |
{ping_frame, integer(), bitstring()} |
{pong_frame, integer(), bitstring()}.
-type sha_hash() :: sha.
-type websocket_handler() :: {websocket_handler,
gleam@option:option(fun((gleam@erlang@process:subject(glisten@tcp:handler_message())) -> nil)),
gleam@option:option(fun((gleam@erlang@process:subject(glisten@tcp:handler_message())) -> nil)),
fun((message(), gleam@erlang@process:subject(glisten@tcp:handler_message())) -> {ok,
nil} |
{error, nil})}.
-spec unmask_data(bitstring(), list(bitstring()), integer(), bitstring()) -> bitstring().
unmask_data(Data, Masks, Index, Resp) ->
case Data of
<<>> ->
Resp;
<<Masked:8/bitstring, Rest/bitstring>> ->
{ok, Mask_value@1} = case gleam@list:at(Masks, Index rem 4) of
{ok, Mask_value} -> {ok, Mask_value};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/websocket"/utf8>>,
function => <<"unmask_data"/utf8>>,
line => 41})
end,
Unmasked = crypto:exor(Mask_value@1, Masked),
unmask_data(
Rest,
Masks,
Index
+ 1,
<<Resp/bitstring, Unmasked/bitstring>>
)
end.
-spec frame_from_message(glisten@tcp:socket(), bitstring()) -> {ok, frame()} |
{error, nil}.
frame_from_message(Socket, Message) ->
<<_@2:1, Rest@1/bitstring>> = case Message of
<<_@1:1, Rest/bitstring>> -> <<_@1:1, Rest/bitstring>>;
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 57})
end,
<<_@4:3, Rest@3/bitstring>> = case Rest@1 of
<<_@3:3, Rest@2/bitstring>> -> <<_@3:3, Rest@2/bitstring>>;
_try@1 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@1,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 58})
end,
<<Opcode@1:4/integer, Rest@5/bitstring>> = case Rest@3 of
<<Opcode:4/integer, Rest@4/bitstring>> -> <<Opcode:4/integer,
Rest@4/bitstring>>;
_try@2 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@2,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 59})
end,
case Opcode@1 of
1 ->
<<1:1, Rest@7/bitstring>> = case Rest@5 of
<<1:1, Rest@6/bitstring>> -> <<1:1, Rest@6/bitstring>>;
_try@3 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@3,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 63})
end,
<<Payload_length@1:7/integer, Rest@9/bitstring>> = case Rest@7 of
<<Payload_length:7/integer, Rest@8/bitstring>> -> <<Payload_length:7/integer,
Rest@8/bitstring>>;
_try@4 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@4,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 64})
end,
{Payload_length@2, Rest@14} = case Payload_length@1 of
126 ->
<<Length@1:16/integer, Rest@11/bitstring>> = case Rest@9 of
<<Length:16/integer, Rest@10/bitstring>> -> <<Length:16/integer,
Rest@10/bitstring>>;
_try@5 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@5,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 67})
end,
{Length@1, Rest@11};
127 ->
<<Length@3:64/integer, Rest@13/bitstring>> = case Rest@9 of
<<Length@2:64/integer, Rest@12/bitstring>> -> <<Length@2:64/integer,
Rest@12/bitstring>>;
_try@6 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@6,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 71})
end,
{Length@3, Rest@13};
_@5 ->
{Payload_length@1, Rest@9}
end,
<<Mask1@1:8/bitstring,
Mask2@1:8/bitstring,
Mask3@1:8/bitstring,
Mask4@1:8/bitstring,
Rest@16/bitstring>> = case Rest@14 of
<<Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest@15/bitstring>> -> <<Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest@15/bitstring>>;
_try@7 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@7,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 76})
end,
Data = case Payload_length@2 - gleam@bit_string:byte_size(Rest@16) of
0 ->
unmask_data(
Rest@16,
[Mask1@1, Mask2@1, Mask3@1, Mask4@1],
0,
<<>>
);
Need ->
{ok, Needed@1} = case glisten@tcp:'receive'(Socket, Need) of
{ok, Needed} -> {ok, Needed};
_try@8 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@8,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 86})
end,
_pipe = Rest@16,
_pipe@1 = gleam@bit_string:append(_pipe, Needed@1),
unmask_data(
_pipe@1,
[Mask1@1, Mask2@1, Mask3@1, Mask4@1],
0,
<<>>
)
end,
_pipe@2 = case Opcode@1 of
1 ->
{text_frame, Payload_length@2, Data};
2 ->
{binary_frame, Payload_length@2, Data}
end,
{ok, _pipe@2};
2 ->
<<1:1, Rest@7/bitstring>> = case Rest@5 of
<<1:1, Rest@6/bitstring>> -> <<1:1, Rest@6/bitstring>>;
_try@3 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@3,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 63})
end,
<<Payload_length@1:7/integer, Rest@9/bitstring>> = case Rest@7 of
<<Payload_length:7/integer, Rest@8/bitstring>> -> <<Payload_length:7/integer,
Rest@8/bitstring>>;
_try@4 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@4,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 64})
end,
{Payload_length@2, Rest@14} = case Payload_length@1 of
126 ->
<<Length@1:16/integer, Rest@11/bitstring>> = case Rest@9 of
<<Length:16/integer, Rest@10/bitstring>> -> <<Length:16/integer,
Rest@10/bitstring>>;
_try@5 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@5,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 67})
end,
{Length@1, Rest@11};
127 ->
<<Length@3:64/integer, Rest@13/bitstring>> = case Rest@9 of
<<Length@2:64/integer, Rest@12/bitstring>> -> <<Length@2:64/integer,
Rest@12/bitstring>>;
_try@6 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@6,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 71})
end,
{Length@3, Rest@13};
_@5 ->
{Payload_length@1, Rest@9}
end,
<<Mask1@1:8/bitstring,
Mask2@1:8/bitstring,
Mask3@1:8/bitstring,
Mask4@1:8/bitstring,
Rest@16/bitstring>> = case Rest@14 of
<<Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest@15/bitstring>> -> <<Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest@15/bitstring>>;
_try@7 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@7,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 76})
end,
Data = case Payload_length@2 - gleam@bit_string:byte_size(Rest@16) of
0 ->
unmask_data(
Rest@16,
[Mask1@1, Mask2@1, Mask3@1, Mask4@1],
0,
<<>>
);
Need ->
{ok, Needed@1} = case glisten@tcp:'receive'(Socket, Need) of
{ok, Needed} -> {ok, Needed};
_try@8 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@8,
module => <<"mist/websocket"/utf8>>,
function => <<"frame_from_message"/utf8>>,
line => 86})
end,
_pipe = Rest@16,
_pipe@1 = gleam@bit_string:append(_pipe, Needed@1),
unmask_data(
_pipe@1,
[Mask1@1, Mask2@1, Mask3@1, Mask4@1],
0,
<<>>
)
end,
_pipe@2 = case Opcode@1 of
1 ->
{text_frame, Payload_length@2, Data};
2 ->
{binary_frame, Payload_length@2, Data}
end,
{ok, _pipe@2};
8 ->
{ok, {close_frame, 0, <<>>}}
end.
-spec frame_to_bit_builder(frame()) -> gleam@bit_builder:bit_builder().
frame_to_bit_builder(Frame) ->
case Frame of
{text_frame, Payload_length, Payload} ->
make_frame(1, Payload_length, Payload);
{close_frame, Payload_length@1, Payload@1} ->
make_frame(8, Payload_length@1, Payload@1);
{binary_frame, Payload_length@2, Payload@2} ->
make_frame(2, Payload_length@2, Payload@2);
{pong_frame, Payload_length@3, Payload@3} ->
make_frame(10, Payload_length@3, Payload@3);
{ping_frame, _@1, _@2} ->
gleam@bit_builder:from_bit_string(<<>>)
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>>;
_@1 ->
<<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 to_text_frame(bitstring()) -> gleam@bit_builder:bit_builder().
to_text_frame(Data) ->
Size = gleam@bit_string:byte_size(Data),
frame_to_bit_builder({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({binary_frame, Size, Data}).
-spec crypto_hash(sha_hash(), binary()) -> binary().
crypto_hash(A, B) ->
crypto:hash(A, B).
-spec base64_encode(binary()) -> binary().
base64_encode(A) ->
base64:encode(A).
-spec parse_key(binary()) -> binary().
parse_key(Key) ->
_pipe = Key,
_pipe@1 = gleam@string:append(
_pipe,
<<"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"/utf8>>
),
_pipe@2 = crypto:hash(sha, _pipe@1),
base64:encode(_pipe@2).
-spec send(
gleam@erlang@process:subject(glisten@tcp:handler_message()),
message()
) -> nil.
send(Sender, Message) ->
_pipe@2 = case Message of
{text_message, Data} ->
_pipe = Data,
_pipe@1 = gleam@bit_string:from_string(_pipe),
to_text_frame(_pipe@1);
{binary_message, Data@1} ->
to_binary_frame(Data@1)
end,
_pipe@3 = {send_message, _pipe@2},
gleam@erlang@process:send(Sender, _pipe@3),
nil.
-spec echo_handler(
message(),
gleam@erlang@process:subject(glisten@tcp:handler_message())
) -> {ok, nil} | {error, nil}.
echo_handler(Message, Sender) ->
_@1 = send(Sender, Message),
{ok, nil}.
-spec with_handler(
fun((message(), gleam@erlang@process:subject(glisten@tcp:handler_message())) -> {ok,
nil} |
{error, nil})
) -> websocket_handler().
with_handler(Func) ->
{websocket_handler, none, none, Func}.
-spec on_init(
websocket_handler(),
fun((gleam@erlang@process:subject(glisten@tcp:handler_message())) -> nil)
) -> websocket_handler().
on_init(Handler, Func) ->
erlang:setelement(3, Handler, {some, Func}).
-spec on_close(
websocket_handler(),
fun((gleam@erlang@process:subject(glisten@tcp:handler_message())) -> nil)
) -> websocket_handler().
on_close(Handler, Func) ->
erlang:setelement(2, Handler, {some, Func}).