Skip to content

Commit 7193686

Browse files
committed
Support encodeUrl mechanism via ServerHttpResponse
Issue: SPR-14529
1 parent 2191d80 commit 7193686

File tree

4 files changed

+67
-0
lines changed

4 files changed

+67
-0
lines changed

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

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

@@ -66,6 +67,8 @@ private enum State {NEW, COMMITTING, COMMITTED};
6667

6768
private final MultiValueMap<String, ResponseCookie> cookies;
6869

70+
private Function<String, String> urlEncoder = url -> url;
71+
6972
private final AtomicReference<State> state = new AtomicReference<>(State.NEW);
7073

7174
private final List<Supplier<? extends Mono<Void>>> commitActions = new ArrayList<>(4);
@@ -117,6 +120,16 @@ public MultiValueMap<String, ResponseCookie> getCookies() {
117120
CollectionUtils.unmodifiableMultiValueMap(this.cookies) : this.cookies);
118121
}
119122

123+
@Override
124+
public String encodeUrl(String url) {
125+
return (this.urlEncoder != null ? this.urlEncoder.apply(url) : url);
126+
}
127+
128+
@Override
129+
public void registerUrlEncoder(Function<String, String> encoder) {
130+
this.urlEncoder = (this.urlEncoder != null ? this.urlEncoder.andThen(encoder) : encoder);
131+
}
132+
120133
@Override
121134
public void beforeCommit(Supplier<? extends Mono<Void>> action) {
122135
if (action != null) {

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

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

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

19+
import java.util.function.Function;
20+
1921
import reactor.core.publisher.Mono;
2022

2123
import org.springframework.http.HttpStatus;
@@ -50,6 +52,24 @@ public interface ServerHttpResponse extends ReactiveHttpOutputMessage {
5052
*/
5153
MultiValueMap<String, ResponseCookie> getCookies();
5254

55+
/**
56+
* A mechanism for URL rewriting that applications and libraries such as
57+
* HTML template libraries to use consistently for all URLs emitted by
58+
* the application. Doing so enables the registration of URL encoders via
59+
* {@link #registerUrlEncoder} that can insert an id for authentication,
60+
* a nonce for CSRF protection, a version for a static resource, etc.
61+
* @param url the URL to encode
62+
* @return the encoded URL or the same
63+
*/
64+
String encodeUrl(String url);
65+
66+
/**
67+
* Register a URL rewriting function for use with {@link #encodeUrl}.
68+
* The function must return an encoded URL or the same URL.
69+
* @param encoder a URL encoding function to use
70+
*/
71+
void registerUrlEncoder(Function<String, String> encoder);
72+
5373
/**
5474
* Indicate that request handling is complete, allowing for any cleanup or
5575
* end-of-processing tasks to be performed such as applying header changes

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,27 @@
4242
*/
4343
public class ServerHttpResponseTests {
4444

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+
}
65+
4566
@Test
4667
public void writeWith() throws Exception {
4768
TestServerHttpResponse response = new TestServerHttpResponse();

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import java.nio.charset.Charset;
2020
import java.nio.charset.StandardCharsets;
21+
import java.util.function.Function;
2122
import java.util.function.Supplier;
2223

2324
import org.reactivestreams.Publisher;
@@ -49,6 +50,8 @@ public class MockServerHttpResponse implements ServerHttpResponse {
4950

5051
private final MultiValueMap<String, ResponseCookie> cookies = new LinkedMultiValueMap<>();
5152

53+
private Function<String, String> urlEncoder = url -> url;
54+
5255
private Flux<DataBuffer> body;
5356

5457
private Flux<Publisher<DataBuffer>> bodyWithFlushes;
@@ -77,6 +80,16 @@ public MultiValueMap<String, ResponseCookie> getCookies() {
7780
return this.cookies;
7881
}
7982

83+
@Override
84+
public String encodeUrl(String url) {
85+
return (this.urlEncoder != null ? this.urlEncoder.apply(url) : url);
86+
}
87+
88+
@Override
89+
public void registerUrlEncoder(Function<String, String> encoder) {
90+
this.urlEncoder = (this.urlEncoder != null ? this.urlEncoder.andThen(encoder) : encoder);
91+
}
92+
8093
public Publisher<DataBuffer> getBody() {
8194
return this.body;
8295
}

0 commit comments

Comments
 (0)