Skip to content

Commit 94824e3

Browse files
committed
Avoid parse cookies when mutating request or response
When mutating a ServerHttpRequest or ClientResponse, the respective builders no longer access cookies automatically which causes them to be parsed and does so only if necessary. Likewise re-applying the read-only HttpHeaders wrapper is avoided. See gh-24680
1 parent 0e9ecb6 commit 94824e3

File tree

3 files changed

+95
-73
lines changed

3 files changed

+95
-73
lines changed

Diff for: spring-web/src/main/java/org/springframework/http/server/reactive/DefaultServerHttpRequestBuilder.java

+11-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@
2020
import java.net.URI;
2121
import java.net.URISyntaxException;
2222
import java.util.Arrays;
23-
import java.util.LinkedList;
2423
import java.util.function.Consumer;
2524

2625
import reactor.core.publisher.Flux;
@@ -31,7 +30,6 @@
3130
import org.springframework.http.HttpMethod;
3231
import org.springframework.lang.Nullable;
3332
import org.springframework.util.Assert;
34-
import org.springframework.util.LinkedMultiValueMap;
3533
import org.springframework.util.MultiValueMap;
3634
import org.springframework.util.StringUtils;
3735

@@ -46,12 +44,10 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
4644

4745
private URI uri;
4846

49-
private HttpHeaders httpHeaders;
47+
private HttpHeaders headers;
5048

5149
private String httpMethodValue;
5250

53-
private final MultiValueMap<String, HttpCookie> cookies;
54-
5551
@Nullable
5652
private String uriPath;
5753

@@ -70,21 +66,12 @@ public DefaultServerHttpRequestBuilder(ServerHttpRequest original) {
7066
Assert.notNull(original, "ServerHttpRequest is required");
7167

7268
this.uri = original.getURI();
69+
this.headers = HttpHeaders.writableHttpHeaders(original.getHeaders());
7370
this.httpMethodValue = original.getMethodValue();
7471
this.body = original.getBody();
75-
76-
this.httpHeaders = HttpHeaders.writableHttpHeaders(original.getHeaders());
77-
78-
this.cookies = new LinkedMultiValueMap<>(original.getCookies().size());
79-
copyMultiValueMap(original.getCookies(), this.cookies);
80-
8172
this.originalRequest = original;
8273
}
8374

84-
private static <K, V> void copyMultiValueMap(MultiValueMap<K,V> source, MultiValueMap<K,V> target) {
85-
source.forEach((key, value) -> target.put(key, new LinkedList<>(value)));
86-
}
87-
8875

8976
@Override
9077
public ServerHttpRequest.Builder method(HttpMethod httpMethod) {
@@ -113,14 +100,14 @@ public ServerHttpRequest.Builder contextPath(String contextPath) {
113100

114101
@Override
115102
public ServerHttpRequest.Builder header(String headerName, String... headerValues) {
116-
this.httpHeaders.put(headerName, Arrays.asList(headerValues));
103+
this.headers.put(headerName, Arrays.asList(headerValues));
117104
return this;
118105
}
119106

120107
@Override
121108
public ServerHttpRequest.Builder headers(Consumer<HttpHeaders> headersConsumer) {
122109
Assert.notNull(headersConsumer, "'headersConsumer' must not be null");
123-
headersConsumer.accept(this.httpHeaders);
110+
headersConsumer.accept(this.headers);
124111
return this;
125112
}
126113

@@ -132,8 +119,8 @@ public ServerHttpRequest.Builder sslInfo(SslInfo sslInfo) {
132119

133120
@Override
134121
public ServerHttpRequest build() {
135-
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath, this.httpHeaders,
136-
this.httpMethodValue, this.cookies, this.sslInfo, this.body, this.originalRequest);
122+
return new MutatedServerHttpRequest(getUriToUse(), this.contextPath,
123+
this.httpMethodValue, this.sslInfo, this.body, this.originalRequest);
137124
}
138125

139126
private URI getUriToUse() {
@@ -179,8 +166,6 @@ private static class MutatedServerHttpRequest extends AbstractServerHttpRequest
179166

180167
private final String methodValue;
181168

182-
private final MultiValueMap<String, HttpCookie> cookies;
183-
184169
@Nullable
185170
private final SslInfo sslInfo;
186171

@@ -190,12 +175,11 @@ private static class MutatedServerHttpRequest extends AbstractServerHttpRequest
190175

191176

192177
public MutatedServerHttpRequest(URI uri, @Nullable String contextPath,
193-
HttpHeaders headers, String methodValue, MultiValueMap<String, HttpCookie> cookies,
194-
@Nullable SslInfo sslInfo, Flux<DataBuffer> body, ServerHttpRequest originalRequest) {
178+
String methodValue, @Nullable SslInfo sslInfo,
179+
Flux<DataBuffer> body, ServerHttpRequest originalRequest) {
195180

196-
super(uri, contextPath, headers);
181+
super(uri, contextPath, originalRequest.getHeaders());
197182
this.methodValue = methodValue;
198-
this.cookies = cookies;
199183
this.sslInfo = sslInfo != null ? sslInfo : originalRequest.getSslInfo();
200184
this.body = body;
201185
this.originalRequest = originalRequest;
@@ -208,7 +192,7 @@ public String getMethodValue() {
208192

209193
@Override
210194
protected MultiValueMap<String, HttpCookie> initCookies() {
211-
return this.cookies;
195+
return this.originalRequest.getCookies();
212196
}
213197

214198
@Nullable

Diff for: spring-web/src/test/java/org/springframework/http/server/reactive/ServerHttpRequestTests.java

+30-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2019 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,98 +49,101 @@ public class ServerHttpRequestTests {
4949

5050
@Test
5151
public void queryParamsNone() throws Exception {
52-
MultiValueMap<String, String> params = createHttpRequest("/path").getQueryParams();
52+
MultiValueMap<String, String> params = createRequest("/path").getQueryParams();
5353
assertThat(params.size()).isEqualTo(0);
5454
}
5555

5656
@Test
5757
public void queryParams() throws Exception {
58-
MultiValueMap<String, String> params = createHttpRequest("/path?a=A&b=B").getQueryParams();
58+
MultiValueMap<String, String> params = createRequest("/path?a=A&b=B").getQueryParams();
5959
assertThat(params.size()).isEqualTo(2);
6060
assertThat(params.get("a")).isEqualTo(Collections.singletonList("A"));
6161
assertThat(params.get("b")).isEqualTo(Collections.singletonList("B"));
6262
}
6363

6464
@Test
6565
public void queryParamsWithMultipleValues() throws Exception {
66-
MultiValueMap<String, String> params = createHttpRequest("/path?a=1&a=2").getQueryParams();
66+
MultiValueMap<String, String> params = createRequest("/path?a=1&a=2").getQueryParams();
6767
assertThat(params.size()).isEqualTo(1);
6868
assertThat(params.get("a")).isEqualTo(Arrays.asList("1", "2"));
6969
}
7070

7171
@Test // SPR-15140
7272
public void queryParamsWithEncodedValue() throws Exception {
73-
MultiValueMap<String, String> params = createHttpRequest("/path?a=%20%2B+%C3%A0").getQueryParams();
73+
MultiValueMap<String, String> params = createRequest("/path?a=%20%2B+%C3%A0").getQueryParams();
7474
assertThat(params.size()).isEqualTo(1);
7575
assertThat(params.get("a")).isEqualTo(Collections.singletonList(" + \u00e0"));
7676
}
7777

7878
@Test
7979
public void queryParamsWithEmptyValue() throws Exception {
80-
MultiValueMap<String, String> params = createHttpRequest("/path?a=").getQueryParams();
80+
MultiValueMap<String, String> params = createRequest("/path?a=").getQueryParams();
8181
assertThat(params.size()).isEqualTo(1);
8282
assertThat(params.get("a")).isEqualTo(Collections.singletonList(""));
8383
}
8484

8585
@Test
8686
public void queryParamsWithNoValue() throws Exception {
87-
MultiValueMap<String, String> params = createHttpRequest("/path?a").getQueryParams();
87+
MultiValueMap<String, String> params = createRequest("/path?a").getQueryParams();
8888
assertThat(params.size()).isEqualTo(1);
8989
assertThat(params.get("a")).isEqualTo(Collections.singletonList(null));
9090
}
9191

9292
@Test
93-
public void mutateRequest() throws Exception {
93+
public void mutateRequestMethod() throws Exception {
94+
ServerHttpRequest request = createRequest("/").mutate().method(HttpMethod.DELETE).build();
95+
assertThat(request.getMethod()).isEqualTo(HttpMethod.DELETE);
96+
}
97+
98+
@Test
99+
public void mutateSslInfo() throws Exception {
94100
SslInfo sslInfo = mock(SslInfo.class);
95-
ServerHttpRequest request = createHttpRequest("/").mutate().sslInfo(sslInfo).build();
101+
ServerHttpRequest request = createRequest("/").mutate().sslInfo(sslInfo).build();
96102
assertThat(request.getSslInfo()).isSameAs(sslInfo);
103+
}
97104

98-
request = createHttpRequest("/").mutate().method(HttpMethod.DELETE).build();
99-
assertThat(request.getMethod()).isEqualTo(HttpMethod.DELETE);
100-
105+
@Test
106+
public void mutateUriAndPath() throws Exception {
101107
String baseUri = "https://aaa.org:8080/a";
102108

103-
request = createHttpRequest(baseUri).mutate().uri(URI.create("https://bbb.org:9090/b")).build();
109+
ServerHttpRequest request = createRequest(baseUri).mutate().uri(URI.create("https://bbb.org:9090/b")).build();
104110
assertThat(request.getURI().toString()).isEqualTo("https://bbb.org:9090/b");
105111

106-
request = createHttpRequest(baseUri).mutate().path("/b/c/d").build();
112+
request = createRequest(baseUri).mutate().path("/b/c/d").build();
107113
assertThat(request.getURI().toString()).isEqualTo("https://aaa.org:8080/b/c/d");
108114

109-
request = createHttpRequest(baseUri).mutate().path("/app/b/c/d").contextPath("/app").build();
115+
request = createRequest(baseUri).mutate().path("/app/b/c/d").contextPath("/app").build();
110116
assertThat(request.getURI().toString()).isEqualTo("https://aaa.org:8080/app/b/c/d");
111117
assertThat(request.getPath().contextPath().value()).isEqualTo("/app");
112118
}
113119

114-
@Test
115-
public void mutateWithInvalidPath() throws Exception {
116-
assertThatIllegalArgumentException().isThrownBy(() ->
117-
createHttpRequest("/").mutate().path("foo-bar"));
118-
}
119-
120120
@Test // SPR-16434
121121
public void mutatePathWithEncodedQueryParams() throws Exception {
122-
ServerHttpRequest request = createHttpRequest("/path?name=%E6%89%8E%E6%A0%B9");
122+
ServerHttpRequest request = createRequest("/path?name=%E6%89%8E%E6%A0%B9");
123123
request = request.mutate().path("/mutatedPath").build();
124124

125125
assertThat(request.getURI().getRawPath()).isEqualTo("/mutatedPath");
126126
assertThat(request.getURI().getRawQuery()).isEqualTo("name=%E6%89%8E%E6%A0%B9");
127127
}
128128

129+
@Test
130+
public void mutateWithInvalidPath() {
131+
assertThatIllegalArgumentException().isThrownBy(() -> createRequest("/").mutate().path("foo-bar"));
132+
}
133+
129134
@Test
130135
public void mutateHeadersViaConsumer() throws Exception {
131136
String headerName = "key";
132137
String headerValue1 = "value1";
133138
String headerValue2 = "value2";
134139

135-
ServerHttpRequest request = createHttpRequest("/path");
140+
ServerHttpRequest request = createRequest("/path");
136141
assertThat(request.getHeaders().get(headerName)).isNull();
137142

138143
request = request.mutate().headers(headers -> headers.add(headerName, headerValue1)).build();
139-
140144
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue1);
141145

142146
request = request.mutate().headers(headers -> headers.add(headerName, headerValue2)).build();
143-
144147
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue1, headerValue2);
145148
}
146149

@@ -151,19 +154,17 @@ public void mutateHeaderBySettingHeaderValues() throws Exception {
151154
String headerValue2 = "value2";
152155
String headerValue3 = "value3";
153156

154-
ServerHttpRequest request = createHttpRequest("/path");
157+
ServerHttpRequest request = createRequest("/path");
155158
assertThat(request.getHeaders().get(headerName)).isNull();
156159

157160
request = request.mutate().header(headerName, headerValue1, headerValue2).build();
158-
159161
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue1, headerValue2);
160162

161163
request = request.mutate().header(headerName, headerValue3).build();
162-
163164
assertThat(request.getHeaders().get(headerName)).containsExactly(headerValue3);
164165
}
165166

166-
private ServerHttpRequest createHttpRequest(String uriString) throws Exception {
167+
private ServerHttpRequest createRequest(String uriString) throws Exception {
167168
URI uri = URI.create(uriString);
168169
MockHttpServletRequest request = new TestHttpServletRequest(uri);
169170
AsyncContext asyncContext = new MockAsyncContext(request, new MockHttpServletResponse());

0 commit comments

Comments
 (0)