-module(cgi). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([load_request/0, read_body/1, with_request_body/2, send_response/1, handle_request/1]). -spec load_request() -> gleam@http@request:request(bitstring()). load_request() -> Env = envoy_ffi:all(), Method = begin _pipe = Env, _pipe@1 = gleam@dict:get(_pipe, <<"REQUEST_METHOD"/utf8>>), _pipe@2 = gleam@result:'try'(_pipe@1, fun gleam@http:parse_method/1), gleam@result:unwrap(_pipe@2, get) end, Port = begin _pipe@3 = Env, _pipe@4 = gleam@dict:get(_pipe@3, <<"SERVER_PORT"/utf8>>), _pipe@5 = gleam@result:'try'(_pipe@4, fun gleam@int:parse/1), gleam@option:from_result(_pipe@5) end, Scheme = case gleam@dict:get(Env, <<"HTTPS"/utf8>>) of {ok, _} -> https; _ -> http end, Query = gleam@option:from_result( gleam@dict:get(Env, <<"QUERY_STRING"/utf8>>) ), Host = begin _pipe@6 = gleam@dict:get(Env, <<"SERVER_NAME"/utf8>>), gleam@result:unwrap(_pipe@6, <<"localhost"/utf8>>) end, Path = begin _pipe@7 = gleam@dict:get(Env, <<"PATH_INFO"/utf8>>), gleam@result:unwrap(_pipe@7, <<"/"/utf8>>) end, Headers = begin _pipe@8 = Env, _pipe@9 = maps:to_list(_pipe@8), gleam@list:filter_map( _pipe@9, fun(Pair) -> case erlang:element(1, Pair) of <<"CONTENT_TYPE"/utf8>> -> {ok, {<<"content-type"/utf8>>, erlang:element(2, Pair)}}; <<"CONTENT_LENGTH"/utf8>> -> {ok, {<<"content-length"/utf8>>, erlang:element(2, Pair)}}; <<"HTTP_"/utf8, Name/binary>> -> {ok, {gleam@string:replace( gleam@string:lowercase(Name), <<"_"/utf8>>, <<"-"/utf8>> ), erlang:element(2, Pair)}}; _ -> {error, nil} end end ) end, Body = <<>>, {request, Method, Headers, Body, Scheme, Host, Port, Path, Query}. -spec request_content_length(gleam@http@request:request(bitstring())) -> integer(). request_content_length(Request) -> _pipe = Request, _pipe@1 = gleam@http@request:get_header(_pipe, <<"content-length"/utf8>>), _pipe@2 = gleam@result:'try'(_pipe@1, fun gleam@int:parse/1), gleam@result:unwrap(_pipe@2, 0). -spec read_body(gleam@http@request:request(bitstring())) -> gleam@http@request:request(bitstring()). read_body(Request) -> Body = cgi_ffi:read_body_sync(request_content_length(Request)), erlang:setelement(4, Request, Body). -spec read_body_async(integer(), fun((bitstring()) -> any())) -> nil. read_body_async(Length, Handle) -> Body = cgi_ffi:read_body_sync(Length), Handle(Body), nil. -spec with_request_body( gleam@http@request:request(bitstring()), fun((gleam@http@request:request(bitstring())) -> any()) ) -> nil. with_request_body(Request, Handle) -> read_body_async( request_content_length(Request), fun(Body) -> Handle(erlang:setelement(4, Request, Body)) end ). -spec status_phrase(integer()) -> binary(). status_phrase(Status) -> case Status of 100 -> <<"Continue"/utf8>>; 101 -> <<"Switching Protocols"/utf8>>; 102 -> <<"Processing"/utf8>>; 103 -> <<"Early Hints"/utf8>>; 200 -> <<"OK"/utf8>>; 201 -> <<"Created"/utf8>>; 202 -> <<"Accepted"/utf8>>; 203 -> <<"Non-Authoritative Information"/utf8>>; 204 -> <<"No Content"/utf8>>; 205 -> <<"Reset Content"/utf8>>; 206 -> <<"Partial Content"/utf8>>; 207 -> <<"Multi-Status"/utf8>>; 208 -> <<"Already Reported"/utf8>>; 226 -> <<"IM Used"/utf8>>; 300 -> <<"Multiple Choices"/utf8>>; 301 -> <<"Moved Permanently"/utf8>>; 302 -> <<"Found"/utf8>>; 303 -> <<"See Other"/utf8>>; 304 -> <<"Not Modified"/utf8>>; 305 -> <<"Use Proxy"/utf8>>; 306 -> <<"Switch Proxy"/utf8>>; 307 -> <<"Temporary Redirect"/utf8>>; 308 -> <<"Permanent Redirect"/utf8>>; 400 -> <<"Bad Request"/utf8>>; 401 -> <<"Unauthorized"/utf8>>; 402 -> <<"Payment Required"/utf8>>; 403 -> <<"Forbidden"/utf8>>; 404 -> <<"Not Found"/utf8>>; 405 -> <<"Method Not Allowed"/utf8>>; 406 -> <<"Not Acceptable"/utf8>>; 407 -> <<"Proxy Authentication Required"/utf8>>; 408 -> <<"Request Timeout"/utf8>>; 409 -> <<"Conflict"/utf8>>; 410 -> <<"Gone"/utf8>>; 411 -> <<"Length Required"/utf8>>; 412 -> <<"Precondition Failed"/utf8>>; 413 -> <<"Request Entity Too Large"/utf8>>; 414 -> <<"Request-URI Too Long"/utf8>>; 415 -> <<"Unsupported Media Type"/utf8>>; 416 -> <<"Requested Range Not Satisfiable"/utf8>>; 417 -> <<"Expectation Failed"/utf8>>; 418 -> <<"I'm a teapot"/utf8>>; 421 -> <<"Misdirected Request"/utf8>>; 422 -> <<"Unprocessable Entity"/utf8>>; 423 -> <<"Locked"/utf8>>; 424 -> <<"Failed Dependency"/utf8>>; 425 -> <<"Too Early"/utf8>>; 426 -> <<"Upgrade Required"/utf8>>; 428 -> <<"Precondition Required"/utf8>>; 429 -> <<"Too Many Requests"/utf8>>; 431 -> <<"Request Header Fields Too Large"/utf8>>; 451 -> <<"Unavailable For Legal Reasons"/utf8>>; 500 -> <<"Internal Server Error"/utf8>>; 501 -> <<"Not Implemented"/utf8>>; 502 -> <<"Bad Gateway"/utf8>>; 503 -> <<"Service Unavailable"/utf8>>; 504 -> <<"Gateway Timeout"/utf8>>; 505 -> <<"HTTP Version Not Supported"/utf8>>; 506 -> <<"Variant Also Negotiates"/utf8>>; 507 -> <<"Insufficient Storage"/utf8>>; 508 -> <<"Loop Detected"/utf8>>; 510 -> <<"Not Extended"/utf8>>; 511 -> <<"Network Authentication Required"/utf8>>; _ -> <<""/utf8>> end. -spec send_response(gleam@http@response:response(binary())) -> nil. send_response(Response) -> S = erlang:element(2, Response), gleam@io:println( <<<<<<"status: "/utf8, (gleam@int:to_string(S))/binary>>/binary, " "/utf8>>/binary, (status_phrase(S))/binary>> ), L = erlang:byte_size(erlang:element(4, Response)), gleam@io:println( <<"content-length: "/utf8, (gleam@int:to_string(L))/binary>> ), gleam@list:each( erlang:element(3, Response), fun(Header) -> gleam@io:println( <<<<(erlang:element(1, Header))/binary, ": "/utf8>>/binary, (erlang:element(2, Header))/binary>> ) end ), gleam@io:println(<<""/utf8>>), gleam@io:println(erlang:element(4, Response)). -spec handle_request( fun((gleam@http@request:request(bitstring())) -> gleam@http@response:response(binary())) ) -> nil. handle_request(F) -> with_request_body(load_request(), fun(Request) -> _pipe = F(Request), send_response(_pipe) end).