Current section

Files

Jump to
mist src mist@websocket.erl
Raw

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(gleam@bit_builder:bit_builder())} |
{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,
gleam@bit_builder:from_bit_string(<<""/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}.