rpc: make HTTP RPC timeouts configurable, raise defaults (#17240)
* rpc: Make HTTP server timeout values configurable * rpc: Remove flags for setting HTTP Timeouts, configuring via .toml is sufficient. * rpc: Replace separate constants with a single default struct. * rpc: Update HTTP Server Read and Write Timeouts to 30s. * rpc: Remove redundant NewDefaultHTTPTimeouts function. * rpc: document HTTPTimeouts. * rpc: sanitize timeout values for library use
This commit is contained in:
parent
c4a1d4fecf
commit
5d7e18539e
@ -415,7 +415,7 @@ func signer(c *cli.Context) error {
|
|||||||
|
|
||||||
// start http server
|
// start http server
|
||||||
httpEndpoint := fmt.Sprintf("%s:%d", c.String(utils.RPCListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
|
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.DefaultHTTPTimeouts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not start RPC api: %v", err)
|
utils.Fatalf("Could not start RPC api: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -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 false, err
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -119,6 +120,10 @@ type Config struct {
|
|||||||
// exposed.
|
// exposed.
|
||||||
HTTPModules []string `toml:",omitempty"`
|
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
|
// 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.
|
// this field is empty, no websocket API endpoint will be started.
|
||||||
WSHost string `toml:",omitempty"`
|
WSHost string `toml:",omitempty"`
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -39,6 +40,7 @@ var DefaultConfig = Config{
|
|||||||
HTTPPort: DefaultHTTPPort,
|
HTTPPort: DefaultHTTPPort,
|
||||||
HTTPModules: []string{"net", "web3"},
|
HTTPModules: []string{"net", "web3"},
|
||||||
HTTPVirtualHosts: []string{"localhost"},
|
HTTPVirtualHosts: []string{"localhost"},
|
||||||
|
HTTPTimeouts: rpc.DefaultHTTPTimeouts,
|
||||||
WSPort: DefaultWSPort,
|
WSPort: DefaultWSPort,
|
||||||
WSModules: []string{"net", "web3"},
|
WSModules: []string{"net", "web3"},
|
||||||
P2P: p2p.Config{
|
P2P: p2p.Config{
|
||||||
|
@ -263,7 +263,7 @@ func (n *Node) startRPC(services map[reflect.Type]Service) error {
|
|||||||
n.stopInProc()
|
n.stopInProc()
|
||||||
return err
|
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.stopIPC()
|
||||||
n.stopInProc()
|
n.stopInProc()
|
||||||
return err
|
return err
|
||||||
@ -331,12 +331,12 @@ func (n *Node) stopIPC() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// startHTTP initializes and starts the HTTP RPC endpoint.
|
// 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
|
// Short circuit if the HTTP endpoint isn't being exposed
|
||||||
if endpoint == "" {
|
if endpoint == "" {
|
||||||
return nil
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules
|
// 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
|
// Generate the whitelist based on the allowed modules
|
||||||
whitelist := make(map[string]bool)
|
whitelist := make(map[string]bool)
|
||||||
for _, module := range modules {
|
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 {
|
if listener, err = net.Listen("tcp", endpoint); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
go NewHTTPServer(cors, vhosts, handler).Serve(listener)
|
go NewHTTPServer(cors, vhosts, timeouts, handler).Serve(listener)
|
||||||
return listener, handler, err
|
return listener, handler, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
56
rpc/http.go
56
rpc/http.go
@ -31,6 +31,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/rs/cors"
|
"github.com/rs/cors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -66,6 +67,38 @@ func (hc *httpConn) Close() error {
|
|||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultHTTPTimeouts represents the default timeout values used if further
|
||||||
|
// configuration is not provided.
|
||||||
|
var DefaultHTTPTimeouts = HTTPTimeouts{
|
||||||
|
ReadTimeout: 30 * time.Second,
|
||||||
|
WriteTimeout: 30 * time.Second,
|
||||||
|
IdleTimeout: 120 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
|
// DialHTTPWithClient creates a new RPC client that connects to an RPC server over HTTP
|
||||||
// using the provided HTTP Client.
|
// using the provided HTTP Client.
|
||||||
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
|
func DialHTTPWithClient(endpoint string, client *http.Client) (*Client, error) {
|
||||||
@ -161,15 +194,30 @@ func (t *httpReadWriteNopCloser) Close() error {
|
|||||||
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
// NewHTTPServer creates a new HTTP RPC server around an API provider.
|
||||||
//
|
//
|
||||||
// Deprecated: Server implements http.Handler
|
// 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
|
// Wrap the CORS-handler within a host-handler
|
||||||
handler := newCorsHandler(srv, cors)
|
handler := newCorsHandler(srv, cors)
|
||||||
handler = newVHostHandler(vhosts, handler)
|
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{
|
return &http.Server{
|
||||||
Handler: handler,
|
Handler: handler,
|
||||||
ReadTimeout: 5 * time.Second,
|
ReadTimeout: timeouts.ReadTimeout,
|
||||||
WriteTimeout: 10 * time.Second,
|
WriteTimeout: timeouts.WriteTimeout,
|
||||||
IdleTimeout: 120 * time.Second,
|
IdleTimeout: timeouts.IdleTimeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user