-module(fcgi). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/fcgi.gleam"). -export([new/1, listen_unix/2, listen_tcp/3, max_body_size/2, body_read_timeout/2, read_all/1, bytes/1, send_file/3, stream/1, string/1, send_chunk/2, to_http_request/2, start/1, supervised/1]). -export_type([address/0, builder/1, context/0, read/0, read_error/0, file_error/0, response_data/0, stream_sender/0, start_error/0, server/0, connection/0, next_request/0, after_response/0, request_error/0, partial_request/0, body_context/0, body_snapshot/0, handle/0, socket/0, socket_error/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(" FastCGI Responder server.\n"). -opaque address() :: {path_address, binary()} | {tcp_address, binary(), integer()}. -opaque builder(GQY) :: {builder, fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())), GQY, integer(), integer()}. -type context() :: {context, gleam@option:option(binary()), gleam@option:option(integer()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@dict:dict(binary(), binary())}. -type read() :: {chunk, bitstring(), fun(() -> {ok, read()} | {error, read_error()})} | 'end'. -type read_error() :: client_disconnected | read_timeout | body_too_large | request_aborted. -type file_error() :: {file_not_found, binary()} | {file_access_denied, binary()} | {file_is_directory, binary()} | {file_other, binary(), binary()} | {invalid_range, integer(), gleam@option:option(integer())}. -opaque response_data() :: {bytes, gleam@bytes_tree:bytes_tree()} | {file, handle(), integer(), integer()} | {stream, fun((stream_sender()) -> nil)}. -opaque stream_sender() :: {stream_sender, socket(), integer()}. -type start_error() :: {listener_error, binary()} | {invalid_max_body_size, integer()} | {invalid_body_read_timeout, integer()}. -type server() :: {server, gleam@otp@static_supervisor:supervisor(), gleam@option:option(integer())}. -type connection() :: {connection, socket(), integer(), integer(), fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data()))}. -type next_request() :: {ready, fcgi@internal@responder:state(), integer(), bitstring(), list(fcgi@internal@responder:event()), boolean()} | closed. -type after_response() :: continue | {drain_body, body_snapshot()}. -type request_error() :: missing_method | {invalid_method, binary()}. -type partial_request() :: {partial_request, gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), gleam@option:option(binary()), list({binary(), binary()}), context()}. -type body_context() :: {body_context, fcgi@internal@responder:state(), bitstring(), boolean(), boolean(), connection(), gleam@erlang@process:subject(body_snapshot())}. -type body_snapshot() :: {body_snapshot, fcgi@internal@responder:state(), boolean(), boolean(), boolean()}. -type handle() :: any(). -type socket() :: any(). -type socket_error() :: {path_exists, binary()} | {invalid_host, binary()} | {posix, gleam@erlang@atom:atom_()}. -file("src/fcgi.gleam", 64). ?DOC( " Build a new FastCGI server with the given handler.\n" "\n" " The handler is invoked once `Params` is fully received. The request\n" " body is delivered incrementally via a `BodyReader` in `req.body`;\n" " call it to obtain the first `Read` and thread `consume` to advance,\n" " or pass it to `read_all` for the buffered case.\n" "\n" " Default: 256 MiB max body, 30 s body read timeout.\n" ). -spec new( fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())) ) -> builder(nil). new(Handler) -> {builder, Handler, nil, 268435456, 30000}. -file("src/fcgi.gleam", 74). ?DOC(" Set the Unix domain socket path the server listens on.\n"). -spec listen_unix(builder(any()), binary()) -> builder(address()). listen_unix(Builder, Path) -> {builder, erlang:element(2, Builder), {path_address, Path}, erlang:element(4, Builder), erlang:element(5, Builder)}. -file("src/fcgi.gleam", 89). ?DOC( " Set the TCP `host` and `port` the server listens on. `host` must be a\n" " numeric IP literal: either IPv4 (e.g. `\"127.0.0.1\"`, `\"0.0.0.0\"`) or\n" " IPv6 (e.g. `\"::1\"`, `\"::\"`).\n" ). -spec listen_tcp(builder(any()), binary(), integer()) -> builder(address()). listen_tcp(Builder, Host, Port) -> {builder, erlang:element(2, Builder), {tcp_address, Host, Port}, erlang:element(4, Builder), erlang:element(5, Builder)}. -file("src/fcgi.gleam", 112). ?DOC( " Set the maximum body bytes the server will deliver to the handler in\n" " total across all body reads. Must be `>= 0`; `start` returns\n" " `InvalidMaxBodySize(bytes)` for negative values.\n" "\n" " When the peer sends more than this many bytes, the next body read\n" " returns `Error(BodyTooLarge)`. The handler may respond as it sees fit,\n" " but the connection is closed after the response is sent because\n" " remaining body bytes cannot be safely drained.\n" "\n" " Default: 256 MiB.\n" ). -spec max_body_size(builder(GRR), integer()) -> builder(GRR). max_body_size(Builder, Bytes) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), Bytes, erlang:element(5, Builder)}. -file("src/fcgi.gleam", 124). ?DOC( " Set how long the server waits for the next stdin or params record\n" " before giving up. Must be `> 0`. Applies between successive socket\n" " reads, including the wait for the first record after `accept`, not\n" " to the request as a whole. Returns `Error(ReadTimeout)` to body\n" " readers. Default: 30,000 ms.\n" ). -spec body_read_timeout(builder(GRU), integer()) -> builder(GRU). body_read_timeout(Builder, Milliseconds) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), Milliseconds}. -file("src/fcgi.gleam", 201). -spec read_all_loop( {ok, read()} | {error, read_error()}, gleam@bytes_tree:bytes_tree() ) -> {ok, gleam@bytes_tree:bytes_tree()} | {error, read_error()}. read_all_loop(Read, Acc) -> case Read of {error, Reason} -> {error, Reason}; {ok, 'end'} -> {ok, Acc}; {ok, {chunk, Data, Consume}} -> read_all_loop(Consume(), gleam@bytes_tree:append(Acc, Data)) end. -file("src/fcgi.gleam", 197). ?DOC( " Buffer the entire body into a `BytesTree`. The server's\n" " `max_body_size` setting is the upper bound; the underlying reader\n" " returns `BodyTooLarge` if the peer exceeds it.\n" "\n" " The buffered length reflects what the upstream proxy actually sent,\n" " not what `CONTENT_LENGTH` advertised.\n" ). -spec read_all(fun(() -> {ok, read()} | {error, read_error()})) -> {ok, gleam@bytes_tree:bytes_tree()} | {error, read_error()}. read_all(Read) -> read_all_loop(Read(), gleam@bytes_tree:new()). -file("src/fcgi.gleam", 243). ?DOC( " Build an in-memory response body. The whole `BytesTree` is sent in\n" " one or more `STDOUT` records.\n" ). -spec bytes(gleam@bytes_tree:bytes_tree()) -> response_data(). bytes(Content) -> {bytes, Content}. -file("src/fcgi.gleam", 252). ?DOC( " Open a file and return a response body that streams it via\n" " `file:sendfile/5` when the response is sent.\n" "\n" " Returns `FileError` if the file is missing, inaccessible, a\n" " directory, or if `offset` or `limit` is negative.\n" ). -spec send_file(binary(), integer(), gleam@option:option(integer())) -> {ok, response_data()} | {error, file_error()}. send_file(Path, Offset, Limit) -> gleam@bool:guard( (Offset < 0) orelse (gleam@option:unwrap(Limit, 0) < 0), {error, {invalid_range, Offset, Limit}}, fun() -> gleam@result:'try'( fcgi_ffi:open_and_size(Path), fun(_use0) -> {Handle, Total_size} = _use0, Max_length = gleam@option:unwrap(Limit, Total_size), Length = gleam@int:clamp(Total_size - Offset, 0, Max_length), {ok, {file, Handle, Offset, Length}} end ) end ). -file("src/fcgi.gleam", 273). ?DOC( " Build a streaming response body. The server calls `producer(sender)`\n" " after sending the response headers; each call to `send_chunk(sender,\n" " data)` writes one or more `STDOUT` records to the upstream proxy.\n" "\n" " A panic raised by `producer` is caught; the response end records are\n" " still emitted so the upstream proxy sees a clean end-of-request.\n" ). -spec stream(fun((stream_sender()) -> nil)) -> response_data(). stream(Producer) -> {stream, Producer}. -file("src/fcgi.gleam", 278). ?DOC(" Build a response body from a `String`.\n"). -spec string(binary()) -> response_data(). string(Content) -> {bytes, gleam_stdlib:wrap_list(Content)}. -file("src/fcgi.gleam", 1136). -spec send_if_nonempty(socket(), gleam@bytes_tree:bytes_tree()) -> {ok, nil} | {error, nil}. send_if_nonempty(Socket, Bytes) -> case erlang:iolist_size(Bytes) of 0 -> {ok, nil}; _ -> fcgi_ffi:send(Socket, Bytes) end. -file("src/fcgi.gleam", 287). ?DOC( " Emit a chunk of body bytes from inside a `Stream` producer.\n" "\n" " Returns `Ok(Nil)` when the chunk is written, or `Error(Nil)` when the\n" " underlying socket write fails (for example, the upstream proxy has\n" " disconnected).\n" ). -spec send_chunk(stream_sender(), gleam@bytes_tree:bytes_tree()) -> {ok, nil} | {error, nil}. send_chunk(Sender, Data) -> {stream_sender, Socket, Request_id} = Sender, send_if_nonempty( Socket, fcgi@internal@responder:encode_stdout_chunk(Request_id, Data) ). -file("src/fcgi.gleam", 550). -spec describe_transport_error(socket_error()) -> binary(). describe_transport_error(Error) -> case Error of {posix, Reason} -> erlang:atom_to_binary(Reason); {path_exists, _} -> <<"unexpected transport error"/utf8>>; {invalid_host, _} -> <<"unexpected transport error"/utf8>> end. -file("src/fcgi.gleam", 529). -spec cleanup_socket(socket(), address()) -> nil. cleanup_socket(Socket, Address) -> fcgi_ffi:close_socket(Socket), case Address of {path_address, Path} -> fcgi_ffi:delete_path(Path); {tcp_address, _, _} -> nil end. -file("src/fcgi.gleam", 537). -spec describe_address(address()) -> binary(). describe_address(Address) -> case Address of {path_address, Path} -> <<"unix:"/utf8, Path/binary>>; {tcp_address, Host, Port} -> Host@1 = case gleam_stdlib:contains_string(Host, <<":"/utf8>>) of true -> <<<<"["/utf8, Host/binary>>/binary, "]"/utf8>>; false -> Host end, <<<>/binary, (erlang:integer_to_binary(Port))/binary>> end. -file("src/fcgi.gleam", 510). -spec start_supervisor( gleam@otp@static_supervisor:builder(), socket(), address() ) -> {ok, gleam@otp@actor:started(gleam@otp@static_supervisor:supervisor())} | {error, start_error()}. start_supervisor(Builder, Socket, Address) -> case gleam@otp@static_supervisor:start(Builder) of {error, Error} -> cleanup_socket(Socket, Address), Reason@1 = case Error of init_timeout -> <<"supervisor init timeout"/utf8>>; {init_failed, Reason} -> Reason; {init_exited, _} -> <<"supervisor init exited"/utf8>> end, {error, {listener_error, Reason@1}}; {ok, Started} -> {ok, Started} end. -file("src/fcgi.gleam", 603). -spec start_connection( socket(), gleam@otp@factory_supervisor:supervisor(connection(), gleam@erlang@process:subject(nil)), integer(), integer(), fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())) ) -> nil. start_connection( Client_socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ) -> Connection = {connection, Client_socket, Max_body_size, Body_read_timeout_ms, Handler}, case gleam@otp@factory_supervisor:start_child(Factory, Connection) of {error, _} -> fcgi_ffi:close_socket(Client_socket); {ok, Started} -> case fcgi_ffi:controlling_process( Client_socket, erlang:element(2, Started) ) of {error, _} -> fcgi_ffi:close_socket(Client_socket), gleam@erlang@process:send_exit(erlang:element(2, Started)); {ok, nil} -> gleam@erlang@process:send(erlang:element(3, Started), nil) end end. -file("src/fcgi.gleam", 557). -spec accept_loop( socket(), gleam@otp@factory_supervisor:supervisor(connection(), gleam@erlang@process:subject(nil)), integer(), integer(), fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())) ) -> nil. accept_loop( Listen_socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ) -> case gen_tcp:accept(Listen_socket) of {error, Reason} -> case erlang:atom_to_binary(Reason) of <<"closed"/utf8>> -> logging:log( info, <<"acceptor stopping: listener closed"/utf8>> ); Name -> logging:log( warning, <<<<"accept failed: "/utf8, Name/binary>>/binary, ", retrying"/utf8>> ), gleam_erlang_ffi:sleep(100), accept_loop( Listen_socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ) end; {ok, Client_socket} -> start_connection( Client_socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ), accept_loop( Listen_socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ) end. -file("src/fcgi.gleam", 486). -spec acceptor_supervised( socket(), gleam@erlang@process:name(gleam@otp@factory_supervisor:message(connection(), gleam@erlang@process:subject(nil))), integer(), integer(), fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())) ) -> gleam@otp@supervision:child_specification(nil). acceptor_supervised( Socket, Factory_name, Max_body_size, Body_read_timeout_ms, Handler ) -> _pipe = gleam@otp@supervision:worker( fun() -> Factory = gleam@otp@factory_supervisor:get_by_name(Factory_name), Pid = proc_lib:spawn_link( fun() -> accept_loop( Socket, Factory, Max_body_size, Body_read_timeout_ms, Handler ) end ), {ok, {started, Pid, nil}} end ), gleam@otp@supervision:restart(_pipe, transient). -file("src/fcgi.gleam", 1205). -spec collect_body_events_loop( list(fcgi@internal@responder:event()), bitstring(), boolean(), boolean() ) -> {bitstring(), boolean(), boolean()}. collect_body_events_loop(Events, Data, Ended, Overflowed) -> case Events of [] -> {Data, Ended, Overflowed}; [{body_chunk, Chunk} | Rest] -> collect_body_events_loop( Rest, <>, Ended, Overflowed ); [body_end | Rest@1] -> collect_body_events_loop(Rest@1, Data, true, Overflowed); [body_too_large | Rest@2] -> collect_body_events_loop(Rest@2, Data, true, true); [{request_ready, _, _, _} | Rest@3] -> collect_body_events_loop(Rest@3, Data, Ended, Overflowed) end. -file("src/fcgi.gleam", 1199). -spec collect_body_events(list(fcgi@internal@responder:event())) -> {bitstring(), boolean(), boolean()}. collect_body_events(Events) -> collect_body_events_loop(Events, <<>>, false, false). -file("src/fcgi.gleam", 711). -spec step_and_flush(connection(), fcgi@internal@responder:state(), bitstring()) -> fcgi@internal@responder:outcome(). step_and_flush(Connection, State, Bytes) -> Outcome = fcgi@internal@responder:step( State, Bytes, erlang:element(3, Connection) ), _ = send_if_nonempty( erlang:element(2, Connection), erlang:element(3, Outcome) ), Outcome. -file("src/fcgi.gleam", 1276). -spec step_body(body_snapshot(), bitstring(), connection()) -> {bitstring(), body_snapshot()}. step_body(Prior, Bytes, Connection) -> Outcome = step_and_flush(Connection, erlang:element(2, Prior), Bytes), {Data, Ended, Overflowed} = collect_body_events(erlang:element(4, Outcome)), Aborted = case erlang:element(5, Outcome) of close_connection -> true; wait_for_more -> false end, Snapshot = {body_snapshot, erlang:element(2, Outcome), erlang:element(3, Prior) orelse Ended, erlang:element(4, Prior) orelse Overflowed, erlang:element(5, Prior) orelse Aborted}, {Data, Snapshot}. -file("src/fcgi.gleam", 1298). -spec recv_connection(connection()) -> {ok, bitstring()} | {error, read_error()}. recv_connection(Connection) -> case fcgi_ffi:recv( erlang:element(2, Connection), 0, erlang:element(4, Connection) ) of {error, {posix, Reason}} -> case erlang:atom_to_binary(Reason) of <<"timeout"/utf8>> -> {error, read_timeout}; _ -> {error, client_disconnected} end; {error, _} -> {error, client_disconnected}; {ok, <<>>} -> {error, client_disconnected}; {ok, More} -> {ok, More} end. -file("src/fcgi.gleam", 1311). -spec drain_body_loop(body_snapshot(), connection()) -> {ok, fcgi@internal@responder:state()} | {error, nil}. drain_body_loop(Snap, Connection) -> gleam@bool:guard( erlang:element(4, Snap) orelse erlang:element(5, Snap), {error, nil}, fun() -> gleam@bool:guard( erlang:element(3, Snap), {ok, erlang:element(2, Snap)}, fun() -> case recv_connection(Connection) of {error, _} -> {error, nil}; {ok, More} -> {_, Next} = step_body(Snap, More, Connection), drain_body_loop(Next, Connection) end end ) end ). -file("src/fcgi.gleam", 1131). -spec send_padding(socket(), integer()) -> {ok, nil} | {error, nil}. send_padding(Socket, Padding_length) -> gleam@bool:guard( Padding_length =:= 0, {ok, nil}, fun() -> fcgi_ffi:send(Socket, <<0:(erlang:max(0, (Padding_length * 8)))>>) end ). -file("src/fcgi.gleam", 1116). -spec drain_sendfile_loop(socket(), handle(), integer(), integer()) -> {ok, nil} | {error, nil}. drain_sendfile_loop(Socket, Handle, Offset, Remaining) -> gleam@bool:guard( Remaining =< 0, {ok, nil}, fun() -> case fcgi_ffi:sendfile(Handle, Socket, Offset, Remaining) of {error, _} -> {error, nil}; {ok, 0} -> {error, nil}; {ok, Sent} -> drain_sendfile_loop( Socket, Handle, Offset + Sent, Remaining - Sent ) end end ). -file("src/fcgi.gleam", 1092). -spec send_via_sendfile(socket(), integer(), handle(), integer(), integer()) -> {ok, nil} | {error, nil}. send_via_sendfile(Socket, Request_id, Handle, Offset, Remaining) -> gleam@bool:guard( Remaining =< 0, {ok, nil}, fun() -> Chunk_size = gleam@int:min(Remaining, 65535), {Header, Padding_length} = fcgi@internal@protocol:encode_stdout_record_header( Request_id, Chunk_size ), gleam@result:'try'( fcgi_ffi:send(Socket, Header), fun(_) -> gleam@result:'try'( drain_sendfile_loop(Socket, Handle, Offset, Chunk_size), fun(_) -> gleam@result:'try'( send_padding(Socket, Padding_length), fun(_) -> send_via_sendfile( Socket, Request_id, Handle, Offset + Chunk_size, Remaining - Chunk_size ) end ) end ) end ) end ). -file("src/fcgi.gleam", 1048). -spec send_response( socket(), integer(), gleam@http@response:response(response_data()) ) -> {ok, nil} | {error, nil}. send_response(Socket, Request_id, Response) -> Headers = fcgi@internal@responder:encode_response_headers( Request_id, Response ), End_records = fcgi@internal@responder:encode_response_end_records( Request_id ), case erlang:element(4, Response) of {bytes, Data} -> Body = fcgi@internal@responder:encode_stdout_chunk(Request_id, Data), Combined = gleam_stdlib:identity([Headers, Body, End_records]), send_if_nonempty(Socket, Combined); {file, Handle, Offset, Length} -> exception_ffi:defer( fun() -> fcgi_ffi:close_file(Handle) end, fun() -> gleam@result:'try'( fcgi_ffi:send(Socket, Headers), fun(_) -> gleam@result:'try'( send_via_sendfile( Socket, Request_id, Handle, Offset, Length ), fun(_) -> fcgi_ffi:send(Socket, End_records) end ) end ) end ); {stream, Producer} -> gleam@result:'try'( fcgi_ffi:send(Socket, Headers), fun(_) -> Sender = {stream_sender, Socket, Request_id}, case exception_ffi:rescue(fun() -> Producer(Sender) end) of {ok, _} -> nil; {error, Exception} -> logging:log( error, <<"stream producer crashed: "/utf8, (gleam@string:inspect(Exception))/binary>> ) end, fcgi_ffi:send(Socket, End_records) end ) end. -file("src/fcgi.gleam", 1041). -spec dispose_response(gleam@http@response:response(response_data())) -> nil. dispose_response(Response) -> case erlang:element(4, Response) of {file, Handle, _, _} -> fcgi_ffi:close_file(Handle); {bytes, _} -> nil; {stream, _} -> nil end. -file("src/fcgi.gleam", 826). -spec error_response(integer(), binary()) -> gleam@http@response:response(response_data()). error_response(Status, Message) -> _pipe = gleam@http@response:new(Status), _pipe@1 = gleam@http@response:set_header( _pipe, <<"content-type"/utf8>>, <<"text/plain; charset=utf-8"/utf8>> ), gleam@http@response:set_body( _pipe@1, {bytes, gleam_stdlib:wrap_list(Message)} ). -file("src/fcgi.gleam", 1024). -spec run_user_handler( fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())), gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context() ) -> gleam@http@response:response(response_data()). run_user_handler(Handler_fn, Req, Ctx) -> case exception_ffi:rescue(fun() -> Handler_fn(Req, Ctx) end) of {ok, Resp} -> Resp; {error, Exception} -> logging:log( error, <<"handler crashed: "/utf8, (gleam@string:inspect(Exception))/binary>> ), error_response(500, <<"internal server error"/utf8>>) end. -file("src/fcgi.gleam", 1163). -spec replace_snapshot( gleam@erlang@process:subject(body_snapshot()), body_snapshot() ) -> nil. replace_snapshot(Mailbox, Snap) -> _ = gleam@erlang@process:'receive'(Mailbox, 0), gleam@erlang@process:send(Mailbox, Snap). -file("src/fcgi.gleam", 1246). -spec deliver_pending(body_context()) -> {ok, read()} | {error, read_error()}. deliver_pending(Ctx) -> Next_ctx = {body_context, erlang:element(2, Ctx), <<>>, erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx)}, {ok, {chunk, erlang:element(3, Ctx), fun() -> next_read(Next_ctx) end}}. -file("src/fcgi.gleam", 1251). -spec pull_more(body_context()) -> {ok, read()} | {error, read_error()}. pull_more(Ctx) -> Prior = {body_snapshot, erlang:element(2, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), false}, gleam@result:'try'( recv_connection(erlang:element(6, Ctx)), fun(More) -> {New_data, Next} = step_body(Prior, More, erlang:element(6, Ctx)), replace_snapshot(erlang:element(7, Ctx), Next), gleam@bool:guard( erlang:element(5, Next), {error, request_aborted}, fun() -> next_read( {body_context, erlang:element(2, Next), <<(erlang:element(3, Ctx))/bitstring, New_data/bitstring>>, erlang:element(3, Next), erlang:element(4, Next), erlang:element(6, Ctx), erlang:element(7, Ctx)} ) end ) end ). -file("src/fcgi.gleam", 1237). -spec next_read(body_context()) -> {ok, read()} | {error, read_error()}. next_read(Ctx) -> gleam@bool:guard( erlang:element(5, Ctx), {error, body_too_large}, fun() -> case {erlang:byte_size(erlang:element(3, Ctx)), erlang:element(4, Ctx)} of {0, true} -> {ok, 'end'}; {0, false} -> pull_more(Ctx); {_, _} -> deliver_pending(Ctx) end end ). -file("src/fcgi.gleam", 1229). -spec buffered_reader(bitstring(), boolean()) -> fun(() -> {ok, read()} | {error, read_error()}). buffered_reader(Data, Overflowed) -> case {Overflowed, erlang:byte_size(Data)} of {true, _} -> fun() -> {error, body_too_large} end; {false, 0} -> fun() -> {ok, 'end'} end; {false, _} -> fun() -> {ok, {chunk, Data, fun() -> {ok, 'end'} end}} end end. -file("src/fcgi.gleam", 1171). -spec build_body_reader( connection(), fcgi@internal@responder:state(), list(fcgi@internal@responder:event()) ) -> {fun(() -> {ok, read()} | {error, read_error()}), gleam@option:option(gleam@erlang@process:subject(body_snapshot()))}. build_body_reader(Connection, State, Events) -> {Data, Ended, Overflowed} = collect_body_events(Events), case Ended orelse Overflowed of true -> {buffered_reader(Data, Overflowed), none}; false -> Snapshot = gleam@erlang@process:new_subject(), gleam@erlang@process:send( Snapshot, {body_snapshot, State, false, false, false} ), Ctx = {body_context, State, Data, false, false, Connection, Snapshot}, {fun() -> next_read(Ctx) end, {some, Snapshot}} end. -file("src/fcgi.gleam", 1017). -spec request_error_message(request_error()) -> binary(). request_error_message(Error) -> case Error of missing_method -> <<"missing REQUEST_METHOD parameter"/utf8>>; {invalid_method, Method} -> <<"invalid REQUEST_METHOD: "/utf8, Method/binary>> end. -file("src/fcgi.gleam", 1002). -spec resolve_unbracketed_host(binary(), gleam@option:option(integer())) -> {binary(), gleam@option:option(integer())}. resolve_unbracketed_host(Raw, Server_port) -> case gleam@string:split_once(Raw, <<":"/utf8>>) of {ok, {Host, Port_str}} when Host =/= <<""/utf8>> -> Port = begin _pipe = gleam_stdlib:parse_int(Port_str), gleam@option:from_result(_pipe) end, {Host, gleam@option:'or'(Port, Server_port)}; _ -> {Raw, Server_port} end. -file("src/fcgi.gleam", 988). -spec resolve_bracketed_host(binary(), gleam@option:option(integer())) -> {binary(), gleam@option:option(integer())}. resolve_bracketed_host(Raw, Server_port) -> case gleam@string:split_once(gleam@string:drop_start(Raw, 1), <<"]"/utf8>>) of {ok, {Host, <<""/utf8>>}} when Host =/= <<""/utf8>> -> {Host, Server_port}; {ok, {Host@1, <<":"/utf8, Port_str/binary>>}} when Host@1 =/= <<""/utf8>> -> Port = begin _pipe = gleam_stdlib:parse_int(Port_str), gleam@option:from_result(_pipe) end, {Host@1, gleam@option:'or'(Port, Server_port)}; _ -> {Raw, Server_port} end. -file("src/fcgi.gleam", 976). -spec resolve_http_host(binary(), binary(), gleam@option:option(integer())) -> {binary(), gleam@option:option(integer())}. resolve_http_host(Raw, Server_name, Server_port) -> gleam@bool:guard( Raw =:= <<""/utf8>>, {Server_name, Server_port}, fun() -> case gleam_stdlib:string_starts_with(Raw, <<"["/utf8>>) of true -> resolve_bracketed_host(Raw, Server_port); false -> resolve_unbracketed_host(Raw, Server_port) end end ). -file("src/fcgi.gleam", 969). -spec https_scheme_from_value(binary()) -> gleam@http:scheme(). https_scheme_from_value(Value) -> case string:lowercase(Value) of <<""/utf8>> -> http; <<"off"/utf8>> -> http; _ -> https end. -file("src/fcgi.gleam", 931). -spec finalize_request(partial_request(), GTO) -> {ok, {gleam@http@request:request(GTO), context()}} | {error, request_error()}. finalize_request(Acc, Body) -> gleam@result:'try'( gleam@option:to_result(erlang:element(2, Acc), missing_method), fun(Method_str) -> gleam@result:'try'( begin _pipe = gleam@http:parse_method(Method_str), gleam@result:replace_error( _pipe, {invalid_method, Method_str} ) end, fun(Method) -> Scheme = case erlang:element(3, Acc) of {some, Value} -> https_scheme_from_value(Value); none -> http end, Server_name = gleam@option:unwrap( erlang:element(4, Acc), <<"localhost"/utf8>> ), Server_port = gleam@option:then( erlang:element(5, Acc), fun(Raw) -> _pipe@1 = gleam_stdlib:parse_int(Raw), gleam@option:from_result(_pipe@1) end ), {Host, Port} = case erlang:element(6, Acc) of {some, Raw@1} -> resolve_http_host(Raw@1, Server_name, Server_port); none -> {Server_name, Server_port} end, Path = gleam@option:unwrap( erlang:element(7, Acc), <<"/"/utf8>> ), Req = {request, Method, erlang:element(9, Acc), Body, Scheme, Host, Port, Path, erlang:element(8, Acc)}, {ok, {Req, erlang:element(10, Acc)}} end ) end ). -file("src/fcgi.gleam", 916). -spec apply_context_var(context(), binary(), binary()) -> context(). apply_context_var(Ctx, Key, Value) -> case Key of <<"REMOTE_ADDR"/utf8>> -> {context, {some, Value}, erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"REMOTE_PORT"/utf8>> -> {context, erlang:element(2, Ctx), begin _pipe = gleam_stdlib:parse_int(Value), gleam@option:from_result(_pipe) end, erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"REMOTE_HOST"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), {some, Value}, erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"REMOTE_USER"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), {some, Value}, erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"AUTH_TYPE"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), {some, Value}, erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"SCRIPT_NAME"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), {some, Value}, erlang:element(8, Ctx), erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"SERVER_PROTOCOL"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), {some, Value}, erlang:element(9, Ctx), erlang:element(10, Ctx)}; <<"SERVER_SOFTWARE"/utf8>> -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), {some, Value}, erlang:element(10, Ctx)}; _ -> {context, erlang:element(2, Ctx), erlang:element(3, Ctx), erlang:element(4, Ctx), erlang:element(5, Ctx), erlang:element(6, Ctx), erlang:element(7, Ctx), erlang:element(8, Ctx), erlang:element(9, Ctx), gleam@dict:insert(erlang:element(10, Ctx), Key, Value)} end. -file("src/fcgi.gleam", 886). -spec apply_pair(partial_request(), binary(), binary()) -> partial_request(). apply_pair(Acc, Key, Value) -> case Key of <<"REQUEST_METHOD"/utf8>> -> {partial_request, {some, Value}, erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), erlang:element(9, Acc), erlang:element(10, Acc)}; <<"HTTPS"/utf8>> -> {partial_request, erlang:element(2, Acc), {some, Value}, erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), erlang:element(9, Acc), erlang:element(10, Acc)}; <<"SERVER_NAME"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), {some, Value}, erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), erlang:element(9, Acc), erlang:element(10, Acc)}; <<"SERVER_PORT"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), {some, Value}, erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), erlang:element(9, Acc), erlang:element(10, Acc)}; <<"HTTP_HOST"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), {some, Value}, erlang:element(7, Acc), erlang:element(8, Acc), [{<<"host"/utf8>>, Value} | erlang:element(9, Acc)], erlang:element(10, Acc)}; <<"PATH_INFO"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), {some, Value}, erlang:element(8, Acc), erlang:element(9, Acc), erlang:element(10, Acc)}; <<"QUERY_STRING"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), {some, Value}, erlang:element(9, Acc), erlang:element(10, Acc)}; <<"CONTENT_TYPE"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), [{<<"content-type"/utf8>>, Value} | erlang:element(9, Acc)], erlang:element(10, Acc)}; <<"CONTENT_LENGTH"/utf8>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), [{<<"content-length"/utf8>>, Value} | erlang:element(9, Acc)], erlang:element(10, Acc)}; <<"HTTP_"/utf8, Name/binary>> -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), [{gleam@string:replace( string:lowercase(Name), <<"_"/utf8>>, <<"-"/utf8>> ), Value} | erlang:element(9, Acc)], erlang:element(10, Acc)}; _ -> {partial_request, erlang:element(2, Acc), erlang:element(3, Acc), erlang:element(4, Acc), erlang:element(5, Acc), erlang:element(6, Acc), erlang:element(7, Acc), erlang:element(8, Acc), erlang:element(9, Acc), apply_context_var(erlang:element(10, Acc), Key, Value)} end. -file("src/fcgi.gleam", 853). ?DOC(false). -spec to_http_request(list({binary(), binary()}), GTK) -> {ok, {gleam@http@request:request(GTK), context()}} | {error, request_error()}. to_http_request(Pairs, Body) -> Initial = {partial_request, none, none, none, none, none, none, none, [], {context, none, none, none, none, none, none, none, none, maps:new()}}, _pipe = gleam@list:fold( Pairs, Initial, fun(Acc, Pair) -> {Key, Value} = Pair, apply_pair(Acc, Key, Value) end ), finalize_request(_pipe, Body). -file("src/fcgi.gleam", 817). -spec parse_params(bitstring()) -> {ok, {gleam@http@request:request(nil), context()}} | {error, binary()}. parse_params(Params) -> case fcgi@internal@protocol:parse_name_value_pairs(Params) of {error, _} -> {error, <<"malformed FastCGI parameters"/utf8>>}; {ok, Pairs} -> _pipe = to_http_request(Pairs, nil), gleam@result:map_error(_pipe, fun request_error_message/1) end. -file("src/fcgi.gleam", 765). -spec serve_request( connection(), fcgi@internal@responder:state(), integer(), bitstring(), list(fcgi@internal@responder:event()) ) -> {ok, after_response()} | {error, nil}. serve_request(Connection, State, Request_id, Params, Events) -> case parse_params(Params) of {error, Message} -> logging:log(warning, <<"rejecting request: "/utf8, Message/binary>>), Response = error_response(400, Message), _pipe = send_response( erlang:element(2, Connection), Request_id, Response ), gleam@result:replace(_pipe, continue); {ok, {Req, Cgi}} -> Overflowed = gleam@list:contains(Events, body_too_large), {Reader, Snapshot} = build_body_reader(Connection, State, Events), Req@1 = gleam@http@request:set_body(Req, Reader), Response@1 = run_user_handler( erlang:element(5, Connection), Req@1, Cgi ), Latest = begin _pipe@1 = Snapshot, _pipe@2 = gleam@option:to_result(_pipe@1, nil), gleam@result:'try'( _pipe@2, fun(_capture) -> gleam@erlang@process:'receive'(_capture, 0) end ) end, Aborted = begin _pipe@3 = Latest, _pipe@4 = gleam@result:map( _pipe@3, fun(Snap) -> erlang:element(5, Snap) end ), gleam@result:unwrap(_pipe@4, false) end, gleam@bool:lazy_guard( Aborted, fun() -> dispose_response(Response@1), {error, nil} end, fun() -> gleam@result:'try'( send_response( erlang:element(2, Connection), Request_id, Response@1 ), fun(_) -> gleam@bool:lazy_guard( Overflowed, fun() -> logging:log( warning, <<<<"closing connection: body exceeded max_body_size of "/utf8, (erlang:integer_to_binary( erlang:element( 3, Connection ) ))/binary>>/binary, " bytes"/utf8>> ), {error, nil} end, fun() -> case Latest of {error, _} -> {ok, continue}; {ok, Snap@1} -> {ok, {drain_body, Snap@1}} end end ) end ) end ) end. -file("src/fcgi.gleam", 754). -spec wait_for_bytes(connection(), fcgi@internal@responder:state()) -> next_request(). wait_for_bytes(Connection, State) -> case fcgi_ffi:recv( erlang:element(2, Connection), 0, erlang:element(4, Connection) ) of {error, _} -> closed; {ok, <<>>} -> closed; {ok, More} -> await_request(Connection, State, More) end. -file("src/fcgi.gleam", 722). -spec await_request(connection(), fcgi@internal@responder:state(), bitstring()) -> next_request(). await_request(Connection, State, Pending) -> Buffered = case State of {idle, Buf} -> Buf; {receiving, Recv} -> erlang:element(2, Recv) end, gleam@bool:lazy_guard( (erlang:byte_size(Pending) =:= 0) andalso (erlang:byte_size(Buffered) =:= 0), fun() -> wait_for_bytes(Connection, State) end, fun() -> Outcome = step_and_flush(Connection, State, Pending), case erlang:element(4, Outcome) of [{request_ready, Request_id, Params, Keep_conn} | Rest] -> {ready, erlang:element(2, Outcome), Request_id, Params, Rest, Keep_conn}; _ -> case erlang:element(5, Outcome) of close_connection -> closed; wait_for_more -> wait_for_bytes( Connection, erlang:element(2, Outcome) ) end end end ). -file("src/fcgi.gleam", 688). -spec run_connection_loop(connection(), fcgi@internal@responder:state()) -> nil. run_connection_loop(Connection, State) -> case await_request(Connection, State, <<>>) of closed -> nil; {ready, State@1, Request_id, Params, Remaining_events, Keep_conn} -> case serve_request( Connection, State@1, Request_id, Params, Remaining_events ) of {error, _} -> nil; {ok, After_response} -> case {Keep_conn, After_response} of {false, continue} -> nil; {false, {drain_body, _}} -> nil; {true, continue} -> run_connection_loop(Connection, State@1); {true, {drain_body, Snap}} -> case drain_body_loop(Snap, Connection) of {error, _} -> nil; {ok, Next_state} -> run_connection_loop(Connection, Next_state) end end end end. -file("src/fcgi.gleam", 630). -spec start_connection_process(connection()) -> {ok, gleam@otp@actor:started(gleam@erlang@process:subject(nil))} | {error, gleam@otp@actor:start_error()}. start_connection_process(Connection) -> Report_back = gleam@erlang@process:new_subject(), Pid = proc_lib:spawn_link( fun() -> Go = gleam@erlang@process:new_subject(), gleam@erlang@process:send(Report_back, Go), case gleam@erlang@process:'receive'(Go, 5000) of {ok, nil} -> run_connection_loop(Connection, {idle, <<>>}), fcgi_ffi:close_socket(erlang:element(2, Connection)); {error, nil} -> fcgi_ffi:close_socket(erlang:element(2, Connection)) end end ), case gleam@erlang@process:'receive'(Report_back, 5000) of {ok, Go@1} -> {ok, {started, Pid, Go@1}}; {error, nil} -> gleam@erlang@process:send_exit(Pid), {error, {init_failed, <<"connection process did not report back"/utf8>>}} end. -file("src/fcgi.gleam", 477). -spec connection_factory_supervised( gleam@erlang@process:name(gleam@otp@factory_supervisor:message(connection(), gleam@erlang@process:subject(nil))) ) -> gleam@otp@supervision:child_specification(gleam@otp@factory_supervisor:supervisor(connection(), gleam@erlang@process:subject(nil))). connection_factory_supervised(Name) -> _pipe = gleam@otp@factory_supervisor:worker_child( fun start_connection_process/1 ), _pipe@1 = gleam@otp@factory_supervisor:named(_pipe, Name), _pipe@2 = gleam@otp@factory_supervisor:supervised(_pipe@1), gleam@otp@supervision:restart(_pipe@2, transient). -file("src/fcgi.gleam", 459). -spec start_path_janitor(binary()) -> {ok, gleam@otp@actor:started(nil)} | {error, gleam@otp@actor:start_error()}. start_path_janitor(Path) -> _pipe@4 = gleam@otp@actor:new_with_initialiser( 1000, fun(_) -> gleam_erlang_ffi:trap_exits(true), Selector = begin _pipe = gleam_erlang_ffi:new_selector(), gleam@erlang@process:select_trapped_exits( _pipe, fun(_) -> nil end ) end, _pipe@1 = gleam@otp@actor:initialised(Path), _pipe@2 = gleam@otp@actor:selecting(_pipe@1, Selector), _pipe@3 = gleam@otp@actor:returning(_pipe@2, nil), {ok, _pipe@3} end ), _pipe@5 = gleam@otp@actor:on_message( _pipe@4, fun(Path@1, _) -> fcgi_ffi:delete_path(Path@1), gleam@otp@actor:stop() end ), gleam@otp@actor:start(_pipe@5). -file("src/fcgi.gleam", 452). -spec path_janitor_supervised(binary()) -> gleam@otp@supervision:child_specification(nil). path_janitor_supervised(Path) -> _pipe = gleam@otp@supervision:worker(fun() -> start_path_janitor(Path) end), gleam@otp@supervision:restart(_pipe, transient). -file("src/fcgi.gleam", 426). -spec build_supervisor( address(), socket(), gleam@erlang@process:name(gleam@otp@factory_supervisor:message(connection(), gleam@erlang@process:subject(nil))), integer(), integer(), fun((gleam@http@request:request(fun(() -> {ok, read()} | {error, read_error()})), context()) -> gleam@http@response:response(response_data())) ) -> gleam@otp@static_supervisor:builder(). build_supervisor( Address, Socket, Factory_name, Max_body_size, Body_read_timeout_ms, Handler ) -> Supervisor = gleam@otp@static_supervisor:new(rest_for_one), Supervisor@1 = case Address of {path_address, Path} -> gleam@otp@static_supervisor:add( Supervisor, path_janitor_supervised(Path) ); {tcp_address, _, _} -> Supervisor end, _pipe = Supervisor@1, _pipe@1 = gleam@otp@static_supervisor:add( _pipe, connection_factory_supervised(Factory_name) ), gleam@otp@static_supervisor:add( _pipe@1, acceptor_supervised( Socket, Factory_name, Max_body_size, Body_read_timeout_ms, Handler ) ). -file("src/fcgi.gleam", 407). -spec resolve_bound_port(socket(), address()) -> {ok, gleam@option:option(integer())} | {error, start_error()}. resolve_bound_port(Socket, Address) -> case Address of {path_address, _} -> {ok, none}; {tcp_address, _, _} -> case fcgi_ffi:socket_port(Socket) of {ok, Port} -> {ok, {some, Port}}; {error, Error} -> fcgi_ffi:close_socket(Socket), {error, {listener_error, <<"bound port lookup failed: "/utf8, (describe_transport_error(Error))/binary>>}} end end. -file("src/fcgi.gleam", 382). -spec listen_on_address(address()) -> {ok, socket()} | {error, start_error()}. listen_on_address(Address) -> case Address of {path_address, Path} -> _pipe = fcgi_ffi:listen_unix(Path), gleam@result:map_error(_pipe, fun(Error) -> case Error of {path_exists, Path@1} -> {listener_error, <<"socket path already exists: "/utf8, Path@1/binary>>}; _ -> {listener_error, <<"listen failed: "/utf8, (describe_transport_error(Error))/binary>>} end end); {tcp_address, Host, Port} -> _pipe@1 = fcgi_ffi:listen_tcp(Host, Port), gleam@result:map_error(_pipe@1, fun(Error@1) -> case Error@1 of {invalid_host, Host@1} -> {listener_error, <<"host must be a numeric IP literal: "/utf8, Host@1/binary>>}; _ -> {listener_error, <<"listen failed: "/utf8, (describe_transport_error(Error@1))/binary>>} end end) end. -file("src/fcgi.gleam", 312). ?DOC(" Start the server.\n"). -spec start(builder(address())) -> {ok, gleam@otp@actor:started(server())} | {error, start_error()}. start(Builder) -> gleam@bool:guard( erlang:element(4, Builder) < 0, {error, {invalid_max_body_size, erlang:element(4, Builder)}}, fun() -> gleam@bool:guard( erlang:element(5, Builder) =< 0, {error, {invalid_body_read_timeout, erlang:element(5, Builder)}}, fun() -> Address = erlang:element(3, Builder), gleam@result:'try'( listen_on_address(Address), fun(Socket) -> gleam@result:'try'( resolve_bound_port(Socket, Address), fun(Bound_port) -> Factory_name = gleam_erlang_ffi:new_name( <<"fcgi_server_factory"/utf8>> ), Supervisor = build_supervisor( Address, Socket, Factory_name, erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(2, Builder) ), gleam@result:'try'( start_supervisor( Supervisor, Socket, Address ), fun(Started) -> case fcgi_ffi:controlling_process( Socket, erlang:element(2, Started) ) of {ok, nil} -> logging:log( info, <<"fcgi listening on "/utf8, (describe_address( Address ))/binary>> ), Server = {server, erlang:element( 3, Started ), Bound_port}, {ok, {started, erlang:element( 2, Started ), Server}}; {error, Error} -> gleam@erlang@process:unlink( erlang:element( 2, Started ) ), gleam@erlang@process:send_abnormal_exit( erlang:element( 2, Started ), erlang:binary_to_atom( <<"shutdown"/utf8>> ) ), cleanup_socket( Socket, Address ), {error, {listener_error, <<"controlling_process failed: "/utf8, (describe_transport_error( Error ))/binary>>}} end end ) end ) end ) end ) end ). -file("src/fcgi.gleam", 362). ?DOC( " Build a `supervision.ChildSpecification` so the server runs under an\n" " OTP supervisor.\n" ). -spec supervised(builder(address())) -> gleam@otp@supervision:child_specification(server()). supervised(Builder) -> gleam@otp@supervision:supervisor(fun() -> _pipe = start(Builder), gleam@result:map_error( _pipe, fun(Error) -> Reason@1 = case Error of {listener_error, Reason} -> Reason; {invalid_max_body_size, Bytes} -> <<"max_body_size must be non-negative; got "/utf8, (erlang:integer_to_binary(Bytes))/binary>>; {invalid_body_read_timeout, Milliseconds} -> <<"body_read_timeout must be positive; got "/utf8, (erlang:integer_to_binary(Milliseconds))/binary>> end, {init_failed, Reason@1} end ) end).