Packages
🐑 a fluffy Gleam web server
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
src/ewe@internal@parser.erl
-module(ewe@internal@parser).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/ewe/internal/parser.gleam").
-export([parse_method/1, new_parsed_request/0, new_parser/0, pretty_print_parsed/1, parse_request/1]).
-export_type([parse_error/0, stage/0, body_type/0, http_version/0, parsed_request/0, parser/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 parse_error() :: {incomplete, parser()} |
invalid |
too_large |
unsupported_version |
host_missing |
multi_line_header_unsupported.
-type stage() :: request_line | headers | {body, body_type()} | trailers | done.
-type body_type() :: {content_length, integer()} |
{chunked, gleam@option:option(integer())} |
empty.
-type http_version() :: http11.
-type parsed_request() :: {parsed_request,
gleam@option:option(gleam@http:method()),
gleam@option:option(gleam@uri:uri()),
gleam@option:option(http_version()),
gleam@option:option(gleam@dict:dict(binary(), binary())),
gleam@option:option(gleam@set:set(binary())),
gleam@option:option(bitstring()),
integer()}.
-type parser() :: {parser,
gleam@http@request:request(bitstring()),
gleam@dict:dict(binary(), binary()),
gleam@set:set(binary()),
integer(),
stage(),
bitstring()}.
-file("src/ewe/internal/parser.gleam", 57).
?DOC(false).
-spec parse_method(bitstring()) -> {ok, gleam@http:method()} |
{error, parse_error()}.
parse_method(Method) ->
case Method of
<<"GET"/utf8>> ->
{ok, get};
<<"POST"/utf8>> ->
{ok, post};
<<"PUT"/utf8>> ->
{ok, put};
<<"DELETE"/utf8>> ->
{ok, delete};
<<"PATCH"/utf8>> ->
{ok, patch};
<<"OPTIONS"/utf8>> ->
{ok, options};
<<"HEAD"/utf8>> ->
{ok, head};
_ ->
{error, invalid}
end.
-file("src/ewe/internal/parser.gleam", 86).
?DOC(false).
-spec new_parsed_request() -> parsed_request().
new_parsed_request() ->
{parsed_request, none, none, none, none, none, none, 0}.
-file("src/ewe/internal/parser.gleam", 109).
?DOC(false).
-spec new_parser() -> parser().
new_parser() ->
{parser,
begin
_pipe = gleam@http@request:new(),
gleam@http@request:set_body(_pipe, <<>>)
end,
maps:new(),
gleam@set:new(),
0,
request_line,
<<>>}.
-file("src/ewe/internal/parser.gleam", 120).
?DOC(false).
-spec pretty_print_parsed(gleam@http@request:request(bitstring())) -> nil.
pretty_print_parsed(Request) ->
gleam_stdlib:println(
<<"\n================================================"/utf8>>
),
gleam_stdlib:println(<<"Request:"/utf8>>),
gleam_stdlib:println(
<<"Method: "/utf8,
(begin
_pipe = erlang:element(2, Request),
gleam@http:method_to_string(_pipe)
end)/binary>>
),
gleam_stdlib:println(
<<"Path: "/utf8, ((erlang:element(8, Request)))/binary>>
),
gleam_stdlib:println(
<<"Query: "/utf8,
(begin
_pipe@1 = erlang:element(9, Request),
gleam@option:unwrap(_pipe@1, <<"-"/utf8>>)
end)/binary>>
),
gleam_stdlib:println(<<"Headers:"/utf8>>),
_pipe@2 = erlang:element(3, Request),
gleam@list:each(
_pipe@2,
fun(Header) ->
gleam_stdlib:println(
<<<<<<" "/utf8, ((erlang:element(1, Header)))/binary>>/binary,
": "/utf8>>/binary,
((erlang:element(2, Header)))/binary>>
)
end
),
gleam_stdlib:println(<<"Body:"/utf8>>),
gleam_stdlib:println(
<<" "/utf8,
(begin
_pipe@3 = erlang:element(4, Request),
_pipe@4 = gleam@bit_array:to_string(_pipe@3),
gleam@result:unwrap(_pipe@4, <<"-"/utf8>>)
end)/binary>>
).
-file("src/ewe/internal/parser.gleam", 169).
?DOC(false).
-spec handle_request_line(parser()) -> {ok, parser()} | {error, parse_error()}.
handle_request_line(Parser) ->
Request_parts = case binary:split(
erlang:element(7, Parser),
<<"\r\n"/utf8>>,
[]
) of
[Request_line, Remaining] ->
{ok, {Request_line, Remaining}};
_ ->
{error, {incomplete, Parser}}
end,
gleam@result:'try'(
Request_parts,
fun(_use0) ->
{Request_line@1, Remaining@1} = _use0,
gleam@bool:guard(
erlang:byte_size(Request_line@1) > 8192,
{error, too_large},
fun() ->
Global = erlang:binary_to_atom(<<"global"/utf8>>),
Request_line_parts = case binary:split(
Request_line@1,
<<" "/utf8>>,
[Global]
) of
[Method, Target, Version] ->
{ok, {Method, Target, Version}};
_ ->
{error, invalid}
end,
gleam@result:'try'(
Request_line_parts,
fun(_use0@1) ->
{Method@1, Target@1, Version@1} = _use0@1,
gleam@bool:guard(
erlang:byte_size(Target@1) > 8000,
{error, too_large},
fun() ->
gleam@result:'try'(
parse_method(Method@1),
fun(Method@2) ->
gleam@result:'try'(
begin
_pipe = gleam@bit_array:to_string(
Target@1
),
_pipe@1 = gleam@result:map(
_pipe,
fun gleam_stdlib:uri_parse/1
),
_pipe@2 = gleam@result:flatten(
_pipe@1
),
gleam@result:replace_error(
_pipe@2,
invalid
)
end,
fun(Target@2) ->
gleam@result:'try'(
case Version@1 of
<<"HTTP/1.1"/utf8>> ->
{ok, http11};
<<"HTTP/"/utf8,
_/binary>> ->
{error,
unsupported_version};
_ ->
{error, invalid}
end,
fun(_) ->
Request = begin
_record = erlang:element(
2,
Parser
),
{request,
Method@2,
erlang:element(
3,
_record
),
erlang:element(
4,
_record
),
http,
begin
_pipe@3 = erlang:element(
4,
Target@2
),
gleam@option:unwrap(
_pipe@3,
<<"localhost"/utf8>>
)
end,
erlang:element(
5,
Target@2
),
erlang:element(
6,
Target@2
),
erlang:element(
7,
Target@2
)}
end,
{ok,
{parser,
Request,
erlang:element(
3,
Parser
),
erlang:element(
4,
Parser
),
erlang:element(
5,
Parser
),
headers,
Remaining@1}}
end
)
end
)
end
)
end
)
end
)
end
)
end
).
-file("src/ewe/internal/parser.gleam", 271).
?DOC(false).
-spec handle_header(parser()) -> {ok, parser()} | {error, parse_error()}.
handle_header(Parser) ->
Header_parts = case binary:split(
erlang:element(7, Parser),
<<"\r\n"/utf8>>,
[]
) of
[Header, Remaining] ->
{ok, {Header, Remaining}};
_ ->
{error, {incomplete, Parser}}
end,
gleam@result:'try'(
Header_parts,
fun(_use0) ->
{Header@1, Remaining@1} = _use0,
gleam@bool:guard(
erlang:byte_size(Header@1) > 8192,
{error, too_large},
fun() ->
Header_parts@1 = case binary:split(
Header@1,
<<":"/utf8>>,
[]
) of
[Name, Value] ->
{ok, {Name, Value}};
_ ->
{error, invalid}
end,
gleam@result:'try'(
Header_parts@1,
fun(_use0@1) ->
{Name@1, Value@1} = _use0@1,
gleam@result:'try'(
begin
_pipe = gleam@bit_array:to_string(Name@1),
_pipe@1 = gleam@result:map(
_pipe,
fun string:lowercase/1
),
gleam@result:replace_error(_pipe@1, invalid)
end,
fun(Name@2) ->
gleam@bool:guard(
gleam_stdlib:contains_string(
Name@2,
<<" "/utf8>>
),
{error, invalid},
fun() ->
Skip = case erlang:element(
6,
Parser
) of
trailers ->
not gleam@set:contains(
erlang:element(
4,
Parser
),
Name@2
);
_ ->
false
end,
case Skip of
true ->
{ok,
{parser,
erlang:element(
2,
Parser
),
erlang:element(
3,
Parser
),
erlang:element(
4,
Parser
),
erlang:element(
5,
Parser
),
erlang:element(
6,
Parser
),
Remaining@1}};
false ->
gleam@result:'try'(
begin
_pipe@2 = gleam@bit_array:to_string(
Value@1
),
_pipe@3 = gleam@result:map(
_pipe@2,
fun gleam@string:trim/1
),
gleam@result:replace_error(
_pipe@3,
invalid
)
end,
fun(Value@2) ->
gleam@bool:guard(
erlang:byte_size(
Value@2
)
> 4096,
{error,
too_large},
fun() ->
Headers = gleam@dict:upsert(
erlang:element(
3,
Parser
),
Name@2,
fun(
Exists
) ->
case Exists of
{some,
Acc} ->
<<<<Acc/binary,
", "/utf8>>/binary,
Value@2/binary>>;
none ->
Value@2
end
end
),
Trailers = case Name@2 of
<<"trailer"/utf8>> ->
_pipe@4 = gleam@string:split(
Value@2,
<<","/utf8>>
),
gleam@list:fold(
_pipe@4,
erlang:element(
4,
Parser
),
fun(
Acc@1,
Trailer
) ->
gleam@set:insert(
Acc@1,
begin
_pipe@5 = gleam@string:trim(
Trailer
),
string:lowercase(
_pipe@5
)
end
)
end
);
_ ->
erlang:element(
4,
Parser
)
end,
Parser@1 = {parser,
erlang:element(
2,
Parser
),
Headers,
Trailers,
erlang:element(
5,
Parser
),
erlang:element(
6,
Parser
),
Remaining@1},
{ok,
Parser@1}
end
)
end
)
end
end
)
end
)
end
)
end
)
end
).
-file("src/ewe/internal/parser.gleam", 349).
?DOC(false).
-spec handle_body(parser(), integer()) -> {ok, parser()} |
{error, parse_error()}.
handle_body(Parser, Content_length) ->
gleam@bool:guard(
Content_length > 10485760,
{error, too_large},
fun() -> case erlang:element(7, Parser) of
<<Body:Content_length/binary>> ->
Request = begin
_record = erlang:element(2, Parser),
{request,
erlang:element(2, _record),
erlang:element(3, _record),
Body,
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
erlang:element(8, _record),
erlang:element(9, _record)}
end,
{ok,
{parser,
Request,
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
done,
erlang:element(7, Parser)}};
<<_:Content_length/binary, _>> ->
{error, invalid};
_ ->
{error, {incomplete, Parser}}
end end
).
-file("src/ewe/internal/parser.gleam", 365).
?DOC(false).
-spec handle_chunked_body(parser(), gleam@option:option(integer())) -> {ok,
parser()} |
{error, parse_error()}.
handle_chunked_body(Parser, Chunk_size) ->
gleam@bool:guard(
erlang:element(5, Parser) > 10485760,
{error, too_large},
fun() -> case Chunk_size of
none ->
case binary:split(
erlang:element(7, Parser),
<<"\r\n"/utf8>>,
[]
) of
[Possible_size, Remaining] ->
gleam@result:'try'(
begin
_pipe = gleam@bit_array:to_string(
Possible_size
),
_pipe@1 = gleam@result:map(
_pipe,
fun(_capture) ->
gleam@int:base_parse(_capture, 16)
end
),
_pipe@2 = gleam@result:flatten(_pipe@1),
gleam@result:replace_error(_pipe@2, invalid)
end,
fun(Chunk_size@1) ->
Parser@1 = {parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
{body, {chunked, {some, Chunk_size@1}}},
Remaining},
handle_chunked_body(
Parser@1,
{some, Chunk_size@1}
)
end
);
_ ->
{error, {incomplete, Parser}}
end;
{some, 0} ->
case erlang:element(7, Parser) of
<<"\r\n"/utf8>> ->
{ok,
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
done,
erlang:element(7, Parser)}};
Trailers ->
{ok,
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
trailers,
Trailers}}
end;
{some, Size} ->
gleam@bool:guard(
(Size > (10485760 div 10)) orelse ((erlang:element(
5,
Parser
)
+ Size)
> 10485760),
{error, too_large},
fun() -> case erlang:element(7, Parser) of
<<Chunk:Size/binary,
"\r\n"/utf8,
Remaining@1/bitstring>> ->
Request = begin
_record = erlang:element(2, Parser),
{request,
erlang:element(2, _record),
erlang:element(3, _record),
<<(erlang:element(
4,
erlang:element(2, Parser)
))/bitstring,
Chunk/bitstring>>,
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
erlang:element(8, _record),
erlang:element(9, _record)}
end,
Parser@2 = {parser,
Request,
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser) + Size,
{body, {chunked, none}},
Remaining@1},
handle_chunked_body(Parser@2, none);
_ ->
{error, {incomplete, Parser}}
end end
)
end end
).
-file("src/ewe/internal/parser.gleam", 222).
?DOC(false).
-spec handle_headers(parser()) -> {ok, parser()} | {error, parse_error()}.
handle_headers(Parser) ->
gleam@bool:guard(
maps:size(erlang:element(3, Parser)) > 100,
{error, too_large},
fun() -> case {erlang:element(7, Parser), erlang:element(6, Parser)} of
{<<"\r\n"/utf8, Remaining/bitstring>>, headers} ->
Content_length = begin
_pipe = gleam_stdlib:map_get(
erlang:element(3, Parser),
<<"content-length"/utf8>>
),
_pipe@1 = gleam@result:map(
_pipe,
fun gleam_stdlib:parse_int/1
),
gleam@result:flatten(_pipe@1)
end,
Transfer_encoding = gleam_stdlib:map_get(
erlang:element(3, Parser),
<<"transfer-encoding"/utf8>>
),
Parser@1 = case {Transfer_encoding, Content_length} of
{{ok, <<"chunked"/utf8>>}, _} ->
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
{body, {chunked, none}},
Remaining};
{_, {ok, Content_length@1}} ->
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
{body, {content_length, Content_length@1}},
Remaining};
{_, _} ->
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
{body, empty},
Remaining}
end,
{ok, Parser@1};
{<<"\r\n"/utf8>>, trailers} ->
Request = begin
_record = erlang:element(2, Parser),
{request,
erlang:element(2, _record),
begin
_pipe@2 = erlang:element(3, Parser),
maps:to_list(_pipe@2)
end,
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
erlang:element(8, _record),
erlang:element(9, _record)}
end,
{ok,
{parser,
Request,
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
done,
erlang:element(7, Parser)}};
{<<"\r\n"/utf8, _/bitstring>>, trailers} ->
{error, invalid};
{<<"\t"/utf8, _/bitstring>>, headers} ->
{error, multi_line_header_unsupported};
{<<" "/utf8, _/bitstring>>, headers} ->
{error, multi_line_header_unsupported};
{_, _} ->
case handle_header(Parser) of
{ok, Parser@2} ->
handle_headers(Parser@2);
{error, Error} ->
{error, Error}
end
end end
).
-file("src/ewe/internal/parser.gleam", 142).
?DOC(false).
-spec parse_request(parser()) -> {ok, gleam@http@request:request(bitstring())} |
{error, parse_error()}.
parse_request(Parser) ->
{Stage_parsed, Finished} = case erlang:element(6, Parser) of
request_line ->
{handle_request_line(Parser), false};
headers ->
{handle_headers(Parser), false};
{body, empty} ->
{{ok,
{parser,
erlang:element(2, Parser),
erlang:element(3, Parser),
erlang:element(4, Parser),
erlang:element(5, Parser),
done,
erlang:element(7, Parser)}},
false};
{body, {content_length, Content_length}} ->
{handle_body(Parser, Content_length), false};
{body, {chunked, Chunk_size}} ->
{handle_chunked_body(Parser, Chunk_size), false};
trailers ->
{handle_headers(Parser), false};
done ->
{{ok, Parser}, true}
end,
case {Stage_parsed, Finished} of
{{ok, Parser@1}, false} ->
parse_request(Parser@1);
{{ok, Parser@2}, true} ->
{ok, erlang:element(2, Parser@2)};
{{error, Error}, _} ->
{error, Error}
end.