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 45e86d0f685c..86b8edb3728a 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
@@ -31,6 +31,7 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.web.server.Compression;
+import org.springframework.boot.web.server.Http2;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.servlet.server.Jsp;
import org.springframework.util.Assert;
@@ -102,6 +103,9 @@ public class ServerProperties {
@NestedConfigurationProperty
private Compression compression = new Compression();
+ @NestedConfigurationProperty
+ private Http2 http2;
+
private Servlet servlet = new Servlet();
private final Tomcat tomcat = new Tomcat();
@@ -210,6 +214,14 @@ public Undertow getUndertow() {
return this.undertow;
}
+ public Http2 getHttp2() {
+ return this.http2;
+ }
+
+ public void setHttp2(Http2 http2) {
+ this.http2 = http2;
+ }
+
/**
* Servlet properties.
*/
@@ -907,6 +919,11 @@ public static class Jetty {
*/
private Integer selectors;
+ /**
+ * Dump jetty configuration in the logs after starting.
+ */
+ private boolean dumpAfterStart = false;
+
public Accesslog getAccesslog() {
return this.accesslog;
}
@@ -935,6 +952,16 @@ public void setSelectors(Integer selectors) {
this.selectors = selectors;
}
+ public boolean isDumpAfterStart() {
+ return this.dumpAfterStart;
+ }
+
+ public void setDumpAfterStart(boolean dumpAfterStart) {
+ this.dumpAfterStart = dumpAfterStart;
+ }
+
+
+
/**
* Jetty access log properties.
*/
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java
index 31e711a88ae7..81e1ef7064c9 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java
@@ -112,6 +112,9 @@ public void customize(ConfigurableServletWebServerFactory factory) {
if (this.serverProperties.getSsl() != null) {
factory.setSsl(this.serverProperties.getSsl());
}
+ if (this.serverProperties.getHttp2() != null) {
+ factory.setHttp2(this.serverProperties.getHttp2());
+ }
if (this.serverProperties.getServlet() != null) {
factory.setJsp(this.serverProperties.getServlet().getJsp());
}
@@ -480,6 +483,8 @@ public static void customizeJetty(final ServerProperties serverProperties,
if (jettyProperties.getAccesslog().isEnabled()) {
customizeAccessLog(factory, jettyProperties.getAccesslog());
}
+
+ factory.setDumpAfterStart(jettyProperties.isDumpAfterStart());
}
private static void customizeConnectionTimeout(
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index 39928ef8b8f2..1a66b5778985 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -111,6 +111,7 @@
2.25.1
5.3.2
9.4.6.v20170531
+ 1.1.3.v20160715
2.2.0.v201112011158
8.5.9.1
1.13
@@ -1577,6 +1578,16 @@
http2-server
${jetty.version}
+
+ org.eclipse.jetty
+ jetty-alpn-server
+ ${jetty.version}
+
+
+ org.eclipse.jetty.alpn
+ alpn-api
+ ${alpn.api.version}
+
org.eclipse.jetty.orbit
javax.servlet.jsp
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 868eed9ea25f..aaed6032f131 100644
--- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
+++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc
@@ -162,6 +162,7 @@ content into your application; rather pick only the properties that you need.
server.error.path=/error # Path of the error controller.
server.error.whitelabel.enabled=true # Enable the default error page displayed in browsers in case of a server error.
server.jetty.acceptors= # Number of acceptor threads to use.
+ server.jetty.dumpAfterStart= false # log all the Jetty configuration in the logs after start
server.jetty.accesslog.append=false # Append to log.
server.jetty.accesslog.date-format=dd/MMM/yyyy:HH:mm:ss Z # Timestamp format of the request log.
server.jetty.accesslog.enabled=false # Enable access log.
diff --git a/spring-boot-starters/spring-boot-starter-jetty/pom.xml b/spring-boot-starters/spring-boot-starter-jetty/pom.xml
index 424526a8df42..23913bde6338 100644
--- a/spring-boot-starters/spring-boot-starter-jetty/pom.xml
+++ b/spring-boot-starters/spring-boot-starter-jetty/pom.xml
@@ -27,6 +27,22 @@
org.eclipse.jetty
jetty-webapp
+
+ org.eclipse.jetty.http2
+ http2-hpack
+
+
+ org.eclipse.jetty.http2
+ http2-server
+
+
+ org.eclipse.jetty
+ jetty-alpn-server
+
+
+ org.eclipse.jetty.alpn
+ alpn-api
+
org.eclipse.jetty.websocket
websocket-server
diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml
index 21d22b2c0e74..b9376eaf845d 100644
--- a/spring-boot/pom.xml
+++ b/spring-boot/pom.xml
@@ -159,6 +159,26 @@
jetty-webapp
true
+
+ org.eclipse.jetty.http2
+ http2-hpack
+ true
+
+
+ org.eclipse.jetty.http2
+ http2-server
+ true
+
+
+ org.eclipse.jetty
+ jetty-alpn-server
+ true
+
+
+ org.eclipse.jetty.alpn
+ alpn-api
+ true
+
org.glassfish.jersey.core
jersey-server
diff --git a/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java b/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java
index 1129d3a58075..b55f4d60b4b5 100644
--- a/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java
+++ b/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/JettyServletWebServerFactory.java
@@ -35,9 +35,13 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.server.AbstractConnector;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
@@ -45,6 +49,7 @@
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
@@ -122,6 +127,11 @@ public class JettyServletWebServerFactory extends AbstractServletWebServerFactor
*/
private int selectors = -1;
+ /**
+ * Dump jetty configuration in the logs after starting.
+ */
+ private boolean dumpAfterStart = false;
+
private List jettyServerCustomizers = new ArrayList<>();
private ResourceLoader resourceLoader;
@@ -176,12 +186,22 @@ public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.useForwardHeaders) {
new ForwardHeadersCustomizer().customize(server);
}
+ if (getHttp2() != null && getHttp2().isEnabled()) {
+ SslContextFactory sslContextFactory = null;
+ if (getHttp2().getSsl() != null && getHttp2().getSsl().isEnabled()) {
+ sslContextFactory = new SslContextFactory();
+ configureSsl(sslContextFactory, getHttp2().getSsl());
+ }
+ AbstractConnector connector = createHttp2Connector(server, sslContextFactory);
+ server.setConnectors(new Connector[] { connector });
+ }
return getJettyWebServer(server);
}
private Server createServer(InetSocketAddress address) {
Server server = new Server(getThreadPool());
server.setConnectors(new Connector[] { createConnector(address, server) });
+ server.setDumpAfterStart(this.dumpAfterStart);
return server;
}
@@ -213,6 +233,43 @@ private AbstractConnector createSslConnector(Server server,
return serverConnector;
}
+ private AbstractConnector createHttp2Connector(Server server, SslContextFactory sslContextFactory) {
+
+ if (sslContextFactory != null) {
+ sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+ }
+ // HTTPS Configuration
+ HttpConfiguration https_config = new HttpConfiguration();
+ https_config.setSecureScheme("https");
+ https_config.setSecurePort(getHttp2().getPort());
+ https_config.addCustomizer(new SecureRequestCustomizer());
+
+ // HTTP/2 Connection Factory
+ HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(https_config);
+ NegotiatingServerConnectionFactory.checkProtocolNegotiationAvailable();
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol("h2");
+
+ // HTTP/2 Connector
+ ServerConnector http2Connector = null;
+ if (sslContextFactory != null) {
+ // SSL Connection Factory
+ SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+ http2Connector = new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(https_config));
+ }
+ else {
+ http2Connector = new ServerConnector(server, alpn, h2, new HttpConnectionFactory(https_config));
+ }
+
+
+ http2Connector.setPort(getHttp2().getPort());
+ //?
+ //http2Connector.setHost(getHttp2().getAddress().getHostName());
+
+ ALPN.debug = getHttp2().getAlpnDebug();
+ return http2Connector;
+ }
+
private Handler addHandlerWrappers(Handler handler) {
if (getCompression() != null && getCompression().getEnabled()) {
handler = applyWrapper(handler, createGzipHandler());
@@ -578,6 +635,14 @@ public void setSelectors(int selectors) {
this.selectors = selectors;
}
+ /**
+ * To dump the Jetty configuration in the logs.
+ * @param dumpAfterStart true
to dump configuration
+ */
+ public void setDumpAfterStart(boolean dumpAfterStart) {
+ this.dumpAfterStart = dumpAfterStart;
+ }
+
/**
* Sets {@link JettyServerCustomizer}s that will be applied to the {@link Server}
* before it is started. Calling this method will replace any existing customizers.
diff --git a/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java b/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java
index 85ff5e5576ab..ba1ed60fe3ed 100644
--- a/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java
+++ b/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java
@@ -48,6 +48,8 @@ public class AbstractConfigurableWebServerFactory
private Ssl ssl;
+ private Http2 http2;
+
private SslStoreProvider sslStoreProvider;
private Compression compression;
@@ -152,6 +154,15 @@ public void setServerHeader(String serverHeader) {
this.serverHeader = serverHeader;
}
+ @Override
+ public void setHttp2(Http2 http2) {
+ this.http2 = http2;
+ }
+
+ public Http2 getHttp2() {
+ return this.http2;
+ }
+
/**
* Return the absolute temp dir for given web server.
* @param prefix server name
diff --git a/spring-boot/src/main/java/org/springframework/boot/web/server/ConfigurableWebServerFactory.java b/spring-boot/src/main/java/org/springframework/boot/web/server/ConfigurableWebServerFactory.java
index 345441d0e83d..42594d9af5e9 100644
--- a/spring-boot/src/main/java/org/springframework/boot/web/server/ConfigurableWebServerFactory.java
+++ b/spring-boot/src/main/java/org/springframework/boot/web/server/ConfigurableWebServerFactory.java
@@ -75,4 +75,10 @@ public interface ConfigurableWebServerFactory
*/
void setServerHeader(String serverHeader);
+ /**
+ * Sets the Http2 configuration that will be applied to the server's default connector.
+ * @param http2 the {@link Http2} configuration
+ */
+ void setHttp2(Http2 http2);
+
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/web/server/Http2.java b/spring-boot/src/main/java/org/springframework/boot/web/server/Http2.java
new file mode 100644
index 000000000000..1ea6897d3df1
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/web/server/Http2.java
@@ -0,0 +1,94 @@
+/*
+ * 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.web.server;
+
+import java.net.InetAddress;
+
+/**
+ * Simple server-independent abstraction for HTTP/2 configuration.
+ *
+ * @author Olivier Lamy
+ * @since 2.0.0
+ */
+public class Http2 {
+
+ /**
+ * Enable HTTP/2 support.
+ */
+ private boolean enabled = false;
+
+ /**
+ * HTTP/2 port.
+ */
+ private Integer port;
+
+ /**
+ * Network address to which the HTTP/2 connector should be bind to.
+ */
+ private InetAddress address;
+
+ /**
+ * Activate alpn debug.
+ */
+ private Boolean alpnDebug;
+
+ /**
+ * Ssl options for HTTP/2 connector.
+ */
+ private Ssl ssl;
+
+ public boolean isEnabled() {
+ return this.enabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public Integer getPort() {
+ return this.port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public InetAddress getAddress() {
+ return this.address;
+ }
+
+ public void setAddress(InetAddress address) {
+ this.address = address;
+ }
+
+ public Boolean getAlpnDebug() {
+ return this.alpnDebug;
+ }
+
+ public void setAlpnDebug(Boolean alpnDebug) {
+ this.alpnDebug = alpnDebug;
+ }
+
+ public Ssl getSsl() {
+ return this.ssl;
+ }
+
+ public void setSsl(Ssl ssl) {
+ this.ssl = ssl;
+ }
+
+}