From 24f28fd2b8c825f673a7ce696781e9176da112a2 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Tue, 24 Jul 2018 14:18:09 -0700 Subject: [PATCH 1/7] rpc: Make HTTP server timeout values configurable --- cmd/clef/main.go | 2 +- cmd/geth/main.go | 3 +++ cmd/geth/usage.go | 3 +++ cmd/utils/flags.go | 27 ++++++++++++++++++++++++++ node/api.go | 2 +- node/config.go | 3 +++ node/defaults.go | 2 ++ node/node.go | 6 +++--- rpc/endpoints.go | 4 ++-- rpc/http.go | 47 ++++++++++++++++++++++++++++++++++++++++++---- 10 files changed, 88 insertions(+), 11 deletions(-) diff --git a/cmd/clef/main.go b/cmd/clef/main.go index 348bcb22f63e..5b94652bcad5 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -415,7 +415,7 @@ func signer(c *cli.Context) error { // start http server httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name)) - listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts) + listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.NewDefaultHTTPTimeouts()) if err != nil { utils.Fatalf("Could not start RPC api: %v", err) } diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 77ef6afe245b..24cabc3f39fd 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -130,6 +130,9 @@ var ( utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCApiFlag, + utils.RPCReadTimeoutFlag, + utils.RPCWriteTimeoutFlag, + utils.RPCIdleTimeoutFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index 6a12a66cc22b..fb5629be7b52 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -149,6 +149,9 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCApiFlag, + utils.RPCReadTimeoutFlag, + utils.RPCWriteTimeoutFlag, + utils.RPCIdleTimeoutFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 522ad06b61c2..5c53fed55d54 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -57,6 +57,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" "gopkg.in/urfave/cli.v1" ) @@ -385,6 +386,21 @@ var ( Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort, } + RPCReadTimeoutFlag = cli.DurationFlag{ + Name: "rpcreadtimeout", + Usage: "HTTP-RPC server read timeout", + Value: rpc.DefaultHTTPReadTimeout, + } + RPCWriteTimeoutFlag = cli.DurationFlag{ + Name: "rpcwritetimeout", + Usage: "HTTP-RPC server write timeout", + Value: rpc.DefaultHTTPWriteTimeout, + } + RPCIdleTimeoutFlag = cli.DurationFlag{ + Name: "rpcidletimeout", + Usage: "HTTP-RPC server idle timeout", + Value: rpc.DefaultHTTPIdleTimeout, + } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", @@ -729,6 +745,17 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) { cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name)) } + + cfg.HTTPTimeouts = rpc.NewDefaultHTTPTimeouts() + if ctx.GlobalIsSet(RPCReadTimeoutFlag.Name) { + cfg.HTTPTimeouts.ReadTimeout = ctx.GlobalDuration(RPCReadTimeoutFlag.Name) + } + if ctx.GlobalIsSet(RPCWriteTimeoutFlag.Name) { + cfg.HTTPTimeouts.WriteTimeout = ctx.GlobalDuration(RPCWriteTimeoutFlag.Name) + } + if ctx.GlobalIsSet(RPCIdleTimeoutFlag.Name) { + cfg.HTTPTimeouts.IdleTimeout = ctx.GlobalDuration(RPCIdleTimeoutFlag.Name) + } } // setWS creates the WebSocket RPC listener interface string from the set diff --git a/node/api.go b/node/api.go index 117b76b6d872..f44c99153989 100644 --- a/node/api.go +++ b/node/api.go @@ -157,7 +157,7 @@ func (api *PrivateAdminAPI) StartRPC(host *string, port *int, cors *string, apis } } - if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts); err != nil { + if err := api.node.startHTTP(fmt.Sprintf("%s:%d", *host, *port), api.node.rpcAPIs, modules, allowedOrigins, allowedVHosts, api.node.config.HTTPTimeouts); err != nil { return false, err } return true, nil diff --git a/node/config.go b/node/config.go index 1b75baaeb26c..2035c3b3a083 100644 --- a/node/config.go +++ b/node/config.go @@ -33,6 +33,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/discover" + "github.com/ethereum/go-ethereum/rpc" ) const ( @@ -119,6 +120,8 @@ type Config struct { // exposed. HTTPModules []string `toml:",omitempty"` + HTTPTimeouts rpc.HTTPTimeouts + // WSHost is the host interface on which to start the websocket RPC server. If // this field is empty, no websocket API endpoint will be started. WSHost string `toml:",omitempty"` diff --git a/node/defaults.go b/node/defaults.go index 8875605807d9..d818ba3eb432 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/nat" + "github.com/ethereum/go-ethereum/rpc" ) const ( @@ -39,6 +40,7 @@ var DefaultConfig = Config{ HTTPPort: DefaultHTTPPort, HTTPModules: []string{"net", "web3"}, HTTPVirtualHosts: []string{"localhost"}, + HTTPTimeouts: rpc.NewDefaultHTTPTimeouts(), WSPort: DefaultWSPort, WSModules: []string{"net", "web3"}, P2P: p2p.Config{ diff --git a/node/node.go b/node/node.go index 7cc11b781adf..ada3837217ab 100644 --- a/node/node.go +++ b/node/node.go @@ -263,7 +263,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error { n.stopInProc() return err } - if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts); err != nil { + if err := n.startHTTP(n.httpEndpoint, apis, n.config.HTTPModules, n.config.HTTPCors, n.config.HTTPVirtualHosts, n.config.HTTPTimeouts); err != nil { n.stopIPC() n.stopInProc() return err @@ -331,12 +331,12 @@ func (n *Node) stopIPC() { } // startHTTP initializes and starts the HTTP RPC endpoint. -func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string) error { +func (n *Node) startHTTP(endpoint string, apis []rpc.API, modules []string, cors []string, vhosts []string, timeouts rpc.HTTPTimeouts) error { // Short circuit if the HTTP endpoint isn't being exposed if endpoint == "" { return nil } - listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts) + listener, handler, err := rpc.StartHTTPEndpoint(endpoint, apis, modules, cors, vhosts, timeouts) if err != nil { return err } diff --git a/rpc/endpoints.go b/rpc/endpoints.go index 692c62d3a49d..8ca6d4eb0c6d 100644 --- a/rpc/endpoints.go +++ b/rpc/endpoints.go @@ -23,7 +23,7 @@ import ( ) // StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules -func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string) (net.Listener, *Server, error) { +func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []string, vhosts []string, timeouts HTTPTimeouts) (net.Listener, *Server, error) { // Generate the whitelist based on the allowed modules whitelist := make(map[string]bool) for _, module := range modules { @@ -47,7 +47,7 @@ func StartHTTPEndpoint(endpoint string, apis []API, modules []string, cors []str if listener, err = net.Listen("tcp", endpoint); err != nil { return nil, nil, err } - go NewHTTPServer(cors, vhosts, handler).Serve(listener) + go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener) return listener, handler, err } diff --git a/rpc/http.go b/rpc/http.go index 6388d689613d..702f7f78edf4 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -66,6 +66,44 @@ func (hc *httpConn) Close() error { return nil } +// HTTPTimeouts represents the configuration params for the HTTP RPC server. +type HTTPTimeouts struct { + // ReadTimeout is the maximum duration for reading the entire + // request, including the body. + // + // Because ReadTimeout does not let Handlers make per-request + // decisions on each request body's acceptable deadline or + // upload rate, most users will prefer to use + // ReadHeaderTimeout. It is valid to use them both. + ReadTimeout time.Duration + + // WriteTimeout is the maximum duration before timing out + // writes of the response. It is reset whenever a new + // request's header is read. Like ReadTimeout, it does not + // let Handlers make decisions on a per-request basis. + WriteTimeout time.Duration + + // IdleTimeout is the maximum amount of time to wait for the + // next request when keep-alives are enabled. If IdleTimeout + // is zero, the value of ReadTimeout is used. If both are + // zero, ReadHeaderTimeout is used. + IdleTimeout time.Duration +} + +const ( + DefaultHTTPReadTimeout = 5 * time.Second + DefaultHTTPWriteTimeout = 10 * time.Second + DefaultHTTPIdleTimeout = 120 * time.Second +) + +func NewDefaultHTTPTimeouts() HTTPTimeouts { + return HTTPTimeouts{ + ReadTimeout: DefaultHTTPReadTimeout, + WriteTimeout: DefaultHTTPWriteTimeout, + IdleTimeout: DefaultHTTPIdleTimeout, + } +} + // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP // using the provided HTTP Client. func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { @@ -161,15 +199,16 @@ func (t *httpReadWriteNopCloser) Close() error { // NewHTTPServer creates a new HTTP RPC server around an API provider. // // Deprecated: Server implements http.Handler -func NewHTTPServer(cors []string, vhosts []string, srv *Server) *http.Server { +func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *Server) *http.Server { // Wrap the CORS-handler within a host-handler handler := newCorsHandler(srv, cors) handler = newVHostHandler(vhosts, handler) + return &http.Server{ Handler: handler, - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, - IdleTimeout: 120 * time.Second, + ReadTimeout: timeouts.ReadTimeout, + WriteTimeout: timeouts.WriteTimeout, + IdleTimeout: timeouts.IdleTimeout, } } From f1d697f48685127596d158f5b1af62af46cdb7b4 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 27 Jul 2018 14:26:54 -0700 Subject: [PATCH 2/7] rpc: Remove flags for setting HTTP Timeouts, configuring via .toml is sufficient. --- cmd/geth/main.go | 3 --- cmd/geth/usage.go | 3 --- cmd/utils/flags.go | 27 --------------------------- 3 files changed, 33 deletions(-) diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 24cabc3f39fd..77ef6afe245b 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -130,9 +130,6 @@ var ( utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCApiFlag, - utils.RPCReadTimeoutFlag, - utils.RPCWriteTimeoutFlag, - utils.RPCIdleTimeoutFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go index fb5629be7b52..6a12a66cc22b 100644 --- a/cmd/geth/usage.go +++ b/cmd/geth/usage.go @@ -149,9 +149,6 @@ var AppHelpFlagGroups = []flagGroup{ utils.RPCListenAddrFlag, utils.RPCPortFlag, utils.RPCApiFlag, - utils.RPCReadTimeoutFlag, - utils.RPCWriteTimeoutFlag, - utils.RPCIdleTimeoutFlag, utils.WSEnabledFlag, utils.WSListenAddrFlag, utils.WSPortFlag, diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 5c53fed55d54..522ad06b61c2 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -57,7 +57,6 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/netutil" "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" "gopkg.in/urfave/cli.v1" ) @@ -386,21 +385,6 @@ var ( Usage: "HTTP-RPC server listening port", Value: node.DefaultHTTPPort, } - RPCReadTimeoutFlag = cli.DurationFlag{ - Name: "rpcreadtimeout", - Usage: "HTTP-RPC server read timeout", - Value: rpc.DefaultHTTPReadTimeout, - } - RPCWriteTimeoutFlag = cli.DurationFlag{ - Name: "rpcwritetimeout", - Usage: "HTTP-RPC server write timeout", - Value: rpc.DefaultHTTPWriteTimeout, - } - RPCIdleTimeoutFlag = cli.DurationFlag{ - Name: "rpcidletimeout", - Usage: "HTTP-RPC server idle timeout", - Value: rpc.DefaultHTTPIdleTimeout, - } RPCCORSDomainFlag = cli.StringFlag{ Name: "rpccorsdomain", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", @@ -745,17 +729,6 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) { if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) { cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name)) } - - cfg.HTTPTimeouts = rpc.NewDefaultHTTPTimeouts() - if ctx.GlobalIsSet(RPCReadTimeoutFlag.Name) { - cfg.HTTPTimeouts.ReadTimeout = ctx.GlobalDuration(RPCReadTimeoutFlag.Name) - } - if ctx.GlobalIsSet(RPCWriteTimeoutFlag.Name) { - cfg.HTTPTimeouts.WriteTimeout = ctx.GlobalDuration(RPCWriteTimeoutFlag.Name) - } - if ctx.GlobalIsSet(RPCIdleTimeoutFlag.Name) { - cfg.HTTPTimeouts.IdleTimeout = ctx.GlobalDuration(RPCIdleTimeoutFlag.Name) - } } // setWS creates the WebSocket RPC listener interface string from the set From 99d1ca7dca186312dd6c12717cd2fdb5f7275536 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 27 Jul 2018 14:27:59 -0700 Subject: [PATCH 3/7] rpc: Replace separate constants with a single default struct. --- rpc/http.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/rpc/http.go b/rpc/http.go index 702f7f78edf4..c53c1ce0ddb4 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -90,18 +90,14 @@ type HTTPTimeouts struct { IdleTimeout time.Duration } -const ( - DefaultHTTPReadTimeout = 5 * time.Second - DefaultHTTPWriteTimeout = 10 * time.Second - DefaultHTTPIdleTimeout = 120 * time.Second -) +var DefaultHTTPTimeouts = HTTPTimeouts{ + ReadTimeout: 5 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 120 * time.Second, +} func NewDefaultHTTPTimeouts() HTTPTimeouts { - return HTTPTimeouts{ - ReadTimeout: DefaultHTTPReadTimeout, - WriteTimeout: DefaultHTTPWriteTimeout, - IdleTimeout: DefaultHTTPIdleTimeout, - } + return DefaultHTTPTimeouts } // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP From 9710bb838523fc94d668e5bd4fab384da06e2270 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Fri, 27 Jul 2018 14:33:21 -0700 Subject: [PATCH 4/7] rpc: Update HTTP Server Read and Write Timeouts to 30s. --- rpc/http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc/http.go b/rpc/http.go index c53c1ce0ddb4..eee8a3220171 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -91,8 +91,8 @@ type HTTPTimeouts struct { } var DefaultHTTPTimeouts = HTTPTimeouts{ - ReadTimeout: 5 * time.Second, - WriteTimeout: 10 * time.Second, + ReadTimeout: 30 * time.Second, + WriteTimeout: 30 * time.Second, IdleTimeout: 120 * time.Second, } From 96a49d97e5e008c3069e923b755c4210a69720de Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Mon, 30 Jul 2018 11:38:04 -0700 Subject: [PATCH 5/7] rpc: Remove redundant NewDefaultHTTPTimeouts function. --- cmd/clef/main.go | 2 +- node/defaults.go | 2 +- rpc/http.go | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/cmd/clef/main.go b/cmd/clef/main.go index 5b94652bcad5..85704754def5 100644 --- a/cmd/clef/main.go +++ b/cmd/clef/main.go @@ -415,7 +415,7 @@ func signer(c *cli.Context) error { // start http server httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name)) - listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.NewDefaultHTTPTimeouts()) + listener, _, err := rpc.StartHTTPEndpoint(httpEndpoint, rpcAPI, []string{"account"}, cors, vhosts, rpc.DefaultHTTPTimeouts) if err != nil { utils.Fatalf("Could not start RPC api: %v", err) } diff --git a/node/defaults.go b/node/defaults.go index d818ba3eb432..c1376dba0f33 100644 --- a/node/defaults.go +++ b/node/defaults.go @@ -40,7 +40,7 @@ var DefaultConfig = Config{ HTTPPort: DefaultHTTPPort, HTTPModules: []string{"net", "web3"}, HTTPVirtualHosts: []string{"localhost"}, - HTTPTimeouts: rpc.NewDefaultHTTPTimeouts(), + HTTPTimeouts: rpc.DefaultHTTPTimeouts, WSPort: DefaultWSPort, WSModules: []string{"net", "web3"}, P2P: p2p.Config{ diff --git a/rpc/http.go b/rpc/http.go index eee8a3220171..f571a79171b8 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -96,10 +96,6 @@ var DefaultHTTPTimeouts = HTTPTimeouts{ IdleTimeout: 120 * time.Second, } -func NewDefaultHTTPTimeouts() HTTPTimeouts { - return DefaultHTTPTimeouts -} - // DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP // using the provided HTTP Client. func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) { From 1307a9c89a462352442547ca835c2c35d073ddd4 Mon Sep 17 00:00:00 2001 From: Ryan Schneider Date: Mon, 30 Jul 2018 11:38:53 -0700 Subject: [PATCH 6/7] rpc: document HTTPTimeouts. --- node/config.go | 2 ++ rpc/http.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/node/config.go b/node/config.go index 2035c3b3a083..a4d5920a8b3b 100644 --- a/node/config.go +++ b/node/config.go @@ -120,6 +120,8 @@ type Config struct { // exposed. HTTPModules []string `toml:",omitempty"` + // HTTPTimeouts allows for customization of the timeout values used by the HTTP RPC + // interface. HTTPTimeouts rpc.HTTPTimeouts // WSHost is the host interface on which to start the websocket RPC server. If diff --git a/rpc/http.go b/rpc/http.go index f571a79171b8..16fb61b0fe24 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -90,6 +90,8 @@ type HTTPTimeouts struct { IdleTimeout time.Duration } +// DefaultHTTPTimeouts represents the default timeout values used if further +// configuration is not provided. var DefaultHTTPTimeouts = HTTPTimeouts{ ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, From 254d2655f8d19054d150e1e6c58f0e47906643ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Tue, 31 Jul 2018 12:14:05 +0300 Subject: [PATCH 7/7] rpc: sanitize timeout values for library use --- rpc/http.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/rpc/http.go b/rpc/http.go index 16fb61b0fe24..f3bd1f29c566 100644 --- a/rpc/http.go +++ b/rpc/http.go @@ -31,6 +31,7 @@ import ( "sync" "time" + "github.com/ethereum/go-ethereum/log" "github.com/rs/cors" ) @@ -198,6 +199,20 @@ func NewHTTPServer(cors []string, vhosts []string, timeouts HTTPTimeouts, srv *S handler := newCorsHandler(srv, cors) handler = newVHostHandler(vhosts, handler) + // Make sure timeout values are meaningful + if timeouts.ReadTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP read timeout", "provided", timeouts.ReadTimeout, "updated", DefaultHTTPTimeouts.ReadTimeout) + timeouts.ReadTimeout = DefaultHTTPTimeouts.ReadTimeout + } + if timeouts.WriteTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP write timeout", "provided", timeouts.WriteTimeout, "updated", DefaultHTTPTimeouts.WriteTimeout) + timeouts.WriteTimeout = DefaultHTTPTimeouts.WriteTimeout + } + if timeouts.IdleTimeout < time.Second { + log.Warn("Sanitizing invalid HTTP idle timeout", "provided", timeouts.IdleTimeout, "updated", DefaultHTTPTimeouts.IdleTimeout) + timeouts.IdleTimeout = DefaultHTTPTimeouts.IdleTimeout + } + // Bundle and start the HTTP server return &http.Server{ Handler: handler, ReadTimeout: timeouts.ReadTimeout,