RequestRateLimiterGatewayFilterFactory commits 429 response instead of throwing
メンテナーはふだん 1 日以内に返信
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 68/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 静か
- 技術スタック
- java, spring, spring-boot
調査の方向性
影響を受けるエントリポイントは RequestRateLimiterGatewayFilterFactory.apply() で、具体的には issue に記載されている allowed=false ブランチです。既存の response-status/setComplete パスと RetryGatewayFilterFactory との相互作用を確認し、提供されている Retry の再現手順を使って、429 の処理が committed 済みのレスポンスを再利用せずに例外ハンドラーへ到達することを検証してください。
索引モデルが issue の本文から書いたものです。
説明
Describe the bug
RequestRateLimiterGatewayFilterFactory calls exchange.getResponse().setComplete() when the rate limit is exceeded.
This commits the response immediately, which has two negative side-effects:
-
Retry breaks with
UnsupportedOperationException: ReadOnlyHttpHeaders.
WhenRetryGatewayFilterFactoryis placed before the rate limiter and configured withseries=CLIENT_ERROR, the retry operator sees the committed429status and re-executes the filter chain using the sameServerWebExchange. On the second pass,RequestRateLimiterGatewayFilterFactoryattempts to addX-RateLimit-*headers to the already-committed response, causing an immediateUnsupportedOperationExceptionfromReadOnlyHttpHeaders. -
Custom exception handlers cannot intercept 429.
AnyWebExceptionHandlerorErrorWebExceptionHandlerbean is bypassed because the filter returns a successfulMono<Void>rather than propagating an error.
Regression / Prior art
This was raised in #575 (2018) where @spencergibb suggested the fix:
"I think rather than setting a status we can use
ResponseStatusExceptioninMono.error."
That change was never applied.
Versions
- Spring Cloud Gateway Server: 4.2.7
- Spring Boot: 3.5.14
- Java: 25
Minimal reproduction
Route configuration
spring:
cloud:
gateway:
routes:
- id: ratelimit-retry-bug
uri: http://localhost:8081
predicates:
- Path=/test/**
filters:
# Retry is placed BEFORE rate limiter
- name: Retry
args:
retries: 3
series: CLIENT_ERROR
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
redis-rate-limiter.requestedTokens: 1
Steps
─────
1. Send a request that exceeds the rate limit.
2. Observe that CustomExceptionHandler is not invoked.
3. If Retry is present with series=CLIENT_ERROR, the application throws:
java.lang.UnsupportedOperationException: null
at org.springframework.http.ReadOnlyHttpHeaders.add(ReadOnlyHttpHeaders.java:97)
at org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory.lambda$apply$1(RequestRateLimiterGatewayFilterFactory.java:
112)
Expected behavior
─────────────────
When allowed=false, the filter should throw rather than commit the response, e.g.:
return Mono.error(new ResponseStatusException(config.getStatusCode(), "Rate limit exceeded" ));
This allows:
• ErrorWebExceptionHandler / WebExceptionHandler to customize the 429 response.
• RetryGatewayFilterFactory to decide whether to retry based on the exception (e.g. do NOT retry on ResponseStatusException), avoiding the ReadOnlyHttpHears crash.
Actual behavior
───────────────
The filter calls setResponseStatus(exchange, config.getStatusCode()) followed by exchange.getResponse().setComplete(), which commits the response and returns a successful Mono<Void>.
Suggested fix
─────────────
Replace the final setComplete() branch in RequestRateLimiterGatewayFilterFactory.apply() with Mono.error(new ResponseStatusException(...)), consistent with the suggestion in #575.
- 主要言語
- Java
- スター
- 4.9k
- フォーク
- 3.5k
- 平均マージ
- 2日 4時間
- マージ済み PR(30日)
- 9
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
spring-cloud/spring-cloud-gateway のほかの issue
-
Add @Order annotation to gatewayRestClientCustomizer Bean対応中かも @CheHyeonYeong が 227 日前に担当しました。 オープンenhancement help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
spring-cloud/spring-cloud-gateway#3626 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信
-
Ambiguity in section 19 of the documentation - Spring Cloud Gateway - Custom Route Predicate対応中かも @won-seoop が 154 日前に担当しました。 オープンdocumentation
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
spring-cloud/spring-cloud-gateway#3463 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
waiting-for-triage
難易度 4/5 3〜5日 初心者へのやさしさ 25/100
spring-cloud/spring-cloud-gateway#4357 · コメント 2 件 ·
メンテナーはふだん 1 日以内に返信
-
Improve TokenRelay filter documentation when Gateway acts as a BFF対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープンwaiting-for-triage
難易度 2/5 半日 初心者へのやさしさ 58/100
spring-cloud/spring-cloud-gateway#4355 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
waiting-for-triage
難易度 3/5 1〜2日 初心者へのやさしさ 35/100
spring-cloud/spring-cloud-gateway#4341 ·
メンテナーはふだん 1 日以内に返信
spring-cloud/spring-cloud-gateway の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
langchain4j/langchain4j#7068 ·
メンテナーはふだん 1 日以内に返信
-
Mend: dependency security vulnerability untriaged
難易度 2/5 1〜3時間 初心者へのやさしさ 73/100
opensearch-project/k-NN#3693 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 67/100
メンテナーはふだん 1 日以内に返信
-
[Bug]: Encrypted PDF portfolios show an empty portfolio panel対応中かも @christophmertins が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Stirling-Tools/Stirling-PDF#8864 · リアクション 1 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 1/5 1時間未満 初心者へのやさしさ 85/100
CodingDocs/awesome-java#41 · コメント 1 件 ·