mirror of
https://github.com/ninenines/cowboy.git
synced 2025-07-14 20:30:23 +00:00
Fix sending of large files with HTTP/2
Also finish implementing the relevant test, getting rid of todos.
This commit is contained in:
parent
f4fddbd0a1
commit
4cbdfdd293
2 changed files with 44 additions and 9 deletions
|
@ -441,8 +441,13 @@ commands(State=#state{socket=Socket, transport=Transport}, Stream=#stream{id=Str
|
||||||
%% implementation necessarily varies between HTTP/1.1 and HTTP/2.
|
%% implementation necessarily varies between HTTP/1.1 and HTTP/2.
|
||||||
commands(State=#state{socket=Socket, transport=Transport}, Stream=#stream{id=StreamID, local=nofin},
|
commands(State=#state{socket=Socket, transport=Transport}, Stream=#stream{id=StreamID, local=nofin},
|
||||||
[{sendfile, IsFin, Offset, Bytes, Path}|Tail]) ->
|
[{sendfile, IsFin, Offset, Bytes, Path}|Tail]) ->
|
||||||
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)),
|
%% @todo We currently have a naive implementation without a
|
||||||
Transport:sendfile(Socket, Path, Offset, Bytes),
|
%% scheduler to prioritize frames that need to be sent.
|
||||||
|
%% A future update will need to queue such data frames
|
||||||
|
%% and only send them when there is nothing currently
|
||||||
|
%% being sent. We would probably also benefit from doing
|
||||||
|
%% asynchronous sends.
|
||||||
|
sendfile(Socket, Transport, StreamID, IsFin, Offset, Bytes, Path, 16384),
|
||||||
commands(State, Stream#stream{local=IsFin}, Tail);
|
commands(State, Stream#stream{local=IsFin}, Tail);
|
||||||
%% @todo sendfile when local!=nofin
|
%% @todo sendfile when local!=nofin
|
||||||
%% Send a push promise.
|
%% Send a push promise.
|
||||||
|
@ -513,6 +518,19 @@ send_data(Socket, Transport, StreamID, IsFin, Data, Length) ->
|
||||||
Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data))
|
Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @todo This is currently awfully slow. But at least it's correct.
|
||||||
|
sendfile(Socket, Transport, StreamID, IsFin, Offset, Bytes, Path, Length) ->
|
||||||
|
if
|
||||||
|
Length < Bytes ->
|
||||||
|
Transport:send(Socket, cow_http2:data_header(StreamID, nofin, Length)),
|
||||||
|
Transport:sendfile(Socket, Path, Offset, Length),
|
||||||
|
sendfile(Socket, Transport, StreamID, IsFin,
|
||||||
|
Offset + Length, Bytes - Length, Path, Length);
|
||||||
|
true ->
|
||||||
|
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)),
|
||||||
|
Transport:sendfile(Socket, Path, Offset, Bytes)
|
||||||
|
end.
|
||||||
|
|
||||||
-spec terminate(#state{}, _) -> no_return().
|
-spec terminate(#state{}, _) -> no_return().
|
||||||
terminate(#state{socket=Socket, transport=Transport,
|
terminate(#state{socket=Socket, transport=Transport,
|
||||||
streams=Streams, children=Children}, Reason) ->
|
streams=Streams, children=Children}, Reason) ->
|
||||||
|
|
|
@ -391,13 +391,30 @@ dir_html(Config) ->
|
||||||
{_, <<"text/html">>} = lists:keyfind(<<"content-type">>, 1, Headers),
|
{_, <<"text/html">>} = lists:keyfind(<<"content-type">>, 1, Headers),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%% @todo This test results in a crash dump.
|
dir_large_file(Config) ->
|
||||||
%dir_large_file(Config) ->
|
doc("Get a large file."),
|
||||||
% doc("Get a large file."),
|
ConnPid = gun_open(Config),
|
||||||
% {200, Headers, _} = do_get(config(prefix, Config) ++ "/large.bin", Config),
|
Ref = gun:get(ConnPid, config(prefix, Config) ++ "/large.bin",
|
||||||
% {_, <<"text/html">>} = lists:keyfind(<<"content-type">>, 1, Headers),
|
[{<<"accept-encoding">>, <<"gzip">>}]),
|
||||||
%% @todo Receive body.
|
{response, nofin, 200, RespHeaders} = gun:await(ConnPid, Ref),
|
||||||
% ok.
|
{_, <<"application/octet-stream">>} = lists:keyfind(<<"content-type">>, 1, RespHeaders),
|
||||||
|
Size = 512*1024*1024,
|
||||||
|
{ok, Size} = do_dir_large_file(ConnPid, Ref, 0),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
do_dir_large_file(ConnPid, Ref, N) ->
|
||||||
|
receive
|
||||||
|
{gun_data, ConnPid, Ref, nofin, Data} ->
|
||||||
|
do_dir_large_file(ConnPid, Ref, N + byte_size(Data));
|
||||||
|
{gun_data, ConnPid, Ref, fin, Data} ->
|
||||||
|
{ok, N + byte_size(Data)};
|
||||||
|
{gun_error, ConnPid, Ref, Reason} ->
|
||||||
|
{error, Reason};
|
||||||
|
{gun_error, ConnPid, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
after 5000 ->
|
||||||
|
{error, timeout}
|
||||||
|
end.
|
||||||
|
|
||||||
dir_text(Config) ->
|
dir_text(Config) ->
|
||||||
doc("Get a .txt file. The extension is unknown by default."),
|
doc("Get a .txt file. The extension is unknown by default."),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue