diff options
author | Loïc Hoguin <[email protected]> | 2018-11-09 17:42:37 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2018-11-09 17:42:37 +0100 |
commit | d7b7580b3913c17b404319cc4c153748d5e59194 (patch) | |
tree | 3026f38e4bcfdcb26d067a4e5aec6d401700c3cc /src/cowboy_compress_h.erl | |
parent | 29043aa7b4d11e377bc76d453f592ea5a6df1f43 (diff) | |
download | cowboy-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.erl | 29 |
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. |