Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ This is the Lotus v1.33.1 release, which introduces performance improvements and
- chore(deps): update of critical underlying dependencies with go-libp2p to v0.42.0 (filecoin-project/lotus#13190) and boxo to v0.32.0 ([filecoin-project/lotus#13202](https://github.com/filecoin-project/lotus/pull/13202)) and boxo v0.33.0([filecoin-project/lotus#13226](https://github.com/filecoin-project/lotus/pull/13226))
- feat(spcli): correctly handle the batch logic of `lotus-miner actor settle-deal`; replace the dealid data source ([filecoin-project/lotus#13189](https://github.com/filecoin-project/lotus/pull/13189))
- feat(spcli): add `--all-deals` to `lotus-miner actor settle-deal`. By default, only expired deals are processed ([filecoin-project/lotus#13243](https://github.com/filecoin-project/lotus/pull/13243))
- fix(rpc): add read, write and idle configurable timeouts to gateway. ([filecoin-project/lotus#13327](https://github.com/filecoin-project/lotus/pull/13327))

## 📝 Changelog

Expand Down
28 changes: 28 additions & 0 deletions cmd/lotus-gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,26 @@ var runCmd = &cli.Command{
Usage: "Enable logging of incoming API requests. Note: This will log POST request bodies which may impact performance due to body buffering and may expose sensitive data in logs",
Value: false,
},
&cli.DurationFlag{
Name: "read-timeout",
Usage: "Maximum duration for reading the entire request, including the body. Use 0 to disable",
Value: gateway.ReadTimeout,
},
&cli.DurationFlag{
Copy link

Copilot AI Sep 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CLI flag is missing the --read-header-timeout option that corresponds to the ReadHeaderTimeout constant. This timeout is configurable in the code but not exposed via CLI, creating an inconsistency in the configuration interface.

Suggested change
&cli.DurationFlag{
&cli.DurationFlag{
Name: "read-header-timeout",
Usage: "Amount of time allowed to read request headers. Use 0 to disable",
Value: gateway.ReadHeaderTimeout,
},
&cli.DurationFlag{

Copilot uses AI. Check for mistakes.

Name: "write-timeout",
Usage: "Maximum duration before timing out writes of the response. Use 0 to disable (sensitive setting - be careful)",
Value: gateway.WriteTimeout,
},
&cli.DurationFlag{
Name: "idle-timeout",
Usage: "Maximum amount of time to wait for the next request when keep-alives are enabled. Use 0 to disable",
Value: gateway.IdleTimeout,
},
&cli.IntFlag{
Name: "max-header-bytes",
Usage: "Maximum number of bytes the server will read parsing the request header's keys and values",
Value: gateway.MaxHeaderBytes,
},
},
Action: func(cctx *cli.Context) error {
log.Info("Starting lotus gateway")
Expand Down Expand Up @@ -208,6 +228,10 @@ var runCmd = &cli.Command{
maxFiltersPerConn = cctx.Int("eth-max-filters-per-conn")
enableCORS = cctx.Bool("cors")
enableRequestLogging = cctx.Bool("request-logging")
readTimeout = cctx.Duration("read-timeout")
writeTimeout = cctx.Duration("write-timeout")
idleTimeout = cctx.Duration("idle-timeout")
maxHeaderBytes = cctx.Int("max-header-bytes")
)

serverOptions := make([]jsonrpc.ServerOption, 0)
Expand Down Expand Up @@ -236,6 +260,10 @@ var runCmd = &cli.Command{
gateway.WithRateLimit(globalRateLimit),
gateway.WithRateLimitTimeout(rateLimitTimeout),
gateway.WithEthMaxFiltersPerConn(maxFiltersPerConn),
gateway.WithReadTimeout(readTimeout),
gateway.WithWriteTimeout(writeTimeout),
gateway.WithIdleTimeout(idleTimeout),
gateway.WithMaxHeaderBytes(maxHeaderBytes),
)
handler, err := gateway.Handler(
gwapi,
Expand Down
60 changes: 60 additions & 0 deletions gateway/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ const (
stateRateLimitTokens = 3

MaxRateLimitTokens = stateRateLimitTokens // Number of tokens consumed for the most expensive types of operations
ReadHeaderTimeout = time.Second * 10 // Maximum duration to wait for request headers to be read
ReadTimeout = time.Second * 60 // Maximum duration for reading the entire request, including the body
WriteTimeout = 0 // Maximum duration before timing out writes of the response (disabled by default)
IdleTimeout = time.Second * 60 // Maximum amount of time to wait for the next request when keep-alives are enabled
MaxHeaderBytes = 1 << 20 // Maximum number of bytes the server will read parsing the request header's keys and values (1MB)
)

type Node struct {
Expand All @@ -46,6 +51,11 @@ type Node struct {
rateLimiter *rate.Limiter
rateLimitTimeout time.Duration
ethMaxFiltersPerConn int
readHeaderTimeout time.Duration
readTimeout time.Duration
writeTimeout time.Duration
idleTimeout time.Duration
maxHeaderBytes int
errLookback error
}

Expand All @@ -57,6 +67,11 @@ type options struct {
rateLimit int
rateLimitTimeout time.Duration
ethMaxFiltersPerConn int
ReadHeaderTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
}

type Option func(*options)
Expand Down Expand Up @@ -115,13 +130,53 @@ func WithEthMaxFiltersPerConn(ethMaxFiltersPerConn int) Option {
}
}

// WithReadHeaderTimeout sets the maximum duration to wait for request headers to be read.
func WithReadHeaderTimeout(readHeaderTimeout time.Duration) Option {
return func(opts *options) {
opts.ReadHeaderTimeout = readHeaderTimeout
}
}

// WithReadTimeout sets the maximum duration for reading the entire request, including the body.
func WithReadTimeout(readTimeout time.Duration) Option {
return func(opts *options) {
opts.ReadTimeout = readTimeout
}
}

// WithWriteTimeout sets the maximum duration before timing out writes of the response.
func WithWriteTimeout(writeTimeout time.Duration) Option {
return func(opts *options) {
opts.WriteTimeout = writeTimeout
}
}

// WithIdleTimeout sets the maximum amount of time to wait for the next request when keep-alives are enabled.
func WithIdleTimeout(idleTimeout time.Duration) Option {
return func(opts *options) {
opts.IdleTimeout = idleTimeout
}
}

// WithMaxHeaderBytes sets the maximum number of bytes the server will read parsing the request header's keys and values.
func WithMaxHeaderBytes(maxHeaderBytes int) Option {
return func(opts *options) {
opts.MaxHeaderBytes = maxHeaderBytes
}
}

// NewNode creates a new gateway node.
func NewNode(v1 v1api.FullNode, v2 v2api.FullNode, opts ...Option) *Node {
options := &options{
maxLookbackDuration: DefaultMaxLookbackDuration,
maxMessageLookbackEpochs: DefaultMaxMessageLookbackEpochs,
rateLimitTimeout: DefaultRateLimitTimeout,
ethMaxFiltersPerConn: DefaultEthMaxFiltersPerConn,
ReadHeaderTimeout: ReadHeaderTimeout,
ReadTimeout: ReadTimeout,
WriteTimeout: WriteTimeout,
IdleTimeout: IdleTimeout,
MaxHeaderBytes: MaxHeaderBytes,
}
for _, opt := range opts {
opt(options)
Expand All @@ -138,6 +193,11 @@ func NewNode(v1 v1api.FullNode, v2 v2api.FullNode, opts ...Option) *Node {
rateLimitTimeout: options.rateLimitTimeout,
errLookback: fmt.Errorf("lookbacks of more than %s are disallowed", options.maxLookbackDuration),
ethMaxFiltersPerConn: options.ethMaxFiltersPerConn,
readHeaderTimeout: options.ReadHeaderTimeout,
readTimeout: options.ReadTimeout,
writeTimeout: options.WriteTimeout,
idleTimeout: options.IdleTimeout,
maxHeaderBytes: options.MaxHeaderBytes,
}
gateway.v1Proxy = &reverseProxyV1{
gateway: gateway,
Expand Down