Browse Source

rpc: make max_body_bytes and max_header_bytes configurable (#3818)

* rpc: make max_body_bytes and max_header_bytes configurable

* update changelog pending
pull/3822/head
Jun Kimura 5 years ago
committed by Marko
parent
commit
51b3428f5c
7 changed files with 65 additions and 25 deletions
  1. +1
    -0
      CHANGELOG_PENDING.md
  2. +15
    -0
      config/config.go
  3. +6
    -0
      config/toml.go
  4. +6
    -0
      docs/tendermint-core/configuration.md
  5. +14
    -11
      node/node.go
  6. +12
    -1
      rpc/lib/server/handlers.go
  7. +11
    -13
      rpc/lib/server/http_server.go

+ 1
- 0
CHANGELOG_PENDING.md View File

@ -21,5 +21,6 @@ program](https://hackerone.com/tendermint).
### IMPROVEMENTS: ### IMPROVEMENTS:
- [abci] \#3809 Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov) - [abci] \#3809 Recover from application panics in `server/socket_server.go` to allow socket cleanup (@ruseinov)
- [rpc] \#3818 Make `max_body_bytes` and `max_header_bytes` configurable
### BUG FIXES: ### BUG FIXES:

+ 15
- 0
config/config.go View File

@ -351,6 +351,12 @@ type RPCConfig struct {
// See https://github.com/tendermint/tendermint/issues/3435 // See https://github.com/tendermint/tendermint/issues/3435
TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"` TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"`
// Maximum size of request body, in bytes
MaxBodyBytes int64 `mapstructure:"max_body_bytes"`
// Maximum size of request header, in bytes
MaxHeaderBytes int `mapstructure:"max_header_bytes"`
// The path to a file containing certificate that is used to create the HTTPS server. // The path to a file containing certificate that is used to create the HTTPS server.
// Migth be either absolute path or path related to tendermint's config directory. // Migth be either absolute path or path related to tendermint's config directory.
// //
@ -385,6 +391,9 @@ func DefaultRPCConfig() *RPCConfig {
MaxSubscriptionsPerClient: 5, MaxSubscriptionsPerClient: 5,
TimeoutBroadcastTxCommit: 10 * time.Second, TimeoutBroadcastTxCommit: 10 * time.Second,
MaxBodyBytes: int64(1000000), // 1MB
MaxHeaderBytes: 1 << 20, // same as the net/http default
TLSCertFile: "", TLSCertFile: "",
TLSKeyFile: "", TLSKeyFile: "",
} }
@ -417,6 +426,12 @@ func (cfg *RPCConfig) ValidateBasic() error {
if cfg.TimeoutBroadcastTxCommit < 0 { if cfg.TimeoutBroadcastTxCommit < 0 {
return errors.New("timeout_broadcast_tx_commit can't be negative") return errors.New("timeout_broadcast_tx_commit can't be negative")
} }
if cfg.MaxBodyBytes < 0 {
return errors.New("max_body_bytes can't be negative")
}
if cfg.MaxHeaderBytes < 0 {
return errors.New("max_header_bytes can't be negative")
}
return nil return nil
} }


+ 6
- 0
config/toml.go View File

@ -192,6 +192,12 @@ max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }}
# See https://github.com/tendermint/tendermint/issues/3435 # See https://github.com/tendermint/tendermint/issues/3435
timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}"
# Maximum size of request body, in bytes
max_body_bytes = {{ .RPC.MaxBodyBytes }}
# Maximum size of request header, in bytes
max_header_bytes = {{ .RPC.MaxHeaderBytes }}
# The path to a file containing certificate that is used to create the HTTPS server. # The path to a file containing certificate that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory. # Migth be either absolute path or path related to tendermint's config directory.
# If the certificate is signed by a certificate authority, # If the certificate is signed by a certificate authority,


+ 6
- 0
docs/tendermint-core/configuration.md View File

@ -138,6 +138,12 @@ max_subscriptions_per_client = 5
# See https://github.com/tendermint/tendermint/issues/3435 # See https://github.com/tendermint/tendermint/issues/3435
timeout_broadcast_tx_commit = "10s" timeout_broadcast_tx_commit = "10s"
# Maximum size of request body, in bytes
max_body_bytes = {{ .RPC.MaxBodyBytes }}
# Maximum size of request header, in bytes
max_header_bytes = {{ .RPC.MaxHeaderBytes }}
# The path to a file containing certificate that is used to create the HTTPS server. # The path to a file containing certificate that is used to create the HTTPS server.
# Migth be either absolute path or path related to tendermint's config directory. # Migth be either absolute path or path related to tendermint's config directory.
# If the certificate is signed by a certificate authority, # If the certificate is signed by a certificate authority,


+ 14
- 11
node/node.go View File

@ -820,6 +820,17 @@ func (n *Node) startRPC() ([]net.Listener, error) {
rpccore.AddUnsafeRoutes() rpccore.AddUnsafeRoutes()
} }
config := rpcserver.DefaultConfig()
config.MaxBodyBytes = n.config.RPC.MaxBodyBytes
config.MaxHeaderBytes = n.config.RPC.MaxHeaderBytes
config.MaxOpenConnections = n.config.RPC.MaxOpenConnections
// If necessary adjust global WriteTimeout to ensure it's greater than
// TimeoutBroadcastTxCommit.
// See https://github.com/tendermint/tendermint/issues/3435
if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit {
config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second
}
// we may expose the rpc over both a unix and tcp socket // we may expose the rpc over both a unix and tcp socket
listeners := make([]net.Listener, len(listenAddrs)) listeners := make([]net.Listener, len(listenAddrs))
for i, listenAddr := range listenAddrs { for i, listenAddr := range listenAddrs {
@ -832,20 +843,12 @@ func (n *Node) startRPC() ([]net.Listener, error) {
if err != nil && err != tmpubsub.ErrSubscriptionNotFound { if err != nil && err != tmpubsub.ErrSubscriptionNotFound {
wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err) wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err)
} }
}))
}),
rpcserver.ReadLimit(config.MaxBodyBytes),
)
wm.SetLogger(wmLogger) wm.SetLogger(wmLogger)
mux.HandleFunc("/websocket", wm.WebsocketHandler) mux.HandleFunc("/websocket", wm.WebsocketHandler)
rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, coreCodec, rpcLogger) rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, coreCodec, rpcLogger)
config := rpcserver.DefaultConfig()
config.MaxOpenConnections = n.config.RPC.MaxOpenConnections
// If necessary adjust global WriteTimeout to ensure it's greater than
// TimeoutBroadcastTxCommit.
// See https://github.com/tendermint/tendermint/issues/3435
if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit {
config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second
}
listener, err := rpcserver.Listen( listener, err := rpcserver.Listen(
listenAddr, listenAddr,
config, config,


+ 12
- 1
rpc/lib/server/handlers.go View File

@ -448,6 +448,9 @@ type wsConnection struct {
// Send pings to server with this period. Must be less than readWait, but greater than zero. // Send pings to server with this period. Must be less than readWait, but greater than zero.
pingPeriod time.Duration pingPeriod time.Duration
// Maximum message size.
readLimit int64
// callback which is called upon disconnect // callback which is called upon disconnect
onDisconnect func(remoteAddr string) onDisconnect func(remoteAddr string)
@ -467,7 +470,6 @@ func NewWSConnection(
cdc *amino.Codec, cdc *amino.Codec,
options ...func(*wsConnection), options ...func(*wsConnection),
) *wsConnection { ) *wsConnection {
baseConn.SetReadLimit(maxBodyBytes)
wsc := &wsConnection{ wsc := &wsConnection{
remoteAddr: baseConn.RemoteAddr().String(), remoteAddr: baseConn.RemoteAddr().String(),
baseConn: baseConn, baseConn: baseConn,
@ -481,6 +483,7 @@ func NewWSConnection(
for _, option := range options { for _, option := range options {
option(wsc) option(wsc)
} }
wsc.baseConn.SetReadLimit(wsc.readLimit)
wsc.BaseService = *cmn.NewBaseService(nil, "wsConnection", wsc) wsc.BaseService = *cmn.NewBaseService(nil, "wsConnection", wsc)
return wsc return wsc
} }
@ -525,6 +528,14 @@ func PingPeriod(pingPeriod time.Duration) func(*wsConnection) {
} }
} }
// ReadLimit sets the maximum size for reading message.
// It should only be used in the constructor - not Goroutine-safe.
func ReadLimit(readLimit int64) func(*wsConnection) {
return func(wsc *wsConnection) {
wsc.readLimit = readLimit
}
}
// OnStart implements cmn.Service by starting the read and write routines. It // OnStart implements cmn.Service by starting the read and write routines. It
// blocks until the connection closes. // blocks until the connection closes.
func (wsc *wsConnection) OnStart() error { func (wsc *wsConnection) OnStart() error {


+ 11
- 13
rpc/lib/server/http_server.go View File

@ -26,6 +26,11 @@ type Config struct {
ReadTimeout time.Duration ReadTimeout time.Duration
// mirrors http.Server#WriteTimeout // mirrors http.Server#WriteTimeout
WriteTimeout time.Duration WriteTimeout time.Duration
// MaxBodyBytes controls the maximum number of bytes the
// server will read parsing the request body.
MaxBodyBytes int64
// mirrors http.Server#MaxHeaderBytes
MaxHeaderBytes int
} }
// DefaultConfig returns a default configuration. // DefaultConfig returns a default configuration.
@ -34,28 +39,21 @@ func DefaultConfig() *Config {
MaxOpenConnections: 0, // unlimited MaxOpenConnections: 0, // unlimited
ReadTimeout: 10 * time.Second, ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second,
MaxBodyBytes: int64(1000000), // 1MB
MaxHeaderBytes: 1 << 20, // same as the net/http default
} }
} }
const (
// maxBodyBytes controls the maximum number of bytes the
// server will read parsing the request body.
maxBodyBytes = int64(1000000) // 1MB
// same as the net/http default
maxHeaderBytes = 1 << 20
)
// StartHTTPServer takes a listener and starts an HTTP server with the given handler. // StartHTTPServer takes a listener and starts an HTTP server with the given handler.
// It wraps handler with RecoverAndLogHandler. // It wraps handler with RecoverAndLogHandler.
// NOTE: This function blocks - you may want to call it in a go-routine. // NOTE: This function blocks - you may want to call it in a go-routine.
func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error {
logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr())) logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr()))
s := &http.Server{ s := &http.Server{
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger),
ReadTimeout: config.ReadTimeout, ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout, WriteTimeout: config.WriteTimeout,
MaxHeaderBytes: maxHeaderBytes,
MaxHeaderBytes: config.MaxHeaderBytes,
} }
err := s.Serve(listener) err := s.Serve(listener)
logger.Info("RPC HTTP server stopped", "err", err) logger.Info("RPC HTTP server stopped", "err", err)
@ -75,10 +73,10 @@ func StartHTTPAndTLSServer(
logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)",
listener.Addr(), certFile, keyFile)) listener.Addr(), certFile, keyFile))
s := &http.Server{ s := &http.Server{
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger),
Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: config.MaxBodyBytes}, logger),
ReadTimeout: config.ReadTimeout, ReadTimeout: config.ReadTimeout,
WriteTimeout: config.WriteTimeout, WriteTimeout: config.WriteTimeout,
MaxHeaderBytes: maxHeaderBytes,
MaxHeaderBytes: config.MaxHeaderBytes,
} }
err := s.ServeTLS(listener, certFile, keyFile) err := s.ServeTLS(listener, certFile, keyFile)


Loading…
Cancel
Save