Fixes RequestHeaderSizeGatewayFilterFactory to check individual headers.
Previously, it checked the aggregate length of all headers. Fixes gh-2567
This commit is contained in:
@@ -1145,8 +1145,8 @@ This will remove the `red` parameter before it is sent downstream.
|
||||
|
||||
=== `RequestHeaderSize` `GatewayFilter` Factory
|
||||
|
||||
The `RequestHeaderSize` `GatewayFilter` factory takes a `maxSize` parameter.
|
||||
It is the maximum data size allowed of the request header (incuding key and value).
|
||||
The `RequestHeaderSize` `GatewayFilter` factory takes `maxSize` and `errorHeaderName` parameters.
|
||||
The `maxSize` parameter is the maximum data size allowed of the request header (incuding key and value). The `errorHeaderName` parameter sets the name of the response header containing an error message, by default it is "errorMessage".
|
||||
The following listing configures a `RequestHeaderSize` `GatewayFilter`:
|
||||
|
||||
.application.yml
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.gateway.filter.factory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -45,8 +46,9 @@ import static org.springframework.cloud.gateway.support.GatewayToStringStyler.fi
|
||||
public class RequestHeaderSizeGatewayFilterFactory
|
||||
extends AbstractGatewayFilterFactory<RequestHeaderSizeGatewayFilterFactory.Config> {
|
||||
|
||||
private static String ERROR = "Request Header/s size is larger than permissible limit."
|
||||
+ " Request Header/s size is %s where permissible limit is %s";
|
||||
private static String ERROR_PREFIX = "Request Header/s size is larger than permissible limit (%s).";
|
||||
|
||||
private static String ERROR = " Request Header/s size for '%s' is %s.";
|
||||
|
||||
public RequestHeaderSizeGatewayFilterFactory() {
|
||||
super(RequestHeaderSizeGatewayFilterFactory.Config.class);
|
||||
@@ -59,25 +61,30 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(RequestHeaderSizeGatewayFilterFactory.Config config) {
|
||||
String errorHeaderName = config.getErrorHeaderName() != null ? config.getErrorHeaderName() : "errorMessage";
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
Long headerSizeInBytes = 0L;
|
||||
HashMap<String, Long> longHeaders = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, List<String>> headerEntry : headers.entrySet()) {
|
||||
headerSizeInBytes += (long) headerEntry.getKey().getBytes().length;
|
||||
long headerSizeInBytes = 0L;
|
||||
headerSizeInBytes += headerEntry.getKey().getBytes().length;
|
||||
List<String> values = headerEntry.getValue();
|
||||
for (String value : values) {
|
||||
headerSizeInBytes += (long) value.getBytes().length;
|
||||
headerSizeInBytes += value.getBytes().length;
|
||||
}
|
||||
if (headerSizeInBytes > config.getMaxSize().toBytes()) {
|
||||
longHeaders.put(headerEntry.getKey(), headerSizeInBytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (headerSizeInBytes > config.getMaxSize().toBytes()) {
|
||||
if (!longHeaders.isEmpty()) {
|
||||
exchange.getResponse().setStatusCode(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE);
|
||||
exchange.getResponse().getHeaders().add("errorMessage",
|
||||
getErrorMessage(headerSizeInBytes, config.getMaxSize()));
|
||||
exchange.getResponse().getHeaders().add(errorHeaderName,
|
||||
getErrorMessage(longHeaders, config.getMaxSize()));
|
||||
return exchange.getResponse().setComplete();
|
||||
|
||||
}
|
||||
@@ -93,14 +100,19 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
};
|
||||
}
|
||||
|
||||
private static String getErrorMessage(Long currentRequestSize, DataSize maxSize) {
|
||||
return String.format(ERROR, DataSize.of(currentRequestSize, DataUnit.BYTES), maxSize);
|
||||
private static String getErrorMessage(HashMap<String, Long> longHeaders, DataSize maxSize) {
|
||||
StringBuilder msg = new StringBuilder(String.format(ERROR_PREFIX, maxSize));
|
||||
longHeaders
|
||||
.forEach((header, size) -> msg.append(String.format(ERROR, header, DataSize.of(size, DataUnit.BYTES))));
|
||||
return msg.toString();
|
||||
}
|
||||
|
||||
public static class Config {
|
||||
|
||||
private DataSize maxSize = DataSize.ofBytes(16000L);
|
||||
|
||||
private String errorHeaderName;
|
||||
|
||||
public DataSize getMaxSize() {
|
||||
return maxSize;
|
||||
}
|
||||
@@ -109,6 +121,13 @@ public class RequestHeaderSizeGatewayFilterFactory
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
public String getErrorHeaderName() {
|
||||
return errorHeaderName;
|
||||
}
|
||||
|
||||
public void setErrorHeaderName(String errorHeaderName) {
|
||||
this.errorHeaderName = errorHeaderName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,31 +46,40 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
||||
@DirtiesContext
|
||||
public class RequestHeaderSizeGatewayFilterFactoryTest extends BaseWebClientTests {
|
||||
|
||||
private static final String responseMesssage = "Request Header/s size is larger than "
|
||||
+ "permissible limit. Request Header/s size is \\d*B where permissible limit is \\d*B";
|
||||
private static final String longString = "11111111112222222222333333333344444444445555555";
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderSizeFilterWorks() {
|
||||
System.err.println("Here: " + longString.length() + ", " + longString.getBytes().length);
|
||||
testClient.get().uri("/headers").header("Host", "www.testrequestheadersizefilter.org")
|
||||
.header("HeaderName", "Some Very Large Header Name").exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader()
|
||||
.valueMatches("errorMessage", responseMesssage);
|
||||
.header("HeaderName", longString).exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader().value("errorMessage",
|
||||
header -> assertThat(header).contains("permissible limit (46B)", "'HeaderName' is 57B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderSizeFilterShortcutWorks() {
|
||||
testClient.get().uri("/headers").header("Host", "www.requestheadersize.org")
|
||||
.header("HeaderName", "Some Very Large Header Name").exchange().expectStatus()
|
||||
testClient.get().uri("/headers").header("Host", "www.requestheadersize.org").header("HeaderName", longString)
|
||||
.exchange().expectStatus().isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader()
|
||||
.value("errorMessage",
|
||||
header -> assertThat(header).contains("permissible limit (46B)", "'HeaderName' is 57B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderSizeFilterMultipleHeadersWorks() {
|
||||
testClient.get().uri("/headers").header("Host", "www.requestheadersize.org").header("HeaderName", longString)
|
||||
.header("HeaderName2", longString).exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader()
|
||||
.valueMatches("errorMessage", responseMesssage);
|
||||
.value("errorMessage", header -> assertThat(header).contains("permissible limit (46B)",
|
||||
"'HeaderName' is 57B", "'HeaderName2' is 58B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setRequestHeaderSizeFilterTakesIntoAccountHeaderName() {
|
||||
testClient.get().uri("/headerName").header("Host", "www.testrequestheadersizefiltername.org")
|
||||
.header("HeaderName", "X").exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader()
|
||||
.valueMatches("errorMessage", responseMesssage);
|
||||
testClient.get().uri("/headers").header("Host", "www.testrequestheadersizefiltername.org")
|
||||
.header("HeaderName", longString).exchange().expectStatus()
|
||||
.isEqualTo(HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE).expectHeader().value("errorMessage",
|
||||
header -> assertThat(header).contains("permissible limit (47B)", "'HeaderName' is 57B"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,7 +106,7 @@ public class RequestHeaderSizeGatewayFilterFactoryTest extends BaseWebClientTest
|
||||
.filters(f -> f.setRequestHeaderSize(DataSize.of(46L, DataUnit.BYTES))).uri(uri))
|
||||
.route("test_request_header_size_name",
|
||||
r -> r.order(1).host("**.testrequestheadersizefiltername.org")
|
||||
.filters(f -> f.setRequestHeaderSize(DataSize.of(1L, DataUnit.BYTES))).uri(uri))
|
||||
.filters(f -> f.setRequestHeaderSize(DataSize.of(47L, DataUnit.BYTES))).uri(uri))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ spring:
|
||||
predicates:
|
||||
- Host=**.requestheadersize.org
|
||||
filters:
|
||||
- RequestHeaderSize=4
|
||||
- RequestHeaderSize=46
|
||||
|
||||
# =====================================
|
||||
- id: rewrite_location_response_header_test
|
||||
|
||||
Reference in New Issue
Block a user