** Bug Reports **
Not sure if this is a bug, but
var mutatedClientResponse = originalClientResponse.mutate().header("xyz", "pqrst").build();
updates the headers in the original originalClientResponse, too.
Doc says
Return a builder to mutate this response, for example to change
the status, headers, cookies, and replace or transform the body.
So maybe is this expected behavior. IMO mutate() returning a builder looks like immutable semantics apply.
analysis
ClientResponse.mutate() returns a DefaultClientResponseBuilder with a null headers field.
|
DefaultClientResponseBuilder(ClientResponse other, boolean mutate) { |
|
Assert.notNull(other, "ClientResponse must not be null"); |
|
this.strategies = other.strategies(); |
|
this.statusCode = other.statusCode(); |
|
if (mutate) { |
|
this.body = other.bodyToFlux(DataBuffer.class); |
|
} |
|
else { |
|
this.headers = new HttpHeaders(); |
|
this.headers.addAll(other.headers().asHttpHeaders()); |
|
} |
|
this.originalResponse = other; |
|
this.request = (other instanceof DefaultClientResponse defaultClientResponse ? |
|
defaultClientResponse.request() : EMPTY_REQUEST); |
|
} |
Headers are implied from this.originalResponse = other; and lazily created via getHeaders:
|
@SuppressWarnings({"ConstantConditions", "NullAway"}) |
|
private HttpHeaders getHeaders() { |
|
if (this.headers == null) { |
|
this.headers = new HttpHeaders(this.originalResponse.headers().asHttpHeaders()); |
|
} |
|
return this.headers; |
|
} |
This refers to the original headers because this.headers = unwrap(httpHeaders) just copies the backing map reference.
|
public HttpHeaders(HttpHeaders httpHeaders) { |
|
Assert.notNull(httpHeaders, "HttpHeaders must not be null"); |
|
this.headers = (httpHeaders == EMPTY ? |
|
CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH)) : |
|
unwrap(httpHeaders)); |
|
} |
So updates to the headers goes to the original (shared) backing map reference.
** Bug Reports **
Not sure if this is a bug, but
updates the headers in the original
originalClientResponse, too.Doc says
So maybe is this expected behavior. IMO
mutate()returning a builder looks like immutable semantics apply.analysis
ClientResponse.mutate()returns aDefaultClientResponseBuilderwith a nullheadersfield.spring-framework/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java
Lines 101 to 115 in 9bdeadc
Headers are implied from
this.originalResponse = other;and lazily created via getHeaders:spring-framework/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponseBuilder.java
Lines 144 to 150 in 9bdeadc
This refers to the original headers because
this.headers = unwrap(httpHeaders)just copies the backing map reference.spring-framework/spring-web/src/main/java/org/springframework/http/HttpHeaders.java
Lines 477 to 482 in 9bdeadc
So updates to the headers goes to the original (shared) backing map reference.