Packages
🐑 a fluffy Gleam web server
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
src/ewe@internal@http1@handler.erl
-module(ewe@internal@http1@handler).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ewe/internal/http1/handler.gleam").
-export([init/0, handle_packet/7]).
-export_type([state/0, message/0, handle_result/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 state() :: {state, gleam@option:option(gleam@erlang@process:timer())}.
-type message() :: idle_timeout.
-type handle_result() :: {continue, state()} |
stop |
{http2_upgrade, bitstring()} |
{http2_cleartext_upgrade,
gleam@http@request:request(ewe@internal@http1:connection()),
binary()}.
-file("src/ewe/internal/http1/handler.gleam", 32).
?DOC(false).
-spec init() -> state().
init() ->
{state, none}.
-file("src/ewe/internal/http1/handler.gleam", 140).
?DOC(false).
-spec handle_resp_file(
gleam@http@request:request(ewe@internal@http1:connection()),
ewe@internal@http1:http_version(),
gleam@http@response:response(ewe@internal@http1:response_body()),
ewe@internal@file:io_device(),
integer(),
integer()
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
handle_resp_file(Req, Version, Resp, Descriptor, Offset, Size) ->
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@http1:append_default_headers(Resp@1, Req, Version),
_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/http1/handler.gleam", 220).
?DOC(false).
-spec encode_gzip(
gleam@http@request:request(ewe@internal@http1: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/http1/handler.gleam", 233).
?DOC(false).
-spec remove_charset(gleam@http@response:response(MBN)) -> gleam@http@response:response(MBN).
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/http1/handler.gleam", 169).
?DOC(false).
-spec handle_resp_body(
gleam@http@request:request(ewe@internal@http1:connection()),
ewe@internal@http1:http_version(),
gleam@http@response:response(ewe@internal@http1:response_body()),
ewe@internal@http1:response_body()
) -> {ok, nil} | {error, glisten@socket:socket_reason()}.
handle_resp_body(Req, Version, Resp, Body) ->
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/http1/handler"/utf8>>,
function => <<"handle_resp_body"/utf8>>,
line => 182})
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@http1:append_default_headers(Resp@1, Req, Version),
_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/http1/handler.gleam", 240).
?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/http1/handler.gleam", 96).
?DOC(false).
-spec call_handler(
gleam@http@request:request(ewe@internal@http1:connection()),
ewe@internal@http1:http_version(),
gleam@erlang@process:subject(glisten@internal@handler:message(any())),
fun((gleam@http@request:request(ewe@internal@http1:connection())) -> gleam@http@response:response(ewe@internal@http1:response_body())),
gleam@http@response:response(ewe@internal@http1:response_body()),
integer()
) -> {ok, state()} | {error, nil}.
call_handler(Req, Version, 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 erlang:element(4, Resp@1) of
websocket ->
{error, nil};
s_s_e ->
{error, nil};
chunked ->
{error, nil};
{file, Descriptor, Offset, Size} ->
Sent = handle_resp_file(
Req,
Version,
Resp@1,
Descriptor,
Offset,
Size
),
case {Sent, is_connection_close(Resp@1)} of
{{ok, nil}, false} ->
Timer = gleam@erlang@process:send_after(
Subject,
Idle_timeout,
{internal, close}
),
{ok, {state, {some, Timer}}};
{_, _} ->
{error, nil}
end;
_ ->
Sent@1 = handle_resp_body(
Req,
Version,
Resp@1,
erlang:element(4, Resp@1)
),
case {Sent@1, is_connection_close(Resp@1)} of
{{ok, nil}, false} ->
Timer@1 = gleam@erlang@process:send_after(
Subject,
Idle_timeout,
{internal, close}
),
{ok, {state, {some, Timer@1}}};
{_, _} ->
{error, nil}
end
end.
-file("src/ewe/internal/http1/handler.gleam", 47).
?DOC(false).
-spec handle_packet(
state(),
bitstring(),
ewe@internal@http1:connection(),
gleam@erlang@process:subject(glisten@internal@handler:message(any())),
fun((gleam@http@request:request(ewe@internal@http1:connection())) -> gleam@http@response:response(ewe@internal@http1:response_body())),
gleam@http@response:response(ewe@internal@http1:response_body()),
integer()
) -> handle_result().
handle_packet(State, Msg, Conn, Subject, Handler, On_crash, Idle_timeout) ->
case erlang:element(2, State) of
{some, Timer} ->
gleam@erlang@process:cancel_timer(Timer);
none ->
timer_not_found
end,
Parsed = ewe@internal@http1:parse_request(
Conn,
ewe@internal@buffer:new(Msg)
),
case Parsed of
{ok, {http1_request, Req, Version}} ->
Called = call_handler(
Req,
Version,
Subject,
Handler,
On_crash,
Idle_timeout
),
case Called of
{ok, State@1} ->
{continue, State@1};
{error, nil} ->
stop
end;
{ok, {http2_upgrade, Data}} ->
{http2_upgrade, Data};
{ok, {http2_cleartext_upgrade, Req@1, Settings}} ->
{http2_cleartext_upgrade, Req@1, Settings};
{error, Reason} ->
Status = case Reason of
invalid_version ->
505;
_ ->
400
end,
_ = 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, Conn),
erlang:element(3, Conn),
_pipe@3
)
end,
stop
end.