Current section

Files

Jump to
ewe src ewe@internal@handler.erl
Raw

src/ewe@internal@handler.erl

-module(ewe@internal@handler).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ewe/internal/handler.gleam").
-export([init/1, loop/3]).
-export_type([glisten_message/0, glisten_state/0, next/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(false).
-type glisten_message() :: idle_timeout.
-type glisten_state() :: {glisten_state,
gleam@option:option(gleam@erlang@process:timer()),
gleam@erlang@process:subject(glisten_message())}.
-type next() :: {continue, glisten_state()} | stop.
-file("src/ewe/internal/handler.gleam", 63).
?DOC(false).
-spec init(any()) -> {glisten_state(),
gleam@option:option(gleam@erlang@process:selector(glisten_message()))}.
init(_) ->
Subject = gleam@erlang@process:new_subject(),
Selector = begin
_pipe = gleam_erlang_ffi:new_selector(),
gleam@erlang@process:select(_pipe, Subject)
end,
{{glisten_state, none, Subject}, {some, Selector}}.
-file("src/ewe/internal/handler.gleam", 180).
?DOC(false).
-spec handle_resp_file(
gleam@http@request:request(ewe@internal@http:connection()),
gleam@http@response:response(ewe@internal@http:response_body()),
ewe@internal@file:io_device(),
integer(),
integer()
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
handle_resp_file(Req, Resp, Descriptor, Offset, Size) ->
Http_version@1 = case erlang:element(5, erlang:element(4, Req)) of
{some, Http_version} -> Http_version;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ewe/internal/handler"/utf8>>,
function => <<"handle_resp_file"/utf8>>,
line => 187,
value => _assert_fail,
start => 5641,
'end' => 5701,
pattern_start => 5652,
pattern_end => 5677})
end,
Resp@1 = case gleam@http@response:get_header(
Resp,
<<"content-length"/utf8>>
) of
{ok, _} ->
Resp;
{error, nil} ->
gleam@http@response:set_header(
Resp,
<<"content-length"/utf8>>,
erlang:integer_to_binary(Size)
)
end,
Sent = begin
_pipe = ewe@internal@http:append_default_headers(
Resp@1,
Req,
Http_version@1
),
_pipe@1 = ewe@internal@encoder:setup_encoded_response(_pipe),
_pipe@2 = glisten@transport:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
_pipe@1
),
gleam@result:'try'(
_pipe@2,
fun(_) ->
_pipe@3 = ewe@internal@file:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
Descriptor,
Offset,
Size
),
gleam@result:replace_error(_pipe@3, badarg)
end
)
end,
_ = ewe_ffi:close_file(Descriptor),
Sent.
-file("src/ewe/internal/handler.gleam", 310).
?DOC(false).
-spec encode_gzip(
gleam@http@request:request(ewe@internal@http:connection()),
gleam@http@response:response(any())
) -> boolean().
encode_gzip(Req, Resp) ->
Req@1 = begin
_pipe = gleam@http@request:get_header(Req, <<"accept-encoding"/utf8>>),
gleam@result:map(
_pipe,
fun(_capture) ->
gleam_stdlib:contains_string(_capture, <<"gzip"/utf8>>)
end
)
end,
Resp@1 = gleam@http@response:get_header(Resp, <<"content-encoding"/utf8>>),
case {Req@1, Resp@1} of
{{ok, true}, {error, nil}} ->
true;
{_, _} ->
false
end.
-file("src/ewe/internal/handler.gleam", 323).
?DOC(false).
-spec remove_charset(gleam@http@response:response(MAQ)) -> gleam@http@response:response(MAQ).
remove_charset(Resp) ->
_pipe = gleam@http@response:get_header(Resp, <<"content-type"/utf8>>),
_pipe@1 = gleam@result:'try'(
_pipe,
fun(_capture) -> gleam@string:split_once(_capture, <<";"/utf8>>) end
),
_pipe@2 = gleam@result:map(
_pipe@1,
fun(Parts) ->
gleam@http@response:set_header(
Resp,
<<"content-type"/utf8>>,
erlang:element(1, Parts)
)
end
),
gleam@result:unwrap(_pipe@2, Resp).
-file("src/ewe/internal/handler.gleam", 258).
?DOC(false).
-spec handle_resp_body(
gleam@http@request:request(ewe@internal@http:connection()),
gleam@http@response:response(ewe@internal@http:response_body()),
ewe@internal@http:response_body()
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
handle_resp_body(Req, Resp, Body) ->
Http_version@1 = case erlang:element(5, erlang:element(4, Req)) of
{some, Http_version} -> Http_version;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ewe/internal/handler"/utf8>>,
function => <<"handle_resp_body"/utf8>>,
line => 263,
value => _assert_fail,
start => 7909,
'end' => 7969,
pattern_start => 7920,
pattern_end => 7945})
end,
Bits@1 = case Body of
{text_data, Text} ->
gleam_stdlib:identity(Text);
{string_tree_data, String_tree} ->
_pipe = unicode:characters_to_binary(String_tree),
gleam_stdlib:identity(_pipe);
{bits_data, Bits} ->
Bits;
{bytes_data, Bytes} ->
erlang:list_to_bitstring(Bytes);
empty ->
<<>>;
_ ->
erlang:error(#{gleam_error => panic,
message => <<"`panic` expression evaluated."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ewe/internal/handler"/utf8>>,
function => <<"handle_resp_body"/utf8>>,
line => 272})
end,
Content_length = erlang:byte_size(Bits@1),
Resp@1 = case Content_length > 1024 of
true ->
case encode_gzip(Req, Resp) of
true ->
Compressed = compresso:gzip(Bits@1),
Content_length@1 = erlang:byte_size(Compressed),
_pipe@1 = remove_charset(Resp),
_pipe@2 = gleam@http@response:set_header(
_pipe@1,
<<"content-encoding"/utf8>>,
<<"gzip"/utf8>>
),
_pipe@3 = gleam@http@response:set_header(
_pipe@2,
<<"vary"/utf8>>,
<<"Accept-Encoding"/utf8>>
),
_pipe@4 = gleam@http@response:set_header(
_pipe@3,
<<"content-length"/utf8>>,
erlang:integer_to_binary(Content_length@1)
),
gleam@http@response:set_body(_pipe@4, Compressed);
_ ->
_pipe@5 = gleam@http@response:set_body(Resp, Bits@1),
gleam@http@response:set_header(
_pipe@5,
<<"content-length"/utf8>>,
erlang:integer_to_binary(Content_length)
)
end;
false ->
_pipe@6 = gleam@http@response:set_body(Resp, Bits@1),
gleam@http@response:set_header(
_pipe@6,
<<"content-length"/utf8>>,
erlang:integer_to_binary(Content_length)
)
end,
_pipe@7 = ewe@internal@http:append_default_headers(
Resp@1,
Req,
Http_version@1
),
_pipe@8 = ewe@internal@encoder:encode_response(_pipe@7),
glisten@transport:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
_pipe@8
).
-file("src/ewe/internal/handler.gleam", 330).
?DOC(false).
-spec is_connection_close(gleam@http@response:response(any())) -> boolean().
is_connection_close(Resp) ->
case gleam@http@response:get_header(Resp, <<"connection"/utf8>>) of
{ok, <<"close"/utf8>>} ->
true;
_ ->
false
end.
-file("src/ewe/internal/handler.gleam", 337).
?DOC(false).
-spec to_hex_string(integer()) -> binary().
to_hex_string(Integer) ->
erlang:integer_to_list(Integer, 16).
-file("src/ewe/internal/handler.gleam", 210).
?DOC(false).
-spec handle_resp_chunked(
gleam@http@request:request(ewe@internal@http:connection()),
gleam@http@response:response(ewe@internal@http:response_body()),
gleam@yielder:yielder(bitstring())
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
handle_resp_chunked(Req, Resp, Yielder) ->
Http_version@1 = case erlang:element(5, erlang:element(4, Req)) of
{some, Http_version} -> Http_version;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"ewe/internal/handler"/utf8>>,
function => <<"handle_resp_chunked"/utf8>>,
line => 215,
value => _assert_fail,
start => 6479,
'end' => 6539,
pattern_start => 6490,
pattern_end => 6515})
end,
Resp@1 = case gleam@http@response:get_header(
Resp,
<<"transfer-encoding"/utf8>>
) of
{ok, <<"chunked"/utf8>>} ->
Resp;
{ok, _} ->
Resp;
{error, _} ->
gleam@http@response:set_header(
Resp,
<<"transfer-encoding"/utf8>>,
<<"chunked"/utf8>>
)
end,
{Resp@3, Yielder@1} = case encode_gzip(Req, Resp@1) of
true ->
Resp@2 = begin
_pipe = remove_charset(Resp@1),
gleam@http@response:set_header(
_pipe,
<<"content-encoding"/utf8>>,
<<"gzip"/utf8>>
)
end,
{Resp@2, compresso:gzip_yielder(Yielder)};
_ ->
{Resp@1, Yielder}
end,
_pipe@1 = ewe@internal@http:append_default_headers(
Resp@3,
Req,
Http_version@1
),
_pipe@2 = ewe@internal@encoder:setup_encoded_response(_pipe@1),
_pipe@3 = glisten@transport:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
_pipe@2
),
gleam@result:'try'(
_pipe@3,
fun(_) ->
_pipe@9 = gleam@yielder:try_fold(
Yielder@1,
nil,
fun(_, Chunk) -> _pipe@4 = gleam@bytes_tree:new(),
_pipe@5 = gleam@bytes_tree:append_string(
_pipe@4,
to_hex_string(erlang:byte_size(Chunk))
),
_pipe@6 = gleam@bytes_tree:append(_pipe@5, <<"\r\n"/utf8>>),
_pipe@7 = gleam@bytes_tree:append(_pipe@6, Chunk),
_pipe@8 = gleam@bytes_tree:append(_pipe@7, <<"\r\n"/utf8>>),
glisten@transport:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
_pipe@8
) end
),
gleam@result:'try'(
_pipe@9,
fun(_) ->
glisten@transport:send(
erlang:element(2, erlang:element(4, Req)),
erlang:element(3, erlang:element(4, Req)),
gleam@bytes_tree:from_bit_array(<<"0\r\n\r\n"/utf8>>)
)
end
)
end
).
-file("src/ewe/internal/handler.gleam", 135).
?DOC(false).
-spec call_handler(
gleam@http@request:request(ewe@internal@http:connection()),
gleam@erlang@process:subject(glisten_message()),
fun((gleam@http@request:request(ewe@internal@http:connection())) -> gleam@http@response:response(ewe@internal@http:response_body())),
gleam@http@response:response(ewe@internal@http:response_body()),
integer()
) -> next().
call_handler(Req, Subject, Handler, On_crash, Idle_timeout) ->
Resp@1 = case ewe_ffi:rescue(fun() -> Handler(Req) end) of
{ok, Resp} ->
Resp;
{error, E} ->
logging:log(error, gleam@string:inspect(E)),
gleam@http@response:set_header(
On_crash,
<<"connection"/utf8>>,
<<"close"/utf8>>
)
end,
case Resp@1 of
{response, _, _, {websocket, Selector}} ->
_ = gleam_erlang_ffi:select(Selector),
stop;
{response, _, _, {s_s_e, Selector}} ->
_ = gleam_erlang_ffi:select(Selector),
stop;
{response, _, _, Body} ->
Sent = case Body of
{file, Descriptor, Offset, Size} ->
handle_resp_file(Req, Resp@1, Descriptor, Offset, Size);
{chunked_data, Yielder} ->
handle_resp_chunked(Req, Resp@1, Yielder);
_ ->
handle_resp_body(Req, Resp@1, Body)
end,
case {Sent, is_connection_close(Resp@1)} of
{{ok, nil}, false} ->
Timer = gleam@erlang@process:send_after(
Subject,
Idle_timeout,
idle_timeout
),
{continue, {glisten_state, {some, Timer}, Subject}};
{_, _} ->
stop
end
end.
-file("src/ewe/internal/handler.gleam", 73).
?DOC(false).
-spec loop(
fun((gleam@http@request:request(ewe@internal@http:connection())) -> gleam@http@response:response(ewe@internal@http:response_body())),
gleam@http@response:response(ewe@internal@http:response_body()),
integer()
) -> fun((glisten_state(), glisten:message(glisten_message()), glisten:connection(glisten_message())) -> glisten:next(glisten_state(), glisten:message(glisten_message()))).
loop(Handler, On_crash, Idle_timeout) ->
fun(State, Msg, Conn) -> case Msg of
{user, idle_timeout} ->
glisten:stop();
{packet, Msg@1} ->
case erlang:element(2, State) of
{some, Timer} ->
gleam@erlang@process:cancel_timer(Timer);
none ->
timer_not_found
end,
Http_conn = ewe@internal@http:transform_connection(Conn),
Parsed = ewe@internal@http:parse_request(
Http_conn,
ewe@internal@buffer:new(Msg@1)
),
case Parsed of
{ok, Req} ->
case call_handler(
Req,
erlang:element(3, State),
Handler,
On_crash,
Idle_timeout
) of
{continue, New_state} ->
glisten:continue(New_state);
stop ->
glisten:stop()
end;
{error, Reason} ->
Status = case Reason of
invalid_version ->
505;
_ ->
400
end,
logging:log(
error,
<<"Received invalid request: "/utf8,
(gleam@string:inspect(Reason))/binary>>
),
_ = begin
_pipe = gleam@http@response:new(Status),
_pipe@1 = gleam@http@response:set_body(_pipe, <<>>),
_pipe@2 = gleam@http@response:set_header(
_pipe@1,
<<"connection"/utf8>>,
<<"close"/utf8>>
),
_pipe@3 = ewe@internal@encoder:encode_response(
_pipe@2
),
glisten@transport:send(
erlang:element(2, Http_conn),
erlang:element(3, Http_conn),
_pipe@3
)
end,
glisten:stop()
end
end end.