Current section

Files

Jump to
dove src dove@internal@response.erl
Raw

src/dove@internal@response.erl

-module(dove@internal@response).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]).
-export([decode_headers/2, decode/1]).
-export_type([decode_result/0]).
-type decode_result() :: more |
{eoh, binary()} |
{header, {binary(), binary()}, binary()} |
{status_line, {{integer(), integer()}, integer(), binary()}, binary()}.
-spec consume_till_crlf(bitstring(), bitstring(), boolean()) -> {ok,
{bitstring(), bitstring()}} |
{error, dove@error:error()}.
consume_till_crlf(Data, Storage, Include_crlf) ->
case erlang:byte_size(Data) of
0 ->
{error, more_needed};
_ ->
case Data of
<<"\r\n"/utf8, Rest/bitstring>> when Include_crlf ->
{ok,
{gleam@bit_array:append(Storage, <<"\r\n"/utf8>>), Rest}};
<<"\r\n"/utf8, Rest@1/bitstring>> ->
{ok, {Storage, Rest@1}};
<<Ch:8, Rest@2/bitstring>> ->
consume_till_crlf(
Rest@2,
gleam@bit_array:append(Storage, <<Ch>>),
Include_crlf
)
end
end.
-spec consume_till_double_crlf(bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, dove@error:error()}.
consume_till_double_crlf(Data, Storage) ->
case erlang:byte_size(Data) of
0 ->
{error, more_needed};
_ ->
case Data of
<<"\r\n\r\n"/utf8, Rest/bitstring>> ->
{ok,
{gleam@bit_array:append(Storage, <<"\r\n\r\n"/utf8>>),
Rest}};
<<Ch:8, Rest@1/bitstring>> ->
consume_till_double_crlf(
Rest@1,
gleam@bit_array:append(Storage, <<Ch>>)
)
end
end.
-spec consume_by_length(bitstring(), integer(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, dove@error:error()}.
consume_by_length(Data, Length, Storage) ->
case erlang:byte_size(Data) of
0 ->
{error, more_needed};
_ ->
<<Ch:8, Rest/bitstring>> = Data,
Storage@1 = gleam@bit_array:append(Storage, <<Ch>>),
case erlang:byte_size(Storage@1) =:= Length of
true ->
{ok, {Storage@1, Rest}};
false ->
consume_by_length(Rest, Length, Storage@1)
end
end.
-spec gather_chunks(bitstring(), bitstring()) -> {ok,
{bitstring(), bitstring()}} |
{error, dove@error:error()}.
gather_chunks(Data, Storage) ->
case erlang:byte_size(Data) of
0 ->
{error, more_needed};
_ ->
gleam@result:then(
consume_till_crlf(Data, <<>>, false),
fun(_use0) ->
{Length, Rest} = _use0,
gleam@result:then(
begin
_pipe = gleam@bit_array:to_string(Length),
gleam@result:replace_error(
_pipe,
invalid_chunked_response
)
end,
fun(Length@1) ->
case gleam@int:base_parse(Length@1, 16) of
{ok, 0} ->
<<"\r\n"/utf8, Rest@1/bitstring>> = Rest,
{ok, {Storage, Rest@1}};
{ok, Length@2} ->
case consume_by_length(Rest, Length@2, <<>>) of
{ok, {Chunk, Rest@2}} ->
<<"\r\n"/utf8, Rest@3/bitstring>> = Rest@2,
gather_chunks(
Rest@3,
gleam@bit_array:append(
Storage,
Chunk
)
);
{error, Error} ->
{error, Error}
end;
{error, nil} ->
{error, invalid_chunked_response}
end
end
)
end
)
end.
-spec decode_headers(binary(), list({binary(), binary()})) -> {ok,
{list({binary(), binary()}), binary()}} |
{error, dove@error:error()}.
decode_headers(Binary, Storage) ->
gleam@result:then(
begin
_pipe = dove_ffi:decode_header(Binary),
gleam@result:replace_error(_pipe, invalid_headers)
end,
fun(Result) -> case Result of
{eoh, Rest} ->
{ok, {Storage, Rest}};
{header, Header, Rest@1} ->
decode_headers(Rest@1, gleam@list:append(Storage, [Header]));
more ->
{error, more_needed};
{status_line, _, _} ->
{error, wrong_packet_type}
end end
).
-spec decode(bitstring()) -> {ok,
{{integer(),
list({binary(), binary()}),
gleam@option:option(bitstring())},
bitstring()}} |
{error, dove@error:error()}.
decode(Response) ->
gleam@result:then(
consume_till_crlf(Response, <<>>, true),
fun(_use0) ->
{Status_line, Rest} = _use0,
gleam@result:then(
begin
_pipe = gleam@bit_array:to_string(Status_line),
gleam@result:replace_error(_pipe, invalid_status_line)
end,
fun(Status_line@1) ->
gleam@result:then(
consume_till_double_crlf(Rest, <<>>),
fun(_use0@1) ->
{Headers, Rest@1} = _use0@1,
gleam@result:then(
begin
_pipe@1 = gleam@bit_array:to_string(Headers),
gleam@result:replace_error(
_pipe@1,
invalid_headers
)
end,
fun(Headers@1) ->
case dove_ffi:decode_status_line(
Status_line@1
) of
{ok, {status_line, {_, Status, _}, _}} ->
gleam@result:then(
decode_headers(Headers@1, []),
fun(_use0@2) ->
{Headers@2, _} = _use0@2,
gleam@result:then(
case {gleam@list:key_find(
Headers@2,
<<"transfer-encoding"/utf8>>
),
begin
_pipe@2 = gleam@list:key_find(
Headers@2,
<<"content-length"/utf8>>
),
_pipe@3 = gleam@result:map(
_pipe@2,
fun gleam@int:parse/1
),
gleam@result:flatten(
_pipe@3
)
end} of
{{ok,
<<"chunked"/utf8>>},
_} ->
gleam@result:then(
gather_chunks(
Rest@1,
<<>>
),
fun(_use0@3) ->
{Body,
Rest@2} = _use0@3,
{ok,
{{Status,
Headers@2,
{some,
Body}},
Rest@2}}
end
);
{_, {ok, Length}} when Length > 0 ->
gleam@result:then(
consume_by_length(
Rest@1,
Length,
<<>>
),
fun(_use0@4) ->
{Body@1,
Rest@3} = _use0@4,
{ok,
{{Status,
Headers@2,
{some,
Body@1}},
Rest@3}}
end
);
_ ->
{ok,
{{Status,
Headers@2,
none},
Rest@1}}
end,
fun(_use0@5) ->
{Response@1, Rest@4} = _use0@5,
case erlang:element(
3,
Response@1
) of
{some, Body@2} ->
case gleam@list:key_find(
Headers@2,
<<"content-encoding"/utf8>>
) of
{ok,
<<"gzip"/utf8>>} ->
gleam@result:then(
begin
_pipe@4 = dove_ffi:gunzip(
Body@2
),
gleam@result:map_error(
_pipe@4,
fun(
Error
) ->
{decompression_error,
Error}
end
)
end,
fun(
Decompressed
) ->
{ok,
{{Status,
Headers@2,
{some,
Decompressed}},
Rest@4}}
end
);
{ok,
<<"deflate"/utf8>>} ->
gleam@result:then(
begin
_pipe@5 = dove_ffi:inflate(
Body@2
),
gleam@result:map_error(
_pipe@5,
fun(
Error@1
) ->
{decompression_error,
Error@1}
end
)
end,
fun(
Decompressed@1
) ->
{ok,
{{Status,
Headers@2,
{some,
Decompressed@1}},
Rest@4}}
end
);
_ ->
{ok,
{{Status,
Headers@2,
{some,
Body@2}},
Rest@4}}
end;
none ->
{ok,
{{Status,
Headers@2,
none},
Rest@4}}
end
end
)
end
);
{ok, more} ->
{error, more_needed};
{error, nil} ->
{error, invalid_status_line};
_ ->
{error, wrong_packet_type}
end
end
)
end
)
end
)
end
).