Packages
mist
0.3.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@http.erl
-module(mist@http).
-compile(no_auto_import).
-export([from_header/1, parse_headers/3, read_data/3, parse_request/2, new_state/0, handler/1]).
-export_type([packet_type/0, http_uri/0, http_packet/0, decoded_packet/0, decode_error/0, buffer/0, handler_error/0, state/0]).
-type packet_type() :: http | httph_bin | http_bin.
-type http_uri() :: {abs_path, bitstring()}.
-type http_packet() :: {http_request,
gleam@erlang@atom:atom_(),
http_uri(),
{integer(), integer()}} |
{http_header,
integer(),
gleam@erlang@atom:atom_(),
bitstring(),
bitstring()}.
-type decoded_packet() :: {binary_data, http_packet(), bitstring()} |
{end_of_headers, bitstring()} |
{more_data, gleam@option:option(integer())}.
-type decode_error() :: invalid_method |
invalid_path |
unknown_header |
invalid_body.
-type buffer() :: {buffer, integer(), bitstring()}.
-type handler_error() :: {invalid_request, decode_error()} | not_found.
-type state() :: {state,
gleam@option:option(gleam@otp@process:timer()),
gleam@option:option(fun((mist@websocket:message(), glisten@tcp:socket()) -> {ok,
nil} |
{error, nil}))}.
-spec from_header(bitstring()) -> binary().
from_header(Value) ->
{ok, Value@2} = case gleam@bit_string:to_string(Value) of
{ok, Value@1} -> {ok, Value@1};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/http"/utf8>>,
function => <<"from_header"/utf8>>,
line => 55})
end,
gleam@string:lowercase(Value@2).
-spec parse_headers(
bitstring(),
glisten@tcp:socket(),
gleam@map:map_(binary(), binary())
) -> {ok, {gleam@map:map_(binary(), binary()), bitstring()}} |
{error, decode_error()}.
parse_headers(Bs, Socket, Headers) ->
case http_ffi:decode_packet(httph_bin, Bs, []) of
{ok, {binary_data, {http_header, _@1, _@2, Field, Value}, Rest}} ->
Field@1 = from_header(Field),
{ok, Value@2} = case gleam@bit_string:to_string(Value) of
{ok, Value@1} -> {ok, Value@1};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/http"/utf8>>,
function => <<"parse_headers"/utf8>>,
line => 72})
end,
_pipe = Headers,
_pipe@1 = gleam@map:insert(_pipe, Field@1, Value@2),
parse_headers(Rest, Socket, _pipe@1);
{ok, {end_of_headers, Rest@1}} ->
{ok, {Headers, Rest@1}};
{ok, {more_data, Size}} ->
Amount_to_read = gleam@option:unwrap(Size, 0),
case read_data(Socket, {buffer, Amount_to_read, Bs}, unknown_header) of
{error, _try@1} -> {error, _try@1};
{ok, Next} ->
parse_headers(Next, Socket, Headers)
end;
_@3 ->
{error, unknown_header}
end.
-spec read_data(glisten@tcp:socket(), buffer(), decode_error()) -> {ok,
bitstring()} |
{error, decode_error()}.
read_data(Socket, Buffer, Error) ->
To_read = gleam@int:min(erlang:element(2, Buffer), 1000000),
Timeout = 15000,
case begin
_pipe = Socket,
_pipe@1 = glisten@tcp:receive_timeout(_pipe, To_read, Timeout),
gleam@result:replace_error(_pipe@1, Error)
end of
{error, _try} -> {error, _try};
{ok, Data} ->
Next_buffer = {buffer,
erlang:element(2, Buffer)
- To_read,
<<(erlang:element(3, Buffer))/bitstring,
Data/bitstring>>},
case erlang:element(2, Next_buffer) > 0 of
true ->
read_data(Socket, Next_buffer, Error);
false ->
{ok, erlang:element(3, Next_buffer)}
end
end.
-spec parse_request(bitstring(), glisten@tcp:socket()) -> {ok,
gleam@http@request:request(bitstring())} |
{error, decode_error()}.
parse_request(Bs, Socket) ->
case http_ffi:decode_packet(http_bin, Bs, []) of
{error, _try} -> {error, _try};
{ok, {binary_data, Req, Rest}} ->
{http_request, Method@1, {abs_path, Path@1}, _@2} = case Req of
{http_request, Method, {abs_path, Path}, _@1} -> {http_request,
Method,
{abs_path,
Path},
_@1};
_try@1 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@1,
module => <<"mist/http"/utf8>>,
function => <<"parse_request"/utf8>>,
line => 117})
end,
case begin
_pipe = Method@1,
_pipe@1 = gleam@erlang@atom:to_string(_pipe),
_pipe@2 = gleam@http:parse_method(_pipe@1),
gleam@result:replace_error(_pipe@2, invalid_method)
end of
{error, _try@2} -> {error, _try@2};
{ok, Method@2} ->
case parse_headers(Rest, Socket, gleam@map:new()) of
{error, _try@3} -> {error, _try@3};
{ok, {Headers, Rest@1}} ->
case begin
_pipe@3 = Path@1,
_pipe@4 = gleam@bit_string:to_string(_pipe@3),
gleam@result:replace_error(
_pipe@4,
invalid_path
)
end of
{error, _try@4} -> {error, _try@4};
{ok, Path@2} ->
Body_size = begin
_pipe@5 = Headers,
_pipe@6 = gleam@map:get(
_pipe@5,
<<"content-length"/utf8>>
),
_pipe@7 = gleam@result:then(
_pipe@6,
fun gleam@int:parse/1
),
gleam@result:unwrap(_pipe@7, 0)
end,
Remaining = Body_size
- gleam@bit_string:byte_size(Rest@1),
case case {Body_size, Remaining} of
{0, 0} ->
{ok, <<>>};
{0, _@3} ->
{ok, Rest@1};
{_@4, 0} ->
{ok, Rest@1};
{_@5, _@6} ->
read_data(
Socket,
{buffer, Remaining, Rest@1},
invalid_body
)
end of
{error, _try@5} -> {error, _try@5};
{ok, Body} ->
Req@1 = begin
_pipe@8 = gleam@http@request:new(
),
_pipe@9 = gleam@http@request:set_body(
_pipe@8,
Body
),
_pipe@10 = gleam@http@request:set_method(
_pipe@9,
Method@2
),
gleam@http@request:set_path(
_pipe@10,
Path@2
)
end,
{ok,
erlang:setelement(
3,
Req@1,
gleam@map:to_list(Headers)
)}
end
end
end
end
end.
-spec new_state() -> state().
new_state() ->
{state, none, none}.
-spec handler(
fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(gleam@bit_builder:bit_builder()))
) -> fun((glisten@tcp:handler_message(), glisten@tcp:loop_state(state())) -> gleam@otp@actor:next(glisten@tcp:loop_state(state()))).
handler(Func) ->
glisten@tcp:handler(
fun(Msg, Socket_state) ->
{loop_state, Socket, Sender, State} = Socket_state,
_@2 = case erlang:element(2, State) of
{some, T} ->
gleam@otp@process:cancel_timer(T);
_@1 ->
timer_not_found
end,
{ok, Req@1} = case parse_request(Msg, Socket) of
{ok, Req} -> {ok, Req};
_try ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try,
module => <<"mist/http"/utf8>>,
function => <<"handler"/utf8>>,
line => 189})
end,
Resp = Func(Req@1),
Raw_response = mist@encoder:to_bit_builder(Resp),
{ok, _@4} = case glisten@tcp:send(Socket, Raw_response) of
{ok, _@3} -> {ok, _@3};
_try@1 ->
erlang:error(#{gleam_error => assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _try@1,
module => <<"mist/http"/utf8>>,
function => <<"handler"/utf8>>,
line => 193})
end,
case gleam@http@response:get_header(Resp, <<"connection"/utf8>>) of
{ok, <<"close"/utf8>>} ->
glisten@tcp:close(Socket),
{stop, normal};
_@5 ->
Timer = gleam@otp@process:send_after(Sender, 10000, close),
{continue,
erlang:setelement(
4,
Socket_state,
erlang:setelement(2, State, {some, Timer})
)}
end
end
).