aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2018-11-03 18:55:40 +0100
committerLoïc Hoguin <[email protected]>2018-11-03 18:55:40 +0100
commitbe09711687218070ee670be7d549df8338b4ae92 (patch)
tree3fd0a125066b2d6eebbe4aee6190e426771c2ee4
parent571719a164326eebdc792b43170fe27f123aac0d (diff)
downloadcowboy-be09711687218070ee670be7d549df8338b4ae92.tar.gz
cowboy-be09711687218070ee670be7d549df8338b4ae92.tar.bz2
cowboy-be09711687218070ee670be7d549df8338b4ae92.zip
Add an option to disable sendfile for a listener
-rw-r--r--doc/src/manual/cowboy_http.asciidoc7
-rw-r--r--doc/src/manual/cowboy_http2.asciidoc8
-rw-r--r--src/cowboy_http.erl9
-rw-r--r--src/cowboy_http2.erl9
-rw-r--r--test/static_handler_SUITE.erl23
5 files changed, 48 insertions, 8 deletions
diff --git a/doc/src/manual/cowboy_http.asciidoc b/doc/src/manual/cowboy_http.asciidoc
index 21a6d65..3592456 100644
--- a/doc/src/manual/cowboy_http.asciidoc
+++ b/doc/src/manual/cowboy_http.asciidoc
@@ -32,6 +32,7 @@ opts() :: #{
max_skip_body_length => non_neg_integer(),
middlewares => [module()],
request_timeout => timeout(),
+ sendfile => boolean(),
shutdown_timeout => timeout(),
stream_handlers => [module()]
}
@@ -96,6 +97,11 @@ middlewares ([cowboy_router, cowboy_handler])::
request_timeout (5000)::
Time in ms with no requests before Cowboy closes the connection.
+sendfile (true)::
+ Whether the sendfile syscall may be used. It can be useful to disable
+ it on systems where the syscall has a buggy implementation, for example
+ under VirtualBox when using shared folders.
+
shutdown_timeout (5000)::
Time in ms Cowboy will wait for child processes to shut down before killing them.
@@ -104,6 +110,7 @@ stream_handlers ([cowboy_stream_h])::
== Changelog
+* *2.6*: The `sendfile` option was added.
* *2.5*: The `linger_timeout` option was added.
* *2.2*: The `max_skip_body_length` option was added.
* *2.0*: The `timeout` option was renamed `request_timeout`.
diff --git a/doc/src/manual/cowboy_http2.asciidoc b/doc/src/manual/cowboy_http2.asciidoc
index 8cba7b0..ea7110b 100644
--- a/doc/src/manual/cowboy_http2.asciidoc
+++ b/doc/src/manual/cowboy_http2.asciidoc
@@ -30,6 +30,7 @@ opts() :: #{
max_frame_size_sent => 16384..16777215 | infinity,
middlewares => [module()],
preface_timeout => timeout(),
+ sendfile => boolean(),
settings_timeout => timeout(),
shutdown_timeout => timeout(),
stream_handlers => [module()]
@@ -119,6 +120,12 @@ preface_timeout (5000)::
Time in ms Cowboy is willing to wait for the connection preface.
+sendfile (true)::
+
+Whether the sendfile syscall may be used. It can be useful to disable
+it on systems where the syscall has a buggy implementation, for example
+under VirtualBox when using shared folders.
+
settings_timeout (5000)::
Time in ms Cowboy is willing to wait for a SETTINGS ack.
@@ -133,6 +140,7 @@ Ordered list of stream handlers that will handle all stream events.
== Changelog
+* *2.6*: The `sendfile` option was added.
* *2.4*: Add the options `initial_connection_window_size`,
`initial_stream_window_size`, `max_concurrent_streams`,
`max_decode_table_size`, `max_encode_table_size`,
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl
index 91a539f..9ce7aa8 100644
--- a/src/cowboy_http.erl
+++ b/src/cowboy_http.erl
@@ -44,6 +44,7 @@
middlewares => [module()],
proxy_header => boolean(),
request_timeout => timeout(),
+ sendfile => boolean(),
shutdown_timeout => timeout(),
stream_handlers => [module()],
tracer_callback => cowboy_tracer_h:tracer_callback(),
@@ -1050,7 +1051,7 @@ commands(State=#state{socket=Socket, transport=Transport, streams=Streams, out_s
end,
commands(State#state{out_state=done}, StreamID, Tail);
%% Send a file.
-commands(State0=#state{socket=Socket, transport=Transport}, StreamID,
+commands(State0=#state{socket=Socket, transport=Transport, opts=Opts}, StreamID,
[{sendfile, IsFin, Offset, Bytes, Path}|Tail]) ->
%% @todo exit with response_body_too_large if we exceed content-length
%% We wrap the sendfile call into a try/catch because on OTP-20
@@ -1066,7 +1067,11 @@ commands(State0=#state{socket=Socket, transport=Transport}, StreamID,
%% This try/catch prevents some noisy logs to be written
%% when these errors occur.
try
- Transport:sendfile(Socket, Path, Offset, Bytes),
+ %% When sendfile is disabled we explicitly use the fallback.
+ _ = case maps:get(sendfile, Opts, true) of
+ true -> Transport:sendfile(Socket, Path, Offset, Bytes);
+ false -> ranch_transport:sendfile(Transport, Socket, Path, Offset, Bytes, [])
+ end,
State = case IsFin of
fin -> State0#state{out_state=done}
%% @todo Add the sendfile command.
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index a1dd93e..d461595 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -44,6 +44,7 @@
middlewares => [module()],
preface_timeout => timeout(),
proxy_header => boolean(),
+ sendfile => boolean(),
settings_timeout => timeout(),
shutdown_timeout => timeout(),
stream_handlers => [module()],
@@ -650,10 +651,14 @@ send_data_frame(State=#state{socket=Socket, transport=Transport},
StreamID, IsFin, {data, Data}) ->
Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data)),
State;
-send_data_frame(State=#state{socket=Socket, transport=Transport},
+send_data_frame(State=#state{socket=Socket, transport=Transport, opts=Opts},
StreamID, IsFin, {sendfile, Offset, Bytes, Path}) ->
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)),
- Transport:sendfile(Socket, Path, Offset, Bytes),
+ %% When sendfile is disabled we explicitly use the fallback.
+ _ = case maps:get(sendfile, Opts, true) of
+ true -> Transport:sendfile(Socket, Path, Offset, Bytes);
+ false -> ranch_transport:sendfile(Transport, Socket, Path, Offset, Bytes, [])
+ end,
State;
%% The stream is terminated in cow_http2_machine:prepare_trailers.
send_data_frame(State=#state{socket=Socket, transport=Transport,
diff --git a/test/static_handler_SUITE.erl b/test/static_handler_SUITE.erl
index 55c3d2a..6467f5b 100644
--- a/test/static_handler_SUITE.erl
+++ b/test/static_handler_SUITE.erl
@@ -23,7 +23,10 @@
%% ct.
all() ->
- cowboy_test:common_all().
+ cowboy_test:common_all() ++ [
+ {group, http_no_sendfile},
+ {group, h2c_no_sendfile}
+ ].
groups() ->
AllTests = ct_helper:all(?MODULE),
@@ -44,7 +47,10 @@ groups() ->
{http_compress, [parallel], GroupTests},
{https_compress, [parallel], GroupTests},
{h2_compress, [parallel], GroupTests},
- {h2c_compress, [parallel], GroupTests}
+ {h2c_compress, [parallel], GroupTests},
+ %% No real need to test sendfile disabled against https or h2.
+ {http_no_sendfile, [parallel], GroupTests},
+ {h2c_no_sendfile, [parallel], GroupTests}
].
init_per_suite(Config) ->
@@ -94,8 +100,17 @@ init_per_group(dir, Config) ->
[{prefix, "/dir"}|Config];
init_per_group(priv_dir, Config) ->
[{prefix, "/priv_dir"}|Config];
-init_per_group(tttt, Config) ->
- Config;
+init_per_group(Name=http_no_sendfile, Config) ->
+ cowboy_test:init_http(Name, #{
+ env => #{dispatch => init_dispatch(Config)},
+ sendfile => false
+ }, [{flavor, vanilla}|Config]);
+init_per_group(Name=h2c_no_sendfile, Config) ->
+ Config1 = cowboy_test:init_http(Name, #{
+ env => #{dispatch => init_dispatch(Config)},
+ sendfile => false
+ }, [{flavor, vanilla}|Config]),
+ lists:keyreplace(protocol, 1, Config1, {protocol, http2});
init_per_group(Name, Config) ->
cowboy_test:init_common_groups(Name, Config, ?MODULE).