Skip to content

Commit c7cfe7e

Browse files
committed
fix large bodies (from atomvm_httpd)
Signed-off-by: harmon25 <dougwright1@gmail.com>
1 parent 78c0511 commit c7cfe7e

File tree

2 files changed

+54
-12
lines changed

2 files changed

+54
-12
lines changed

src/gen_tcp_server.erl

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
addr => any
4848
}).
4949
-define(DEFAULT_SOCKET_OPTIONS, #{}).
50+
-define(MAX_SEND_CHUNK, 2048).
5051

5152
%%
5253
%% API
@@ -168,16 +169,7 @@ try_send(Socket, Packet) when is_binary(Packet) ->
168169
"Trying to send binary packet data to socket ~p. Packet (or len): ~p", [
169170
Socket, case byte_size(Packet) < 32 of true -> Packet; _ -> byte_size(Packet) end
170171
]),
171-
case socket:send(Socket, Packet) of
172-
ok ->
173-
?TRACE("sent.", []),
174-
ok;
175-
{ok, Rest} ->
176-
?TRACE("sent. remaining: ~p", [Rest]),
177-
try_send(Socket, Rest);
178-
Error ->
179-
io:format("Send failed due to error ~p~n", [Error])
180-
end;
172+
try_send_binary(Socket, Packet);
181173
try_send(Socket, Char) when is_integer(Char) ->
182174
%% TODO handle unicode
183175
?TRACE("Sending char ~p as ~p", [Char, <<Char:8>>]),
@@ -196,6 +188,20 @@ try_send_iolist(Socket, [H | T]) ->
196188
try_send(Socket, H),
197189
try_send_iolist(Socket, T).
198190

191+
try_send_binary(_Socket, <<>>) ->
192+
ok;
193+
try_send_binary(Socket, Packet) when is_binary(Packet) ->
194+
ChunkSize = erlang:min(byte_size(Packet), ?MAX_SEND_CHUNK),
195+
<<Chunk:ChunkSize/binary, Rest/binary>> = Packet,
196+
case socket:send(Socket, Chunk) of
197+
ok ->
198+
try_send_binary(Socket, Rest);
199+
{ok, Remaining} ->
200+
try_send_binary(Socket, <<Remaining/binary, Rest/binary>>);
201+
Error ->
202+
io:format("Send failed due to error ~p~n", [Error])
203+
end.
204+
199205
is_string([]) ->
200206
true;
201207
is_string([H | T]) when is_integer(H) ->

src/httpd.erl

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -570,21 +570,49 @@ create_error(StatusCode, Error) ->
570570
create_reply(StatusCode, ContentType, Reply) when is_list(ContentType) orelse is_binary(ContentType) ->
571571
create_reply(StatusCode, #{"Content-Type" => ContentType}, Reply);
572572
create_reply(StatusCode, Headers, Reply) when is_map(Headers) ->
573+
ReplyLen = iolist_length(Reply),
574+
HeadersWithLen = ensure_content_length(Headers, ReplyLen),
573575
[
574576
<<"HTTP/1.1 ">>, erlang:integer_to_binary(StatusCode), <<" ">>, moniker(StatusCode),
575577
<<"\r\n">>,
576-
io_lib:format("Server: atomvm-~s\r\n", [get_version_str(erlang:system_info(atomvm_version))]),
577-
to_headers_list(Headers),
578+
io_lib:format("Server: atomvm-~s\r\n", [get_version_str(get_atomvm_version())]),
579+
to_headers_list(HeadersWithLen),
578580
<<"\r\n">>,
579581
Reply
580582
].
581583

584+
%% @private
585+
ensure_content_length(Headers, ReplyLen) ->
586+
LenBin = erlang:integer_to_binary(ReplyLen),
587+
CleanHeaders = remove_content_length_header(Headers),
588+
CleanHeaders#{<<"Content-Length">> => LenBin}.
589+
590+
%% @private
591+
remove_content_length_header(Headers) ->
592+
KeysToRemove = [
593+
"Content-Length",
594+
<<"Content-Length">>,
595+
"content-length",
596+
<<"content-length">>
597+
],
598+
lists:foldl(fun(Key, Acc) -> maps:remove(Key, Acc) end, Headers, KeysToRemove).
599+
582600
%% @private
583601
maybe_binary_to_string(Bin) when is_binary(Bin) ->
584602
erlang:binary_to_list(Bin);
585603
maybe_binary_to_string(Other) ->
586604
Other.
587605

606+
%% @private
607+
iolist_length(Bin) when is_binary(Bin) ->
608+
erlang:byte_size(Bin);
609+
iolist_length(Int) when is_integer(Int), Int >= 0, Int =< 255 ->
610+
1;
611+
iolist_length([]) ->
612+
0;
613+
iolist_length([H | T]) ->
614+
iolist_length(H) + iolist_length(T).
615+
588616
%% @private
589617
to_headers_list(Headers) ->
590618
[io_lib:format("~s: ~s\r\n", [maybe_binary_to_string(Key), maybe_binary_to_string(Value)]) || {Key, Value} <- maps:to_list(Headers)].
@@ -596,6 +624,14 @@ get_version_str(Version) when is_binary(Version) ->
596624
get_version_str(_) ->
597625
"unknown".
598626

627+
get_atomvm_version() ->
628+
case catch erlang:system_info(atomvm_version) of
629+
{'EXIT', _} ->
630+
undefined;
631+
Version ->
632+
Version
633+
end.
634+
599635
%% @private
600636
moniker(?OK) ->
601637
<<"OK">>;

0 commit comments

Comments
 (0)