From beb257f452df7f5c1d35d5f64e960bdbdf97a1dc Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 6 Sep 2017 09:51:22 +0100 Subject: [PATCH] Rework JettyEmbeddedErrorHandler to support Jetty 8 Closes gh-10175 --- .../jetty/JettyEmbeddedErrorHandler.java | 29 ++++- ...yEmbeddedServletContainerFactoryTests.java | 112 ++++++++++++++++++ 2 files changed, 137 insertions(+), 4 deletions(-) create mode 100644 spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/Jetty8JettyEmbeddedServletContainerFactoryTests.java diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java index 42f4ffafb5..dbe808c0f1 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedErrorHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-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. @@ -17,6 +17,9 @@ package org.springframework.boot.context.embedded.jetty; import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -35,9 +38,20 @@ import org.eclipse.jetty.server.handler.ErrorHandler; * prefers Tomcat, Jetty and Undertow to all behave in the same way. * * @author Phillip Webb + * @author Andy Wilkinson */ class JettyEmbeddedErrorHandler extends ErrorHandler { + private static final Set SUPPORTED_METHODS; + + static { + Set supportedMethods = new HashSet(); + supportedMethods.add("GET"); + supportedMethods.add("HEAD"); + supportedMethods.add("POST"); + SUPPORTED_METHODS = Collections.unmodifiableSet(supportedMethods); + } + private final ErrorHandler delegate; JettyEmbeddedErrorHandler(ErrorHandler delegate) { @@ -47,14 +61,21 @@ class JettyEmbeddedErrorHandler extends ErrorHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException { - String method = request.getMethod(); - if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) - && !HttpMethod.HEAD.is(method)) { + if (!isSupported(request.getMethod())) { request = new ErrorHttpServletRequest(request); } this.delegate.handle(target, baseRequest, request, response); } + private boolean isSupported(String method) { + for (String supportedMethod : SUPPORTED_METHODS) { + if (supportedMethod.equalsIgnoreCase(method)) { + return true; + } + } + return false; + } + private static class ErrorHttpServletRequest extends HttpServletRequestWrapper { private boolean simulateGetMethod = true; diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/Jetty8JettyEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/Jetty8JettyEmbeddedServletContainerFactoryTests.java new file mode 100644 index 0000000000..a4b411d518 --- /dev/null +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/Jetty8JettyEmbeddedServletContainerFactoryTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-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.boot.context.embedded.jetty; + +import java.io.IOException; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.context.embedded.EmbeddedServletContainer; +import org.springframework.boot.junit.runner.classpath.ClassPathExclusions; +import org.springframework.boot.junit.runner.classpath.ClassPathOverrides; +import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner; +import org.springframework.boot.web.servlet.ErrorPage; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link JettyEmbeddedServletContainerFactory} with Jetty 8. + * + * @author Andy Wilkinson + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions({ "jetty-*.jar", "tomcat-embed-jasper-*.jar" }) +@ClassPathOverrides({ "org.eclipse.jetty:jetty-servlets:8.1.15.v20140411", + "org.eclipse.jetty:jetty-webapp:8.1.15.v20140411" }) +public class Jetty8JettyEmbeddedServletContainerFactoryTests { + + @Test + public void errorHandling() { + JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory( + 0); + factory.addErrorPages(new ErrorPage("/error")); + EmbeddedServletContainer jetty = factory + .getEmbeddedServletContainer(new ServletContextInitializer() { + + @Override + public void onStartup(ServletContext servletContext) + throws ServletException { + servletContext.addServlet("test", new TestServlet()) + .addMapping("/test"); + servletContext.addServlet("error", new ErrorPageServlet()) + .addMapping("/error"); + } + + }); + jetty.start(); + int port = jetty.getPort(); + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(new ResponseErrorHandler() { + + @Override + public boolean hasError(ClientHttpResponse response) throws IOException { + return false; + } + + @Override + public void handleError(ClientHttpResponse response) throws IOException { + } + + }); + ResponseEntity response = restTemplate + .getForEntity("http://localhost:" + port, String.class); + assertThat(response.getBody()).isEqualTo("An error occurred"); + } + + private static final class TestServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + throw new RuntimeException("boom"); + } + + } + + private static final class ErrorPageServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws IOException { + resp.getWriter().print("An error occurred"); + resp.flushBuffer(); + } + } + +}