You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by cz...@apache.org on 2020/08/24 05:10:52 UTC
[felix-dev] branch master updated: FELIX-5311 Allow Usage of HTTP/2
with Jetty Felix Http Service (#42)
This is an automated email from the ASF dual-hosted git repository.
cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/felix-dev.git
The following commit(s) were added to refs/heads/master by this push:
new b668728 FELIX-5311 Allow Usage of HTTP/2 with Jetty Felix Http Service (#42)
b668728 is described below
commit b668728eebaac3883a7874644a92a8bf62d974e7
Author: Eric Norman <er...@gmail.com>
AuthorDate: Sun Aug 23 22:09:12 2020 -0700
FELIX-5311 Allow Usage of HTTP/2 with Jetty Felix Http Service (#42)
---
http/jetty/pom.xml | 141 ++++++++++++++++++++-
.../jetty/internal/ConfigMetaTypeProvider.java | 35 +++++
.../felix/http/jetty/internal/JettyConfig.java | 47 +++++++
.../felix/http/jetty/internal/JettyService.java | 23 ++++
4 files changed, 244 insertions(+), 2 deletions(-)
diff --git a/http/jetty/pom.xml b/http/jetty/pom.xml
index 04477cb..8e317c5 100644
--- a/http/jetty/pom.xml
+++ b/http/jetty/pom.xml
@@ -49,6 +49,98 @@
<build>
<plugins>
+
+ <!-- Use a groovy script to preserve the META-INF/services/* files for the artifacts that are embeded in the uber jar -->
+ <plugin>
+ <groupId>org.codehaus.gmaven</groupId>
+ <artifactId>groovy-maven-plugin</artifactId>
+ <version>2.1.1</version>
+ <executions>
+ <execution>
+ <id>groovy-magic</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>execute</goal>
+ </goals>
+ <configuration>
+ <source><![CDATA[
+ // make an output dir for the merged resource files
+ def slDir = new File(project.build.directory, "serviceloader-resources");
+ slDir.mkdirs();
+
+ // scan each of the artifacts to preserve the information found in any META-INF/services/* files
+ project.artifacts.each() { artifact ->
+
+ if (artifact.getArtifactHandler().isAddedToClasspath() && !org.apache.maven.artifact.Artifact.SCOPE_TEST.equals( artifact.getScope() )) {
+ def jar;
+ try {
+ jar = new java.util.jar.JarFile(artifact.file)
+ jar.stream().each() { entry ->
+ if (!entry.isDirectory() && entry.name.startsWith("META-INF/services/")) {
+
+ // check if we already have a file with this name
+ def svcFile = new File(slDir, entry.name)
+ def svcSet = new LinkedHashSet();
+ if (svcFile.exists()) {
+ // found existing file, so load the items from the existing file so we can merge
+ svcFile.eachLine { className ->
+ className = className.trim();
+ if (!className.isEmpty()) {
+ svcSet.add(className);
+ }
+ }
+ }
+
+ // read the content of the found entry
+ def lineReader;
+ try {
+ lineReader = new BufferedReader(new InputStreamReader(jar.getInputStream(entry), java.nio.charset.StandardCharsets.UTF_8));
+ def className;
+ while ( ( className = lineReader.readLine() ) != null ) {
+ className = className.trim();
+ if (!className.isEmpty()) {
+ svcSet.add(className);
+ }
+ }
+ } finally {
+ // cleanup
+ if (lineReader != null) {
+ lineReader.close()
+ }
+ }
+
+ // write the merged data to the output file
+ if (!svcSet.isEmpty()) {
+ // make any missing folders
+ svcFile.getParentFile().mkdirs();
+
+ svcFile.withWriter('utf-8') { writer ->
+ svcSet.each() { item ->
+ writer.writeLine item;
+ }
+
+ // finish up with a blank line
+ writer.println();
+ }
+ }
+
+ }
+ }
+ } finally {
+ // cleanup
+ if (jar != null) {
+ jar.close();
+ }
+ }
+ }
+
+ }
+ ]]></source>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
@@ -112,11 +204,19 @@
osgi.service;objectClass:List<String>="org.osgi.service.http.runtime.HttpServiceRuntime";
uses:="org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto",
osgi.service;objectClass:List<String>="org.osgi.service.http.HttpService";
- uses:="org.osgi.service.http"
+ uses:="org.osgi.service.http",
+ osgi.serviceloader;osgi.serviceloader="org.eclipse.jetty.http.HttpFieldPreEncoder"
</Provide-Capability>
<Require-Capability>
- osgi.contract;filter:="(&(osgi.contract=JavaServlet)(version=3.1))"
+ osgi.contract;filter:="(&(osgi.contract=JavaServlet)(version=3.1))",
+ osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional,
+ osgi.extender;filter:="(osgi.extender=osgi.serviceloader.processor)";resolution:=optional,
+ osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.http.HttpFieldPreEncoder)";resolution:=optional;cardinality:=multiple,
+ osgi.serviceloader;filter:="(osgi.serviceloader=org.eclipse.jetty.io.ssl.ALPNProcessor$Server)";resolution:=optional;cardinality:=multiple
</Require-Capability>
+ <Include-Resource>
+ {maven-resources},${project.build.directory}/serviceloader-resources
+ </Include-Resource>
<_removeheaders>
Private-Package,Conditional-Package
</_removeheaders>
@@ -164,6 +264,23 @@
org.eclipse.jetty.webapp;resolution:=optional,
*
</Import-Package>
+ <!-- We need to override this from the base configuration to exclude the ServiceLoader capabilities -->
+ <Provide-Capability>
+ osgi.implementation;osgi.implementation="osgi.http";version:Version="1.1";
+ uses:="javax.servlet,javax.servlet.http,org.osgi.service.http.context,org.osgi.service.http.whiteboard",
+ osgi.service;objectClass:List<String>="org.osgi.service.http.runtime.HttpServiceRuntime";
+ uses:="org.osgi.service.http.runtime,org.osgi.service.http.runtime.dto",
+ osgi.service;objectClass:List<String>="org.osgi.service.http.HttpService";
+ uses:="org.osgi.service.http"
+ </Provide-Capability>
+ <!-- We need to override this from the base configuration to exclude the ServiceLoader capabilities -->
+ <Require-Capability>
+ osgi.contract;filter:="(&(osgi.contract=JavaServlet)(version=3.1))"
+ </Require-Capability>
+ <!-- We need to override this from the base configuration to exclude the ServiceLoader resources -->
+ <Include-Resource>
+ {maven-resources}
+ </Include-Resource>
<_removeheaders>
X-Jetty-Version,Private-Package,Conditional-Package
</_removeheaders>
@@ -249,6 +366,26 @@
<version>${jetty.version}</version>
</dependency>
<dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-common</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-hpack</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <version>${jetty.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.service.http</artifactId>
<version>1.2.1</version>
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
index 9e48814..db08887 100644
--- a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/ConfigMetaTypeProvider.java
@@ -432,6 +432,41 @@ class ConfigMetaTypeProvider implements MetaTypeProvider
"If not -1, stop timeout for the server in milliseconds.", -1L,
bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_STOP_TIMEOUT)));
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_HTTP2_ENABLE,
+ "Enable Http/2",
+ "Whether to enable HTTP/2. Default is false.",
+ false,
+ bundle.getBundleContext().getProperty(JettyConfig.FELIX_HTTP2_ENABLE)));
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_HTTP2_MAX_CONCURRENT_STREAMS,
+ "Http/2 Max Concurrent Streams",
+ "The max number of concurrent streams per connection. Default is 128.",
+ 128,
+ bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_HTTP2_MAX_CONCURRENT_STREAMS)));
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_HTTP2_INITIAL_STREAM_RECV_WINDOW,
+ "Http/2 Initial Stream Recieve Window",
+ "The initial stream receive window (client to server). Default is 524288.",
+ 524288,
+ bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_HTTP2_INITIAL_STREAM_RECV_WINDOW)));
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_HTTP2_INITIAL_SESSION_RECV_WINDOW,
+ "Http/2 Initial Session Recieve Window",
+ "The initial session receive window (client to server). Default is 1048576.",
+ 1048576,
+ bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_HTTP2_INITIAL_SESSION_RECV_WINDOW)));
+
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_ALPN_PROTOCOLS,
+ "ALPN Protocols",
+ "The ALPN protocols to consider. Default is h2, http/1.1.",
+ AttributeDefinition.STRING,
+ new String[] {"h2", "http/1.1"},
+ 2147483647,
+ null, null,
+ getStringArray(bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_ALPN_PROTOCOLS))));
+ adList.add(new AttributeDefinitionImpl(JettyConfig.FELIX_JETTY_ALPN_DEFAULT_PROTOCOL,
+ "ALPN Default Protocol",
+ "The default protocol when negotiation fails. Default is http/1.1.",
+ "http/1.1",
+ bundle.getBundleContext().getProperty(JettyConfig.FELIX_JETTY_ALPN_DEFAULT_PROTOCOL)));
+
return new ObjectClassDefinition()
{
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
index 549c7fe..aa340d7 100644
--- a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyConfig.java
@@ -267,6 +267,24 @@ public final class JettyConfig
/** Felix specific property to specify the stop timeout of the jetty server */
public static final String FELIX_JETTY_STOP_TIMEOUT = "org.apache.felix.jetty.stopTimeout";
+ /** Felix specific property to control whether to enable HTTP/2. */
+ public static final String FELIX_HTTP2_ENABLE = "org.apache.felix.http2.enable";
+
+ /** Felix specific property to specify the max number of concurrent streams per connection */
+ public static final String FELIX_JETTY_HTTP2_MAX_CONCURRENT_STREAMS = "org.apache.felix.jetty.http2.maxConcurrentStreams";
+
+ /** Felix specific property to specify the initial stream receive window (client to server) */
+ public static final String FELIX_JETTY_HTTP2_INITIAL_STREAM_RECV_WINDOW = "org.apache.felix.jetty.http2.initialStreamRecvWindow";
+
+ /** Felix specific property to specify the initial session receive window (client to server) */
+ public static final String FELIX_JETTY_HTTP2_INITIAL_SESSION_RECV_WINDOW = "org.apache.felix.jetty.http2.initialSessionRecvWindow";
+
+ /** Felix specific property to specify the ALPN protocols to consider */
+ public static final String FELIX_JETTY_ALPN_PROTOCOLS = "org.apache.felix.jetty.alpn.protocols";
+
+ /** Felix specific property to specify the default protocol when negotiation fails */
+ public static final String FELIX_JETTY_ALPN_DEFAULT_PROTOCOL = "org.apache.felix.jetty.alpn.defaultProtocol";
+
private static String validateContextPath(String ctxPath)
{
// undefined, empty, or root context path
@@ -530,6 +548,35 @@ public final class JettyConfig
return useHttps && getHttpsPort() > 0;
}
+ /**
+ * Returns <code>true</code> if HTTP/2 is configured to be used (
+ * {@link #FELIX_HTTP2_ENABLE})
+ */
+ public boolean isUseHttp2()
+ {
+ return getBooleanProperty(FELIX_HTTP2_ENABLE, false);
+ }
+
+ public int getHttp2MaxConcurrentStreams() {
+ return getIntProperty(FELIX_JETTY_HTTP2_MAX_CONCURRENT_STREAMS, 128);
+ }
+
+ public int getHttp2InitialStreamRecvWindow() {
+ return getIntProperty(FELIX_JETTY_HTTP2_INITIAL_STREAM_RECV_WINDOW, 524288);
+ }
+
+ public int getHttp2InitialSessionRecvWindow() {
+ return getIntProperty(FELIX_JETTY_HTTP2_INITIAL_SESSION_RECV_WINDOW, 1048576);
+ }
+
+ public String[] getAlpnProtocols() {
+ return getStringArrayProperty(FELIX_JETTY_ALPN_PROTOCOLS, new String[] {"h2", "http/1.1"} );
+ }
+
+ public String getAlpnDefaultProtocol() {
+ return getProperty(FELIX_JETTY_ALPN_DEFAULT_PROTOCOL, "http/1.1");
+ }
+
public boolean isProxyLoadBalancerConnection()
{
return getBooleanProperty(FELIX_PROXY_LOAD_BALANCER_CONNECTION_ENABLE, false);
diff --git a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
index a4b99c8..a067dde 100644
--- a/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
+++ b/http/jetty/src/main/java/org/apache/felix/http/jetty/internal/JettyService.java
@@ -34,7 +34,9 @@ import javax.servlet.SessionTrackingMode;
import org.apache.felix.http.base.internal.HttpServiceController;
import org.apache.felix.http.base.internal.logger.SystemLogger;
import org.apache.felix.http.jetty.internal.webapp.WebAppBundleTracker;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.security.HashLoginService;
import org.eclipse.jetty.security.UserStore;
@@ -471,6 +473,27 @@ public final class JettyService extends AbstractLifeCycle.AbstractLifeCycleListe
httpConfiguration.addCustomizer(customizerWrapper);
}
+ if (this.config.isUseHttp2()) {
+ //add ALPN factory
+ SslConnectionFactory alpnConnFactory = new SslConnectionFactory(sslContextFactory, "alpn");
+ connector.addConnectionFactory(alpnConnFactory);
+
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory(this.config.getAlpnProtocols());
+ alpn.setDefaultProtocol(this.config.getAlpnDefaultProtocol());
+ connector.addConnectionFactory(alpn);
+
+ //Configure a HTTP2 on the ssl connector
+ HTTP2ServerConnectionFactory http2factory = new HTTP2ServerConnectionFactory(httpConfiguration);
+ http2factory.setMaxConcurrentStreams(this.config.getHttp2MaxConcurrentStreams());
+ http2factory.setInitialStreamRecvWindow(this.config.getHttp2InitialStreamRecvWindow());
+ http2factory.setInitialSessionRecvWindow(this.config.getHttp2InitialSessionRecvWindow());
+ connector.addConnectionFactory(http2factory);
+
+ //use http/2 cipher comparator
+ sslContextFactory.setCipherComparator(org.eclipse.jetty.http2.HTTP2Cipher.COMPARATOR);
+ sslContextFactory.setUseCipherSuitesOrder(true);
+ }
+
configureConnector(connector, this.config.getHttpsPort());
return startConnector(connector);
}