-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 = <<Current/bitstring, Data/bitstring>>,
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, <<Buf/bitstring, Bytes/bitstring>>};
{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]).