From be09711687218070ee670be7d549df8338b4ae92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Sat, 3 Nov 2018 18:55:40 +0100 Subject: Add an option to disable sendfile for a listener --- doc/src/manual/cowboy_http.asciidoc | 7 +++++++ doc/src/manual/cowboy_http2.asciidoc | 8 ++++++++ src/cowboy_http.erl | 9 +++++++-- src/cowboy_http2.erl | 9 +++++++-- test/static_handler_SUITE.erl | 23 +++++++++++++++++++---- 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). -- cgit v1.2.3