Browse Source

Allow ServerHttpRequest content-type mutation

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 gh-26615
pull/26635/head
Brian Clozel 4 years ago
parent
commit
5a11569790
  1. 7
      spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java
  2. 14
      spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestTests.java

7
spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java

@ -38,6 +38,7 @@ import org.springframework.util.StringUtils;
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Brian Clozel
* @since 5.0 * @since 5.0
*/ */
class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder { class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
@ -131,7 +132,7 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
@Override @Override
public ServerHttpRequest build() { public ServerHttpRequest build() {
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath, 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() { private URI getUriToUse() {
@ -190,9 +191,9 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
public MutatedServerHttpRequest(URI uri, @Nullable String contextPath, public MutatedServerHttpRequest(URI uri, @Nullable String contextPath,
String methodValue, @Nullable SslInfo sslInfo, @Nullable InetSocketAddress remoteAddress, 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.methodValue = methodValue;
this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress()); this.remoteAddress = (remoteAddress != null ? remoteAddress : originalRequest.getRemoteAddress());
this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo()); this.sslInfo = (sslInfo != null ? sslInfo : originalRequest.getSslInfo());

14
spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestTests.java

@ -30,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.testfixture.servlet.DelegatingServletInputStream; import org.springframework.web.testfixture.servlet.DelegatingServletInputStream;
import org.springframework.web.testfixture.servlet.MockAsyncContext; import org.springframework.web.testfixture.servlet.MockAsyncContext;
@ -46,6 +47,7 @@ import static org.mockito.Mockito.mock;
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @author Sam Brannen * @author Sam Brannen
* @author Brian Clozel
*/ */
public class ServerHttpRequestTests { public class ServerHttpRequestTests {
@ -166,6 +168,18 @@ public class ServerHttpRequestTests {
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue3); 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 @Test
void mutateWithExistingContextPath() throws Exception { void mutateWithExistingContextPath() throws Exception {
ServerHttpRequest request = createRequest("/context/path", "/context"); ServerHttpRequest request = createRequest("/context/path", "/context");

Loading…
Cancel
Save