-module(fcgi@internal@protocol).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/fcgi/internal/protocol.gleam").
-export([encode_record_tree_unchecked/3, encode_record_bits_unchecked/3, encode_name_value_pairs/1, encode_record/1, encode_stdout_record_header/2, chunk_stdout/2, parse_name_value_pairs/1, parse_record/1]).
-export_type([outgoing/0, status/0, incoming/0, parse_failure/0, parse_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 outgoing() :: {end_request, integer(), integer(), status()} |
{stdout, integer(), bitstring()} |
{get_values_result, list({binary(), binary()})} |
{unknown_type, integer()}.
-type status() :: request_complete |
cant_multiplex_connection |
overloaded |
unknown_role.
-type incoming() :: {begin_request, integer(), integer(), boolean()} |
{abort_request, integer()} |
{params, integer(), bitstring()} |
{stdin, integer(), bitstring()} |
{get_values, list(binary())} |
{incoming_unknown, integer(), integer()}.
-type parse_failure() :: {unsupported_version, integer()} |
malformed_record |
malformed_name_value.
-type parse_result() :: {parsed, incoming(), bitstring()} |
need_more |
{parse_error, parse_failure()}.
-file("src/fcgi/internal/protocol.gleam", 105).
?DOC(false).
-spec padding_for(integer()) -> integer().
padding_for(Content_length) ->
Remainder = case 8 of
0 -> 0;
Gleam@denominator -> Content_length rem Gleam@denominator
end,
case Remainder of
0 ->
0;
_ ->
8 - Remainder
end.
-file("src/fcgi/internal/protocol.gleam", 83).
?DOC(false).
-spec encode_record_tree_unchecked(
integer(),
integer(),
gleam@bytes_tree:bytes_tree()
) -> gleam@bytes_tree:bytes_tree().
encode_record_tree_unchecked(Record_type, Request_id, Body) ->
Content_length = erlang:iolist_size(Body),
Padding_length = padding_for(Content_length),
Header = <<1:8,
Record_type:8,
Request_id:16,
Content_length:16,
Padding_length:8,
0:8>>,
Padding = <<0:(erlang:max(0, (Padding_length * 8)))>>,
_pipe = gleam@bytes_tree:from_bit_array(Header),
_pipe@1 = gleam_stdlib:iodata_append(_pipe, Body),
gleam@bytes_tree:append(_pipe@1, Padding).
-file("src/fcgi/internal/protocol.gleam", 74).
?DOC(false).
-spec encode_record_bits_unchecked(integer(), integer(), bitstring()) -> gleam@bytes_tree:bytes_tree().
encode_record_bits_unchecked(Record_type, Request_id, Body) ->
_pipe = gleam@bytes_tree:from_bit_array(Body),
encode_record_tree_unchecked(Record_type, Request_id, _pipe).
-file("src/fcgi/internal/protocol.gleam", 138).
?DOC(false).
-spec encode_length(integer()) -> bitstring().
encode_length(N) ->
case N < 128 of
true ->
<<N:8>>;
false ->
<<1:1, N:31>>
end.
-file("src/fcgi/internal/protocol.gleam", 122).
?DOC(false).
-spec encode_name_value_pairs(list({binary(), binary()})) -> gleam@bytes_tree:bytes_tree().
encode_name_value_pairs(Pairs) ->
gleam@list:fold(
Pairs,
gleam@bytes_tree:new(),
fun(Acc, Pair) ->
{Name, Value} = Pair,
Name_bytes = gleam_stdlib:identity(Name),
Value_bytes = gleam_stdlib:identity(Value),
Name_length = erlang:byte_size(Name_bytes),
Value_length = erlang:byte_size(Value_bytes),
_pipe = Acc,
_pipe@1 = gleam@bytes_tree:append(_pipe, encode_length(Name_length)),
_pipe@2 = gleam@bytes_tree:append(
_pipe@1,
encode_length(Value_length)
),
_pipe@3 = gleam@bytes_tree:append(_pipe@2, Name_bytes),
gleam@bytes_tree:append(_pipe@3, Value_bytes)
end
).
-file("src/fcgi/internal/protocol.gleam", 113).
?DOC(false).
-spec status_to_int(status()) -> integer().
status_to_int(Status) ->
case Status of
request_complete ->
0;
cant_multiplex_connection ->
1;
overloaded ->
2;
unknown_role ->
3
end.
-file("src/fcgi/internal/protocol.gleam", 50).
?DOC(false).
-spec encode_record(outgoing()) -> gleam@bytes_tree:bytes_tree().
encode_record(Record) ->
case Record of
{end_request, Id, App_status, Protocol_status} ->
Body = <<App_status:32, (status_to_int(Protocol_status)):8, 0:24>>,
encode_record_bits_unchecked(3, Id, Body);
{stdout, Id@1, Data} ->
encode_record_bits_unchecked(6, Id@1, Data);
{get_values_result, Pairs} ->
encode_record_tree_unchecked(10, 0, encode_name_value_pairs(Pairs));
{unknown_type, Type_byte} ->
Body@1 = <<Type_byte:8, 0:56>>,
encode_record_bits_unchecked(11, 0, Body@1)
end.
-file("src/fcgi/internal/protocol.gleam", 149).
?DOC(false).
-spec encode_stdout_record_header(integer(), integer()) -> {bitstring(),
integer()}.
encode_stdout_record_header(Request_id, Content_length) ->
Padding_length = padding_for(Content_length),
Header = <<1:8,
6:8,
Request_id:16,
Content_length:16,
Padding_length:8,
0:8>>,
{Header, Padding_length}.
-file("src/fcgi/internal/protocol.gleam", 170).
?DOC(false).
-spec chunk_stdout_loop(integer(), bitstring(), list(outgoing())) -> list(outgoing()).
chunk_stdout_loop(Request_id, Body, Acc) ->
Total = erlang:byte_size(Body),
gleam@bool:guard(
Total =:= 0,
lists:reverse(Acc),
fun() ->
gleam@bool:guard(
Total =< 65535,
lists:reverse([{stdout, Request_id, Body} | Acc]),
fun() ->
Head@1 = case gleam_stdlib:bit_array_slice(Body, 0, 65535) of
{ok, Head} -> Head;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fcgi/internal/protocol"/utf8>>,
function => <<"chunk_stdout_loop"/utf8>>,
line => 183,
value => _assert_fail,
start => 4563,
'end' => 4634,
pattern_start => 4574,
pattern_end => 4582})
end,
Tail@1 = case gleam_stdlib:bit_array_slice(
Body,
65535,
Total - 65535
) of
{ok, Tail} -> Tail;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"fcgi/internal/protocol"/utf8>>,
function => <<"chunk_stdout_loop"/utf8>>,
line => 184,
value => _assert_fail@1,
start => 4637,
'end' => 4767,
pattern_start => 4648,
pattern_end => 4656})
end,
chunk_stdout_loop(
Request_id,
Tail@1,
[{stdout, Request_id, Head@1} | Acc]
)
end
)
end
).
-file("src/fcgi/internal/protocol.gleam", 166).
?DOC(false).
-spec chunk_stdout(integer(), bitstring()) -> list(outgoing()).
chunk_stdout(Request_id, Body) ->
chunk_stdout_loop(Request_id, Body, []).
-file("src/fcgi/internal/protocol.gleam", 343).
?DOC(false).
-spec parse_length(bitstring()) -> {ok, {integer(), bitstring()}} |
{error, parse_failure()}.
parse_length(Bytes) ->
case Bytes of
<<0:1, N:7, Rest/bitstring>> ->
{ok, {N, Rest}};
<<1:1, N@1:31, Rest@1/bitstring>> ->
{ok, {N@1, Rest@1}};
_ ->
{error, malformed_name_value}
end.
-file("src/fcgi/internal/protocol.gleam", 316).
?DOC(false).
-spec parse_one_pair(bitstring()) -> {ok, {{binary(), binary()}, bitstring()}} |
{error, parse_failure()}.
parse_one_pair(Bytes) ->
gleam@result:'try'(
parse_length(Bytes),
fun(_use0) ->
{Name_length, After_name_length} = _use0,
gleam@result:'try'(
parse_length(After_name_length),
fun(_use0@1) ->
{Value_length, After_value_length} = _use0@1,
case After_value_length of
<<Name_bytes:Name_length/binary,
Value_bytes:Value_length/binary,
Rest/bitstring>> ->
gleam@result:'try'(
begin
_pipe = gleam@bit_array:to_string(
Name_bytes
),
gleam@result:replace_error(
_pipe,
malformed_name_value
)
end,
fun(Name) ->
gleam@result:map(
begin
_pipe@1 = gleam@bit_array:to_string(
Value_bytes
),
gleam@result:replace_error(
_pipe@1,
malformed_name_value
)
end,
fun(Value) -> {{Name, Value}, Rest} end
)
end
);
_ ->
{error, malformed_name_value}
end
end
)
end
).
-file("src/fcgi/internal/protocol.gleam", 303).
?DOC(false).
-spec parse_name_value_pairs_loop(bitstring(), list({binary(), binary()})) -> {ok,
list({binary(), binary()})} |
{error, parse_failure()}.
parse_name_value_pairs_loop(Bytes, Acc) ->
gleam@bool:guard(
erlang:byte_size(Bytes) =:= 0,
{ok, lists:reverse(Acc)},
fun() ->
gleam@result:'try'(
parse_one_pair(Bytes),
fun(_use0) ->
{Pair, Rest} = _use0,
parse_name_value_pairs_loop(Rest, [Pair | Acc])
end
)
end
).
-file("src/fcgi/internal/protocol.gleam", 297).
?DOC(false).
-spec parse_name_value_pairs(bitstring()) -> {ok, list({binary(), binary()})} |
{error, parse_failure()}.
parse_name_value_pairs(Bytes) ->
parse_name_value_pairs_loop(Bytes, []).
-file("src/fcgi/internal/protocol.gleam", 287).
?DOC(false).
-spec parse_get_values(bitstring(), bitstring()) -> parse_result().
parse_get_values(Body, Rest) ->
case parse_name_value_pairs(Body) of
{error, Reason} ->
{parse_error, Reason};
{ok, Pairs} ->
Names = gleam@list:map(Pairs, fun gleam@pair:first/1),
{parsed, {get_values, Names}, Rest}
end.
-file("src/fcgi/internal/protocol.gleam", 276).
?DOC(false).
-spec parse_begin_request(integer(), bitstring(), bitstring()) -> parse_result().
parse_begin_request(Id, Body, Rest) ->
case Body of
<<Role:16, _:7, Keep_bit:1, _:40>> ->
{parsed, {begin_request, Id, Role, Keep_bit =:= 1}, Rest};
_ ->
{parse_error, malformed_record}
end.
-file("src/fcgi/internal/protocol.gleam", 260).
?DOC(false).
-spec parse_body(integer(), integer(), bitstring(), bitstring()) -> parse_result().
parse_body(Record_type, Id, Body, Rest) ->
case Record_type of
1 ->
parse_begin_request(Id, Body, Rest);
2 ->
{parsed, {abort_request, Id}, Rest};
4 ->
{parsed, {params, Id, Body}, Rest};
5 ->
{parsed, {stdin, Id, Body}, Rest};
9 ->
parse_get_values(Body, Rest);
_ ->
{parsed, {incoming_unknown, Id, Record_type}, Rest}
end.
-file("src/fcgi/internal/protocol.gleam", 238).
?DOC(false).
-spec parse_after_header(
integer(),
integer(),
integer(),
integer(),
integer(),
bitstring()
) -> parse_result().
parse_after_header(
Version,
Record_type,
Id,
Content_length,
Padding_length,
Rest
) ->
gleam@bool:guard(
Version /= 1,
{parse_error, {unsupported_version, Version}},
fun() -> case Rest of
<<Body:Content_length/binary,
_:Padding_length/binary,
Remaining/bitstring>> ->
parse_body(Record_type, Id, Body, Remaining);
_ ->
need_more
end end
).
-file("src/fcgi/internal/protocol.gleam", 215).
?DOC(false).
-spec parse_record(bitstring()) -> parse_result().
parse_record(Buffer) ->
case Buffer of
<<Version:8,
Record_type:8,
Id:16,
Content_length:16,
Padding_length:8,
_:8,
Rest/bitstring>> ->
parse_after_header(
Version,
Record_type,
Id,
Content_length,
Padding_length,
Rest
);
_ ->
need_more
end.