-module(ewe). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/ewe.gleam"). -export([ip_address_to_string/1, get_client_info/1, get_server_info/1, new/1, bind/2, bind_all/1, with_port/2, with_random_port/1, with_ipv6/1, with_tls/3, with_name/2, on_start/2, on_crash/2, start/1, supervised/1, read_body/2, with_read_body/3, json/2, text/2, bytes/2]). -export_type([ip_address/0, server_info/0, builder/1, body_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. -type ip_address() :: {ip_v4, integer(), integer(), integer(), integer()} | {ip_v6, integer(), integer(), integer(), integer(), integer(), integer(), integer(), integer()}. -type server_info() :: {server_info, gleam@http:scheme(), ip_address(), integer()}. -opaque builder(JMZ) :: {builder, fun((gleam@http@request:request(JMZ)) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree())), integer(), binary(), boolean(), gleam@option:option({binary(), binary()}), fun((server_info()) -> nil), gleam@http@response:response(gleam@bytes_tree:bytes_tree()), gleam@erlang@process:name(ewe@internal@info:message(server_info()))}. -type body_error() :: body_too_large | invalid_body. -file("src/ewe.gleam", 49). -spec glisten_to_ewe_ip(glisten:ip_address()) -> ip_address(). glisten_to_ewe_ip(Ip) -> case Ip of {ip_v4, N1, N2, N3, N4} -> {ip_v4, N1, N2, N3, N4}; {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} -> {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} end. -file("src/ewe.gleam", 57). -spec glisten_options_to_ewe_ip(glisten@socket@options:ip_address()) -> ip_address(). glisten_options_to_ewe_ip(Ip) -> case Ip of {ip_v4, N1, N2, N3, N4} -> {ip_v4, N1, N2, N3, N4}; {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} -> {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} end. -file("src/ewe.gleam", 65). -spec ewe_to_glisten_ip(ip_address()) -> glisten:ip_address(). ewe_to_glisten_ip(Ip) -> case Ip of {ip_v4, N1, N2, N3, N4} -> {ip_v4, N1, N2, N3, N4}; {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} -> {ip_v6, N1@1, N2@1, N3@1, N4@1, N5, N6, N7, N8} end. -file("src/ewe.gleam", 44). ?DOC(" Converts an `IpAddress` to a string for later printing.\n"). -spec ip_address_to_string(ip_address()) -> binary(). ip_address_to_string(Address) -> _pipe = ewe_to_glisten_ip(Address), glisten:ip_address_to_string(_pipe). -file("src/ewe.gleam", 84). ?DOC(" Performs an attempt to get the client's IP address and port.\n"). -spec get_client_info(ewe@internal@http:connection()) -> {ok, {ip_address(), integer()}} | {error, nil}. get_client_info(Connection) -> _pipe = glisten@transport:peername( erlang:element(2, Connection), erlang:element(3, Connection) ), gleam@result:map( _pipe, fun(Tuple) -> {Ip, Port} = Tuple, {glisten_options_to_ewe_ip(Ip), Port} end ). -file("src/ewe.gleam", 95). ?DOC( " Retrieves server's information. Requires the same name as the one used in\n" " `ewe.with_name` and server to be started. Otherwise, will crash the program.\n" ). -spec get_server_info( gleam@erlang@process:name(ewe@internal@info:message(server_info())) ) -> {ok, server_info()} | {error, nil}. get_server_info(Name) -> ewe@internal@info:get(gleam@erlang@process:named_subject(Name)). -file("src/ewe.gleam", 140). ?DOC( " Creates new server builder with handler provided.\n" " \n" " Default configuration:\n" " - port: `8080`\n" " - interface: `127.0.0.1`\n" " - No ipv6 support\n" " - No TLS support\n" " - Default process name for server information retrieval\n" " - on_start: prints `Listening on ://:`\n" " - on_crash: empty 500 response\n" ). -spec new( fun((gleam@http@request:request(JNG)) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree())) ) -> builder(JNG). new(Handler) -> {builder, Handler, 8080, <<"127.0.0.1"/utf8>>, false, none, fun(Server) -> Address = case erlang:element(3, Server) of {ip_v6, _, _, _, _, _, _, _, _} -> <<<<"["/utf8, (ip_address_to_string(erlang:element(3, Server)))/binary>>/binary, "]"/utf8>>; {ip_v4, _, _, _, _} -> ip_address_to_string(erlang:element(3, Server)) end, Url = <<<<<<<<(gleam@http:scheme_to_string( erlang:element(2, Server) ))/binary, "://"/utf8>>/binary, Address/binary>>/binary, ":"/utf8>>/binary, (erlang:integer_to_binary(erlang:element(4, Server)))/binary>>, gleam_stdlib:println(<<"Listening on "/utf8, Url/binary>>) end, begin _pipe = gleam@http@response:new(500), gleam@http@response:set_body(_pipe, gleam@bytes_tree:new()) end, gleam_erlang_ffi:new_name(<<"ewe_server_info"/utf8>>)}. -file("src/ewe.gleam", 169). ?DOC(" Binds server to a specific interface. Crashes program if interface is invalid.\n"). -spec bind(builder(JNK), binary()) -> builder(JNK). bind(Builder, Interface) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), Interface, erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 175). ?DOC(" Binds server to all interfaces.\n"). -spec bind_all(builder(JNN)) -> builder(JNN). bind_all(Builder) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), <<"0.0.0.0"/utf8>>, erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 181). ?DOC(" Sets listening port for server.\n"). -spec with_port(builder(JNQ), integer()) -> builder(JNQ). with_port(Builder, Port) -> {builder, erlang:element(2, Builder), Port, erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 187). ?DOC(" Sets listening port for server to a random port. Useful for testing.\n"). -spec with_random_port(builder(JNT)) -> builder(JNT). with_random_port(Builder) -> {builder, erlang:element(2, Builder), 0, erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 193). ?DOC(" Enables IPv6 support.\n"). -spec with_ipv6(builder(JNW)) -> builder(JNW). with_ipv6(Builder) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), true, erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 199). ?DOC(" Enables TLS support, requires certificate and key file.\n"). -spec with_tls(builder(JNZ), binary(), binary()) -> builder(JNZ). with_tls(Builder, Certificate, Keyfile) -> Cert = case ewe@internal@file:open(Certificate) of {ok, _} -> Certificate; {error, _} -> erlang:error(#{gleam_error => panic, message => <<"Failed to find cert file"/utf8>>, file => <>, module => <<"ewe"/utf8>>, function => <<"with_tls"/utf8>>, line => 206}) end, Key = case ewe@internal@file:open(Keyfile) of {ok, _} -> Keyfile; {error, _} -> erlang:error(#{gleam_error => panic, message => <<"Failed to find key file"/utf8>>, file => <>, module => <<"ewe"/utf8>>, function => <<"with_tls"/utf8>>, line => 211}) end, {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), {some, {Cert, Key}}, erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 220). ?DOC( " Sets a custom process name for server information retrieval, allowing to\n" " use `ewe.get_server_info` after server starts.\n" ). -spec with_name( builder(JOC), gleam@erlang@process:name(ewe@internal@info:message(server_info())) ) -> builder(JOC). with_name(Builder, Name) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), Name}. -file("src/ewe.gleam", 229). ?DOC(" Sets a custom handler that will be called after server starts.\n"). -spec on_start(builder(JOH), fun((server_info()) -> nil)) -> builder(JOH). on_start(Builder, On_start) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), On_start, erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 238). ?DOC(" Sets a custom response that will be sent when server crashes.\n"). -spec on_crash( builder(JOK), gleam@http@response:response(gleam@bytes_tree:bytes_tree()) ) -> builder(JOK). on_crash(Builder, On_crash) -> {builder, erlang:element(2, Builder), erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), On_crash, erlang:element(9, Builder)}. -file("src/ewe.gleam", 249). ?DOC(" Starts the server.\n"). -spec start(builder(ewe@internal@http:connection())) -> {ok, gleam@otp@actor:started(gleam@otp@static_supervisor:supervisor())} | {error, gleam@otp@actor:start_error()}. start(Builder) -> Name = gleam_erlang_ffi:new_name(<<"ewe_glisten"/utf8>>), Worker_name = erlang:element(9, Builder), Subject = gleam@erlang@process:named_subject(Worker_name), Info_worker = ewe@internal@info:start_worker(Worker_name), Glisten_supervisor = begin _pipe = glisten:new( fun(Conn) -> {ewe@internal@http:transform_connection(Conn), none} end, ewe@internal@handler:loop( erlang:element(2, Builder), erlang:element(8, Builder) ) ), _pipe@1 = glisten:bind(_pipe, erlang:element(4, Builder)), _pipe@2 = (fun(Glisten_builder) -> case erlang:element(5, Builder) of true -> glisten:with_ipv6(Glisten_builder); false -> Glisten_builder end end)(_pipe@1), _pipe@3 = (fun(Glisten_builder@1) -> case erlang:element(6, Builder) of {some, {Cert, Key}} -> glisten:with_tls(Glisten_builder@1, Cert, Key); none -> Glisten_builder@1 end end)(_pipe@2), _pipe@4 = glisten:start_with_listener_name( _pipe@3, erlang:element(3, Builder), Name ), gleam@result:map( _pipe@4, fun(Started) -> Scheme = case erlang:element(6, Builder) of {some, {_, _}} -> https; none -> http end, Server_info = glisten:get_server_info(Name, 10000), Ip_address = glisten_to_ewe_ip(erlang:element(3, Server_info)), Server = {server_info, Scheme, Ip_address, erlang:element(2, Server_info)}, ewe@internal@info:set(Subject, Server), Started end ) end, Glisten_child = gleam@otp@supervision:supervisor( fun() -> Glisten_supervisor end ), _pipe@5 = gleam@otp@static_supervisor:new(one_for_all), _pipe@6 = gleam@otp@static_supervisor:add(_pipe@5, Glisten_child), _pipe@7 = gleam@otp@static_supervisor:add(_pipe@6, Info_worker), gleam@otp@static_supervisor:start(_pipe@7). -file("src/ewe.gleam", 309). ?DOC(" Creates a supervisor that can be appended to a supervision tree.\n"). -spec supervised(builder(ewe@internal@http:connection())) -> gleam@otp@supervision:child_specification(gleam@otp@static_supervisor:supervisor()). supervised(Builder) -> gleam@otp@supervision:supervisor(fun() -> start(Builder) end). -file("src/ewe.gleam", 331). ?DOC( " Reads body from a request. If request body is malformed, `InvalidBody`\n" " error is returned. On success, returns a request with body converted to\n" " `BitArray`.\n" " - When `transfer-encoding` header set as `chunked`, `BodyTooLarge` error is returned if\n" " accumulated body is larger than `size_limit`.\n" " - Ensures that `content-length` is in `size_limit` scope.\n" ). -spec read_body( gleam@http@request:request(ewe@internal@http:connection()), integer() ) -> {ok, gleam@http@request:request(bitstring())} | {error, body_error()}. read_body(Req, Size_limit) -> case ewe@internal@http:read_body(Req, Size_limit) of {ok, Req@1} -> {ok, Req@1}; {error, body_too_large} -> {error, body_too_large}; {error, _} -> {error, invalid_body} end. -file("src/ewe.gleam", 346). ?DOC( " With this option, body is read before handler is called, making request\n" " body available in handler (`Request(BitArray)`). If body is invalid or too\n" " large, `on_failure` function is called.\n" ). -spec with_read_body( builder(bitstring()), integer(), fun((body_error()) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree())) ) -> builder(ewe@internal@http:connection()). with_read_body(Builder, Size_limit, On_failure) -> Handler = fun(Req) -> case read_body(Req, Size_limit) of {ok, Req@1} -> (erlang:element(2, Builder))(Req@1); {error, E} -> On_failure(E) end end, {builder, Handler, erlang:element(3, Builder), erlang:element(4, Builder), erlang:element(5, Builder), erlang:element(6, Builder), erlang:element(7, Builder), erlang:element(8, Builder), erlang:element(9, Builder)}. -file("src/ewe.gleam", 367). ?DOC( " Sets response body to a JSON (use `gleam_json` package and encode using\n" " `json.to_string_tree`), sets `content-type` to `application/json;\n" " charset=utf-8` and `content-length` headers.\n" ). -spec json(gleam@http@response:response(any()), gleam@string_tree:string_tree()) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree()). json(Response, Json) -> Content_length = begin _pipe = erlang:iolist_size(Json), erlang:integer_to_binary(_pipe) end, Body = gleam_stdlib:wrap_list(Json), _pipe@1 = gleam@http@response:set_body(Response, Body), _pipe@2 = gleam@http@response:set_header( _pipe@1, <<"content-type"/utf8>>, <<"application/json; charset=utf-8"/utf8>> ), gleam@http@response:set_header( _pipe@2, <<"content-length"/utf8>>, Content_length ). -file("src/ewe.gleam", 382). ?DOC( " Sets response body to a text, sets `content-type` to\n" " `text/plain; charset=utf-8` and `content-length` headers.\n" ). -spec text(gleam@http@response:response(any()), binary()) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree()). text(Response, Text) -> Content_length = begin _pipe = erlang:byte_size(Text), erlang:integer_to_binary(_pipe) end, Body = gleam_stdlib:wrap_list(Text), _pipe@1 = gleam@http@response:set_body(Response, Body), _pipe@2 = gleam@http@response:set_header( _pipe@1, <<"content-type"/utf8>>, <<"text/plain; charset=utf-8"/utf8>> ), gleam@http@response:set_header( _pipe@2, <<"content-length"/utf8>>, Content_length ). -file("src/ewe.gleam", 394). ?DOC( " Sets response body to a bytes, sets `content-length` header. Doesn't set\n" " `content-type` header.\n" ). -spec bytes(gleam@http@response:response(any()), gleam@bytes_tree:bytes_tree()) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree()). bytes(Response, Bytes) -> Content_length = begin _pipe = erlang:iolist_size(Bytes), erlang:integer_to_binary(_pipe) end, _pipe@1 = gleam@http@response:set_body(Response, Bytes), gleam@http@response:set_header( _pipe@1, <<"content-length"/utf8>>, Content_length ).