-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(MCQ)) -> gleam@http@response:response(MCQ). 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 => <>, 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.