Skip to content

Commit 02a2c40

Browse files
committed
Move URL transform methods from ServerHttpResponse to ServerWebExchange
This commit moves `encodeUrl` and `registerUrlEncoder` from ServerHttpResponse to ServerWebExchange. It also renames `encodeUrl` to `transformUrl` and `registerUrlEncoder` to `addUrlTransformer` to make it clearer that these methods do not perform actual URL encodings (i.e. they do not replaceinvalid characters). The `add` prefix (instead of `register`) makes it clearer that each function is added in addition to the previous one. Issue: SPR-15924
1 parent 5d4ee09 commit 02a2c40

File tree

10 files changed

+110
-74
lines changed

10 files changed

+110
-74
lines changed

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

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121
import java.util.concurrent.atomic.AtomicReference;
22-
import java.util.function.Function;
2322
import java.util.function.Supplier;
2423
import java.util.stream.Collectors;
2524

@@ -69,9 +68,6 @@ private enum State {NEW, COMMITTING, COMMITTED}
6968

7069
private final MultiValueMap<String, ResponseCookie> cookies;
7170

72-
@Nullable
73-
private Function<String, String> urlEncoder = url -> url;
74-
7571
private final AtomicReference<State> state = new AtomicReference<>(State.NEW);
7672

7773
private final List<Supplier<? extends Mono<Void>>> commitActions = new ArrayList<>(4);
@@ -136,16 +132,6 @@ public void addCookie(ResponseCookie cookie) {
136132
}
137133
}
138134

139-
@Override
140-
public String encodeUrl(String url) {
141-
return (this.urlEncoder != null ? this.urlEncoder.apply(url) : url);
142-
}
143-
144-
@Override
145-
public void registerUrlEncoder(Function<String, String> encoder) {
146-
this.urlEncoder = (this.urlEncoder != null ? this.urlEncoder.andThen(encoder) : encoder);
147-
}
148-
149135
@Override
150136
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
151137
this.commitActions.add(action);

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
package org.springframework.http.server.reactive;
1818

19-
import java.util.function.Function;
20-
2119
import org.springframework.http.HttpStatus;
2220
import org.springframework.http.ReactiveHttpOutputMessage;
2321
import org.springframework.http.ResponseCookie;
@@ -59,22 +57,4 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
5957
*/
6058
void addCookie(ResponseCookie cookie);
6159

62-
/**
63-
* A mechanism for URL rewriting that applications and libraries such as
64-
* HTML template libraries to use consistently for all URLs emitted by
65-
* the application. Doing so enables the registration of URL encoders via
66-
* {@link #registerUrlEncoder} that can insert an id for authentication,
67-
* a nonce for CSRF protection, or other.
68-
* @param url the URL to encode
69-
* @return the encoded URL or the same
70-
*/
71-
String encodeUrl(String url);
72-
73-
/**
74-
* Register a URL rewriting function for use with {@link #encodeUrl}.
75-
* The function must return an encoded URL or the same URL.
76-
* @param encoder a URL encoding function to use
77-
*/
78-
void registerUrlEncoder(Function<String, String> encoder);
79-
8060
}

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
*/
1616
package org.springframework.http.server.reactive;
1717

18-
import java.util.function.Function;
1918
import java.util.function.Supplier;
2019

2120
import org.reactivestreams.Publisher;
@@ -80,16 +79,6 @@ public void addCookie(ResponseCookie cookie) {
8079
getDelegate().addCookie(cookie);
8180
}
8281

83-
@Override
84-
public String encodeUrl(String url) {
85-
return getDelegate().encodeUrl(url);
86-
}
87-
88-
@Override
89-
public void registerUrlEncoder(Function<String, String> encoder) {
90-
getDelegate().registerUrlEncoder(encoder);
91-
}
92-
9382
@Override
9483
public DataBufferFactory bufferFactory() {
9584
return getDelegate().bufferFactory();

spring-web/src/main/java/org/springframework/web/server/ServerWebExchange.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.time.Instant;
2121
import java.util.Map;
2222
import java.util.function.Consumer;
23+
import java.util.function.Function;
2324

2425
import reactor.core.publisher.Mono;
2526

@@ -177,6 +178,23 @@ default <T> T getAttributeOrDefault(String name, T defaultValue) {
177178
*/
178179
boolean checkNotModified(@Nullable String etag, Instant lastModified);
179180

181+
/**
182+
* Transform the given url according to the registered transformation function(s).
183+
* By default, this method returns the given {@code url}, though additional
184+
* transformation functions can by registered with {@link #addUrlTransformer}
185+
* @param url the URL to transform
186+
* @return the transformed URL
187+
*/
188+
String transformUrl(String url);
189+
190+
/**
191+
* Register an additional URL transformation function for use with {@link #transformUrl}.
192+
* The given function can be used to insert an id for authentication, a nonce for CSRF
193+
* protection, etc.
194+
* <p>Note that the given function is applied after any previously registered functions.
195+
* @param transformer a URL transformation function to add
196+
*/
197+
void addUrlTransformer(Function<String, String> transformer);
180198

181199
/**
182200
* Return a builder to mutate properties of this exchange by wrapping it

spring-web/src/main/java/org/springframework/web/server/ServerWebExchangeDecorator.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -18,6 +18,7 @@
1818
import java.security.Principal;
1919
import java.time.Instant;
2020
import java.util.Map;
21+
import java.util.function.Function;
2122

2223
import reactor.core.publisher.Mono;
2324

@@ -120,6 +121,15 @@ public boolean checkNotModified(@Nullable String etag, Instant lastModified) {
120121
return getDelegate().checkNotModified(etag, lastModified);
121122
}
122123

124+
@Override
125+
public String transformUrl(String url) {
126+
return getDelegate().transformUrl(url);
127+
}
128+
129+
@Override
130+
public void addUrlTransformer(Function<String, String> transformer) {
131+
getDelegate().addUrlTransformer(transformer);
132+
}
123133

124134
@Override
125135
public String toString() {

spring-web/src/main/java/org/springframework/web/server/adapter/DefaultServerWebExchange.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.concurrent.ConcurrentHashMap;
27+
import java.util.function.Function;
2728

2829
import reactor.core.publisher.Mono;
2930

@@ -91,6 +92,8 @@ public class DefaultServerWebExchange implements ServerWebExchange {
9192

9293
private volatile boolean notModified;
9394

95+
private Function<String, String> urlTransformer = url -> url;
96+
9497

9598
public DefaultServerWebExchange(ServerHttpRequest request, ServerHttpResponse response,
9699
WebSessionManager sessionManager, ServerCodecConfigurer codecConfigurer, LocaleContextResolver localeContextResolver) {
@@ -322,4 +325,16 @@ private boolean validateIfModifiedSince(Instant lastModified) {
322325
return true;
323326
}
324327

328+
@Override
329+
public String transformUrl(String url) {
330+
return this.urlTransformer.apply(url);
331+
}
332+
333+
@Override
334+
public void addUrlTransformer(Function<String, String> transformer) {
335+
Assert.notNull(transformer, "'encoder' must not be null");
336+
this.urlTransformer = this.urlTransformer.andThen(transformer);
337+
}
338+
339+
325340
}

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

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 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.
@@ -32,36 +32,14 @@
3232
import org.springframework.http.ResponseCookie;
3333

3434
import static junit.framework.TestCase.assertTrue;
35-
import static org.junit.Assert.assertEquals;
36-
import static org.junit.Assert.assertFalse;
37-
import static org.junit.Assert.assertSame;
35+
import static org.junit.Assert.*;
3836

3937
/**
4038
* @author Rossen Stoyanchev
4139
* @author Sebastien Deleuze
4240
*/
4341
public class ServerHttpResponseTests {
4442

45-
@Test
46-
public void encodeUrlDefault() throws Exception {
47-
TestServerHttpResponse response = new TestServerHttpResponse();
48-
assertEquals("/foo", response.encodeUrl("/foo"));
49-
}
50-
51-
@Test
52-
public void encodeUrlWithEncoder() throws Exception {
53-
TestServerHttpResponse response = new TestServerHttpResponse();
54-
response.registerUrlEncoder(s -> s + "?nonce=123");
55-
assertEquals("/foo?nonce=123", response.encodeUrl("/foo"));
56-
}
57-
58-
@Test
59-
public void encodeUrlWithMultipleEncoders() throws Exception {
60-
TestServerHttpResponse response = new TestServerHttpResponse();
61-
response.registerUrlEncoder(s -> s + ";p=abc");
62-
response.registerUrlEncoder(s -> s + "?q=123");
63-
assertEquals("/foo;p=abc?q=123", response.encodeUrl("/foo"));
64-
}
6543

6644
@Test
6745
public void writeWith() throws Exception {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright 2002-2017 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.web.server.adapter;
18+
19+
import org.junit.Before;
20+
import org.junit.Test;
21+
22+
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
23+
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
24+
import org.springframework.web.server.ServerWebExchange;
25+
26+
import static org.junit.Assert.*;
27+
28+
/**
29+
* @author Arjen Poutsma
30+
*/
31+
public class ServerWebExchangeTests {
32+
33+
private ServerWebExchange exchange;
34+
35+
@Before
36+
public void createExchange() {
37+
MockServerHttpRequest request = MockServerHttpRequest.get("http://example.com").build();
38+
this.exchange = new MockServerWebExchange(request);
39+
}
40+
41+
@Test
42+
public void transformUrlDefault() throws Exception {
43+
assertEquals("/foo", this.exchange.transformUrl("/foo"));
44+
}
45+
46+
@Test
47+
public void transformUrlWithEncoder() throws Exception {
48+
this.exchange.addUrlTransformer(s -> s + "?nonce=123");
49+
assertEquals("/foo?nonce=123", this.exchange.transformUrl("/foo"));
50+
}
51+
52+
@Test
53+
public void transformUrlWithMultipleEncoders() throws Exception {
54+
this.exchange.addUrlTransformer(s -> s + ";p=abc");
55+
this.exchange.addUrlTransformer(s -> s + "?q=123");
56+
assertEquals("/foo;p=abc?q=123", this.exchange.transformUrl("/foo"));
57+
}
58+
59+
60+
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RedirectView.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,9 @@ protected StringBuilder appendCurrentRequestQuery(String targetUrl, ServerHttpRe
289289
* @param exchange current exchange
290290
*/
291291
protected Mono<Void> sendRedirect(String targetUrl, ServerWebExchange exchange) {
292+
String transformedUrl = (isRemoteHost(targetUrl) ? targetUrl : exchange.transformUrl(targetUrl));
292293
ServerHttpResponse response = exchange.getResponse();
293-
String encodedURL = (isRemoteHost(targetUrl) ? targetUrl : response.encodeUrl(targetUrl));
294-
response.getHeaders().setLocation(URI.create(encodedURL));
294+
response.getHeaders().setLocation(URI.create(transformedUrl));
295295
response.setStatusCode(getStatusCode());
296296
return Mono.empty();
297297
}

spring-webflux/src/main/java/org/springframework/web/reactive/result/view/RequestContext.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public String getContextPath() {
203203
*/
204204
public String getContextUrl(String relativeUrl) {
205205
String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl);
206-
return getExchange().getResponse().encodeUrl(url);
206+
return getExchange().transformUrl(url);
207207
}
208208

209209
/**
@@ -220,7 +220,7 @@ public String getContextUrl(String relativeUrl, Map<String, ?> params) {
220220
String url = StringUtils.applyRelativePath(getContextPath() + "/", relativeUrl);
221221
UriTemplate template = new UriTemplate(url);
222222
url = template.expand(params).toASCIIString();
223-
return getExchange().getResponse().encodeUrl(url);
223+
return getExchange().transformUrl(url);
224224
}
225225

226226
/**

0 commit comments

Comments
 (0)