-module(ewe@internal@http). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/ewe/internal/http.gleam"). -export([transform_connection/1, upgrade_websocket/3, append_default_headers/3, handle_continue/1, parse_request/2, read_body/2, stream_body/1]). -export_type([response_body/0, parse_error/0, connection/0, http_version/0, upgrade_websocket_error/0, stream/0, body_chunk/0, chunked_stream_state/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 response_body() :: {text_data, binary()} | {bytes_data, gleam@bytes_tree:bytes_tree()} | {bits_data, bitstring()} | {string_tree_data, gleam@string_tree:string_tree()} | {websocket_connection, gleam@erlang@process:selector(gleam@erlang@process:down())} | empty. -type parse_error() :: invalid_method | invalid_target | invalid_version | invalid_headers | missing_host | duplicate_host | invalid_content_length | invalid_body | body_too_large | malformed_request | packet_discard. -type connection() :: {connection, glisten@transport:transport(), glisten@socket:socket(), ewe@internal@buffer:buffer(), gleam@option:option(http_version())}. -type http_version() :: http10 | http11. -type upgrade_websocket_error() :: version_not11_or_greater | method_not_get | missing_connection_header | invalid_connection_header | missing_upgrade_header | invalid_upgrade_header | missing_websocket_version | missing_websocket_key. -type stream() :: {consumed, bitstring(), fun((integer()) -> {ok, stream()} | {error, parse_error()})} | done. -type body_chunk() :: incomplete | {chunk, bitstring(), integer(), ewe@internal@buffer:buffer()} | {final_chunk, ewe@internal@buffer:buffer()}. -type chunked_stream_state() :: {chunked_stream_state, ewe@internal@buffer:buffer(), ewe@internal@buffer:buffer(), boolean()}. -file("src/ewe/internal/http.gleam", 131). ?DOC(false). -spec transform_connection(glisten:connection(any())) -> connection(). transform_connection(Conn) -> {connection, erlang:element(3, Conn), erlang:element(2, Conn), ewe@internal@buffer:empty(), none}. -file("src/ewe/internal/http.gleam", 326). ?DOC(false). -spec upgrade_websocket( gleam@http@request:request(connection()), glisten@transport:transport(), glisten@socket:socket() ) -> {ok, {list(binary()), boolean()}} | {error, upgrade_websocket_error()}. upgrade_websocket(Req, Transport, Socket) -> Http_version@1 = case erlang:element(5, erlang:element(4, Req)) of {some, Http_version} -> Http_version; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"ewe/internal/http"/utf8>>, function => <<"upgrade_websocket"/utf8>>, line => 331, value => _assert_fail, start => 8085, 'end' => 8145, pattern_start => 8096, pattern_end => 8121}) end, gleam@bool:guard( Http_version@1 /= http11, {error, version_not11_or_greater}, fun() -> gleam@bool:guard( erlang:element(2, Req) /= get, {error, method_not_get}, fun() -> gleam@result:'try'( case gleam@http@request:get_header( Req, <<"connection"/utf8>> ) of {ok, <<"Upgrade"/utf8>>} -> {ok, nil}; {ok, _} -> {error, invalid_connection_header}; {error, _} -> {error, missing_connection_header} end, fun(_) -> gleam@result:'try'( case gleam@http@request:get_header( Req, <<"upgrade"/utf8>> ) of {ok, <<"websocket"/utf8>>} -> {ok, nil}; {ok, _} -> {error, invalid_upgrade_header}; {error, _} -> {error, missing_upgrade_header} end, fun(_) -> gleam@bool:guard( gleam@http@request:get_header( Req, <<"sec-websocket-version"/utf8>> ) =:= {error, nil}, {error, missing_websocket_version}, fun() -> gleam@result:'try'( begin _pipe = gleam@http@request:get_header( Req, <<"sec-websocket-key"/utf8>> ), gleam@result:replace_error( _pipe, missing_websocket_key ) end, fun(Key) -> Accept_key = gramps@websocket:parse_websocket_key( Key ), Extensions = begin _pipe@1 = gleam@http@request:get_header( Req, <<"sec-websocket-extensions"/utf8>> ), _pipe@2 = gleam@result:map( _pipe@1, fun(_capture) -> gleam@string:split( _capture, <<";"/utf8>> ) end ), gleam@result:unwrap( _pipe@2, [] ) end, Permessage_deflate = gramps@websocket:has_deflate( Extensions ), Resp = begin _pipe@3 = gleam@http@response:new( 101 ), _pipe@4 = gleam@http@response:set_body( _pipe@3, gleam@bytes_tree:new( ) ), _pipe@5 = gleam@http@response:set_header( _pipe@4, <<"connection"/utf8>>, <<"upgrade"/utf8>> ), _pipe@6 = gleam@http@response:set_header( _pipe@5, <<"upgrade"/utf8>>, <<"websocket"/utf8>> ), _pipe@7 = gleam@http@response:set_header( _pipe@6, <<"sec-websocket-accept"/utf8>>, Accept_key ), gleam@http@response:set_header( _pipe@7, <<"sec-websocket-version"/utf8>>, <<"13"/utf8>> ) end, Resp@1 = case Permessage_deflate of true -> gleam@http@response:set_header( Resp, <<"sec-websocket-extensions"/utf8>>, <<"permessage-deflate"/utf8>> ); false -> Resp end, _ = begin _pipe@8 = ewe@internal@encoder:encode_response( Resp@1 ), glisten@transport:send( Transport, Socket, _pipe@8 ) end, {ok, {Extensions, Permessage_deflate}} end ) end ) end ) end ) end ) end ). -file("src/ewe/internal/http.gleam", 393). ?DOC(false). -spec append_default_headers( gleam@http@response:response(gleam@bytes_tree:bytes_tree()), gleam@http@request:request(connection()), http_version() ) -> gleam@http@response:response(gleam@bytes_tree:bytes_tree()). append_default_headers(Resp, Req, Version) -> Body_size = erlang:iolist_size(erlang:element(4, Resp)), Set_close = gleam@http@request:get_header(Req, <<"connection"/utf8>>) =:= {ok, <<"close"/utf8>>}, Resp@1 = case gleam@http@response:get_header( Resp, <<"content-length"/utf8>> ) of {ok, _} -> Resp; {error, nil} -> gleam@http@response:set_header( Resp, <<"content-length"/utf8>>, erlang:integer_to_binary(Body_size) ) end, case {Version, Set_close} of {http10, _} -> gleam@http@response:set_header( Resp@1, <<"connection"/utf8>>, <<"close"/utf8>> ); {_, true} -> gleam@http@response:set_header( Resp@1, <<"connection"/utf8>>, <<"close"/utf8>> ); {http11, false} -> case gleam@http@response:get_header(Resp@1, <<"connection"/utf8>>) of {ok, _} -> Resp@1; {error, nil} -> gleam@http@response:set_header( Resp@1, <<"connection"/utf8>>, <<"keep-alive"/utf8>> ) end end. -file("src/ewe/internal/http.gleam", 533). ?DOC(false). -spec available_cookie_key(gleam@dict:dict(binary(), binary()), integer()) -> binary(). available_cookie_key(Headers, Idx) -> Key = case Idx of 0 -> <<"set-cookie"/utf8>>; N -> <<"set-cookie-"/utf8, (erlang:integer_to_binary(N))/binary>> end, case gleam@dict:has_key(Headers, Key) of true -> available_cookie_key(Headers, Idx + 1); false -> Key end. -file("src/ewe/internal/http.gleam", 515). ?DOC(false). -spec insert_header(gleam@dict:dict(binary(), binary()), binary(), binary()) -> gleam@dict:dict(binary(), binary()). insert_header(Headers, Field, Value) -> case Field /= <<"set-cookie"/utf8>> of true -> gleam@dict:upsert(Headers, Field, fun(Target) -> case Target of {some, Existing} -> <<<>/binary, Value/binary>>; none -> Value end end); false -> gleam@dict:insert(Headers, available_cookie_key(Headers, 0), Value) end. -file("src/ewe/internal/http.gleam", 550). ?DOC(false). -spec handle_continue(gleam@http@request:request(connection())) -> {ok, nil} | {error, parse_error()}. handle_continue(Req) -> Expect = begin _pipe = erlang:element(3, Req), gleam@list:find( _pipe, fun(Tupple) -> (erlang:element(1, Tupple) =:= <<"expect"/utf8>>) andalso (string:lowercase( erlang:element(2, Tupple) ) =:= <<"100-continue"/utf8>>) end ) end, case Expect of {ok, _} -> _pipe@1 = gleam@http@response:new(100), _pipe@2 = gleam@http@response:set_body( _pipe@1, gleam@bytes_tree:new() ), _pipe@3 = ewe@internal@encoder:encode_response(_pipe@2), _pipe@4 = glisten@transport:send( erlang:element(2, erlang:element(4, Req)), erlang:element(3, erlang:element(4, Req)), _pipe@3 ), gleam@result:replace_error(_pipe@4, malformed_request); {error, nil} -> {ok, nil} end. -file("src/ewe/internal/http.gleam", 822). ?DOC(false). -spec is_forbidden_trailer(binary()) -> boolean(). is_forbidden_trailer(Field) -> case string:lowercase(Field) of <<"transfer-encoding"/utf8>> -> true; <<"content-length"/utf8>> -> true; <<"host"/utf8>> -> true; <<"cache-control"/utf8>> -> true; <<"expect"/utf8>> -> true; <<"max-forwards"/utf8>> -> true; <<"pragma"/utf8>> -> true; <<"range"/utf8>> -> true; <<"te"/utf8>> -> true; _ -> false end. -file("src/ewe/internal/http.gleam", 781). ?DOC(false). -spec handle_trailers( gleam@http@request:request(bitstring()), gleam@set:set(binary()), ewe@internal@buffer:buffer() ) -> gleam@http@request:request(bitstring()). handle_trailers(Req, Set, Rest) -> case ewe@internal@decoder:decode_packet(httph_bin, Rest) of {ok, {packet, http_eoh, _}} -> Req; {ok, {packet, {http_header, Idx, Field, Value}, Header_rest}} -> Field_name@1 = case ewe@internal@decoder:formatted_field_by_idx(Idx) of {ok, Field_name} -> {ok, Field_name}; {error, nil} -> _pipe = gleam@bit_array:to_string(Field), gleam@result:map(_pipe, fun string:lowercase/1) end, case Field_name@1 of {ok, Field_name@2} -> case gleam@set:contains(Set, Field_name@2) andalso not is_forbidden_trailer( Field_name@2 ) of true -> case gleam@bit_array:to_string(Value) of {ok, Value@1} -> _pipe@1 = gleam@http@request:set_header( Req, Field_name@2, Value@1 ), handle_trailers( _pipe@1, Set, ewe@internal@buffer:new(Header_rest) ); {error, nil} -> handle_trailers(Req, Set, Rest) end; false -> handle_trailers(Req, Set, Rest) end; {error, nil} -> handle_trailers(Req, Set, Rest) end; _ -> Req end. -file("src/ewe/internal/http.gleam", 664). ?DOC(false). -spec parse_body_chunk(ewe@internal@buffer:buffer()) -> {ok, body_chunk()} | {error, parse_error()}. parse_body_chunk(Buffer) -> case binary:split(erlang:element(2, Buffer), <<"\r\n"/utf8>>, []) of [<<"0"/utf8>>, Rest] -> {ok, {final_chunk, ewe@internal@buffer:new(Rest)}}; [Chunk_size, Rest@1] -> gleam@result:'try'( begin _pipe = gleam@bit_array:to_string(Chunk_size), _pipe@1 = gleam@result:'try'( _pipe, fun(_capture) -> gleam@int:base_parse(_capture, 16) end ), gleam@result:replace_error(_pipe@1, invalid_body) end, fun(Size) -> case binary:split(Rest@1, <<"\r\n"/utf8>>, []) of [Chunk, Rest@2] -> case erlang:byte_size(Chunk) =:= Size of true -> {ok, {chunk, Chunk, Size, ewe@internal@buffer:new(Rest@2)}}; false -> {error, invalid_body} end; _ -> {ok, incomplete} end end ); _ -> {ok, incomplete} end. -file("src/ewe/internal/http.gleam", 423). ?DOC(false). -spec read_from_socket( glisten@transport:transport(), glisten@socket:socket(), ewe@internal@buffer:buffer(), parse_error() ) -> {ok, ewe@internal@buffer:buffer()} | {error, parse_error()}. read_from_socket(Transport, Socket, Buffer, On_error) -> Read_size = gleam@int:min(erlang:element(3, Buffer), 1000000), gleam@result:'try'( begin _pipe = glisten@transport:receive_timeout( Transport, Socket, Read_size, 10000 ), gleam@result:replace_error(_pipe, On_error) end, fun(Data) -> New_buffer = ewe@internal@buffer:append_size( Buffer, Data, Read_size ), case erlang:element(3, New_buffer) of 0 -> {ok, New_buffer}; _ -> read_from_socket(Transport, Socket, New_buffer, On_error) end end ). -file("src/ewe/internal/http.gleam", 449). ?DOC(false). -spec parse_headers( glisten@transport:transport(), glisten@socket:socket(), ewe@internal@buffer:buffer(), gleam@dict:dict(binary(), binary()) ) -> {ok, {gleam@dict:dict(binary(), binary()), bitstring()}} | {error, parse_error()}. parse_headers(Transport, Socket, Buffer, Headers) -> case ewe@internal@decoder:decode_packet(httph_bin, Buffer) of {ok, {packet, http_eoh, Rest}} -> {ok, {Headers, Rest}}; {ok, {packet, {http_header, Idx, Field, Value}, Rest@1}} -> gleam@result:'try'( case ewe@internal@decoder:formatted_field_by_idx(Idx) of {ok, Field@1} -> {ok, Field@1}; {error, nil} -> _pipe = gleam@bit_array:to_string(Field), _pipe@1 = gleam@result:map( _pipe, fun string:lowercase/1 ), gleam@result:replace_error(_pipe@1, invalid_headers) end, fun(Field@2) -> gleam@result:'try'( begin _pipe@2 = ewe_ffi:validate_field_value(Value), gleam@result:replace_error(_pipe@2, invalid_headers) end, fun(Value@1) -> New_buffer = ewe@internal@buffer:new(Rest@1), gleam@result:'try'(case Field@2 of <<"host"/utf8>> -> case gleam@dict:has_key( Headers, Field@2 ) of true -> {error, duplicate_host}; false -> {ok, nil} end; <<"content-length"/utf8>> -> _pipe@3 = gleam_stdlib:parse_int( Value@1 ), _pipe@4 = gleam@result:'try'( _pipe@3, fun(Value@2) -> case Value@2 < 0 of true -> {error, nil}; false -> {ok, nil} end end ), gleam@result:replace_error( _pipe@4, invalid_content_length ); _ -> {ok, nil} end, fun(_) -> _pipe@5 = insert_header( Headers, Field@2, Value@1 ), parse_headers( Transport, Socket, New_buffer, _pipe@5 ) end) end ) end ); {ok, {more, Size}} -> Read_size = gleam@option:unwrap(Size, 0), Sized_buffer = ewe@internal@buffer:sized(Buffer, Read_size), gleam@result:'try'( read_from_socket( Transport, Socket, Sized_buffer, invalid_headers ), fun(New_buffer@1) -> parse_headers(Transport, Socket, New_buffer@1, Headers) end ); _ -> {error, invalid_headers} end. -file("src/ewe/internal/http.gleam", 141). ?DOC(false). -spec parse_request(connection(), ewe@internal@buffer:buffer()) -> {ok, gleam@http@request:request(connection())} | {error, parse_error()}. parse_request(Conn, Buffer) -> Transport = erlang:element(2, Conn), Socket = erlang:element(3, Conn), case ewe@internal@decoder:decode_packet(http_bin, Buffer) of {ok, {packet, {http_request, Atom_method, {abs_path, Target}, Version}, Rest}} -> gleam@result:'try'( begin _pipe = ewe@internal@decoder:decode_method(Atom_method), gleam@result:replace_error(_pipe, invalid_method) end, fun(Method) -> gleam@result:'try'( begin _pipe@1 = gleam@bit_array:to_string(Target), _pipe@2 = gleam@result:'try'( _pipe@1, fun gleam_stdlib:uri_parse/1 ), gleam@result:replace_error(_pipe@2, invalid_target) end, fun(Uri) -> gleam@result:'try'(case Version of {1, 0} -> {ok, http10}; {1, 1} -> {ok, http11}; _ -> {error, invalid_version} end, fun(Version@1) -> gleam@result:'try'( parse_headers( Transport, Socket, ewe@internal@buffer:new(Rest), maps:new() ), fun(_use0) -> {Headers, Rest@1} = _use0, Scheme = case Transport of tcp -> http; ssl -> https end, gleam@result:'try'( begin _pipe@3 = gleam_stdlib:map_get( Headers, <<"host"/utf8>> ), gleam@result:replace_error( _pipe@3, missing_host ) end, fun(Host) -> {Host@2, Port@1} = case gleam@string:split_once( Host, <<":"/utf8>> ) of {ok, {Host@1, Port}} -> {Host@1, {some, Port}}; {error, _} -> {Host, none} end, Port@3 = gleam@option:map( Port@1, fun(Port@2) -> _pipe@4 = gleam_stdlib:parse_int( Port@2 ), gleam@result:unwrap( _pipe@4, case Scheme of http -> 80; https -> 443 end ) end ), {ok, {request, Method, maps:to_list( Headers ), {connection, erlang:element( 2, Conn ), erlang:element( 3, Conn ), ewe@internal@buffer:new( Rest@1 ), {some, Version@1}}, Scheme, Host@2, Port@3, erlang:element( 6, Uri ), erlang:element( 7, Uri )}} end ) end ) end) end ) end ); {ok, {more, Size}} -> gleam@result:'try'( read_from_socket( Transport, Socket, ewe@internal@buffer:sized( Buffer, gleam@option:unwrap(Size, 0) ), malformed_request ), fun(New_buffer) -> parse_request(Conn, New_buffer) end ); _ -> {error, packet_discard} end. -file("src/ewe/internal/http.gleam", 573). ?DOC(false). -spec do_stream_body( gleam@http@request:request(connection()), ewe@internal@buffer:buffer() ) -> fun((integer()) -> {ok, stream()} | {error, parse_error()}). do_stream_body(Req, Buffer) -> fun(Size) -> Buffer_size = erlang:byte_size(erlang:element(2, Buffer)), case {erlang:element(3, Buffer), Buffer_size} of {0, 0} -> {ok, done}; {0, _} -> {Data, Rest} = ewe@internal@buffer:split(Buffer, Size), {ok, {consumed, Data, do_stream_body(Req, ewe@internal@buffer:new(Rest))}}; {_, Buffer_size@1} when Buffer_size@1 >= Size -> {Data@1, Rest@1} = ewe@internal@buffer:split(Buffer, Size), New_buffer = ewe@internal@buffer:new_sized( Rest@1, erlang:element(3, Buffer) ), {ok, {consumed, Data@1, do_stream_body(Req, New_buffer)}}; {_, _} -> gleam@result:'try'( read_from_socket( erlang:element(2, erlang:element(4, Req)), erlang:element(3, erlang:element(4, Req)), ewe@internal@buffer:empty(), invalid_body ), fun(Read_buffer) -> New_buffer@1 = ewe@internal@buffer:new_sized( <<(erlang:element(2, Buffer))/bitstring, (erlang:element(2, Read_buffer))/bitstring>>, gleam@int:max( 0, erlang:element(3, Buffer) - erlang:byte_size( erlang:element(2, Read_buffer) ) ) ), {Data@2, Rest@2} = ewe@internal@buffer:split( New_buffer@1, Size ), {ok, {consumed, Data@2, do_stream_body( Req, ewe@internal@buffer:new(Rest@2) )}} end ) end end. -file("src/ewe/internal/http.gleam", 621). ?DOC(false). -spec read_chunked_body( glisten@transport:transport(), glisten@socket:socket(), ewe@internal@buffer:buffer(), bitstring(), integer(), integer() ) -> {ok, {bitstring(), ewe@internal@buffer:buffer()}} | {error, parse_error()}. read_chunked_body( Transport, Socket, Buffer, Accumulated_body, Body_size_limit, Body_current_size ) -> gleam@bool:guard( Body_current_size > Body_size_limit, {error, body_too_large}, fun() -> case parse_body_chunk(Buffer) of {ok, {final_chunk, Rest}} -> {ok, {Accumulated_body, Rest}}; {ok, incomplete} -> gleam@result:'try'( read_from_socket( Transport, Socket, Buffer, invalid_body ), fun(New_buffer) -> read_chunked_body( Transport, Socket, New_buffer, Accumulated_body, Body_size_limit, Body_current_size ) end ); {ok, {chunk, Chunk, Size, Rest@1}} -> read_chunked_body( Transport, Socket, Rest@1, <>, Body_size_limit, Body_current_size + Size ); {error, Error} -> {error, Error} end end ). -file("src/ewe/internal/http.gleam", 231). ?DOC(false). -spec read_body(gleam@http@request:request(connection()), integer()) -> {ok, gleam@http@request:request(bitstring())} | {error, parse_error()}. read_body(Req, Size_limit) -> gleam@result:'try'( handle_continue(Req), fun(_) -> Transport = erlang:element(2, erlang:element(4, Req)), Socket = erlang:element(3, erlang:element(4, Req)), Transfer_encoding = begin _pipe = gleam@http@request:get_header( Req, <<"transfer-encoding"/utf8>> ), gleam@result:map(_pipe, fun string:lowercase/1) end, case Transfer_encoding of {ok, <<"chunked"/utf8>>} -> gleam@result:'try'( read_chunked_body( Transport, Socket, erlang:element(4, erlang:element(4, Req)), <<>>, Size_limit, 0 ), fun(_use0) -> {Body, Rest_buffer} = _use0, Req@1 = gleam@http@request:set_body(Req, Body), case gleam@list:key_find( erlang:element(3, Req@1), <<"trailer"/utf8>> ) of {ok, Trailer} -> Set@1 = begin _pipe@1 = Trailer, _pipe@2 = gleam@string:split( _pipe@1, <<","/utf8>> ), gleam@list:fold( _pipe@2, gleam@set:new(), fun(Set, Field) -> gleam@set:insert( Set, begin _pipe@3 = gleam@string:trim( Field ), string:lowercase( _pipe@3 ) end ) end ) end, {ok, handle_trailers( Req@1, Set@1, Rest_buffer )}; {error, nil} -> {ok, Req@1} end end ); _ -> Content_length = begin _pipe@4 = gleam@http@request:get_header( Req, <<"content-length"/utf8>> ), _pipe@5 = gleam@result:'try'( _pipe@4, fun gleam_stdlib:parse_int/1 ), gleam@result:unwrap(_pipe@5, 0) end, gleam@bool:guard( Content_length > Size_limit, {error, body_too_large}, fun() -> Left = Content_length - erlang:byte_size( erlang:element( 2, erlang:element(4, erlang:element(4, Req)) ) ), _pipe@7 = case {Content_length, Left} of {0, 0} -> {ok, <<>>}; {0, _} -> {ok, erlang:element( 2, erlang:element( 4, erlang:element(4, Req) ) )}; {_, 0} -> {ok, erlang:element( 2, erlang:element( 4, erlang:element(4, Req) ) )}; {_, _} -> _pipe@6 = read_from_socket( Transport, Socket, ewe@internal@buffer:sized( erlang:element( 4, erlang:element(4, Req) ), Left ), invalid_body ), gleam@result:map( _pipe@6, fun(Buffer) -> erlang:element(2, Buffer) end ) end, gleam@result:map( _pipe@7, fun(_capture) -> gleam@http@request:set_body(Req, _capture) end ) end ) end end ). -file("src/ewe/internal/http.gleam", 711). ?DOC(false). -spec read_from_socket_until( glisten@transport:transport(), glisten@socket:socket(), chunked_stream_state(), integer() ) -> {ok, {bitstring(), chunked_stream_state()}} | {error, parse_error()}. read_from_socket_until(Transport, Socket, State, Until) -> Size = erlang:byte_size(erlang:element(2, erlang:element(2, State))), case {erlang:element(4, State), Size} of {_, Size@1} when Size@1 >= Until -> {Data, Rest} = ewe@internal@buffer:split( erlang:element(2, State), Until ), {ok, {Data, {chunked_stream_state, ewe@internal@buffer:new(Rest), erlang:element(3, State), erlang:element(4, State)}}}; {true, _} -> {ok, {erlang:element(2, erlang:element(2, State)), State}}; {false, _} -> case parse_body_chunk(erlang:element(3, State)) of {ok, {final_chunk, _}} -> read_from_socket_until( Transport, Socket, {chunked_stream_state, erlang:element(2, State), ewe@internal@buffer:empty(), true}, Until ); {ok, incomplete} -> gleam@result:'try'( read_from_socket( Transport, Socket, erlang:element(3, State), invalid_body ), fun(New_buffer) -> read_from_socket_until( Transport, Socket, {chunked_stream_state, erlang:element(2, State), New_buffer, erlang:element(4, State)}, Until ) end ); {ok, {chunk, Chunk, Size@2, Rest@1}} -> read_from_socket_until( Transport, Socket, {chunked_stream_state, ewe@internal@buffer:append_size( erlang:element(2, State), Chunk, Size@2 ), Rest@1, erlang:element(4, State)}, Until ); {error, Error} -> {error, Error} end end. -file("src/ewe/internal/http.gleam", 688). ?DOC(false). -spec do_stream_body_chunked( gleam@http@request:request(connection()), chunked_stream_state() ) -> fun((integer()) -> {ok, stream()} | {error, parse_error()}). do_stream_body_chunked(Req, Chunked_stream_state) -> fun(Size) -> Read_result = read_from_socket_until( erlang:element(2, erlang:element(4, Req)), erlang:element(3, erlang:element(4, Req)), Chunked_stream_state, Size ), case Read_result of {ok, {Data, {chunked_stream_state, _, _, true}}} -> {ok, {consumed, Data, fun(_) -> {ok, done} end}}; {ok, {Data@1, State}} -> {ok, {consumed, Data@1, do_stream_body_chunked(Req, State)}}; {error, _} -> {error, invalid_body} end end. -file("src/ewe/internal/http.gleam", 299). ?DOC(false). -spec stream_body(gleam@http@request:request(connection())) -> {ok, fun((integer()) -> {ok, stream()} | {error, parse_error()})} | {error, parse_error()}. stream_body(Req) -> gleam@result:'try'( begin _pipe = handle_continue(Req), gleam@result:replace_error(_pipe, invalid_body) end, fun(_) -> case gleam@http@request:get_header( Req, <<"transfer-encoding"/utf8>> ) of {ok, <<"chunked"/utf8>>} -> State = {chunked_stream_state, ewe@internal@buffer:empty(), erlang:element(4, erlang:element(4, Req)), false}, {ok, do_stream_body_chunked(Req, State)}; _ -> Content_length = begin _pipe@1 = gleam@http@request:get_header( Req, <<"content-length"/utf8>> ), _pipe@2 = gleam@result:'try'( _pipe@1, fun gleam_stdlib:parse_int/1 ), gleam@result:unwrap(_pipe@2, 0) end, Remaining = Content_length - erlang:byte_size( erlang:element( 2, erlang:element(4, erlang:element(4, Req)) ) ), Stream_buffer = ewe@internal@buffer:sized( erlang:element(4, erlang:element(4, Req)), gleam@int:max(0, Remaining) ), _pipe@3 = do_stream_body(Req, Stream_buffer), {ok, _pipe@3} end end ).