From 7db724f04a0a8468424960790d8414aa374b249e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Fri, 5 May 2017 13:48:25 +0200 Subject: Add inactivity_timeout and other options improvements --- doc/src/manual/cowboy_http.asciidoc | 57 ++++++++++++++++++++++++------------ doc/src/manual/cowboy_http2.asciidoc | 25 ++++++++++++++-- src/cowboy_http.erl | 48 +++++++++++++----------------- src/cowboy_http2.erl | 19 +++++++++--- src/cowboy_stream_h.erl | 2 +- 5 files changed, 98 insertions(+), 53 deletions(-) diff --git a/doc/src/manual/cowboy_http.asciidoc b/doc/src/manual/cowboy_http.asciidoc index 949fa88..45d5464 100644 --- a/doc/src/manual/cowboy_http.asciidoc +++ b/doc/src/manual/cowboy_http.asciidoc @@ -11,25 +11,30 @@ as a Ranch protocol. == Options +// @todo Might be worth moving cowboy_clear/tls/stream_h options +// to their respective manual, when they are added. + [source,erlang] ---- opts() :: #{ - env := cowboy_middleware:env(), - idle_timeout := timeout(), - max_empty_lines := non_neg_integer(), - max_header_name_length := non_neg_integer(), - max_header_value_length := non_neg_integer(), - max_headers := non_neg_integer(), - max_keepalive := non_neg_integer(), - max_method_length := non_neg_integer(), - max_request_line_length := non_neg_integer(), - middlewares := [module()], - request_timeout := timeout() + connection_type => worker | supervisor, + env => cowboy_middleware:env(), + idle_timeout => timeout(), + inactivity_timeout => timeout(), + max_empty_lines => non_neg_integer(), + max_header_name_length => non_neg_integer(), + max_header_value_length => non_neg_integer(), + max_headers => non_neg_integer(), + max_keepalive => non_neg_integer(), + max_method_length => non_neg_integer(), + max_request_line_length => non_neg_integer(), + middlewares => [module()], + request_timeout => timeout(), + shutdown_timeout => timeout(), + stream_handlers => [module()] } ---- -// @todo Document the shutdown option. - Configuration for the HTTP/1.1 protocol. This configuration is passed to Cowboy when starting listeners @@ -41,12 +46,18 @@ Ranch functions `ranch:get_protocol_options/1` and The default value is given next to the option name: +connection_type (supervisor):: + Whether the connection process also acts as a supervisor. + env (#{}):: Middleware environment. idle_timeout (60000):: Time in ms with no data received before Cowboy closes the connection. +inactivity_timeout (300000):: + Time in ms with nothing received at all before Cowboy closes the connection. + max_empty_lines (5):: Maximum number of empty lines before a request. @@ -62,24 +73,34 @@ max_headers (100):: max_keepalive (100):: Maximum number of requests allowed per connection. -max_request_line_length (8000):: - Maximum length of the request line. - max_method_length (32):: Maximum length of the method. +max_request_line_length (8000):: + Maximum length of the request line. + middlewares ([cowboy_router, cowboy_handler]):: Middlewares to run for every request. request_timeout (5000):: Time in ms with no requests before Cowboy closes the connection. +shutdown_timeout (5000):: + Time in ms Cowboy will wait for child processes to shut down before killing them. + +stream_handlers ([cowboy_stream_h]):: + Ordered list of stream handlers that will handle all stream events. + == Changelog +* *2.0*: The `timeout` option was renamed `request_timeout`. +* *2.0*: The `idle_timeout`, `inactivity_timeout` and `shutdown_timeout` options were added. * *2.0*: The `max_method_length` option was added. -* *2.0*: The `env` option is now a map instead of a proplist. * *2.0*: The `max_request_line_length` default was increased from 4096 to 8000. -* *2.0*: The `compress` option was temporarily removed. +* *2.0*: The `connection_type` option was added. +* *2.0*: The `env` option is now a map instead of a proplist. +* *2.0*: The `stream_handlers` option was added. +* *2.0*: The `compress` option was removed in favor of the `cowboy_compress_h` stream handler. * *2.0*: Options are now a map instead of a proplist. * *2.0*: Protocol introduced. Replaces `cowboy_protocol`. diff --git a/doc/src/manual/cowboy_http2.asciidoc b/doc/src/manual/cowboy_http2.asciidoc index d82bc76..d966459 100644 --- a/doc/src/manual/cowboy_http2.asciidoc +++ b/doc/src/manual/cowboy_http2.asciidoc @@ -11,12 +11,19 @@ as a Ranch protocol. == Options +// @todo Might be worth moving cowboy_clear/tls/stream_h options +// to their respective manual, when they are added. + [source,erlang] ---- opts() :: #{ - env := cowboy_middleware:env(), - middlewares := [module()], - preface_timeout := timeout() + connection_type => worker | supervisor, + env => cowboy_middleware:env(), + inactivity_timeout => timeout(), + middlewares => [module()], + preface_timeout => timeout(), + shutdown_timeout => timeout(), + stream_handlers => [module()] } ---- @@ -31,15 +38,27 @@ Ranch functions `ranch:get_protocol_options/1` and The default value is given next to the option name: +connection_type (supervisor):: + Whether the connection process also acts as a supervisor. + env (#{}):: Middleware environment. +inactivity_timeout (300000):: + Time in ms with nothing received at all before Cowboy closes the connection. + middlewares ([cowboy_router, cowboy_handler]):: Middlewares to run for every request. preface_timeout (5000):: Time in ms Cowboy is willing to wait for the connection preface. +shutdown_timeout (5000):: + Time in ms Cowboy will wait for child processes to shut down before killing them. + +stream_handlers ([cowboy_stream_h]):: + Ordered list of stream handlers that will handle all stream events. + == Changelog * *2.0*: Protocol introduced. diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index 9c2bf30..52b856f 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -20,18 +20,23 @@ -export([system_terminate/4]). -export([system_code_change/4]). -%% @todo map --type opts() :: [{compress, boolean()} - | {env, cowboy_middleware:env()} - | {max_empty_lines, non_neg_integer()} - | {max_header_name_length, non_neg_integer()} - | {max_header_value_length, non_neg_integer()} - | {max_headers, non_neg_integer()} - | {max_keepalive, non_neg_integer()} - | {max_request_line_length, non_neg_integer()} - | {middlewares, [module()]} - | {onresponse, cowboy:onresponse_fun()} - | {timeout, timeout()}]. +-type opts() :: #{ + connection_type => worker | supervisor, + env => cowboy_middleware:env(), + idle_timeout => timeout(), + inactivity_timeout => timeout(), + max_empty_lines => non_neg_integer(), + max_header_name_length => non_neg_integer(), + max_header_value_length => non_neg_integer(), + max_headers => non_neg_integer(), + max_keepalive => non_neg_integer(), + max_method_length => non_neg_integer(), + max_request_line_length => non_neg_integer(), + middlewares => [module()], + request_timeout => timeout(), + shutdown_timeout => timeout(), + stream_handlers => [module()] +}. -export_type([opts/0]). -record(ps_request_line, { @@ -131,17 +136,7 @@ init(Parent, Ref, Socket, Transport, Opts) -> end. %% @todo Send a response depending on in_state and whether one was already sent. - -%% @todo -%% Timeouts: -%% - waiting for new request (if no stream is currently running) -%% -> request_timeout: for whole request/headers, set at init/when we set ps_request_line{} state -%% - waiting for new request, or body (when a stream is currently running) -%% -> idle_timeout: amount of time we wait without receiving any data -%% - if we skip the body, skip only for a specific duration -%% -> skip_body_timeout: also have a skip_body_length -%% - global -%% -> inactivity_timeout: max time to wait without anything happening before giving up +%% @todo If we skip the body, skip for a specific duration. before_loop(State=#state{socket=Socket, transport=Transport}, Buffer) -> %% @todo disable this when we get to the body, until the stream asks for it? @@ -149,9 +144,10 @@ before_loop(State=#state{socket=Socket, transport=Transport}, Buffer) -> Transport:setopts(Socket, [{active, once}]), loop(State, Buffer). -loop(State=#state{parent=Parent, socket=Socket, transport=Transport, +loop(State=#state{parent=Parent, socket=Socket, transport=Transport, opts=Opts, timer=TimerRef, children=Children, streams=Streams}, Buffer) -> {OK, Closed, Error} = Transport:messages(), + InactivityTimeout = maps:get(inactivity_timeout, Opts, 300000), receive %% Socket messages. {OK, Socket, Data} -> @@ -199,9 +195,7 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport, Msg -> error_logger:error_msg("Received stray message ~p.~n", [Msg]), loop(State, Buffer) - %% @todo Configurable timeout. This should be a global inactivity timeout - %% that triggers when really nothing happens (ie something went really wrong). - after 300000 -> + after InactivityTimeout -> terminate(State, {internal_error, timeout, 'No message or data received before timeout.'}) end. diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl index a280c5f..e6e8a8b 100644 --- a/src/cowboy_http2.erl +++ b/src/cowboy_http2.erl @@ -22,6 +22,17 @@ -export([system_terminate/4]). -export([system_code_change/4]). +-type opts() :: #{ + connection_type => worker | supervisor, + env => cowboy_middleware:env(), + inactivity_timeout => timeout(), + middlewares => [module()], + preface_timeout => timeout(), + shutdown_timeout => timeout(), + stream_handlers => [module()] +}. +-export_type([opts/0]). + -record(stream, { id = undefined :: cowboy_stream:streamid(), %% Stream handlers and their state. @@ -41,7 +52,7 @@ ref :: ranch:ref(), socket = undefined :: inet:socket(), transport :: module(), - opts = #{} :: map(), + opts = #{} :: opts(), %% Remote address and port for the connection. peer = undefined :: {inet:ip_address(), inet:port_number()}, @@ -151,9 +162,10 @@ before_loop(State, Buffer) -> loop(State, Buffer). loop(State=#state{parent=Parent, socket=Socket, transport=Transport, - children=Children, parse_state=PS}, Buffer) -> + opts=Opts, children=Children, parse_state=PS}, Buffer) -> Transport:setopts(Socket, [{active, once}]), {OK, Closed, Error} = Transport:messages(), + InactivityTimeout = maps:get(inactivity_timeout, Opts, 300000), receive %% Socket messages. {OK, Socket, Data} -> @@ -198,8 +210,7 @@ loop(State=#state{parent=Parent, socket=Socket, transport=Transport, Msg -> error_logger:error_msg("Received stray message ~p.", [Msg]), loop(State, Buffer) - %% @todo Configurable timeout. - after 60000 -> + after InactivityTimeout -> terminate(State, {internal_error, timeout, 'No message or data received before timeout.'}) end. diff --git a/src/cowboy_stream_h.erl b/src/cowboy_stream_h.erl index ec32ff0..5d64e8d 100644 --- a/src/cowboy_stream_h.erl +++ b/src/cowboy_stream_h.erl @@ -46,7 +46,7 @@ init(_StreamID, Req=#{ref := Ref}, Opts) -> Env = maps:get(env, Opts, #{}), Middlewares = maps:get(middlewares, Opts, [cowboy_router, cowboy_handler]), - Shutdown = maps:get(shutdown, Opts, 5000), + Shutdown = maps:get(shutdown_timeout, Opts, 5000), Pid = proc_lib:spawn_link(?MODULE, proc_lib_hack, [Req, Env, Middlewares]), {[{spawn, Pid, Shutdown}], #state{ref=Ref, pid=Pid}}. -- cgit v1.2.3