rpc: Make HTTP server timeout values configurable

This commit is contained in:
Ryan Schneider 2018-07-24 14:18:09 -07:00 committed by Péter Szilágyi
parent fb9f7261ec
commit 24f28fd2b8
No known key found for this signature in database
GPG key ID: E9AE538CEDF8293D
10 changed files with 88 additions and 11 deletions

View file

@ -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)
}

View file

@ -130,6 +130,9 @@ var (
utils.RPCListenAddrFlag,
utils.RPCPortFlag,
utils.RPCApiFlag,
utils.RPCReadTimeoutFlag,
utils.RPCWriteTimeoutFlag,
utils.RPCIdleTimeoutFlag,
utils.WSEnabledFlag,
utils.WSListenAddrFlag,
utils.WSPortFlag,

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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"`

View file

@ -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{

View file

@ -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
}

View file

@ -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
}

View file

@ -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,
}
}