From cffcb5dfb44d138b68f953ac4adc1efa97861710 Mon Sep 17 00:00:00 2001 From: yihuang Date: Wed, 9 Mar 2022 15:31:51 +0800 Subject: [PATCH] rpc: add configurable timeouts to http server (#979) * Add configurable timeouts to http server Closes: #963 * add flags * changelog * fix toml --- CHANGELOG.md | 4 +++ server/config/config.go | 64 +++++++++++++++++++++++++++-------------- server/config/toml.go | 6 ++++ server/flags/flags.go | 22 +++++++------- server/json_rpc.go | 7 +++-- server/start.go | 2 ++ 6 files changed, 71 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aac6031197..44e39663c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### Improvements + +- (rpc) [tharsis#979](https://github.com/tharsis/ethermint/pull/979) Add configurable timeouts to http server + ### Bug Fixes * (rpc) [#970] (https://github.com/tharsis/ethermint/pull/970) Fix unexpected nil reward values on `eth_feeHistory` response diff --git a/server/config/config.go b/server/config/config.go index 9a018f8125..4909d50708 100644 --- a/server/config/config.go +++ b/server/config/config.go @@ -41,6 +41,10 @@ const ( DefaultEVMTimeout = 5 * time.Second // default 1.0 eth DefaultTxFeeCap float64 = 1.0 + + DefaultHTTPTimeout = 30 * time.Second + + DefaultHTTPIdleTimeout = 120 * time.Second ) var evmTracers = []string{"json", "markdown", "struct", "access_list"} @@ -86,6 +90,10 @@ type JSONRPCConfig struct { LogsCap int32 `mapstructure:"logs-cap"` // BlockRangeCap defines the max block range allowed for `eth_getLogs` query. BlockRangeCap int32 `mapstructure:"block-range-cap"` + // HTTPTimeout is the read/write timeout of http json-rpc server. + HTTPTimeout time.Duration `mapstructure:"http-timeout"` + // HTTPIdleTimeout is the idle timeout of http json-rpc server. + HTTPIdleTimeout time.Duration `mapstructure:"http-idle-timeout"` } // TLSConfig defines the certificate and matching private key for the server. @@ -170,17 +178,19 @@ func GetAPINamespaces() []string { // DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default func DefaultJSONRPCConfig() *JSONRPCConfig { return &JSONRPCConfig{ - Enable: true, - API: GetDefaultAPINamespaces(), - Address: DefaultJSONRPCAddress, - WsAddress: DefaultJSONRPCWsAddress, - GasCap: DefaultGasCap, - EVMTimeout: DefaultEVMTimeout, - TxFeeCap: DefaultTxFeeCap, - FilterCap: DefaultFilterCap, - FeeHistoryCap: DefaultFeeHistoryCap, - BlockRangeCap: DefaultBlockRangeCap, - LogsCap: DefaultLogsCap, + Enable: true, + API: GetDefaultAPINamespaces(), + Address: DefaultJSONRPCAddress, + WsAddress: DefaultJSONRPCWsAddress, + GasCap: DefaultGasCap, + EVMTimeout: DefaultEVMTimeout, + TxFeeCap: DefaultTxFeeCap, + FilterCap: DefaultFilterCap, + FeeHistoryCap: DefaultFeeHistoryCap, + BlockRangeCap: DefaultBlockRangeCap, + LogsCap: DefaultLogsCap, + HTTPTimeout: DefaultHTTPTimeout, + HTTPIdleTimeout: DefaultHTTPIdleTimeout, } } @@ -214,6 +224,14 @@ func (c JSONRPCConfig) Validate() error { return errors.New("JSON-RPC block range cap cannot be negative") } + if c.HTTPTimeout < 0 { + return errors.New("JSON-RPC HTTP timeout duration cannot be negative") + } + + if c.HTTPIdleTimeout < 0 { + return errors.New("JSON-RPC HTTP idle timeout duration cannot be negative") + } + // TODO: validate APIs seenAPIs := make(map[string]bool) for _, api := range c.API { @@ -262,17 +280,19 @@ func GetConfig(v *viper.Viper) Config { Tracer: v.GetString("evm.tracer"), }, JSONRPC: JSONRPCConfig{ - Enable: v.GetBool("json-rpc.enable"), - API: v.GetStringSlice("json-rpc.api"), - Address: v.GetString("json-rpc.address"), - WsAddress: v.GetString("json-rpc.ws-address"), - GasCap: v.GetUint64("json-rpc.gas-cap"), - FilterCap: v.GetInt32("json-rpc.filter-cap"), - FeeHistoryCap: v.GetInt32("json-rpc.feehistory-cap"), - TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"), - EVMTimeout: v.GetDuration("json-rpc.evm-timeout"), - LogsCap: v.GetInt32("json-rpc.logs-cap"), - BlockRangeCap: v.GetInt32("json-rpc.block-range-cap"), + Enable: v.GetBool("json-rpc.enable"), + API: v.GetStringSlice("json-rpc.api"), + Address: v.GetString("json-rpc.address"), + WsAddress: v.GetString("json-rpc.ws-address"), + GasCap: v.GetUint64("json-rpc.gas-cap"), + FilterCap: v.GetInt32("json-rpc.filter-cap"), + FeeHistoryCap: v.GetInt32("json-rpc.feehistory-cap"), + TxFeeCap: v.GetFloat64("json-rpc.txfee-cap"), + EVMTimeout: v.GetDuration("json-rpc.evm-timeout"), + LogsCap: v.GetInt32("json-rpc.logs-cap"), + BlockRangeCap: v.GetInt32("json-rpc.block-range-cap"), + HTTPTimeout: v.GetDuration("json-rpc.http-timeout"), + HTTPIdleTimeout: v.GetDuration("json-rpc.http-idle-timeout"), }, TLS: TLSConfig{ CertificatePath: v.GetString("tls.certificate-path"), diff --git a/server/config/toml.go b/server/config/toml.go index 8d108ea84d..e0b9ca9422 100644 --- a/server/config/toml.go +++ b/server/config/toml.go @@ -53,6 +53,12 @@ logs-cap = {{ .JSONRPC.LogsCap }} # BlockRangeCap defines the max block range allowed for 'eth_getLogs' query. block-range-cap = {{ .JSONRPC.BlockRangeCap }} +# HTTPTimeout is the read/write timeout of http json-rpc server. +http-timeout = "{{ .JSONRPC.HTTPTimeout }}" + +# HTTPIdleTimeout is the idle timeout of http json-rpc server. +http-idle-timeout = "{{ .JSONRPC.HTTPIdleTimeout }}" + ############################################################################### ### TLS Configuration ### ############################################################################### diff --git a/server/flags/flags.go b/server/flags/flags.go index e213f5bb17..3774a19690 100644 --- a/server/flags/flags.go +++ b/server/flags/flags.go @@ -31,16 +31,18 @@ const ( // JSON-RPC flags const ( - JSONRPCEnable = "json-rpc.enable" - JSONRPCAPI = "json-rpc.api" - JSONRPCAddress = "json-rpc.address" - JSONWsAddress = "json-rpc.ws-address" - JSONRPCGasCap = "json-rpc.gas-cap" - JSONRPCEVMTimeout = "json-rpc.evm-timeout" - JSONRPCTxFeeCap = "json-rpc.txfee-cap" - JSONRPCFilterCap = "json-rpc.filter-cap" - JSONRPCLogsCap = "json-rpc.logs-cap" - JSONRPCBlockRangeCap = "json-rpc.block-range-cap" + JSONRPCEnable = "json-rpc.enable" + JSONRPCAPI = "json-rpc.api" + JSONRPCAddress = "json-rpc.address" + JSONWsAddress = "json-rpc.ws-address" + JSONRPCGasCap = "json-rpc.gas-cap" + JSONRPCEVMTimeout = "json-rpc.evm-timeout" + JSONRPCTxFeeCap = "json-rpc.txfee-cap" + JSONRPCFilterCap = "json-rpc.filter-cap" + JSONRPCLogsCap = "json-rpc.logs-cap" + JSONRPCBlockRangeCap = "json-rpc.block-range-cap" + JSONRPCHTTPTimeout = "json-rpc.http-timeout" + JSONRPCHTTPIdleTimeout = "json-rpc.http-idle-timeout" ) // EVM flags diff --git a/server/json_rpc.go b/server/json_rpc.go index 4c893d714e..244ad4dc98 100644 --- a/server/json_rpc.go +++ b/server/json_rpc.go @@ -59,8 +59,11 @@ func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr, tmEn } httpSrv := &http.Server{ - Addr: config.JSONRPC.Address, - Handler: handlerWithCors.Handler(r), + Addr: config.JSONRPC.Address, + Handler: handlerWithCors.Handler(r), + ReadTimeout: config.JSONRPC.HTTPTimeout, + WriteTimeout: config.JSONRPC.HTTPTimeout, + IdleTimeout: config.JSONRPC.HTTPIdleTimeout, } httpSrvDone := make(chan struct{}, 1) diff --git a/server/start.go b/server/start.go index 97f1d33aae..d72a2b9ad7 100644 --- a/server/start.go +++ b/server/start.go @@ -159,6 +159,8 @@ which accepts a path for the resulting pprof file. cmd.Flags().Float64(srvflags.JSONRPCTxFeeCap, config.DefaultTxFeeCap, "Sets a cap on transaction fee that can be sent via the RPC APIs (1 = default 1 photon)") cmd.Flags().Int32(srvflags.JSONRPCFilterCap, config.DefaultFilterCap, "Sets the global cap for total number of filters that can be created") cmd.Flags().Duration(srvflags.JSONRPCEVMTimeout, config.DefaultEVMTimeout, "Sets a timeout used for eth_call (0=infinite)") + cmd.Flags().Duration(srvflags.JSONRPCHTTPTimeout, config.DefaultHTTPTimeout, "Sets a read/write timeout for json-rpc http server (0=infinite)") + cmd.Flags().Duration(srvflags.JSONRPCHTTPIdleTimeout, config.DefaultHTTPIdleTimeout, "Sets a idle timeout for json-rpc http server (0=infinite)") cmd.Flags().Int32(srvflags.JSONRPCLogsCap, config.DefaultLogsCap, "Sets the max number of results can be returned from single `eth_getLogs` query") cmd.Flags().Int32(srvflags.JSONRPCBlockRangeCap, config.DefaultBlockRangeCap, "Sets the max block range allowed for `eth_getLogs` query")