Issue #6728 - QUIC and HTTP/3
Added http3 Jetty module and distribution test. Implemented simple logic to send the Alt-Svc header in HTTP/2 responses. Updated JNA dependency to use jna-jpms. Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
3d6578deee
commit
578ae30311
|
@ -130,7 +130,7 @@
|
|||
<configuration>
|
||||
<includeGroupIds>jakarta.transaction,org.eclipse.jetty</includeGroupIds>
|
||||
<excludeGroupIds>
|
||||
org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs
|
||||
org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.http3,org.eclipse.jetty.quic,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs
|
||||
</excludeGroupIds>
|
||||
<excludeArtifactIds>
|
||||
apache-jsp,apache-jstl,jetty-start,jetty-slf4j-impl
|
||||
|
@ -148,7 +148,7 @@
|
|||
<configuration>
|
||||
<includeGroupIds>jakarta.transaction,org.eclipse.jetty</includeGroupIds>
|
||||
<excludeGroupIds>
|
||||
org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs
|
||||
org.eclipse.jetty.orbit,org.eclipse.jetty.http2,org.eclipse.jetty.http3,org.eclipse.jetty.quic,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs
|
||||
</excludeGroupIds>
|
||||
<excludeArtifactIds>
|
||||
apache-jsp,apache-jstl,jetty-start
|
||||
|
@ -264,6 +264,33 @@
|
|||
<outputDirectory>${source-assembly-directory}/lib/http2</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-lib-http3-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.http3,org.eclipse.jetty.quic,org.eclipse.jetty.quic.libquiche</includeGroupIds>
|
||||
<includeArtifactIds>http3-server,http3-common,http3-qpack,quic-server,quic-common,quic-quiche-common,quic-quiche-jna,quic-quiche-foreign-incubator,jetty-quiche-native</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<outputDirectory>${assembly-directory}/lib/http3</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-lib-http3-src-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeGroupIds>org.eclipse.jetty.http3</includeGroupIds>
|
||||
<includeArtifactIds>http3-server</includeArtifactIds>
|
||||
<includeTypes>jar</includeTypes>
|
||||
<classifier>sources</classifier>
|
||||
<outputDirectory>${source-assembly-directory}/lib/http3</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-lib-fcgi-deps</id>
|
||||
<phase>generate-resources</phase>
|
||||
|
@ -690,6 +717,10 @@
|
|||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http3</groupId>
|
||||
<artifactId>http3-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
|
|
|
@ -86,6 +86,7 @@ public enum HttpHeader
|
|||
*/
|
||||
ACCEPT_RANGES("Accept-Ranges"),
|
||||
AGE("Age"),
|
||||
ALT_SVC("Alt-Svc"),
|
||||
ETAG("ETag"),
|
||||
LOCATION("Location"),
|
||||
PROXY_AUTHENTICATE("Proxy-Authenticate"),
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "https://www.eclipse.org/jetty/configure_10_0.dtd">
|
||||
|
||||
<Configure id="Server" class="org.eclipse.jetty.server.Server">
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New id="http3Connector" class="org.eclipse.jetty.http3.server.HTTP3ServerConnector">
|
||||
<Arg><Ref refid="Server" /></Arg>
|
||||
<Arg><Ref refid="sslContextFactory" /></Arg>
|
||||
<Arg>
|
||||
<Array type="org.eclipse.jetty.server.ConnectionFactory">
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.http3.server.HTTP3ServerConnectionFactory">
|
||||
<Arg><Ref refid="sslHttpConfig" /></Arg>
|
||||
<Get name="HTTP3Configuration">
|
||||
<Set name="streamIdleTimeout" property="jetty.http3.streamIdleTimeout" />
|
||||
</Get>
|
||||
</New>
|
||||
</Item>
|
||||
</Array>
|
||||
</Arg>
|
||||
<Set name="host" property="jetty.quic.host" />
|
||||
<Set name="port"><Property name="jetty.quic.port" default="8444" /></Set>
|
||||
<Set name="idleTimeout" property="jetty.quic.idleTimeout" />
|
||||
<Get name="quicConfiguration">
|
||||
<Set name="maxBidirectionalRemoteStreams" property="jetty.quic.maxBidirectionalRemoteStreams" />
|
||||
<Set name="sessionRecvWindow" property="jetty.quic.sessionRecvWindow" />
|
||||
<Set name="bidirectionalStreamRecvWindow" property="jetty.quic.bidirectionalStreamRecvWindow" />
|
||||
</Get>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
</Configure>
|
|
@ -0,0 +1,49 @@
|
|||
[description]
|
||||
Enables the support for the HTTP/3 protocol.
|
||||
|
||||
[tags]
|
||||
connector
|
||||
http3
|
||||
http
|
||||
quic
|
||||
|
||||
[depend]
|
||||
http2
|
||||
|
||||
[files]
|
||||
maven://net.java.dev.jna/jna-jpms/${jna.version}|lib/http3/jna-jpms-${jna.version}.jar
|
||||
maven://org.mortbay.jetty.quic.libquiche/jetty-quiche-native/${jetty-quiche-native.version}|lib/http3/jetty-quiche-native-${jetty-quiche-native.version}.jar
|
||||
|
||||
[lib]
|
||||
lib/http3/*.jar
|
||||
|
||||
[xml]
|
||||
etc/jetty-http3.xml
|
||||
|
||||
[ini-template]
|
||||
# tag::documentation[]
|
||||
## The host/address to bind the connector to.
|
||||
# jetty.quic.host=0.0.0.0
|
||||
|
||||
## The port the connector listens on.
|
||||
# jetty.quic.port=8444
|
||||
|
||||
## The connector idle timeout, in milliseconds.
|
||||
# jetty.quic.idleTimeout=30000
|
||||
|
||||
## Specifies the maximum number of concurrent requests per session.
|
||||
# jetty.quic.maxBidirectionalRemoteStreams=128
|
||||
|
||||
## Specifies the session receive window (client to server) in bytes.
|
||||
# jetty.quic.sessionRecvWindow=4194304
|
||||
|
||||
## Specifies the stream receive window (client to server) in bytes.
|
||||
# jetty.quic.bidirectionalStreamRecvWindow=2097152
|
||||
|
||||
## Specifies the stream idle timeout, in milliseconds.
|
||||
# jetty.http3.streamIdleTimeout=30000
|
||||
# end::documentation[]
|
||||
|
||||
[ini]
|
||||
jna.version?=@jna.version@
|
||||
jetty-quiche-native.version?=@jetty-quiche-native.version@
|
|
@ -15,6 +15,8 @@ package org.eclipse.jetty.http3.server;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http3.api.Session;
|
||||
import org.eclipse.jetty.http3.api.Stream;
|
||||
import org.eclipse.jetty.http3.frames.HeadersFrame;
|
||||
|
@ -38,6 +40,16 @@ public class HTTP3ServerConnectionFactory extends AbstractHTTP3ServerConnectionF
|
|||
public HTTP3ServerConnectionFactory(HttpConfiguration configuration)
|
||||
{
|
||||
super(configuration, new HTTP3SessionListener());
|
||||
configuration.addCustomizer((connector, httpConfig, request) ->
|
||||
{
|
||||
HTTP3ServerConnector http3Connector = connector.getServer().getBean(HTTP3ServerConnector.class);
|
||||
if (http3Connector != null && HttpVersion.HTTP_2.is(request.getHttpVersion().asString()))
|
||||
{
|
||||
HttpField altSvc = http3Connector.getAltSvcHttpField();
|
||||
if (altSvc != null)
|
||||
request.getResponse().getHttpFields().add(altSvc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class HTTP3SessionListener implements Session.Server.Listener
|
||||
|
|
|
@ -15,6 +15,9 @@ package org.eclipse.jetty.http3.server;
|
|||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.quic.server.QuicServerConnector;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
|
@ -27,6 +30,8 @@ import org.eclipse.jetty.util.thread.Scheduler;
|
|||
*/
|
||||
public class HTTP3ServerConnector extends QuicServerConnector
|
||||
{
|
||||
private HttpField altSvcHttpField;
|
||||
|
||||
public HTTP3ServerConnector(Server server, SslContextFactory.Server sslContextFactory, ConnectionFactory... factories)
|
||||
{
|
||||
this(server, null, null, null, sslContextFactory, factories);
|
||||
|
@ -41,4 +46,16 @@ public class HTTP3ServerConnector extends QuicServerConnector
|
|||
getQuicConfiguration().setMaxUnidirectionalRemoteStreams(8);
|
||||
getQuicConfiguration().setUnidirectionalStreamRecvWindow(1024 * 1024);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStart() throws Exception
|
||||
{
|
||||
super.doStart();
|
||||
altSvcHttpField = new PreEncodedHttpField(HttpHeader.ALT_SVC, String.format("h3=\":%d\"", getLocalPort()));
|
||||
}
|
||||
|
||||
public HttpField getAltSvcHttpField()
|
||||
{
|
||||
return altSvcHttpField;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ package org.eclipse.jetty.quic.common;
|
|||
import java.io.IOException;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.EventListener;
|
||||
|
@ -514,7 +515,7 @@ public abstract class QuicSession extends ContainerLifeCycle
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("connection closed {}", QuicSession.this);
|
||||
byteBufferPool.release(cipherBuffer);
|
||||
finishOutwardClose(null);
|
||||
finishOutwardClose(new ClosedChannelException());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<artifactId>jna-jpms</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.nio.channels.SelectionKey;
|
|||
import java.nio.file.Files;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
|
@ -157,14 +158,19 @@ public class QuicServerConnector extends AbstractNetworkConnector
|
|||
super.doStart();
|
||||
selectorManager.accept(datagramChannel);
|
||||
|
||||
Set<String> aliases = sslContextFactory.getAliases();
|
||||
if (aliases.isEmpty())
|
||||
throw new IllegalStateException("Invalid KeyStore: no aliases");
|
||||
String alias = sslContextFactory.getCertAlias();
|
||||
if (alias == null)
|
||||
alias = aliases.stream().findFirst().orElse("mykey");
|
||||
char[] keyStorePassword = sslContextFactory.getKeyStorePassword().toCharArray();
|
||||
String keyManagerPassword = sslContextFactory.getKeyManagerPassword();
|
||||
SSLKeyPair keyPair = new SSLKeyPair(
|
||||
sslContextFactory.getKeyStoreResource().getFile(),
|
||||
sslContextFactory.getKeyStoreType(),
|
||||
keyStorePassword,
|
||||
alias == null ? "mykey" : alias,
|
||||
alias,
|
||||
keyManagerPassword == null ? keyStorePassword : keyManagerPassword.toCharArray()
|
||||
);
|
||||
File[] pemFiles = keyPair.export(new File(System.getProperty("java.io.tmpdir")));
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -1126,7 +1126,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<artifactId>jna-jpms</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -84,6 +84,11 @@
|
|||
<artifactId>http2-http-client-transport</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http3</groupId>
|
||||
<artifactId>http3-http-client-transport</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.demos</groupId>
|
||||
<artifactId>demo-simple-webapp</artifactId>
|
||||
|
|
|
@ -33,9 +33,12 @@ import org.eclipse.jetty.client.HttpClient;
|
|||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
|
||||
import org.eclipse.jetty.http3.client.HTTP3Client;
|
||||
import org.eclipse.jetty.http3.client.http.HttpClientTransportOverHTTP3;
|
||||
import org.eclipse.jetty.io.ClientConnector;
|
||||
import org.eclipse.jetty.start.FS;
|
||||
import org.eclipse.jetty.toolchain.test.PathAssert;
|
||||
|
@ -1106,4 +1109,38 @@ public class DistributionTests extends AbstractJettyHomeTest
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testH3() throws Exception
|
||||
{
|
||||
String jettyVersion = System.getProperty("jettyVersion");
|
||||
JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
|
||||
.jettyVersion(jettyVersion)
|
||||
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
|
||||
.build();
|
||||
|
||||
try (JettyHomeTester.Run run1 = distribution.start("--add-modules=http3,test-keystore"))
|
||||
{
|
||||
assertTrue(run1.awaitFor(10, TimeUnit.SECONDS));
|
||||
assertEquals(0, run1.getExitValue());
|
||||
|
||||
int h2Port = distribution.freePort();
|
||||
int h3Port = distribution.freePort();
|
||||
try (JettyHomeTester.Run run2 = distribution.start(List.of("jetty.ssl.selectors=1", "jetty.ssl.port=" + h2Port, "jetty.quic.port=" + h3Port)))
|
||||
{
|
||||
assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS));
|
||||
|
||||
HTTP3Client http3Client = new HTTP3Client();
|
||||
http3Client.getQuicConfiguration().setVerifyPeerCertificates(false);
|
||||
this.client = new HttpClient(new HttpClientTransportOverHTTP3(http3Client));
|
||||
this.client.start();
|
||||
ContentResponse response = this.client.newRequest("localhost", h3Port)
|
||||
.scheme(HttpScheme.HTTPS.asString())
|
||||
.path("/path")
|
||||
.timeout(15, TimeUnit.SECONDS)
|
||||
.send();
|
||||
assertEquals(HttpStatus.NOT_FOUND_404, response.getStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# Jetty Logging using jetty-slf4j-impl
|
||||
org.eclipse.jetty.logging.appender.MESSAGE_ESCAPE=false
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
|
||||
#org.eclipse.jetty.tests.distribution.LEVEL=DEBUG
|
||||
|
|
Loading…
Reference in New Issue