Skip to content

Commit 5245b68

Browse files
committed
Basic HTTP/3 server.
1 parent 9320464 commit 5245b68

File tree

10 files changed

+869
-72
lines changed

10 files changed

+869
-72
lines changed

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

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

232+
/**
233+
* Create an HTTP3 server using the specified options
234+
*
235+
* @param options the options to use
236+
* @return the server
237+
*/
238+
HttpServer createHttpServer(Http3ServerOptions options);
239+
232240
/**
233241
* Create an HTTP/HTTPS server using default options
234242
*
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.QuicServerOptions;
7+
8+
import java.time.Duration;
9+
10+
@DataObject
11+
public class Http3ServerOptions extends QuicServerOptions {
12+
13+
public Http3ServerOptions() {
14+
}
15+
16+
public Http3ServerOptions(Http3ServerOptions other) {
17+
super(other);
18+
}
19+
20+
@Override
21+
public Http3ServerOptions setQLogConfig(QLogConfig qLogConfig) {
22+
return (Http3ServerOptions)super.setQLogConfig(qLogConfig);
23+
}
24+
25+
@Override
26+
public Http3ServerOptions setLoadBalanced(boolean loadBalanced) {
27+
return (Http3ServerOptions)super.setLoadBalanced(loadBalanced);
28+
}
29+
30+
@Override
31+
public Http3ServerOptions setValidateClientAddress(boolean validateClientAddress) {
32+
return (Http3ServerOptions)super.setValidateClientAddress(validateClientAddress);
33+
}
34+
35+
@Override
36+
public Http3ServerOptions setClientAddressValidationTimeWindow(Duration clientAddressValidationTimeWindow) {
37+
return (Http3ServerOptions)super.setClientAddressValidationTimeWindow(clientAddressValidationTimeWindow);
38+
}
39+
40+
@Override
41+
public Http3ServerOptions setClientAddressValidationKey(KeyCertOptions validationKey) {
42+
return (Http3ServerOptions)super.setClientAddressValidationKey(validationKey);
43+
}
44+
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package io.vertx.core.http.impl;
2+
3+
import io.netty.channel.ChannelInitializer;
4+
import io.netty.channel.ChannelPipeline;
5+
import io.netty.handler.codec.http3.*;
6+
import io.netty.handler.codec.quic.QuicStreamChannel;
7+
import io.netty.util.CharsetUtil;
8+
import io.vertx.core.Future;
9+
import io.vertx.core.Handler;
10+
import io.vertx.core.Vertx;
11+
import io.vertx.core.http.*;
12+
import io.vertx.core.http.impl.http3.Http3Connection;
13+
import io.vertx.core.http.impl.http3.Http3ServerConnection;
14+
import io.vertx.core.internal.ContextInternal;
15+
import io.vertx.core.internal.VertxInternal;
16+
import io.vertx.core.internal.quic.QuicConnectionInternal;
17+
import io.vertx.core.net.*;
18+
import io.vertx.core.net.impl.quic.QuicConnectionHandler;
19+
20+
import java.time.Duration;
21+
import java.util.Arrays;
22+
import java.util.concurrent.TimeUnit;
23+
24+
public class Http3Server implements HttpServer {
25+
26+
private final VertxInternal vertx;
27+
private final Http3ServerOptions options;
28+
private volatile Handler<HttpServerRequest> requestHandler;
29+
private QuicServer quicServer;
30+
31+
public Http3Server(VertxInternal vertx, Http3ServerOptions options) {
32+
33+
options = new Http3ServerOptions(options);
34+
options.getSslOptions().setApplicationLayerProtocols(Arrays.asList(Http3.supportedApplicationProtocols()));
35+
options.getTransportOptions().setInitialMaxData(10000000L);
36+
options.getTransportOptions().setInitialMaxStreamDataBidirectionalLocal(1000000L);
37+
options.getTransportOptions().setInitialMaxStreamDataBidirectionalRemote(1000000L);
38+
options.getTransportOptions().setInitialMaxStreamDataUnidirectional(1000000L);
39+
options.getTransportOptions().setInitialMaxStreamsBidirectional(100L);
40+
options.getTransportOptions().setInitialMaxStreamsUnidirectional(100L);
41+
42+
this.vertx = vertx;
43+
this.options = options;
44+
}
45+
46+
@Override
47+
public HttpServer requestHandler(Handler<HttpServerRequest> handler) {
48+
this.requestHandler = handler;
49+
return this;
50+
}
51+
52+
@Override
53+
public Handler<HttpServerRequest> requestHandler() {
54+
return requestHandler;
55+
}
56+
57+
@Override
58+
public HttpServer invalidRequestHandler(Handler<HttpServerRequest> handler) {
59+
throw new UnsupportedOperationException();
60+
}
61+
62+
@Override
63+
public HttpServer connectionHandler(Handler<HttpConnection> handler) {
64+
throw new UnsupportedOperationException();
65+
}
66+
67+
@Override
68+
public HttpServer webSocketHandshakeHandler(Handler<ServerWebSocketHandshake> handler) {
69+
throw new UnsupportedOperationException();
70+
}
71+
72+
@Override
73+
public HttpServer exceptionHandler(Handler<Throwable> handler) {
74+
throw new UnsupportedOperationException();
75+
}
76+
77+
@Override
78+
public HttpServer webSocketHandler(Handler<ServerWebSocket> handler) {
79+
throw new UnsupportedOperationException();
80+
}
81+
82+
@Override
83+
public Handler<ServerWebSocket> webSocketHandler() {
84+
throw new UnsupportedOperationException();
85+
}
86+
87+
@Override
88+
public Future<Boolean> updateSSLOptions(ServerSSLOptions options, boolean force) {
89+
throw new UnsupportedOperationException();
90+
}
91+
92+
@Override
93+
public Future<Boolean> updateTrafficShapingOptions(TrafficShapingOptions options) {
94+
throw new UnsupportedOperationException();
95+
}
96+
97+
@Override
98+
public Future<HttpServer> listen() {
99+
return listen(SocketAddress.inetSocketAddress(443, "0.0.0.0"));
100+
}
101+
102+
private void handleConnection(QuicConnection connection) {
103+
String host = connection.localAddress().host();
104+
int port = connection.localAddress().port();
105+
String serverOrigin = "https://" + host + ":" + port;
106+
ContextInternal context = vertx.getOrCreateContext();
107+
Handler<HttpServerRequest> handler = requestHandler;
108+
Http3ServerConnection serverConnection = new Http3ServerConnection(context);
109+
serverConnection.streamHandler(stream -> {
110+
HttpServerRequestImpl request = new HttpServerRequestImpl(handler, stream, stream.context(),
111+
false, HttpServerOptions.DEFAULT_MAX_FORM_ATTRIBUTE_SIZE,
112+
HttpServerOptions.DEFAULT_MAX_FORM_FIELDS, HttpServerOptions.DEFAULT_MAX_FORM_BUFFERED_SIZE, serverOrigin);
113+
request.init();
114+
});
115+
QuicConnectionInternal connInternal = (QuicConnectionInternal) connection;
116+
ChannelPipeline pipeline = connInternal.channelHandlerContext().pipeline();
117+
pipeline.remove(QuicConnectionHandler.class);
118+
Http3ServerConnectionHandler http3Handler = new Http3ServerConnectionHandler(
119+
new ChannelInitializer<QuicStreamChannel>() {
120+
@Override
121+
protected void initChannel(QuicStreamChannel ch) {
122+
ch.pipeline().addLast(serverConnection);
123+
}
124+
});
125+
pipeline.addLast(http3Handler);
126+
pipeline.addLast(serverConnection);
127+
}
128+
129+
@Override
130+
public Future<HttpServer> listen(SocketAddress address) {
131+
synchronized (this) {
132+
if (quicServer != null) {
133+
return vertx.getOrCreateContext().failedFuture("Already listening on port " + address.port());
134+
}
135+
quicServer = QuicServer.create(vertx, options);
136+
}
137+
quicServer.handler(this::handleConnection);
138+
return quicServer
139+
.bind(address)
140+
.map(this);
141+
}
142+
143+
@Override
144+
public Future<Void> shutdown(long timeout, TimeUnit unit) {
145+
QuicServer s;
146+
synchronized (this) {
147+
s = quicServer;
148+
if (s == null) {
149+
return vertx.getOrCreateContext().succeededFuture();
150+
}
151+
quicServer = null;
152+
}
153+
return s.shutdown(Duration.ofMillis(unit.toMillis(timeout)));
154+
}
155+
156+
@Override
157+
public int actualPort() {
158+
throw new UnsupportedOperationException();
159+
}
160+
}

0 commit comments

Comments
 (0)