0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00

Add sendfile support to cowboy_req:stream_body

It is now possible to stream one or more sendfile tuples.
A simple example of what can now be done would be for
example to build a tar file on the fly using the sendfile
syscall for sending the files, or to support Range requests
with more than one range with the sendfile syscall.

When using cowboy_compress_h unfortunately we have to read
the file in order to send it. More options will be added
at a later time to make sure users don't read too much
into memory. This is a new feature however so existing
code is not affected.

Also rework cowboy_http's data sending to be flatter.
This commit is contained in:
Loïc Hoguin 2018-11-09 17:42:37 +01:00
parent 29043aa7b4
commit d7b7580b39
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
12 changed files with 219 additions and 100 deletions

View file

@ -808,21 +808,35 @@ stream_reply(Status, Headers=#{}, Req=#{pid := Pid, streamid := StreamID})
Pid ! {{Pid, StreamID}, {headers, Status, response_headers(Headers, Req)}},
done_replying(Req, headers).
-spec stream_body(iodata(), fin | nofin, req()) -> ok.
-spec stream_body(resp_body(), fin | nofin, req()) -> ok.
%% Error out if headers were not sent.
%% Don't send any body for HEAD responses.
stream_body(_, _, #{method := <<"HEAD">>, has_sent_resp := headers}) ->
ok;
%% Don't send a message if the data is empty, except for the
%% very last message with IsFin=fin.
stream_body(Data, IsFin=nofin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
%% very last message with IsFin=fin. When using sendfile this
%% is converted to a data tuple, however.
stream_body({sendfile, _, 0, _}, nofin, _) ->
ok;
stream_body({sendfile, _, 0, _}, IsFin=fin,
#{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
Pid ! {{Pid, StreamID}, {data, IsFin, <<>>}},
ok;
stream_body({sendfile, O, B, P}, IsFin,
#{pid := Pid, streamid := StreamID, has_sent_resp := headers})
when is_integer(O), O >= 0, is_integer(B), B > 0 ->
Pid ! {{Pid, StreamID}, {data, IsFin, {sendfile, O, B, P}}},
ok;
stream_body(Data, IsFin=nofin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers})
when not is_tuple(Data) ->
case iolist_size(Data) of
0 -> ok;
_ ->
Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
ok
end;
stream_body(Data, IsFin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers}) ->
stream_body(Data, IsFin, #{pid := Pid, streamid := StreamID, has_sent_resp := headers})
when not is_tuple(Data) ->
Pid ! {{Pid, StreamID}, {data, IsFin, Data}},
ok.