-module(fcgi@internal@responder). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/fcgi/internal/responder.gleam"). -export([encode_overloaded_end/1, step/3, encode_stdout_chunk/2, encode_response_headers/2, encode_response_end_records/1]). -export_type([next/0, event/0, outcome/0, state/0, receiving_state/0, decision/0, transition/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 next() :: wait_for_more | close_connection. -type event() :: {request_ready, integer(), bitstring(), boolean()} | {body_chunk, bitstring()} | body_end | body_too_large. -type outcome() :: {outcome, state(), gleam@bytes_tree:bytes_tree(), list(event()), next()}. -type state() :: {idle, bitstring()} | {receiving, receiving_state()}. -type receiving_state() :: {receiving_state, bitstring(), integer(), boolean(), bitstring(), boolean(), integer(), boolean(), boolean(), boolean()}. -type decision() :: keep_parsing | {halt, next()}. -type transition() :: {transition, state(), gleam@bytes_tree:bytes_tree(), list(event()), decision()}. -file("src/fcgi/internal/responder.gleam", 133). ?DOC(false). -spec is_request_boundary(state(), state()) -> boolean(). is_request_boundary(Before, After_state) -> case {Before, After_state} of {{receiving, _}, {idle, _}} -> true; {_, _} -> false end. -file("src/fcgi/internal/responder.gleam", 180). ?DOC(false). -spec skip_record(state()) -> transition(). skip_record(State) -> {transition, State, gleam@bytes_tree:new(), [], keep_parsing}. -file("src/fcgi/internal/responder.gleam", 444). ?DOC(false). -spec lookup_capability(binary()) -> {ok, {binary(), binary()}} | {error, nil}. lookup_capability(Name) -> case Name of <<"FCGI_MPXS_CONNS"/utf8>> -> {ok, {Name, <<"0"/utf8>>}}; _ -> {error, nil} end. -file("src/fcgi/internal/responder.gleam", 435). ?DOC(false). -spec handle_get_values(state(), list(binary())) -> transition(). handle_get_values(State, Names) -> Pairs = gleam@list:filter_map(Names, fun lookup_capability/1), Reply = fcgi@internal@protocol:encode_record({get_values_result, Pairs}), {transition, State, Reply, [], keep_parsing}. -file("src/fcgi/internal/responder.gleam", 419). ?DOC(false). -spec handle_abort(integer()) -> transition(). handle_abort(Request_id) -> Reply = fcgi@internal@protocol:encode_record( {end_request, Request_id, 0, request_complete} ), {transition, {idle, <<>>}, Reply, [], {halt, close_connection}}. -file("src/fcgi/internal/responder.gleam", 376). ?DOC(false). -spec handle_stdin_in_bounds(receiving_state(), bitstring(), integer()) -> transition(). handle_stdin_in_bounds(Recv, Data, New_total) -> Updated = {receiving, {receiving_state, erlang:element(2, Recv), erlang:element(3, Recv), erlang:element(4, Recv), erlang:element(5, Recv), erlang:element(6, Recv), New_total, erlang:element(8, Recv), erlang:element(9, Recv), erlang:element(10, Recv)}}, case erlang:element(6, Recv) of true -> {transition, Updated, gleam@bytes_tree:new(), [{body_chunk, Data}], keep_parsing}; false -> {transition, Updated, gleam@bytes_tree:new(), [], keep_parsing} end. -file("src/fcgi/internal/responder.gleam", 487). ?DOC(false). -spec encode_overloaded_end(integer()) -> gleam@bytes_tree:bytes_tree(). encode_overloaded_end(Request_id) -> fcgi@internal@protocol:encode_record( {end_request, Request_id, 0, overloaded} ). -file("src/fcgi/internal/responder.gleam", 400). ?DOC(false). -spec handle_stdin_overflow(receiving_state()) -> transition(). handle_stdin_overflow(Recv) -> case erlang:element(6, Recv) of true -> {transition, {receiving, {receiving_state, erlang:element(2, Recv), erlang:element(3, Recv), erlang:element(4, Recv), erlang:element(5, Recv), erlang:element(6, Recv), erlang:element(7, Recv), erlang:element(8, Recv), true, erlang:element(10, Recv)}}, gleam@bytes_tree:new(), [body_too_large], keep_parsing}; false -> {transition, {idle, erlang:element(2, Recv)}, encode_overloaded_end(erlang:element(3, Recv)), [], {halt, close_connection}} end. -file("src/fcgi/internal/responder.gleam", 354). ?DOC(false). -spec handle_stdin_chunk(receiving_state(), bitstring(), integer()) -> transition(). handle_stdin_chunk(Recv, Data, Max_body_size) -> gleam@bool:guard( erlang:element(9, Recv), {transition, {receiving, Recv}, gleam@bytes_tree:new(), [], keep_parsing}, fun() -> New_total = erlang:element(7, Recv) + erlang:byte_size(Data), case New_total > Max_body_size of true -> handle_stdin_overflow(Recv); false -> handle_stdin_in_bounds(Recv, Data, New_total) end end ). -file("src/fcgi/internal/responder.gleam", 335). ?DOC(false). -spec finish_stdin(receiving_state()) -> transition(). finish_stdin(Recv) -> case erlang:element(6, Recv) of true -> {transition, {idle, erlang:element(2, Recv)}, gleam@bytes_tree:new(), [body_end], keep_parsing}; false -> {transition, {receiving, {receiving_state, erlang:element(2, Recv), erlang:element(3, Recv), erlang:element(4, Recv), erlang:element(5, Recv), erlang:element(6, Recv), erlang:element(7, Recv), true, erlang:element(9, Recv), erlang:element(10, Recv)}}, gleam@bytes_tree:new(), [], keep_parsing} end. -file("src/fcgi/internal/responder.gleam", 324). ?DOC(false). -spec handle_stdin(receiving_state(), bitstring(), integer()) -> transition(). handle_stdin(Recv, Data, Max_body_size) -> case erlang:byte_size(Data) of 0 -> finish_stdin(Recv); _ -> handle_stdin_chunk(Recv, Data, Max_body_size) end. -file("src/fcgi/internal/responder.gleam", 271). ?DOC(false). -spec merge_input(bitstring(), boolean(), bitstring(), integer()) -> {bitstring(), boolean()}. merge_input(Current, Overflow, Data, Max) -> gleam@bool:guard( Overflow, {<<>>, true}, fun() -> Combined = <>, case erlang:byte_size(Combined) > Max of true -> {<<>>, true}; false -> {Combined, false} end end ). -file("src/fcgi/internal/responder.gleam", 298). ?DOC(false). -spec emit_request_ready(receiving_state()) -> transition(). emit_request_ready(Recv) -> Ready_event = {request_ready, erlang:element(3, Recv), erlang:element(5, Recv), erlang:element(4, Recv)}, case erlang:element(8, Recv) of true -> {transition, {idle, erlang:element(2, Recv)}, gleam@bytes_tree:new(), [Ready_event, body_end], keep_parsing}; false -> {transition, {receiving, {receiving_state, erlang:element(2, Recv), erlang:element(3, Recv), erlang:element(4, Recv), erlang:element(5, Recv), true, erlang:element(7, Recv), erlang:element(8, Recv), erlang:element(9, Recv), erlang:element(10, Recv)}}, gleam@bytes_tree:new(), [Ready_event], keep_parsing} end. -file("src/fcgi/internal/responder.gleam", 285). ?DOC(false). -spec finish_params(receiving_state()) -> transition(). finish_params(Recv) -> case erlang:element(10, Recv) of true -> {transition, {idle, <<>>}, encode_overloaded_end(erlang:element(3, Recv)), [], {halt, close_connection}}; false -> emit_request_ready(Recv) end. -file("src/fcgi/internal/responder.gleam", 247). ?DOC(false). -spec handle_params(receiving_state(), bitstring()) -> transition(). handle_params(Recv, Data) -> case erlang:byte_size(Data) of 0 -> finish_params(Recv); _ -> {Params, Overflow} = merge_input( erlang:element(5, Recv), erlang:element(10, Recv), Data, 65535 ), {transition, {receiving, {receiving_state, erlang:element(2, Recv), erlang:element(3, Recv), erlang:element(4, Recv), Params, erlang:element(6, Recv), erlang:element(7, Recv), erlang:element(8, Recv), erlang:element(9, Recv), Overflow}}, gleam@bytes_tree:new(), [], keep_parsing} end. -file("src/fcgi/internal/responder.gleam", 231). ?DOC(false). -spec handle_begin_request_busy(receiving_state(), integer()) -> transition(). handle_begin_request_busy(Recv, Id) -> Reply = fcgi@internal@protocol:encode_record( {end_request, Id, 0, cant_multiplex_connection} ), {transition, {receiving, Recv}, Reply, [], keep_parsing}. -file("src/fcgi/internal/responder.gleam", 189). ?DOC(false). -spec handle_begin_request_idle(bitstring(), integer(), integer(), boolean()) -> transition(). handle_begin_request_idle(Buffer, Id, Role, Keep) -> case Role =:= 1 of true -> {transition, {receiving, {receiving_state, Buffer, Id, Keep, <<>>, false, 0, false, false, false}}, gleam@bytes_tree:new(), [], keep_parsing}; false -> Reply = fcgi@internal@protocol:encode_record( {end_request, Id, 0, unknown_role} ), {transition, {idle, Buffer}, Reply, [], {halt, close_connection}} end. -file("src/fcgi/internal/responder.gleam", 140). ?DOC(false). -spec handle_record(state(), fcgi@internal@protocol:incoming(), integer()) -> transition(). handle_record(State, Record, Max_body_size) -> case {State, Record} of {_, {begin_request, 0, _, _}} -> {transition, State, gleam@bytes_tree:new(), [], {halt, close_connection}}; {{idle, Buffer}, {begin_request, Id, Role, Keep}} -> handle_begin_request_idle(Buffer, Id, Role, Keep); {{receiving, Recv}, {begin_request, Id@1, _, _}} -> handle_begin_request_busy(Recv, Id@1); {{receiving, Recv@1}, {params, Id@2, Data}} when (Id@2 =:= erlang:element( 3, Recv@1 )) andalso not erlang:element(6, Recv@1) -> handle_params(Recv@1, Data); {{receiving, Recv@2}, {stdin, Id@3, Data@1}} when Id@3 =:= erlang:element( 3, Recv@2 ) -> handle_stdin(Recv@2, Data@1, Max_body_size); {{receiving, Recv@3}, {abort_request, Id@4}} when Id@4 =:= erlang:element( 3, Recv@3 ) -> handle_abort(erlang:element(3, Recv@3)); {_, {get_values, Names}} -> handle_get_values(State, Names); {_, {incoming_unknown, 0, Type_byte}} -> Reply = fcgi@internal@protocol:encode_record( {unknown_type, Type_byte} ), {transition, State, Reply, [], keep_parsing}; {_, {params, _, _}} -> skip_record(State); {_, {stdin, _, _}} -> skip_record(State); {_, {abort_request, _}} -> skip_record(State); {_, {incoming_unknown, _, _}} -> skip_record(State) end. -file("src/fcgi/internal/responder.gleam", 75). ?DOC(false). -spec step_loop( state(), gleam@bytes_tree:bytes_tree(), list(event()), integer() ) -> outcome(). step_loop(State, Outgoing, Events_rev, Max_body_size) -> Buffer = case State of {idle, Buf} -> Buf; {receiving, Recv} -> erlang:element(2, Recv) end, case fcgi@internal@protocol:parse_record(Buffer) of need_more -> {outcome, State, Outgoing, lists:reverse(Events_rev), wait_for_more}; {parse_error, _} -> {outcome, State, Outgoing, lists:reverse(Events_rev), close_connection}; {parsed, Record, Rest} -> Action = handle_record(State, Record, Max_body_size), Advanced = case erlang:element(2, Action) of {idle, _} -> {idle, Rest}; {receiving, Recv@1} -> {receiving, {receiving_state, Rest, erlang:element(3, Recv@1), erlang:element(4, Recv@1), erlang:element(5, Recv@1), erlang:element(6, Recv@1), erlang:element(7, Recv@1), erlang:element(8, Recv@1), erlang:element(9, Recv@1), erlang:element(10, Recv@1)}} end, Combined_outgoing = gleam_stdlib:iodata_append( Outgoing, erlang:element(3, Action) ), Combined_events = gleam@list:fold( erlang:element(4, Action), Events_rev, fun(Acc, Evt) -> [Evt | Acc] end ), case {erlang:element(5, Action), is_request_boundary(State, erlang:element(2, Action))} of {{halt, Next}, _} -> {outcome, Advanced, Combined_outgoing, lists:reverse(Combined_events), Next}; {keep_parsing, true} -> {outcome, Advanced, Combined_outgoing, lists:reverse(Combined_events), wait_for_more}; {keep_parsing, false} -> step_loop( Advanced, Combined_outgoing, Combined_events, Max_body_size ) end end. -file("src/fcgi/internal/responder.gleam", 59). ?DOC(false). -spec step(state(), bitstring(), integer()) -> outcome(). step(State, Bytes, Max_body_size) -> Combined = case State of {idle, Buf} -> {idle, <>}; {receiving, Recv} -> {receiving, {receiving_state, <<(erlang:element(2, Recv))/bitstring, Bytes/bitstring>>, erlang:element(3, Recv), erlang:element(4, Recv), erlang:element(5, Recv), erlang:element(6, Recv), erlang:element(7, Recv), erlang:element(8, Recv), erlang:element(9, Recv), erlang:element(10, Recv)}} end, step_loop(Combined, gleam@bytes_tree:new(), [], Max_body_size). -file("src/fcgi/internal/responder.gleam", 528). ?DOC(false). -spec contains_crlf(binary()) -> boolean(). contains_crlf(Value) -> gleam_stdlib:contains_string(Value, <<"\r"/utf8>>) orelse gleam_stdlib:contains_string( Value, <<"\n"/utf8>> ). -file("src/fcgi/internal/responder.gleam", 524). ?DOC(false). -spec is_safe_header(binary(), binary()) -> boolean(). is_safe_header(Name, Value) -> not contains_crlf(Name) andalso not contains_crlf(Value). -file("src/fcgi/internal/responder.gleam", 501). ?DOC(false). -spec encode_http_headers(gleam@http@response:response(any())) -> gleam@bytes_tree:bytes_tree(). encode_http_headers(Resp) -> Start = begin _pipe = gleam_stdlib:wrap_list(<<"Status: "/utf8>>), _pipe@1 = gleam@bytes_tree:append_string( _pipe, erlang:integer_to_binary(erlang:element(2, Resp)) ), gleam@bytes_tree:append_string(_pipe@1, <<"\r\n"/utf8>>) end, With_headers = gleam@list:fold( erlang:element(3, Resp), Start, fun(Acc, Header) -> {Name, Value} = Header, case is_safe_header(Name, Value) of true -> _pipe@2 = Acc, _pipe@3 = gleam@bytes_tree:append_string(_pipe@2, Name), _pipe@4 = gleam@bytes_tree:append_string( _pipe@3, <<": "/utf8>> ), _pipe@5 = gleam@bytes_tree:append_string(_pipe@4, Value), gleam@bytes_tree:append_string(_pipe@5, <<"\r\n"/utf8>>); false -> Acc end end ), gleam@bytes_tree:append_string(With_headers, <<"\r\n"/utf8>>). -file("src/fcgi/internal/responder.gleam", 495). ?DOC(false). -spec encode_records(list(fcgi@internal@protocol:outgoing())) -> gleam@bytes_tree:bytes_tree(). encode_records(Records) -> gleam@list:fold( Records, gleam@bytes_tree:new(), fun(Acc, Record) -> gleam_stdlib:iodata_append( Acc, fcgi@internal@protocol:encode_record(Record) ) end ). -file("src/fcgi/internal/responder.gleam", 469). ?DOC(false). -spec encode_stdout_chunk(integer(), gleam@bytes_tree:bytes_tree()) -> gleam@bytes_tree:bytes_tree(). encode_stdout_chunk(Request_id, Payload) -> Size = erlang:iolist_size(Payload), gleam@bool:guard( Size =:= 0, gleam@bytes_tree:new(), fun() -> case Size =< 65535 of true -> fcgi@internal@protocol:encode_record_tree_unchecked( 6, Request_id, Payload ); false -> encode_records( fcgi@internal@protocol:chunk_stdout( Request_id, erlang:list_to_bitstring(Payload) ) ) end end ). -file("src/fcgi/internal/responder.gleam", 451). ?DOC(false). -spec encode_response_headers(integer(), gleam@http@response:response(any())) -> gleam@bytes_tree:bytes_tree(). encode_response_headers(Request_id, Resp) -> encode_stdout_chunk(Request_id, encode_http_headers(Resp)). -file("src/fcgi/internal/responder.gleam", 458). ?DOC(false). -spec encode_response_end_records(integer()) -> gleam@bytes_tree:bytes_tree(). encode_response_end_records(Request_id) -> Stdout_end = {stdout, Request_id, <<>>}, Request_end = {end_request, Request_id, 0, request_complete}, encode_records([Stdout_end, Request_end]).