Skip to content

Commit 7ee19c0

Browse files
authored
feat: add max-range-request-file-size flag (#296)
* feat: add max-range-request-file-size flag adds protection against CDN bugs with large file range requests. defaults to 5GiB to prevent Cloudflare from returning entire file instead of requested range, which causes excess bandwidth billing.
1 parent db44d6e commit 7ee19c0

File tree

5 files changed

+39
-11
lines changed

5 files changed

+39
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ The following emojis are used to highlight certain changes:
1515

1616
### Added
1717

18+
- `--max-range-request-file-size` / `RAINBOW_MAX_RANGE_REQUEST_FILE_SIZE`: Configurable limit for HTTP Range requests on large files (default: 5GiB). Range requests for files larger than this limit return HTTP 501 Not Implemented to protect against CDN issues. Specifically addresses Cloudflare's bug where range requests for files over 5GiB are silently ignored, causing the entire file to be returned instead of the requested range, leading to excess bandwidth consumption and billing.
19+
1820
### Changed
1921

2022
- Update to Boxo [v0.35.0](https://github.com/ipfs/boxo/releases/tag/v0.35.0)
2123
- Update to go-libp2p-kad-dht [v0.35.0](https://github.com/libp2p/go-libp2p-kad-dht/releases/tag/v0.35.0)
2224

2325
### Fixed
2426

27+
- Fixed bitswap client initialization to use `time.Duration` instead of `delay.Fixed()` for rebroadcast delay, matching the updated bitswap client API
28+
2529
### Removed
2630

2731
### Security

docs/environment-variables.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- [`RAINBOW_HTTP_RETRIEVAL_METRICS_LABELS_FOR_ENDPOINTS`](#rainbow_http_retrieval_metrics_labels_for_endpoints)
2929
- [`RAINBOW_MAX_CONCURRENT_REQUESTS`](#rainbow_max_concurrent_requests)
3030
- [`RAINBOW_RETRIEVAL_TIMEOUT`](#rainbow_retrieval_timeout)
31+
- [`RAINBOW_MAX_RANGE_REQUEST_FILE_SIZE`](#rainbow_max_range_request_file_size)
3132
- [Experiments](#experiments)
3233
- [`RAINBOW_SEED_PEERING`](#rainbow_seed_peering)
3334
- [`RAINBOW_SEED_PEERING_MAX_INDEX`](#rainbow_seed_peering_max_index)
@@ -298,6 +299,20 @@ If content cannot be retrieved within this period, the gateway returns a `504 Ga
298299

299300
Default: `30s`
300301

302+
### `RAINBOW_MAX_RANGE_REQUEST_FILE_SIZE`
303+
304+
Maximum file size in bytes for which HTTP Range requests are supported. Range requests for files larger than this limit will return `501 Not Implemented` error with a message suggesting to switch to verifiable block requests (`application/vnd.ipld.raw`).
305+
306+
This setting provides protection against issues with CDN and reverse proxy implementations that have bugs or limitations when handling byte range requests for large files. Cloudflare, in particular, has a [known issue](https://github.com/ipfs/boxo/issues/856#issuecomment-2786431369) where range requests for files over 5 GiB are silently ignored - instead of returning the requested byte range, Cloudflare returns the entire file. This causes serious problems:
307+
- **Excess bandwidth consumption and billing**: Clients expecting a small range (e.g., web browsers requesting parts of a large SQLite database) will receive and be billed for the entire multi-gigabyte file
308+
- **Client failures**: Naive clients like JavaScript applications may crash or hang when they receive gigabytes of data instead of the requested range
309+
310+
When a range request exceeds the configured limit, the gateway will return an HTTP 501 error suggesting the client to use verifiable block requests instead, which are more suitable for large file transfers and can be independently verified.
311+
312+
Set to `0` to disable this limit and allow range requests for files of any size (use with caution if your gateway is behind a CDN or reverse proxy).
313+
314+
Default: `5368709120` (5 GiB - matches Cloudflare's threshold to prevent excess billing)
315+
301316
## Experiments
302317

303318
### `RAINBOW_SEED_PEERING`

handlers.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,11 +314,12 @@ func setupGatewayHandler(cfg Config, nd *Node) (http.Handler, error) {
314314
}
315315

316316
gwConf := gateway.Config{
317-
DeserializedResponses: true,
318-
PublicGateways: publicGateways,
319-
NoDNSLink: noDNSLink,
320-
MaxConcurrentRequests: cfg.MaxConcurrentRequests, // When exceeded, returns 429 with Retry-After: 60 (hardcoded in boxo)
321-
RetrievalTimeout: cfg.RetrievalTimeout,
317+
DeserializedResponses: true,
318+
PublicGateways: publicGateways,
319+
NoDNSLink: noDNSLink,
320+
MaxConcurrentRequests: cfg.MaxConcurrentRequests, // When exceeded, returns 429 with Retry-After: 60 (hardcoded in boxo)
321+
RetrievalTimeout: cfg.RetrievalTimeout,
322+
MaxRangeRequestFileSize: cfg.MaxRangeRequestFileSize,
322323
}
323324
gwHandler := gateway.NewHandler(gwConf, backend)
324325

main.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,12 @@ Generate an identity seed and launch a gateway:
480480
EnvVars: []string{"RAINBOW_RETRIEVAL_TIMEOUT"},
481481
Usage: "Maximum duration for initial content retrieval and time between writes",
482482
},
483+
&cli.Int64Flag{
484+
Name: "max-range-request-file-size",
485+
Value: 5368709120, // 5 GiB
486+
EnvVars: []string{"RAINBOW_MAX_RANGE_REQUEST_FILE_SIZE"},
487+
Usage: "Maximum file size in bytes for which range requests are supported. Range requests for larger files will return 501. Set to 0 to disable limit",
488+
},
483489
&cli.StringSliceFlag{
484490
Name: "dnslink-resolvers",
485491
Value: cli.NewStringSlice(". : auto"),
@@ -710,9 +716,10 @@ share the same seed as long as the indexes are different.
710716
HTTPRetrievalWorkers: httpRetrievalWorkers,
711717
HTTPRetrievalMaxDontHaveErrors: httpRetrievalMaxDontHaveErrors,
712718
HTTPRetrievalMetricsLabelsForEndpoints: httpRetrievalMetricsLabelsForEndpoints,
713-
// Gateway rate limiting and timeout configuration
714-
MaxConcurrentRequests: cctx.Int("max-concurrent-requests"),
715-
RetrievalTimeout: cctx.Duration("retrieval-timeout"),
719+
// Gateway limits
720+
MaxConcurrentRequests: cctx.Int("max-concurrent-requests"),
721+
RetrievalTimeout: cctx.Duration("retrieval-timeout"),
722+
MaxRangeRequestFileSize: cctx.Int64("max-range-request-file-size"),
716723
}
717724

718725
// Store original values for display

setup.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,10 @@ type Config struct {
174174
// Bootstrap peers configuration (with "auto" support)
175175
Bootstrap []string
176176

177-
// Gateway rate limiting and timeout configuration
178-
MaxConcurrentRequests int
179-
RetrievalTimeout time.Duration
177+
// Gateway limits
178+
MaxConcurrentRequests int
179+
RetrievalTimeout time.Duration
180+
MaxRangeRequestFileSize int64
180181
}
181182

182183
func SetupNoLibp2p(ctx context.Context, cfg Config, dnsCache *cachedDNS) (*Node, error) {

0 commit comments

Comments
 (0)