diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index b90d9e9c430..7c359abb4cd 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -397,28 +397,26 @@ public class ServerProperties { private List additionalTldSkipPatterns = new ArrayList<>(); /** - * Static resource configuration. + * Comma-separated list of additional unencoded characters that should be allowed + * in URI paths. Only "< > [ \ ] ^ ` { | }" are allowed. */ - private final Resource resource = new Resource(); + private List relaxedPathChars = new ArrayList<>(); /** - * Modeler MBean Registry configuration. + * Comma-separated list of additional unencoded characters that should be allowed + * in URI query strings. Only "< > [ \ ] ^ ` { | }" are allowed. */ - private final Mbeanregistry mbeanregistry = new Mbeanregistry(); + private List relaxedQueryChars = new ArrayList<>(); /** - * Specify additional unencoded characters that Tomcat should allow in a - * querystring. The value may be any combination of the following characters: " < - * > [ \ ] ^ ` { | } . Any other characters present in the value will be ignored. + * Static resource configuration. */ - private String relaxedQueryChars; + private final Resource resource = new Resource(); /** - * Specify additional unencoded characters that Tomcat should allow in the path. - * The value may be any combination of the following characters: " < > [ \ ] ^ ` { - * | } . Any other characters present in the value will be ignored. + * Modeler MBean Registry configuration. */ - private String relaxedPathChars; + private final Mbeanregistry mbeanregistry = new Mbeanregistry(); public int getMaxThreads() { return this.maxThreads; @@ -568,30 +566,30 @@ public class ServerProperties { this.additionalTldSkipPatterns = additionalTldSkipPatterns; } - public Resource getResource() { - return this.resource; - } - - public Mbeanregistry getMbeanregistry() { - return this.mbeanregistry; - } - - public String getRelaxedPathChars() { + public List getRelaxedPathChars() { return this.relaxedPathChars; } - public void setRelaxedPathChars(String relaxedPathChars) { + public void setRelaxedPathChars(List relaxedPathChars) { this.relaxedPathChars = relaxedPathChars; } - public String getRelaxedQueryChars() { + public List getRelaxedQueryChars() { return this.relaxedQueryChars; } - public void setRelaxedQueryChars(String relaxedQueryChars) { + public void setRelaxedQueryChars(List relaxedQueryChars) { this.relaxedQueryChars = relaxedQueryChars; } + public Resource getResource() { + return this.resource; + } + + public Mbeanregistry getMbeanregistry() { + return this.mbeanregistry; + } + /** * Tomcat access log properties. */ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java index c90eeda1130..c898af57920 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java @@ -17,6 +17,8 @@ package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; +import java.util.List; +import java.util.stream.Collectors; import org.apache.catalina.Lifecycle; import org.apache.catalina.valves.AccessLogValve; @@ -103,10 +105,10 @@ public class TomcatWebServerFactoryCustomizer .to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); propertyMapper.from(tomcatProperties::getProcessorCache) .to((processorCache) -> customizeProcessorCache(factory, processorCache)); - propertyMapper.from(tomcatProperties::getRelaxedQueryChars) - .to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars)); - propertyMapper.from(tomcatProperties::getRelaxedPathChars) + propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText() .to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars)); + propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText() + .to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars)); customizeStaticResources(factory); customizeErrorReportValve(properties.getError(), factory); } @@ -154,12 +156,16 @@ public class TomcatWebServerFactoryCustomizer }); } + private void customizeRelaxedPathChars(ConfigurableTomcatWebServerFactory factory, String relaxedChars) { + factory.addConnectorCustomizers((connector) -> connector.setAttribute("relaxedPathChars", relaxedChars)); + } + private void customizeRelaxedQueryChars(ConfigurableTomcatWebServerFactory factory, String relaxedChars) { factory.addConnectorCustomizers((connector) -> connector.setAttribute("relaxedQueryChars", relaxedChars)); } - private void customizeRelaxedPathChars(ConfigurableTomcatWebServerFactory factory, String relaxedChars) { - factory.addConnectorCustomizers((connector) -> connector.setAttribute("relaxedPathChars", relaxedChars)); + private String joinCharacters(List content) { + return content.stream().map(String::valueOf).collect(Collectors.joining()); } private void customizeRemoteIpValve(ConfigurableTomcatWebServerFactory factory) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 76c77d4abb4..d5a4dfcb6b4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -2089,6 +2089,76 @@ } ], "hints": [ + { + "name": "server.tomcat.relaxed-query-chars", + "values": [ + { + "value": "<" + }, + { + "value": ">" + }, + { + "value": "[" + }, + { + "value": "\\" + }, + { + "value": "]" + }, + { + "value": "^" + }, + { + "value": "`" + }, + { + "value": "{" + }, + { + "value": "|" + }, + { + "value": "}" + } + ] + }, + { + "name": "server.tomcat.relaxed-path-chars", + "values": [ + { + "value": "<" + }, + { + "value": ">" + }, + { + "value": "[" + }, + { + "value": "\\" + }, + { + "value": "]" + }, + { + "value": "^" + }, + { + "value": "`" + }, + { + "value": "{" + }, + { + "value": "|" + }, + { + "value": "}" + } + ] + }, { "name": "spring.liquibase.change-log", "providers": [ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 0a43545b9b2..ea6d2b5362e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -127,8 +127,8 @@ class ServerPropertiesTests { map.put("server.tomcat.remote-ip-header", "Remote-Ip"); map.put("server.tomcat.internal-proxies", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); map.put("server.tomcat.background-processor-delay", "10"); - map.put("server.tomcat.relaxed-path-chars", "|"); - map.put("server.tomcat.relaxed-query-chars", "^^"); + map.put("server.tomcat.relaxed-path-chars", "|,<"); + map.put("server.tomcat.relaxed-query-chars", "^ , | "); bind(map); ServerProperties.Tomcat tomcat = this.properties.getTomcat(); Accesslog accesslog = tomcat.getAccesslog(); @@ -148,8 +148,8 @@ class ServerPropertiesTests { assertThat(tomcat.getProtocolHeader()).isEqualTo("X-Forwarded-Protocol"); assertThat(tomcat.getInternalProxies()).isEqualTo("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"); assertThat(tomcat.getBackgroundProcessorDelay()).isEqualTo(Duration.ofSeconds(10)); - assertThat(tomcat.getRelaxedPathChars()).isEqualTo("|"); - assertThat(tomcat.getRelaxedQueryChars()).isEqualTo("^^"); + assertThat(tomcat.getRelaxedPathChars()).containsExactly('|', '<'); + assertThat(tomcat.getRelaxedQueryChars()).containsExactly('^', '|'); } @Test diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java index 2545ba1ce6e..ebf9ff96bee 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java @@ -198,6 +198,22 @@ class TomcatWebServerFactoryCustomizerTests { }); } + @Test + void customRelaxedPathChars() { + bind("server.tomcat.relaxed-path-chars=|,^"); + customizeAndRunServer((server) -> assertThat( + ((AbstractHttp11Protocol) server.getTomcat().getConnector().getProtocolHandler()) + .getRelaxedPathChars()).isEqualTo("|^")); + } + + @Test + void customRelaxedQueryChars() { + bind("server.tomcat.relaxed-query-chars=^ , | "); + customizeAndRunServer((server) -> assertThat( + ((AbstractHttp11Protocol) server.getTomcat().getConnector().getProtocolHandler()) + .getRelaxedQueryChars()).isEqualTo("^|")); + } + @Test void deduceUseForwardHeaders() { this.environment.setProperty("DYNO", "-");