Skip to content

Commit e45eff6

Browse files
committed
HTTP/3
1 parent d3d5d7e commit e45eff6

20 files changed

+2470
-80
lines changed

vertx-core/src/main/java/io/vertx/core/Vertx.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,22 @@ default NetClient createNetClient() {
229229
*/
230230
HttpServer createHttpServer(HttpServerOptions options);
231231

232+
/**
233+
* Create an HTTP3 client using the specified options
234+
*
235+
* @param options the options to use
236+
* @return the server
237+
*/
238+
HttpClientAgent createHttpClient(Http3ClientOptions options);
239+
240+
/**
241+
* Create an HTTP3 server using the specified options
242+
*
243+
* @param options the options to use
244+
* @return the server
245+
*/
246+
HttpServer createHttpServer(Http3ServerOptions options);
247+
232248
/**
233249
* Create an HTTP/HTTPS server using default options
234250
*
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package io.vertx.core.http;
2+
3+
import io.vertx.codegen.annotations.DataObject;
4+
import io.vertx.core.net.QLogConfig;
5+
import io.vertx.core.net.QuicClientOptions;
6+
7+
@DataObject
8+
public class Http3ClientOptions extends QuicClientOptions {
9+
10+
public Http3ClientOptions() {
11+
}
12+
13+
public Http3ClientOptions(Http3ClientOptions other) {
14+
super(other);
15+
}
16+
17+
@Override
18+
public Http3ClientOptions setQLogConfig(QLogConfig qLogConfig) {
19+
return (Http3ClientOptions)super.setQLogConfig(qLogConfig);
20+
}
21+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.vertx.core.http;
2+
3+
import io.vertx.codegen.annotations.DataObject;
4+
import io.vertx.core.net.KeyCertOptions;
5+
import io.vertx.core.net.QLogConfig;
6+
import io.vertx.core.net.QuicClientAddressValidation;
7+
import io.vertx.core.net.QuicServerOptions;
8+
9+
import java.time.Duration;
10+
11+
@DataObject
12+
public class Http3ServerOptions extends QuicServerOptions {
13+
14+
public Http3ServerOptions() {
15+
}
16+
17+
public Http3ServerOptions(Http3ServerOptions other) {
18+
super(other);
19+
}
20+
21+
@Override
22+
public Http3ServerOptions setQLogConfig(QLogConfig qLogConfig) {
23+
return (Http3ServerOptions)super.setQLogConfig(qLogConfig);
24+
}
25+
26+
@Override
27+
public Http3ServerOptions setLoadBalanced(boolean loadBalanced) {
28+
return (Http3ServerOptions)super.setLoadBalanced(loadBalanced);
29+
}
30+
31+
@Override
32+
public Http3ServerOptions setClientAddressValidation(QuicClientAddressValidation clientAddressValidation) {
33+
return (Http3ServerOptions)super.setClientAddressValidation(clientAddressValidation);
34+
}
35+
36+
@Override
37+
public Http3ServerOptions setClientAddressValidationTimeWindow(Duration clientAddressValidationTimeWindow) {
38+
return (Http3ServerOptions)super.setClientAddressValidationTimeWindow(clientAddressValidationTimeWindow);
39+
}
40+
41+
@Override
42+
public Http3ServerOptions setClientAddressValidationKey(KeyCertOptions validationKey) {
43+
return (Http3ServerOptions)super.setClientAddressValidationKey(validationKey);
44+
}
45+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2011-2025 Contributors to the Eclipse Foundation
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
7+
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
8+
*
9+
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
10+
*/
11+
package io.vertx.core.http.impl;
12+
13+
import io.netty.handler.codec.http3.Http3;
14+
import io.vertx.core.Future;
15+
import io.vertx.core.Promise;
16+
import io.vertx.core.http.Http3ClientOptions;
17+
import io.vertx.core.http.impl.http3.Http3ClientConnection;
18+
import io.vertx.core.internal.ContextInternal;
19+
import io.vertx.core.internal.VertxInternal;
20+
import io.vertx.core.internal.quic.QuicConnectionInternal;
21+
import io.vertx.core.net.*;
22+
import io.vertx.core.spi.metrics.ClientMetrics;
23+
24+
import java.time.Duration;
25+
import java.util.Arrays;
26+
import java.util.concurrent.locks.Lock;
27+
import java.util.concurrent.locks.ReentrantLock;
28+
29+
public class Http3ChannelConnector implements HttpChannelConnector {
30+
31+
private final VertxInternal vertx;
32+
private final Lock lock;
33+
private Future<QuicClient> clientFuture;
34+
private final Http3ClientOptions options;
35+
36+
public Http3ChannelConnector(VertxInternal vertxInternal, Http3ClientOptions options) {
37+
38+
options = new Http3ClientOptions(options);
39+
options.getSslOptions().setApplicationLayerProtocols(Arrays.asList(Http3.supportedApplicationProtocols()));
40+
options.getTransportOptions().setInitialMaxData(10000000L);
41+
options.getTransportOptions().setInitialMaxStreamDataBidirectionalLocal(1000000L);
42+
options.getTransportOptions().setInitialMaxStreamDataBidirectionalRemote(1000000L);
43+
options.getTransportOptions().setInitialMaxStreamDataUnidirectional(1000000L);
44+
options.getTransportOptions().setInitialMaxStreamsBidirectional(100L);
45+
options.getTransportOptions().setInitialMaxStreamsUnidirectional(100L);
46+
47+
this.vertx = vertxInternal;
48+
this.lock = new ReentrantLock();
49+
this.options = options;
50+
}
51+
52+
@Override
53+
public Future<HttpClientConnection> httpConnect(ContextInternal context, SocketAddress server, HostAndPort authority, HttpConnectParams params, long maxLifetimeMillis, ClientMetrics<?, ?, ?> metrics) {
54+
55+
lock.lock();
56+
Future<QuicClient> fut = clientFuture;
57+
if (fut == null) {
58+
QuicClient client = QuicClient.create(vertx, this.options);
59+
fut = client.bind(SocketAddress.inetSocketAddress(0, "localhost")).map(client);
60+
clientFuture = fut;
61+
lock.unlock();
62+
} else {
63+
lock.unlock();
64+
}
65+
Promise<HttpClientConnection> promise = context.promise();
66+
67+
fut.onComplete((res, err) -> {
68+
if (err == null) {
69+
Future<QuicConnection> f = res.connect(server);
70+
f.onComplete((res2, err2) -> {
71+
if (err2 == null) {
72+
Http3ClientConnection c = new Http3ClientConnection((QuicConnectionInternal) res2);
73+
c.init();
74+
promise.complete(c);
75+
} else {
76+
promise.fail(err2);
77+
}
78+
});
79+
} else {
80+
promise.fail(err);
81+
}
82+
});
83+
84+
return promise.future();
85+
}
86+
87+
@Override
88+
public Future<Void> shutdown(Duration timeout) {
89+
if (clientFuture == null) {
90+
return vertx.getOrCreateContext().succeededFuture();
91+
} else {
92+
return clientFuture.compose(client -> client.shutdown(timeout));
93+
}
94+
}
95+
96+
@Override
97+
public Future<Void> close() {
98+
if (clientFuture == null) {
99+
return vertx.getOrCreateContext().succeededFuture();
100+
} else {
101+
return clientFuture.compose(QuicEndpoint::close);
102+
}
103+
}
104+
}

vertx-core/src/main/java/io/vertx/core/http/impl/HttpClientImpl.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,14 @@
4545
*/
4646
public class HttpClientImpl extends HttpClientBase implements HttpClientInternal, MetricsProvider {
4747

48-
static class Config {
49-
List<String> nonProxyHosts;
50-
boolean verifyHost;
51-
boolean defaultSsl;
52-
String defaultHost;
53-
int defaultPort;
54-
int maxRedirects;
55-
int initialPoolKind;
48+
public static class Config {
49+
public List<String> nonProxyHosts;
50+
public boolean verifyHost;
51+
public boolean defaultSsl;
52+
public String defaultHost;
53+
public int defaultPort;
54+
public int maxRedirects;
55+
public int initialPoolKind;
5656
}
5757

5858
// Pattern to check we are not dealing with an absoluate URI
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package io.vertx.core.http.impl.http3;
2+
3+
import io.netty.channel.ChannelHandlerContext;
4+
import io.netty.channel.ChannelInitializer;
5+
import io.netty.channel.ChannelPipeline;
6+
import io.netty.handler.codec.http3.DefaultHttp3Headers;
7+
import io.netty.handler.codec.http3.Http3ClientConnectionHandler;
8+
import io.netty.handler.codec.http3.Http3RequestStreamInitializer;
9+
import io.netty.handler.codec.quic.QuicStreamChannel;
10+
import io.vertx.core.Future;
11+
import io.vertx.core.Handler;
12+
import io.vertx.core.MultiMap;
13+
import io.vertx.core.http.*;
14+
import io.vertx.core.http.impl.HttpClientConnection;
15+
import io.vertx.core.http.impl.HttpClientStream;
16+
import io.vertx.core.http.impl.headers.HttpRequestHeaders;
17+
import io.vertx.core.internal.ContextInternal;
18+
import io.vertx.core.internal.quic.QuicConnectionInternal;
19+
import io.vertx.core.internal.quic.QuicStreamInternal;
20+
import io.vertx.core.net.HostAndPort;
21+
22+
import java.util.function.Consumer;
23+
import java.util.function.Function;
24+
25+
public class Http3ClientConnection extends Http3Connection implements HttpClientConnection {
26+
27+
public Http3ClientConnection(QuicConnectionInternal connection) {
28+
super(connection);
29+
}
30+
31+
public void init() {
32+
33+
super.init();
34+
35+
Http3ClientConnectionHandler http3Handler = new Http3ClientConnectionHandler();
36+
37+
ChannelPipeline pipeline = connection.channelHandlerContext().pipeline();
38+
39+
pipeline.addBefore("handler", "http3", http3Handler);
40+
41+
42+
43+
}
44+
45+
@Override
46+
public MultiMap newHttpRequestHeaders() {
47+
return new HttpRequestHeaders(new DefaultHttp3Headers());
48+
}
49+
50+
@Override
51+
public long activeStreams() {
52+
return 0;
53+
}
54+
55+
@Override
56+
public long concurrency() {
57+
// For now hardcode
58+
return 10;
59+
}
60+
61+
@Override
62+
public HostAndPort authority() {
63+
return HostAndPort.authority("localhost", 8443);
64+
}
65+
66+
@Override
67+
public HttpClientConnection evictionHandler(Handler<Void> handler) {
68+
return null;
69+
}
70+
71+
@Override
72+
public HttpClientConnection invalidMessageHandler(Handler<Object> handler) {
73+
return null;
74+
}
75+
76+
@Override
77+
public HttpClientConnection concurrencyChangeHandler(Handler<Long> handler) {
78+
return null;
79+
}
80+
81+
@Override
82+
public ChannelHandlerContext channelHandlerContext() {
83+
return null;
84+
}
85+
86+
@Override
87+
public Future<HttpClientStream> createStream(ContextInternal context) {
88+
return connection.createStream(context, true, new Function<Consumer<QuicStreamChannel>, ChannelInitializer<QuicStreamChannel>>() {
89+
@Override
90+
public ChannelInitializer<QuicStreamChannel> apply(Consumer<QuicStreamChannel> quicStreamChannelConsumer) {
91+
return new Http3RequestStreamInitializer() {
92+
@Override
93+
protected void initRequestStream(QuicStreamChannel ch) {
94+
quicStreamChannelConsumer.accept(ch);
95+
}
96+
};
97+
}
98+
}).map(stream -> {
99+
QuicStreamInternal streamInternal = (QuicStreamInternal) stream;
100+
Http3ClientStream http3Stream = new Http3ClientStream(this, streamInternal, context);
101+
http3Stream.init();
102+
return http3Stream;
103+
});
104+
}
105+
106+
@Override
107+
public ContextInternal context() {
108+
return context;
109+
}
110+
111+
@Override
112+
public boolean isValid() {
113+
return false;
114+
}
115+
116+
@Override
117+
public Object metric() {
118+
return null;
119+
}
120+
121+
@Override
122+
public long lastResponseReceivedTimestamp() {
123+
return 0;
124+
}
125+
126+
@Override
127+
public String indicatedServerName() {
128+
return "";
129+
}
130+
131+
}

0 commit comments

Comments
 (0)