diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml
index 1494fa47ca..b134c0fe83 100644
--- a/spring-boot-project/spring-boot-dependencies/pom.xml
+++ b/spring-boot-project/spring-boot-dependencies/pom.xml
@@ -124,7 +124,7 @@
3.1.0
2.29.1
6.3.1
- 9.4.20.v20190813
+ 9.4.21.v20190926
2.2.0.v201112011158
8.5.40
1.0.3
diff --git a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java
index 8b722e2ec7..9099ee7385 100644
--- a/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java
+++ b/spring-boot-project/spring-boot-tools/spring-boot-test-support/src/main/java/org/springframework/boot/testsupport/classpath/ModifiedClassPathExtension.java
@@ -31,6 +31,7 @@ import org.springframework.boot.testsupport.junit.platform.Launcher;
import org.springframework.boot.testsupport.junit.platform.LauncherDiscoveryRequest;
import org.springframework.boot.testsupport.junit.platform.LauncherDiscoveryRequestBuilder;
import org.springframework.boot.testsupport.junit.platform.SummaryGeneratingListener;
+import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
@@ -97,7 +98,7 @@ class ModifiedClassPathExtension implements InvocationInterceptor {
private void runTest(ClassLoader classLoader, String testClassName, String testMethodName)
throws ClassNotFoundException, Throwable {
Class> testClass = classLoader.loadClass(testClassName);
- Method testMethod = ReflectionUtils.findMethod(testClass, testMethodName);
+ Method testMethod = findMethod(testClass, testMethodName);
LauncherDiscoveryRequest request = new LauncherDiscoveryRequestBuilder(classLoader)
.selectors(DiscoverySelectors.selectMethod(testClass, testMethod)).build();
Launcher launcher = new Launcher(classLoader);
@@ -110,6 +111,20 @@ class ModifiedClassPathExtension implements InvocationInterceptor {
}
}
+ private Method findMethod(Class> testClass, String testMethodName) {
+ Method method = ReflectionUtils.findMethod(testClass, testMethodName);
+ if (method == null) {
+ Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(testClass);
+ for (Method candidate : methods) {
+ if (candidate.getName().equals(testMethodName)) {
+ return candidate;
+ }
+ }
+ }
+ Assert.state(method != null, "Unable to find " + testClass + "." + testMethodName);
+ return method;
+ }
+
private void intercept(Invocation invocation, ExtensionContext extensionContext) throws Throwable {
if (isModifiedClassPathClassLoader(extensionContext)) {
invocation.proceed();
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java
index 1a96431bad..d46a4a40e5 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyEmbeddedErrorHandler.java
@@ -26,17 +26,28 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.server.handler.ErrorHandler.ErrorPageMapper;
+
+import org.springframework.util.ReflectionUtils;
/**
* Variation of Jetty's {@link ErrorHandler} that supports all {@link HttpMethod
- * HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. Jetty
- * intentionally only
+ * HttpMethods} rather than just {@code GET}, {@code POST} and {@code HEAD}. By default
+ * Jetty intentionally only
* supports a limited set of HTTP methods for error pages, however, Spring Boot
* prefers Tomcat, Jetty and Undertow to all behave in the same way.
*
* @author Phillip Webb
+ * @author Christoph Dreis
*/
-class JettyEmbeddedErrorHandler extends ErrorHandler {
+class JettyEmbeddedErrorHandler extends ErrorHandler implements ErrorPageMapper {
+
+ static final boolean ERROR_PAGE_FOR_METHOD_AVAILABLE;
+
+ static {
+ ERROR_PAGE_FOR_METHOD_AVAILABLE = ReflectionUtils.findMethod(ErrorHandler.class, "errorPageForMethod",
+ String.class) != null;
+ }
private final ErrorHandler delegate;
@@ -47,13 +58,28 @@ 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)) {
- request = new ErrorHttpServletRequest(request);
+ if (!ERROR_PAGE_FOR_METHOD_AVAILABLE) {
+ String method = request.getMethod();
+ if (!HttpMethod.GET.is(method) && !HttpMethod.POST.is(method) && !HttpMethod.HEAD.is(method)) {
+ request = new ErrorHttpServletRequest(request);
+ }
}
this.delegate.handle(target, baseRequest, request, response);
}
+ @Override
+ public boolean errorPageForMethod(String method) { // Available in Jetty 9.4.21+
+ return true;
+ }
+
+ @Override
+ public String getErrorPage(HttpServletRequest request) {
+ if (this.delegate instanceof ErrorPageMapper) {
+ return ((ErrorPageMapper) this.delegate).getErrorPage(request);
+ }
+ return null;
+ }
+
private static class ErrorHttpServletRequest extends HttpServletRequestWrapper {
private boolean simulateGetMethod = true;
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/AbstractJettyServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/AbstractJettyServletWebServerFactoryTests.java
new file mode 100644
index 0000000000..4ed7c62367
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/AbstractJettyServletWebServerFactoryTests.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2012-2019 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
+ *
+ * https://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.web.embedded.jetty;
+
+import java.nio.charset.Charset;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.jasper.servlet.JspServlet;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+import org.springframework.boot.web.server.PortInUseException;
+import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
+import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Abstract base class for {@link JettyServletWebServerFactory} tests.
+ *
+ * @author Phillip Webb
+ */
+abstract class AbstractJettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryTests {
+
+ @Override
+ protected JettyServletWebServerFactory getFactory() {
+ return new JettyServletWebServerFactory(0);
+ }
+
+ @Override
+ protected void addConnector(int port, AbstractServletWebServerFactory factory) {
+ ((JettyServletWebServerFactory) factory).addServerCustomizers((server) -> {
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(port);
+ server.addConnector(connector);
+ });
+ }
+
+ @Override
+ protected JspServlet getJspServlet() throws Exception {
+ WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
+ ServletHolder holder = context.getServletHandler().getServlet("jsp");
+ if (holder == null) {
+ return null;
+ }
+ holder.start();
+ holder.initialize();
+ return (JspServlet) holder.getServlet();
+ }
+
+ @Override
+ protected Map getActualMimeMappings() {
+ WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
+ return context.getMimeTypes().getMimeMap();
+ }
+
+ @Override
+ protected Charset getCharset(Locale locale) {
+ WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
+ String charsetName = context.getLocaleEncoding(locale);
+ return (charsetName != null) ? Charset.forName(charsetName) : null;
+ }
+
+ @Override
+ protected void handleExceptionCausedByBlockedPortOnPrimaryConnector(RuntimeException ex, int blockedPort) {
+ assertThat(ex).isInstanceOf(PortInUseException.class);
+ assertThat(((PortInUseException) ex).getPort()).isEqualTo(blockedPort);
+ }
+
+ @Override
+ protected void handleExceptionCausedByBlockedPortOnSecondaryConnector(RuntimeException ex, int blockedPort) {
+ this.handleExceptionCausedByBlockedPortOnPrimaryConnector(ex, blockedPort);
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServlet9419WebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServlet9419WebServerFactoryTests.java
new file mode 100644
index 0000000000..5a2e1741f6
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServlet9419WebServerFactoryTests.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2012-2019 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
+ *
+ * https://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.web.embedded.jetty;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
+import org.springframework.boot.testsupport.classpath.ClassPathOverrides;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link JettyServletWebServerFactory} with Jetty 9.4.19.
+ *
+ * @author Phillip Webb
+ */
+@ClassPathExclusions({ "jetty-*.jar", "tomcat-embed*.jar" })
+@ClassPathOverrides({ "org.eclipse.jetty:jetty-io:9.4.19.v20190610", "org.eclipse.jetty:jetty-server:9.4.19.v20190610",
+ "org.eclipse.jetty:jetty-servlet:9.4.19.v20190610", "org.eclipse.jetty:jetty-util:9.4.19.v20190610",
+ "org.eclipse.jetty:jetty-webapp:9.4.19.v20190610", "org.mortbay.jasper:apache-jsp:8.5.40" })
+class JettyServlet9419WebServerFactoryTests extends AbstractJettyServletWebServerFactoryTests {
+
+ @Test
+ void correctVersionOfJettyUsed() {
+ assertThat(JettyEmbeddedErrorHandler.ERROR_PAGE_FOR_METHOD_AVAILABLE).isFalse();
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java
index c78436406b..107f0e8feb 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactoryTests.java
@@ -17,18 +17,14 @@
package org.springframework.boot.web.embedded.jetty;
import java.net.InetAddress;
-import java.nio.charset.Charset;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.Locale;
-import java.util.Map;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
-import org.apache.jasper.servlet.JspServlet;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
@@ -36,7 +32,6 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.eclipse.jetty.webapp.Configuration;
@@ -44,11 +39,8 @@ import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
-import org.springframework.boot.web.server.PortInUseException;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.WebServerException;
-import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory;
-import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -64,11 +56,11 @@ import static org.mockito.Mockito.mock;
* @author Andy Wilkinson
* @author Henri Kerola
*/
-class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryTests {
+class JettyServletWebServerFactoryTests extends AbstractJettyServletWebServerFactoryTests {
- @Override
- protected JettyServletWebServerFactory getFactory() {
- return new JettyServletWebServerFactory(0);
+ @Test
+ void correctVersionOfJettyUsed() {
+ assertThat(JettyEmbeddedErrorHandler.ERROR_PAGE_FOR_METHOD_AVAILABLE).isTrue();
}
@Test
@@ -144,15 +136,6 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT
assertThat(server.isStopped()).isTrue();
}
- @Override
- protected void addConnector(int port, AbstractServletWebServerFactory factory) {
- ((JettyServletWebServerFactory) factory).addServerCustomizers((server) -> {
- ServerConnector connector = new ServerConnector(server);
- connector.setPort(port);
- server.addConnector(connector);
- });
- }
-
@Test
void sslEnabledMultiProtocolsConfiguration() {
JettyServletWebServerFactory factory = getFactory();
@@ -312,40 +295,4 @@ class JettyServletWebServerFactoryTests extends AbstractServletWebServerFactoryT
});
}
- @Override
- protected JspServlet getJspServlet() throws Exception {
- WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
- ServletHolder holder = context.getServletHandler().getServlet("jsp");
- if (holder == null) {
- return null;
- }
- holder.start();
- holder.initialize();
- return (JspServlet) holder.getServlet();
- }
-
- @Override
- protected Map getActualMimeMappings() {
- WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
- return context.getMimeTypes().getMimeMap();
- }
-
- @Override
- protected Charset getCharset(Locale locale) {
- WebAppContext context = (WebAppContext) ((JettyWebServer) this.webServer).getServer().getHandler();
- String charsetName = context.getLocaleEncoding(locale);
- return (charsetName != null) ? Charset.forName(charsetName) : null;
- }
-
- @Override
- protected void handleExceptionCausedByBlockedPortOnPrimaryConnector(RuntimeException ex, int blockedPort) {
- assertThat(ex).isInstanceOf(PortInUseException.class);
- assertThat(((PortInUseException) ex).getPort()).isEqualTo(blockedPort);
- }
-
- @Override
- protected void handleExceptionCausedByBlockedPortOnSecondaryConnector(RuntimeException ex, int blockedPort) {
- this.handleExceptionCausedByBlockedPortOnPrimaryConnector(ex, blockedPort);
- }
-
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java
index af71fdac11..6a815adbca 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java
@@ -1077,7 +1077,6 @@ public abstract class AbstractServletWebServerFactoryTests {
HttpComponentsClientHttpRequestFactory requestFactory, String... headers)
throws IOException, URISyntaxException {
ClientHttpRequest request = requestFactory.createRequest(new URI(url), method);
- request.getHeaders().add("Cookie", "JSESSIONID=123");
for (String header : headers) {
String[] parts = header.split(":");
request.getHeaders().add(parts[0], parts[1]);