Current section
Files
Jump to
Current section
Files
src/fcgi@internal@responder.erl
-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([continuation/0, event/0, outcome/0, state/0, receiving_state/0, decision/0, action/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 continuation() :: 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()),
continuation()}.
-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, continuation()}.
-type action() :: {action,
state(),
gleam@bytes_tree:bytes_tree(),
list(event()),
decision()}.
-file("src/fcgi/internal/responder.gleam", 135).
?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", 182).
?DOC(false).
-spec skip_record(state()) -> action().
skip_record(State) ->
{action, State, gleam@bytes_tree:new(), [], keep_parsing}.
-file("src/fcgi/internal/responder.gleam", 435).
?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", 426).
?DOC(false).
-spec handle_get_values(state(), list(binary())) -> action().
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}),
{action, State, Reply, [], keep_parsing}.
-file("src/fcgi/internal/responder.gleam", 411).
?DOC(false).
-spec handle_abort(integer()) -> action().
handle_abort(Request_id) ->
Reply = fcgi@internal@protocol:encode_record(
{end_request, Request_id, 0, request_complete}
),
{action, {idle, <<>>}, Reply, [], {halt, close_connection}}.
-file("src/fcgi/internal/responder.gleam", 368).
?DOC(false).
-spec handle_stdin_in_bounds(receiving_state(), bitstring(), integer()) -> action().
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 ->
{action,
Updated,
gleam@bytes_tree:new(),
[{body_chunk, Data}],
keep_parsing};
false ->
{action, Updated, gleam@bytes_tree:new(), [], keep_parsing}
end.
-file("src/fcgi/internal/responder.gleam", 478).
?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", 392).
?DOC(false).
-spec handle_stdin_overflow(receiving_state()) -> action().
handle_stdin_overflow(Recv) ->
case erlang:element(6, Recv) of
true ->
{action,
{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 ->
{action,
{idle, erlang:element(2, Recv)},
encode_overloaded_end(erlang:element(3, Recv)),
[],
{halt, close_connection}}
end.
-file("src/fcgi/internal/responder.gleam", 347).
?DOC(false).
-spec handle_stdin_chunk(receiving_state(), bitstring(), integer()) -> action().
handle_stdin_chunk(Recv, Data, Max_body_size) ->
gleam@bool:guard(
erlang:element(9, Recv),
{action, {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", 328).
?DOC(false).
-spec finish_stdin(receiving_state()) -> action().
finish_stdin(Recv) ->
case erlang:element(6, Recv) of
true ->
{action,
{idle, erlang:element(2, Recv)},
gleam@bytes_tree:new(),
[body_end],
keep_parsing};
false ->
{action,
{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", 317).
?DOC(false).
-spec handle_stdin(receiving_state(), bitstring(), integer()) -> action().
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", 265).
?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", 292).
?DOC(false).
-spec emit_request_ready(receiving_state()) -> action().
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 ->
{action,
{idle, erlang:element(2, Recv)},
gleam@bytes_tree:new(),
[Ready_event, body_end],
keep_parsing};
false ->
{action,
{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", 279).
?DOC(false).
-spec finish_params(receiving_state()) -> action().
finish_params(Recv) ->
case erlang:element(10, Recv) of
true ->
{action,
{idle, <<>>},
encode_overloaded_end(erlang:element(3, Recv)),
[],
{halt, close_connection}};
false ->
emit_request_ready(Recv)
end.
-file("src/fcgi/internal/responder.gleam", 242).
?DOC(false).
-spec handle_params(receiving_state(), bitstring()) -> action().
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
),
{action,
{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", 227).
?DOC(false).
-spec handle_begin_request_busy(receiving_state(), integer()) -> action().
handle_begin_request_busy(Recv, Id) ->
Reply = fcgi@internal@protocol:encode_record(
{end_request, Id, 0, cant_multiplex_connection}
),
{action, {receiving, Recv}, Reply, [], keep_parsing}.
-file("src/fcgi/internal/responder.gleam", 186).
?DOC(false).
-spec handle_begin_request_idle(bitstring(), integer(), integer(), boolean()) -> action().
handle_begin_request_idle(Buffer, Id, Role, Keep) ->
case Role =:= 1 of
true ->
{action,
{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}
),
{action, {idle, Buffer}, Reply, [], {halt, close_connection}}
end.
-file("src/fcgi/internal/responder.gleam", 142).
?DOC(false).
-spec handle_record(state(), fcgi@internal@protocol:incoming(), integer()) -> action().
handle_record(State, Record, Max_body_size) ->
case {State, Record} of
{_, {begin_request, 0, _, _}} ->
{action,
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}
),
{action, 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", 79).
?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, Continuation}, _} ->
{outcome,
Advanced,
Combined_outgoing,
lists:reverse(Combined_events),
Continuation};
{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", 64).
?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", 519).
?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", 515).
?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", 492).
?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", 486).
?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", 460).
?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", 442).
?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", 449).
?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]).