From 87321edf3665df7509012ab7fa7b05ec2217be39 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 21 Mar 2014 11:29:04 -0700 Subject: [PATCH] Support Tomcat uri encoding (with UTF-8 default) Introduce an extra `server.tomcat.uri-encoding` property used to configure the URI encoding for the embedded tomcat container. Defaults to `UTF-8` instead of the usual tomcat default of `ISO-8859-1`. Fixes gh-540 --- .../autoconfigure/web/ServerProperties.java | 14 +++++++++++ .../web/ServerPropertiesTests.java | 10 ++++++++ .../appendix-application-properties.adoc | 1 + ...TomcatEmbeddedServletContainerFactory.java | 25 +++++++++++++++++++ ...tEmbeddedServletContainerFactoryTests.java | 24 ++++++++++++++++-- 5 files changed, 72 insertions(+), 2 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index c74b058476..9d3d9f8f20 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -43,6 +43,7 @@ import org.springframework.util.StringUtils; * {@link EmbeddedServletContainerCustomizerBeanPostProcessor} is active. * * @author Dave Syer + * @author Stephane Nicoll */ @ConfigurationProperties(prefix = "server", ignoreUnknownFields = false) public class ServerProperties implements EmbeddedServletContainerCustomizer { @@ -144,6 +145,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { private int maxThreads = 0; // Number of threads in protocol handler + private String uriEncoding; + public int getMaxThreads() { return this.maxThreads; } @@ -200,6 +203,14 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { this.remoteIpHeader = remoteIpHeader; } + public String getUriEncoding() { + return this.uriEncoding; + } + + public void setUriEncoding(String uriEncoding) { + this.uriEncoding = uriEncoding; + } + void customizeTomcat(TomcatEmbeddedServletContainerFactory factory) { if (getBasedir() != null) { factory.setBaseDirectory(getBasedir()); @@ -247,6 +258,9 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { valve.setSuffix(".log"); factory.addContextValves(valve); } + if (getUriEncoding() != null) { + factory.setUriEncoding(getUriEncoding()); + } } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index 07538ee253..229c7cd21f 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.verify; * Tests for {@link ServerProperties}. * * @author Dave Syer + * @author Stephane Nicoll */ public class ServerPropertiesTests { @@ -87,4 +88,13 @@ public class ServerPropertiesTests { verify(factory).setPort(8080); } + @Test + public void testCustomizeUriEncoding() throws Exception { + Map map = new HashMap(); + map.put("server.tomcat.uriEncoding", "US-ASCII"); + new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues( + map)); + assertEquals("US-ASCII", this.properties.getTomcat().getUriEncoding()); + } + } diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index ab64eac042..2690e91c9b 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -61,6 +61,7 @@ server.tomcat.remote-ip-header=x-forwarded-for server.tomcat.basedir=/tmp # base dir (usually not needed, defaults to tmp) server.tomcat.background-processor-delay=30; # in seconds server.tomcat.max-threads = 0 # number of threads in protocol handler +server.tomcat.uri-encoding = UTF-8 # character encoding to use for URL decoding # SPRING MVC ({sc-spring-boot-autoconfigure}/web/HttpMapperProperties.{sc-ext}[HttpMapperProperties]) http.mappers.json-pretty-print=false # pretty print JSON diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index e4d28442f1..0569a4bc25 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -66,6 +66,7 @@ import org.springframework.util.StreamUtils; * @author Phillip Webb * @author Dave Syer * @author Brock Mills + * @author Stephane Nicoll * @see #setPort(int) * @see #setContextLifecycleListeners(Collection) * @see TomcatEmbeddedServletContainer @@ -93,6 +94,8 @@ public class TomcatEmbeddedServletContainerFactory extends private String tldSkip; + private String uriEncoding = "UTF-8"; + /** * Create a new {@link TomcatEmbeddedServletContainerFactory} instance. */ @@ -206,6 +209,10 @@ public class TomcatEmbeddedServletContainerFactory extends .setAddress(getAddress()); } } + if (getUriEncoding() != null) { + connector.setURIEncoding(getUriEncoding()); + } + // If ApplicationContext is slow to start we want Tomcat not to bind to the socket // prematurely... connector.setProperty("bindOnInit", "false"); @@ -455,10 +462,28 @@ public class TomcatEmbeddedServletContainerFactory extends return this.additionalTomcatConnectors; } + /** + * Set the character encoding to use for URL decoding. If not specified 'UTF-8' will + * be used. + * @param uriEncoding the uri encoding to set + */ + public void setUriEncoding(String uriEncoding) { + this.uriEncoding = uriEncoding; + } + + /** + * Returns the character encoding to use for URL decoding. + */ + public String getUriEncoding() { + return this.uriEncoding; + } + private static class TomcatErrorPage { private final String location; + private final String exceptionType; + private final int errorCode; private final Object nativePage; diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java index bb1ab962d2..fd232e3776 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactoryTests.java @@ -44,6 +44,7 @@ import static org.mockito.Mockito.verify; * * @author Phillip Webb * @author Dave Syer + * @author Stephane Nicoll */ public class TomcatEmbeddedServletContainerFactoryTests extends AbstractEmbeddedServletContainerFactoryTests { @@ -190,11 +191,30 @@ public class TomcatEmbeddedServletContainerFactoryTests extends factory.addConnectorCustomizers((TomcatConnectorCustomizer[]) null); } + @Test + public void uriEncoding() throws Exception { + TomcatEmbeddedServletContainerFactory factory = getFactory(); + factory.setUriEncoding("US-ASCII"); + Tomcat tomcat = getTomcat(factory); + assertEquals("US-ASCII", tomcat.getConnector().getURIEncoding()); + } + + @Test + public void defaultUriEncoding() throws Exception { + TomcatEmbeddedServletContainerFactory factory = getFactory(); + Tomcat tomcat = getTomcat(factory); + assertEquals("UTF-8", tomcat.getConnector().getURIEncoding()); + } + private void assertTimeout(TomcatEmbeddedServletContainerFactory factory, int expected) { - this.container = factory.getEmbeddedServletContainer(); - Tomcat tomcat = ((TomcatEmbeddedServletContainer) this.container).getTomcat(); + Tomcat tomcat = getTomcat(factory); Context context = (Context) tomcat.getHost().findChildren()[0]; assertThat(context.getSessionTimeout(), equalTo(expected)); } + private Tomcat getTomcat(TomcatEmbeddedServletContainerFactory factory) { + this.container = factory.getEmbeddedServletContainer(); + return ((TomcatEmbeddedServletContainer) this.container).getTomcat(); + } + }