Skip to content

Commit

Permalink
Allow ServerHttpRequest content-type mutation
Browse files Browse the repository at this point in the history
Prior to this commit, `ServerHttpRequest.mutate()` would not reflect
changes made on the "Accept" and "Content-Type" HTTP headers.
This was due to the fact that the instantiation of a new request based
on the mutated values would not use the writable HTTP headers used
during the mutation, but rather a read-only view of the headers backed
by `ReadOnlyHttpHeaders`.

`ReadOnlyHttpHeaders` caches those values for performance reasons, so
getting those from the new request would not reflect the changes made
during the mutation phase.

This commit ensures that the new request uses the mutated headers.

Fixes spring-projectsgh-26615
  • Loading branch information
bclozel authored and lxbzmy committed Mar 26, 2022
1 parent 57a68ad commit b707d75
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Brian Clozel
* @since 5.0
*/
class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
Expand Down Expand Up @@ -131,7 +132,7 @@ public ServerHttpRequest.Builder remoteAddress(InetSocketAddress remoteAddress)
@Override
public ServerHttpRequest build() {
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath,
this.httpMethodValue, this.sslInfo, this.remoteAddress, this.body, this.originalRequest);
this.httpMethodValue, this.sslInfo, this.remoteAddress, this.headers, this.body, this.originalRequest);
}

private URI getUriToUse() {
Expand Down Expand Up @@ -190,9 +191,9 @@ private static class MutatedServerHttpRequest extends AbstractServerHttpRequest

public MutatedServerHttpRequest(URI uri, @Nullable String contextPath,
String methodValue, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress,
Flux<DataBuffer> body, ServerHttpRequest originalRequest) {
HttpHeaders headers, Flux<DataBuffer> body, ServerHttpRequest originalRequest) {

super(uri, contextPath, originalRequest.getHeaders());
super(uri, contextPath, headers);
this.methodValue = methodValue;
this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress());
this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap;
import org.springframework.web.testfixture.servlet.DelegatingServletInputStream;
import org.springframework.web.testfixture.servlet.MockAsyncContext;
Expand All @@ -46,6 +47,7 @@
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @author Brian Clozel
*/
public class ServerHttpRequestTests {

Expand Down Expand Up @@ -166,6 +168,18 @@ public void mutateHeaderBySettingHeaderValues() throws Exception {
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue3);
}

@Test // gh-26615
void mutateContentTypeHeaderValue() throws Exception {
ServerHttpRequest request = createRequest("/path").mutate()
.headers(headers -> headers.setContentType(MediaType.APPLICATION_JSON)).build();

assertThat(request.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);

ServerHttpRequest mutated = request.mutate()
.headers(headers -> headers.setContentType(MediaType.APPLICATION_CBOR)).build();
assertThat(mutated.getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_CBOR);
}

@Test
void mutateWithExistingContextPath() throws Exception {
ServerHttpRequest request = createRequest("/context/path", "/context");
Expand Down

0 comments on commit b707d75

Please sign in to comment.