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

Add an option to disable sendfile for a listener

This commit is contained in:
Loïc Hoguin 2018-11-03 18:55:40 +01:00
parent 571719a164
commit be09711687
No known key found for this signature in database
GPG key ID: 8A9DF795F6FED764
5 changed files with 48 additions and 8 deletions

View file

@ -32,6 +32,7 @@ opts() :: #{
max_skip_body_length => non_neg_integer(), max_skip_body_length => non_neg_integer(),
middlewares => [module()], middlewares => [module()],
request_timeout => timeout(), request_timeout => timeout(),
sendfile => boolean(),
shutdown_timeout => timeout(), shutdown_timeout => timeout(),
stream_handlers => [module()] stream_handlers => [module()]
} }
@ -96,6 +97,11 @@ middlewares ([cowboy_router, cowboy_handler])::
request_timeout (5000):: request_timeout (5000)::
Time in ms with no requests before Cowboy closes the connection. 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):: shutdown_timeout (5000)::
Time in ms Cowboy will wait for child processes to shut down before killing them. 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 == Changelog
* *2.6*: The `sendfile` option was added.
* *2.5*: The `linger_timeout` option was added. * *2.5*: The `linger_timeout` option was added.
* *2.2*: The `max_skip_body_length` option was added. * *2.2*: The `max_skip_body_length` option was added.
* *2.0*: The `timeout` option was renamed `request_timeout`. * *2.0*: The `timeout` option was renamed `request_timeout`.

View file

@ -30,6 +30,7 @@ opts() :: #{
max_frame_size_sent => 16384..16777215 | infinity, max_frame_size_sent => 16384..16777215 | infinity,
middlewares => [module()], middlewares => [module()],
preface_timeout => timeout(), preface_timeout => timeout(),
sendfile => boolean(),
settings_timeout => timeout(), settings_timeout => timeout(),
shutdown_timeout => timeout(), shutdown_timeout => timeout(),
stream_handlers => [module()] stream_handlers => [module()]
@ -119,6 +120,12 @@ preface_timeout (5000)::
Time in ms Cowboy is willing to wait for the connection preface. 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):: settings_timeout (5000)::
Time in ms Cowboy is willing to wait for a SETTINGS ack. 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 == Changelog
* *2.6*: The `sendfile` option was added.
* *2.4*: Add the options `initial_connection_window_size`, * *2.4*: Add the options `initial_connection_window_size`,
`initial_stream_window_size`, `max_concurrent_streams`, `initial_stream_window_size`, `max_concurrent_streams`,
`max_decode_table_size`, `max_encode_table_size`, `max_decode_table_size`, `max_encode_table_size`,

View file

@ -44,6 +44,7 @@
middlewares => [module()], middlewares => [module()],
proxy_header => boolean(), proxy_header => boolean(),
request_timeout => timeout(), request_timeout => timeout(),
sendfile => boolean(),
shutdown_timeout => timeout(), shutdown_timeout => timeout(),
stream_handlers => [module()], stream_handlers => [module()],
tracer_callback => cowboy_tracer_h:tracer_callback(), tracer_callback => cowboy_tracer_h:tracer_callback(),
@ -1050,7 +1051,7 @@ commands(State=#state{socket=Socket, transport=Transport, streams=Streams, out_s
end, end,
commands(State#state{out_state=done}, StreamID, Tail); commands(State#state{out_state=done}, StreamID, Tail);
%% Send a file. %% 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]) -> [{sendfile, IsFin, Offset, Bytes, Path}|Tail]) ->
%% @todo exit with response_body_too_large if we exceed content-length %% @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 %% 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 %% This try/catch prevents some noisy logs to be written
%% when these errors occur. %% when these errors occur.
try 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 State = case IsFin of
fin -> State0#state{out_state=done} fin -> State0#state{out_state=done}
%% @todo Add the sendfile command. %% @todo Add the sendfile command.

View file

@ -44,6 +44,7 @@
middlewares => [module()], middlewares => [module()],
preface_timeout => timeout(), preface_timeout => timeout(),
proxy_header => boolean(), proxy_header => boolean(),
sendfile => boolean(),
settings_timeout => timeout(), settings_timeout => timeout(),
shutdown_timeout => timeout(), shutdown_timeout => timeout(),
stream_handlers => [module()], stream_handlers => [module()],
@ -650,10 +651,14 @@ send_data_frame(State=#state{socket=Socket, transport=Transport},
StreamID, IsFin, {data, Data}) -> StreamID, IsFin, {data, Data}) ->
Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data)), Transport:send(Socket, cow_http2:data(StreamID, IsFin, Data)),
State; 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}) -> StreamID, IsFin, {sendfile, Offset, Bytes, Path}) ->
Transport:send(Socket, cow_http2:data_header(StreamID, IsFin, Bytes)), 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; State;
%% The stream is terminated in cow_http2_machine:prepare_trailers. %% The stream is terminated in cow_http2_machine:prepare_trailers.
send_data_frame(State=#state{socket=Socket, transport=Transport, send_data_frame(State=#state{socket=Socket, transport=Transport,

View file

@ -23,7 +23,10 @@
%% ct. %% ct.
all() -> all() ->
cowboy_test:common_all(). cowboy_test:common_all() ++ [
{group, http_no_sendfile},
{group, h2c_no_sendfile}
].
groups() -> groups() ->
AllTests = ct_helper:all(?MODULE), AllTests = ct_helper:all(?MODULE),
@ -44,7 +47,10 @@ groups() ->
{http_compress, [parallel], GroupTests}, {http_compress, [parallel], GroupTests},
{https_compress, [parallel], GroupTests}, {https_compress, [parallel], GroupTests},
{h2_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) -> init_per_suite(Config) ->
@ -94,8 +100,17 @@ init_per_group(dir, Config) ->
[{prefix, "/dir"}|Config]; [{prefix, "/dir"}|Config];
init_per_group(priv_dir, Config) -> init_per_group(priv_dir, Config) ->
[{prefix, "/priv_dir"}|Config]; [{prefix, "/priv_dir"}|Config];
init_per_group(tttt, Config) -> init_per_group(Name=http_no_sendfile, Config) ->
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) -> init_per_group(Name, Config) ->
cowboy_test:init_common_groups(Name, Config, ?MODULE). cowboy_test:init_common_groups(Name, Config, ?MODULE).