Introduce ForwardedHeaderFilter for WebFlux
This commit introduces a ForwardedHeaderFilter for WebFlux, similar to the existing Servlet version. As part of this the DefaultServerHttpRequestBuilder had to be changed to no longer use delegation, but instead use a deep copy at the point of mutate(). Otherwise, headers could not be removed. Issue: SPR-15954
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.filter.reactive;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.junit.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerWebExchange;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
public class ForwardedHeaderFilterTests {
|
||||
|
||||
private final ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
|
||||
|
||||
private final TestWebFilterChain filterChain = new TestWebFilterChain();
|
||||
|
||||
|
||||
@Test
|
||||
public void removeOnly() {
|
||||
MockServerWebExchange exchange = MockServerHttpRequest.get("/")
|
||||
.header("Forwarded", "for=192.0.2.60;proto=http;by=203.0.113.43")
|
||||
.header("X-Forwarded-Host", "example.com")
|
||||
.header("X-Forwarded-Port", "8080")
|
||||
.header("X-Forwarded-Proto", "http")
|
||||
.header("X-Forwarded-Prefix", "prefix")
|
||||
.toExchange();
|
||||
|
||||
this.filter.setRemoveOnly(true);
|
||||
this.filter.filter(exchange, this.filterChain).block(Duration.ZERO);
|
||||
|
||||
HttpHeaders result = this.filterChain.getHeaders();
|
||||
assertNotNull(result);
|
||||
assertFalse(result.containsKey("Forwarded"));
|
||||
assertFalse(result.containsKey("X-Forwarded-Host"));
|
||||
assertFalse(result.containsKey("X-Forwarded-Port"));
|
||||
assertFalse(result.containsKey("X-Forwarded-Proto"));
|
||||
assertFalse(result.containsKey("X-Forwarded-Prefix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void xForwardedRequest() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerHttpRequest.get("http://example.com/path")
|
||||
.header("X-Forwarded-Host", "84.198.58.199")
|
||||
.header("X-Forwarded-Port", "443")
|
||||
.header("X-Forwarded-Proto", "https")
|
||||
.toExchange();
|
||||
|
||||
this.filter.filter(exchange, this.filterChain).block(Duration.ZERO);
|
||||
|
||||
URI uri = this.filterChain.uri;
|
||||
assertEquals(new URI("https://84.198.58.199/path"), uri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forwardedRequest() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerHttpRequest.get("http://example.com/path")
|
||||
.header("Forwarded", "host=84.198.58.199;proto=https")
|
||||
|
||||
.toExchange();
|
||||
|
||||
this.filter.filter(exchange, this.filterChain).block(Duration.ZERO);
|
||||
|
||||
URI uri = this.filterChain.uri;
|
||||
assertEquals(new URI("https://84.198.58.199/path"), uri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestUriWithForwardedPrefix() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerHttpRequest.get("http://example.com/path")
|
||||
.header("X-Forwarded-Prefix", "/prefix")
|
||||
.toExchange();
|
||||
|
||||
this.filter.filter(exchange, this.filterChain).block(Duration.ZERO);
|
||||
|
||||
URI uri = this.filterChain.uri;
|
||||
assertEquals(new URI("http://example.com/prefix/path"), uri);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestUriWithForwardedPrefixTrailingSlash() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerHttpRequest.get("http://example.com/path")
|
||||
.header("X-Forwarded-Prefix", "/prefix/")
|
||||
.toExchange();
|
||||
|
||||
this.filter.filter(exchange, this.filterChain).block(Duration.ZERO);
|
||||
|
||||
URI uri = this.filterChain.uri;
|
||||
assertEquals(new URI("http://example.com/prefix/path"), uri);
|
||||
}
|
||||
|
||||
|
||||
private static class TestWebFilterChain implements WebFilterChain {
|
||||
|
||||
@Nullable
|
||||
private HttpHeaders httpHeaders;
|
||||
|
||||
@Nullable
|
||||
private URI uri;
|
||||
|
||||
@Nullable
|
||||
public HttpHeaders getHeaders() {
|
||||
return this.httpHeaders;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public URI getUri() {
|
||||
return this.uri;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange) {
|
||||
this.httpHeaders = exchange.getRequest().getHeaders();
|
||||
this.uri = exchange.getRequest().getURI();
|
||||
return Mono.empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user