Packages
mist
0.3.0
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/1, message_to_frame/1, frame_to_bit_builder/1, upgrade/2, send/2, crypto_hash/2, base64_encode/1, parse_key/1, upgrade_socket/1, echo_handler/2]).
-export_type([message/0, frame/0, sha_hash/0]).
-type message() :: {text_message, binary()}.
-type frame() :: {text_frame, integer(), binary()} |
{ping_frame, integer(), binary()} |
{pong_frame, integer(), binary()}.
-type sha_hash() :: sha.
-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 => 40})
end,
Unmasked = crypto:exor(Mask_value@1, Masked),
unmask_data(
Rest,
Masks,
Index
+ 1,
<<Resp/bitstring, Unmasked/bitstring>>
)
end.
-spec frame_from_message(bitstring()) -> {ok, frame()} | {error, nil}.
frame_from_message(Message) ->
<<_@3:1,
_@4:3,
Opcode@1:4/integer,
1:1,
Payload_length@1:7/integer,
Mask1@1:8/bitstring,
Mask2@1:8/bitstring,
Mask3@1:8/bitstring,
Mask4@1:8/bitstring,
Rest@1/bitstring>> = case Message of
<<_@1:1,
_@2:3,
Opcode:4/integer,
1:1,
Payload_length:7/integer,
Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
Rest/bitstring>> -> <<_@1:1,
_@2:3,
Opcode:4/integer,
1:1,
Payload_length:7/integer,
Mask1:8/bitstring,
Mask2:8/bitstring,
Mask3:8/bitstring,
Mask4:8/bitstring,
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 => 53})
end,
case begin
_pipe = Rest@1,
_pipe@1 = unmask_data(
_pipe,
[Mask1@1, Mask2@1, Mask3@1, Mask4@1],
0,
<<>>
),
gleam@bit_string:to_string(_pipe@1)
end of
{error, _try@1} -> {error, _try@1};
{ok, Data} ->
_pipe@2 = case Opcode@1 of
1 ->
{text_frame, Payload_length@1, Data}
end,
{ok, _pipe@2}
end.
-spec message_to_frame(binary()) -> frame().
message_to_frame(Data) ->
{text_frame,
begin
_pipe = Data,
_pipe@1 = gleam@bit_string:from_string(_pipe),
gleam@bit_string:byte_size(_pipe@1)
end,
Data}.
-spec frame_to_bit_builder(frame()) -> gleam@bit_builder:bit_builder().
frame_to_bit_builder(Frame) ->
case Frame of
{text_frame, Payload_length, Payload} ->
Fin = 1,
Mask_flag = 0,
Payload_bs = gleam@bit_string:from_string(Payload),
_pipe = <<Fin:1,
0:3,
1:4,
Mask_flag:1,
Payload_length:7,
Payload_bs/bitstring>>,
gleam@bit_builder:from_bit_string(_pipe);
{ping_frame, _@1, _@2} ->
gleam@bit_builder:from_bit_string(<<>>);
{pong_frame, Payload_length@1, Payload@1} ->
Payload_bs@1 = gleam@bit_string:from_string(Payload@1),
_pipe@1 = <<1:1,
0:3,
10:4,
0:1,
Payload_length@1:7,
Payload_bs@1/bitstring>>,
gleam@bit_builder:from_bit_string(_pipe@1)
end.
-spec upgrade(glisten@tcp:socket(), gleam@http@request:request(bitstring())) -> {ok,
nil} |
{error, nil}.
upgrade(Socket, Req) ->
case begin
_pipe = upgrade_socket(Req),
gleam@result:replace_error(_pipe, nil)
end of
{error, _try} -> {error, _try};
{ok, Resp} ->
case begin
_pipe@1 = Resp,
_pipe@2 = mist@http:to_bit_builder(_pipe@1),
_pipe@3 = glisten@tcp:send(Socket, _pipe@2),
gleam@result:replace_error(_pipe@3, nil)
end of
{error, _try@1} -> {error, _try@1};
{ok, _@1} ->
{ok, nil}
end
end.
-spec send(glisten@tcp:socket(), binary()) -> {ok, nil} |
{error, glisten@tcp:socket_reason()}.
send(Socket, Data) ->
Size = begin
_pipe = Data,
_pipe@1 = gleam@bit_string:from_string(_pipe),
gleam@bit_string:byte_size(_pipe@1)
end,
Msg = frame_to_bit_builder({text_frame, Size, Data}),
Resp = glisten@tcp:send(Socket, Msg),
Resp.
-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 upgrade_socket(gleam@http@request:request(bitstring())) -> {ok,
gleam@http@response:response(bitstring())} |
{error, gleam@http@request:request(bitstring())}.
upgrade_socket(Req) ->
case begin
_pipe = gleam@http@request:get_header(Req, <<"upgrade"/utf8>>),
gleam@result:replace_error(_pipe, Req)
end of
{error, _try} -> {error, _try};
{ok, _@1} ->
case begin
_pipe@1 = gleam@http@request:get_header(
Req,
<<"sec-websocket-key"/utf8>>
),
gleam@result:replace_error(_pipe@1, Req)
end of
{error, _try@1} -> {error, _try@1};
{ok, Key} ->
case begin
_pipe@2 = gleam@http@request:get_header(
Req,
<<"sec-websocket-version"/utf8>>
),
gleam@result:replace_error(_pipe@2, Req)
end of
{error, _try@2} -> {error, _try@2};
{ok, _@2} ->
Accept_key = parse_key(Key),
_pipe@3 = gleam@http@response:new(101),
_pipe@4 = gleam@http@response:set_body(
_pipe@3,
<<""/utf8>>
),
_pipe@5 = gleam@http@response:prepend_header(
_pipe@4,
<<"Upgrade"/utf8>>,
<<"websocket"/utf8>>
),
_pipe@6 = gleam@http@response:prepend_header(
_pipe@5,
<<"Connection"/utf8>>,
<<"Upgrade"/utf8>>
),
_pipe@7 = gleam@http@response:prepend_header(
_pipe@6,
<<"Sec-WebSocket-Accept"/utf8>>,
Accept_key
),
{ok, _pipe@7}
end
end
end.
-spec echo_handler(message(), glisten@tcp:socket()) -> {ok, nil} | {error, nil}.
echo_handler(Msg, Socket) ->
{ok, _@2} = case send(Socket, erlang:element(2, Msg)) of
{ok, _@1} -> {ok, _@1};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/websocket"/utf8>>,
function => <<"echo_handler"/utf8>>,
line => 174})
end,
{ok, nil}.