Skip rate-limit header mutation on committed response#4192
Open
seonwooj0810 wants to merge 1 commit into
Open
Conversation
RequestRateLimiterGatewayFilterFactory adds the rate-limiter response headers via getHeaders().add(). When the filter chain is re-subscribed with the same ServerWebExchange (for example by the retry filter after a denied request), the response is already committed and getHeaders() returns ReadOnlyHttpHeaders, so the add() call fails with UnsupportedOperationException. Guard the header mutation with ServerHttpResponse.isCommitted() so the filter no longer throws when re-applied to a committed response. The default complete-signal behavior is unchanged. Fixes spring-cloudgh-4175 Signed-off-by: seonwoo_jung <79202163+seonwooj0810@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes gh-4175
Problem
RequestRateLimiterGatewayFilterFactorycopies the rate-limiter response headers onto the exchange response viaexchange.getResponse().getHeaders().add(...)unconditionally:When the filter chain is re-subscribed with the same
ServerWebExchange— most notably whenRetryGatewayFilterFactoryis placed before the rate limiter and retries onseries=CLIENT_ERROR— the response from the first (denied) pass is already committed.getHeaders()then returns aReadOnlyHttpHeadersinstance, andadd(...)fails withUnsupportedOperationException.Change
Guard the header mutation with
ServerHttpResponse.isCommitted()so headers are only added while the response can still be modified. This is a minimal, compatibility-preserving fix: the default complete-signal behavior on a denied request is unchanged, and the existingthrowOnLimitoption keeps working. (Approach suggested by @dongyikuan919 in the issue thread.)Test evidence
Added
deniedDoesNotMutateHeadersWhenResponseAlreadyCommittedtoRequestRateLimiterGatewayFilterFactoryTests, which commits the response before invoking the filter and asserts it completes instead of erroring.onError(java.lang.UnsupportedOperationException).Tests run: 7, Failures: 0, Errors: 0, Skipped: 0.Built and tested with JDK 21 against
spring-cloud-gateway-server-webflux;spring-javaformat:applyreports no formatting changes.Verification done: (1) no in-flight PR for #4175 (only the previously merged #4072 that added
throwOnLimit); (2) issue has no self-claim; (3) fix is in.javafiles; (4) confirmed the unguardedgetHeaders().add()still present on currentmain; (5) commit signed off for DCO.