aboutsummaryrefslogtreecommitdiffstats
path: root/src/cowboy_compress_h.erl
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-11-09 17:42:37 +0100
committerLoïc Hoguin <[email protected]>2018-11-09 17:42:37 +0100
commitd7b7580b3913c17b404319cc4c153748d5e59194 (patch)
tree3026f38e4bcfdcb26d067a4e5aec6d401700c3cc /src/cowboy_compress_h.erl
parent29043aa7b4d11e377bc76d453f592ea5a6df1f43 (diff)
downloadcowboy-d7b7580b3913c17b404319cc4c153748d5e59194.tar.gz
cowboy-d7b7580b3913c17b404319cc4c153748d5e59194.tar.bz2
cowboy-d7b7580b3913c17b404319cc4c153748d5e59194.zip
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.
Diffstat (limited to 'src/cowboy_compress_h.erl')
-rw-r--r--src/cowboy_compress_h.erl29
1 files changed, 28 insertions, 1 deletions
diff --git a/src/cowboy_compress_h.erl b/src/cowboy_compress_h.erl
index 781cc0a..ed81db6 100644
--- a/src/cowboy_compress_h.erl
+++ b/src/cowboy_compress_h.erl
@@ -98,7 +98,7 @@ fold(Commands, State) ->
fold([], State, Acc) ->
{lists:reverse(Acc), State};
-%% We do not compress sendfile bodies.
+%% We do not compress full sendfile bodies.
fold([Response={response, _, _, {sendfile, _, _, _}}|Tail], State, Acc) ->
fold(Tail, State, [Response|Acc]);
%% We compress full responses directly, unless they are lower than
@@ -171,6 +171,21 @@ gzip_headers({headers, Status, Headers0}, State) ->
<<"content-encoding">> => <<"gzip">>
}}, State#state{deflate=Z}}.
+%% It is not possible to combine zlib and the sendfile
+%% syscall as far as I can tell, because the zlib format
+%% includes a checksum at the end of the stream. We have
+%% to read the file in memory, making this not suitable for
+%% large files.
+gzip_data({data, nofin, Sendfile={sendfile, _, _, _}}, State=#state{deflate=Z}) ->
+ {ok, Data0} = read_file(Sendfile),
+ Data = zlib:deflate(Z, Data0),
+ {{data, nofin, Data}, State};
+gzip_data({data, fin, Sendfile={sendfile, _, _, _}}, State=#state{deflate=Z}) ->
+ {ok, Data0} = read_file(Sendfile),
+ Data = zlib:deflate(Z, Data0, finish),
+ zlib:deflateEnd(Z),
+ zlib:close(Z),
+ {{data, fin, Data}, State#state{deflate=undefined}};
gzip_data({data, nofin, Data0}, State=#state{deflate=Z}) ->
Data = zlib:deflate(Z, Data0),
{{data, nofin, Data}, State};
@@ -179,3 +194,15 @@ gzip_data({data, fin, Data0}, State=#state{deflate=Z}) ->
zlib:deflateEnd(Z),
zlib:close(Z),
{{data, fin, Data}, State#state{deflate=undefined}}.
+
+read_file({sendfile, Offset, Bytes, Path}) ->
+ {ok, IoDevice} = file:open(Path, [read, raw, binary]),
+ try
+ _ = case Offset of
+ 0 -> ok;
+ _ -> file:position(IoDevice, {bof, Offset})
+ end,
+ file:read(IoDevice, Bytes)
+ after
+ file:close(IoDevice)
+ end.