Skip to content

ClientResponse.mutate() builder can modify originalResponse headers. #37086

Description

@sparecycles

** 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    in: webIssues in web modules (web, webmvc, webflux, websocket)status: waiting-for-triageAn issue we've not yet triaged or decided on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions