diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerBuilderImpl.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerBuilderImpl.java index 8e9993546e8..1c680fb5dae 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerBuilderImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HttpServerBuilderImpl.java @@ -86,10 +86,10 @@ public HttpServer build() { if (sslOptions != null) { sslOptions = sslOptions.copy(); } - server = new CleanableHttpServer(vertx, new TcpHttpServer(vertx, new HttpServerConfig(config), sslOptions, sslEngineOptions, null, registerWebSocketWriteHandlers)); + server = new CleanableHttpServer(vertx, new TcpHttpServer(vertx, new HttpServerConfig(config), sslOptions, sslEngineOptions, true, registerWebSocketWriteHandlers)); } } else if (useQuic) { - server = new CleanableHttpServer(vertx, new QuicHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), null)); + server = new CleanableHttpServer(vertx, new QuicHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), true)); } else { throw new IllegalArgumentException("You must set at least one supported HTTP version"); } diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/HybridHttpServer.java b/vertx-core/src/main/java/io/vertx/core/http/impl/HybridHttpServer.java index 57bb3d555b5..045065daf69 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/HybridHttpServer.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/HybridHttpServer.java @@ -57,18 +57,18 @@ public HybridHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLO this.engineOptions = engineOptions; } - public HttpServerInternal tcpServer(HttpServerMetrics httpMetrics) { + public HttpServerInternal tcpServer() { if (tcpServer == null) { - TcpHttpServer server = new TcpHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), engineOptions, httpMetrics, false); + TcpHttpServer server = new TcpHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), engineOptions, false, false); setHandlers(server); tcpServer = server; } return tcpServer; } - public HttpServerInternal quicServer(HttpServerMetrics httpMetrics) { + public HttpServerInternal quicServer() { if (quicServer == null) { - QuicHttpServer server = new QuicHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), httpMetrics); + QuicHttpServer server = new QuicHttpServer(vertx, new HttpServerConfig(config), sslOptions.copy(), false); setHandlers(server); quicServer = server; } @@ -158,11 +158,8 @@ public Future listen(SocketAddress address) { @Override public Future listen(ContextInternal context) { - SocketAddress tcpLocalAddress = SocketAddress.inetSocketAddress(config.getTcpPort(), config.getTcpHost()); - SocketAddress udpLocalAddress = SocketAddress.inetSocketAddress(config.getQuicPort(), config.getQuicHost()); - httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, tcpLocalAddress, udpLocalAddress) : null; - return listen(tcpServer(httpMetrics).listen(context), - quicServer(httpMetrics).listen(context)); + return listen(tcpServer().listen(context), + quicServer().listen(context)).onSuccess(this::createMetrics); } @Override @@ -173,8 +170,17 @@ public Future listen(ContextInternal context, SocketAddress address) HttpServerConfig config = new HttpServerConfig(this.config); config.setHost(address.host()); config.setPort(address.port()); - httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, address, address) : null; - return listen(tcpServer(httpMetrics).listen(context, address), quicServer(httpMetrics).listen(context, address)); + return listen(tcpServer().listen(context, address), quicServer().listen(context, address)).onSuccess(this::createMetrics); + } + + private void createMetrics(HttpServer ignored) { + SocketAddress tcpLocalAddress = SocketAddress.inetSocketAddress(tcpServer.actualPort(), config.getTcpHost()); + SocketAddress udpLocalAddress = SocketAddress.inetSocketAddress(quicServer.actualPort(), config.getQuicHost()); + + httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, tcpLocalAddress, udpLocalAddress) : null; + + tcpServer.setMetrics(httpMetrics); + quicServer.setMetrics(httpMetrics); } private Future listen(Future f1, Future f2) { diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/quic/QuicHttpServer.java b/vertx-core/src/main/java/io/vertx/core/http/impl/quic/QuicHttpServer.java index 5f1ba43a1a4..c577b47f3d8 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/quic/QuicHttpServer.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/quic/QuicHttpServer.java @@ -48,9 +48,10 @@ public class QuicHttpServer implements HttpServerInternal { private Handler exceptionHandler; private QuicServerImpl quicServer; private HttpServerMetrics httpMetrics; + private ConnectionHandler internalConnectionHandler; private volatile int actualPort; - public QuicHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLOptions sslOptions, HttpServerMetrics httpMetrics) { + public QuicHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLOptions sslOptions, boolean manageMetrics) { // We own the copy sslOptions.setApplicationLayerProtocols(Arrays.asList(Http3.supportedApplicationProtocols())); @@ -61,8 +62,7 @@ public QuicHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLOpt this.http3Config = config.getHttp3Config() != null ? config.getHttp3Config() : new Http3ServerConfig(); this.quicConfig = config.getQuicConfig() != null ? config.getQuicConfig() : new QuicServerConfig(); this.actualPort = 0; - this.httpMetrics = httpMetrics; - this.manageMetrics = httpMetrics == null; + this.manageMetrics = manageMetrics; } @Override @@ -139,7 +139,6 @@ public Future updateTrafficShapingOptions(TrafficShapingOptions options private static class ConnectionHandler implements Handler { private final QuicServer transport; - private final HttpServerMetrics httpMetrics; private final Handler requestHandler; private final Handler connectionHandler; private final boolean handle100ContinueAutomatically; @@ -148,9 +147,9 @@ private static class ConnectionHandler implements Handler { private final int maxFormBufferedSize; private final Http3Settings localSettings; private final boolean logEnabled; + private HttpServerMetrics httpMetrics; public ConnectionHandler(QuicServer transport, - HttpServerMetrics httpMetrics, Handler requestHandler, Handler connectionHandler, boolean handle100ContinueAutomatically, @@ -160,7 +159,6 @@ public ConnectionHandler(QuicServer transport, Http3Settings localSettings, boolean logEnabled) { this.transport = transport; - this.httpMetrics = httpMetrics; this.requestHandler = requestHandler; this.connectionHandler = connectionHandler; this.handle100ContinueAutomatically = handle100ContinueAutomatically; @@ -198,6 +196,10 @@ public void handle(QuicConnection connection) { ctx.dispatch(http3Connection, handler); } } + + void setMetrics(HttpServerMetrics httpMetrics) { + this.httpMetrics = httpMetrics; + } } @Override @@ -228,9 +230,6 @@ public Future listen(ContextInternal current, SocketAddress address) requestHandler = this.requestHandler; connectionHandler = this.connectionHandler; quicServer = new QuicServerImpl(vertx, quicConfig, "http", sslOptions); - if (manageMetrics) { - httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, null, address) : null; - } } if (requestHandler == null) { @@ -240,14 +239,23 @@ public Future listen(ContextInternal current, SocketAddress address) boolean logEnabled = quicConfig.getLogConfig() != null && quicConfig.getLogConfig().isEnabled(); quicConfig.setLogConfig(null); - quicServer.connectHandler(new ConnectionHandler(quicServer, httpMetrics, requestHandler, connectionHandler, + internalConnectionHandler = new ConnectionHandler(quicServer, requestHandler, connectionHandler, config.isHandle100ContinueAutomatically(), config.getMaxFormAttributeSize(), config.getMaxFormFields(), config.getMaxFormBufferedBytes(), - http3Config.getInitialSettings() != null ? http3Config.getInitialSettings().copy() : new Http3Settings(), logEnabled)); + http3Config.getInitialSettings() != null ? http3Config.getInitialSettings().copy() : new Http3Settings(), logEnabled); + + quicServer.connectHandler(internalConnectionHandler); quicServer.exceptionHandler(exceptionHandler); return quicServer .bind(current, address) .map(addr -> { actualPort = addr.port(); + + if (manageMetrics) { + var actualAddress = SocketAddress.inetSocketAddress(actualPort, address.host()); + httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, null, actualAddress) : null; + internalConnectionHandler.setMetrics(httpMetrics); + } + return this; }); } @@ -281,6 +289,13 @@ public int actualPort() { return actualPort; } + @Override + public void setMetrics(HttpServerMetrics httpMetrics) { + this.httpMetrics = httpMetrics; + + internalConnectionHandler.setMetrics(httpMetrics); + } + @Override public Metrics getMetrics() { return httpMetrics; diff --git a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java index aad11c3ba50..b0e026f357e 100644 --- a/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java +++ b/vertx-core/src/main/java/io/vertx/core/http/impl/tcp/TcpHttpServer.java @@ -64,15 +64,14 @@ public class TcpHttpServer implements HttpServerInternal { private HttpServerMetrics httpMetrics; public TcpHttpServer(VertxInternal vertx, HttpServerConfig config, ServerSSLOptions sslOptions, - SSLEngineOptions engineOptions, HttpServerMetrics httpMetrics, boolean registerWebSocketWriteHandlers) { + SSLEngineOptions engineOptions, boolean manageMetrics, boolean registerWebSocketWriteHandlers) { this.vertx = vertx; this.config = config; this.ssl = sslOptions != null; this.sslOptions = sslOptions; this.engineOptions = engineOptions; this.registerWebSocketWriteHandlers = registerWebSocketWriteHandlers; - this.httpMetrics = httpMetrics; - this.manageMetrics = httpMetrics == null; + this.manageMetrics = manageMetrics; } @Override @@ -272,13 +271,16 @@ public synchronized Future listen(ContextInternal context, SocketAdd initializer.configurePipeline(soi.channel(), null, null, ((NetSocketImpl) so).metrics()); }); tcpServer = server; - if (manageMetrics) { - httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, address, null) : null; - } - closeSequence = new CloseSequence(p -> doClose(server, p), p -> doShutdown(server, p )); + + closeSequence = new CloseSequence(p -> doClose(server, p), p -> doShutdown(server, p)); Promise result = context.promise(); tcpServer.listen(listenContext, address).onComplete(ar -> { if (ar.succeeded()) { + if (manageMetrics) { + var actualAddress = SocketAddress.inetSocketAddress(tcpServer.actualPort(), address.host()); + httpMetrics = vertx.metrics() != null ? vertx.metrics().createHttpServerMetrics(config, actualAddress, null) : null; + } + result.complete(this); } else { result.fail(ar.cause()); @@ -335,6 +337,11 @@ private Completable foo(Completable completable) { return completable; } + @Override + public void setMetrics(HttpServerMetrics httpMetrics) { + this.httpMetrics = httpMetrics; + } + @Override public Future shutdown(Duration timeout) { CloseSequence seq; diff --git a/vertx-core/src/main/java/io/vertx/core/internal/http/HttpServerInternal.java b/vertx-core/src/main/java/io/vertx/core/internal/http/HttpServerInternal.java index 24830d1cad2..2b9cad603a0 100644 --- a/vertx-core/src/main/java/io/vertx/core/internal/http/HttpServerInternal.java +++ b/vertx-core/src/main/java/io/vertx/core/internal/http/HttpServerInternal.java @@ -14,6 +14,7 @@ import io.vertx.core.http.HttpServer; import io.vertx.core.internal.ContextInternal; import io.vertx.core.net.SocketAddress; +import io.vertx.core.spi.metrics.HttpServerMetrics; import io.vertx.core.spi.metrics.MetricsProvider; /** @@ -31,4 +32,6 @@ default HttpServerInternal unwrap() { return this; } + default void setMetrics(HttpServerMetrics httpMetrics) {} + } diff --git a/vertx-core/src/main/java/io/vertx/core/net/impl/tcp/NetServerImpl.java b/vertx-core/src/main/java/io/vertx/core/net/impl/tcp/NetServerImpl.java index d210433609e..6014c36ad12 100644 --- a/vertx-core/src/main/java/io/vertx/core/net/impl/tcp/NetServerImpl.java +++ b/vertx-core/src/main/java/io/vertx/core/net/impl/tcp/NetServerImpl.java @@ -531,11 +531,13 @@ private void bind( } }); } + var actualLocalAddress = localAddress; // Update port to actual port when it is not a domain socket as wildcard port 0 might have been used if (bindAddress.isInetSocket()) { actualPort = ((InetSocketAddress)ch.localAddress()).getPort(); + actualLocalAddress = SocketAddress.inetSocketAddress(actualPort, localAddress.host()); } - metrics = createMetrics(vertx.metrics(), localAddress); + metrics = createMetrics(vertx.metrics(), actualLocalAddress); promise.complete(ch); } else { promise.fail(res.cause()); diff --git a/vertx-core/src/test/java/io/vertx/tests/metrics/HttpMetricsPortTest.java b/vertx-core/src/test/java/io/vertx/tests/metrics/HttpMetricsPortTest.java new file mode 100644 index 00000000000..061bd98cecf --- /dev/null +++ b/vertx-core/src/test/java/io/vertx/tests/metrics/HttpMetricsPortTest.java @@ -0,0 +1,57 @@ +package io.vertx.tests.metrics; + +import io.vertx.core.Vertx; +import io.vertx.core.VertxOptions; +import io.vertx.core.http.HttpClientRequest; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.core.http.RequestOptions; +import io.vertx.core.metrics.MetricsOptions; +import io.vertx.test.core.TestUtils; +import io.vertx.test.fakemetrics.FakeHttpServerMetrics; +import io.vertx.test.fakemetrics.FakeMetricsBase; +import io.vertx.test.fakemetrics.FakeMetricsFactory; +import io.vertx.test.http.HttpTestBase; +import org.junit.Test; + +public class HttpMetricsPortTest extends HttpTestBase { + + @Override + protected HttpServerOptions createBaseServerOptions() { + return new HttpServerOptions().setPort(0).setHost(DEFAULT_HTTP_HOST); + } + + @Override + protected VertxOptions getOptions() { + VertxOptions options = super.getOptions(); + options.setMetricsOptions(new MetricsOptions().setEnabled(true)); + return options; + } + + @Override + protected Vertx createVertx(VertxOptions options) { + return Vertx.builder().with(options) + .withMetrics(new FakeMetricsFactory()) + .build(); + } + + @Test + public void actualPortInMetricsWhenDynamicPortIsUsed() throws Exception { + server.requestHandler(req -> { + FakeHttpServerMetrics metrics = FakeMetricsBase.httpMetricsOf(server); + + assertEquals(server.actualPort(), metrics.tcpLocalAddress().port()); + + req.response().end(); + testComplete(); + }); + + startServer(); + + var requestOptions = new RequestOptions() + .setPort(server.actualPort()); + + client.request(new RequestOptions(requestOptions).setURI(TestUtils.randomAlphaString(16))).onComplete(onSuccess(HttpClientRequest::send)); + await(); + } + +}