diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 5f535780bfd48..ee81cea9987ba 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -18,6 +18,7 @@ import io.quarkus.arc.deployment.AdditionalBeanBuildItem; import io.quarkus.arc.deployment.BeanContainerBuildItem; import io.quarkus.arc.deployment.SyntheticBeanBuildItem; +import io.quarkus.arc.deployment.UnremovableBeanBuildItem; import io.quarkus.arc.processor.BuiltinScope; import io.quarkus.bootstrap.classloading.ClassPathElement; import io.quarkus.bootstrap.classloading.QuarkusClassLoader; @@ -46,6 +47,7 @@ import io.quarkus.runtime.shutdown.ShutdownConfig; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; import io.quarkus.vertx.core.deployment.EventLoopCountBuildItem; +import io.quarkus.vertx.http.HttpServerOptionsCustomizer; import io.quarkus.vertx.http.deployment.devmode.HttpRemoteDevClientProvider; import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem; import io.quarkus.vertx.http.deployment.spi.FrameworkEndpointsBuildItem; @@ -113,6 +115,11 @@ AdditionalBeanBuildItem additionalBeans() { .build(); } + @BuildStep + UnremovableBeanBuildItem shouldNotRemoveHttpServerOptionsCustomizers() { + return UnremovableBeanBuildItem.beanTypes(HttpServerOptionsCustomizer.class); + } + /** * Workaround for https://github.com/quarkusio/quarkus/issues/4720 by filtering Vertx multiple instance warning in dev * mode. diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/customizers/HttpServerOptionsCustomizerTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/customizers/HttpServerOptionsCustomizerTest.java new file mode 100644 index 0000000000000..f58df75f961e3 --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/customizers/HttpServerOptionsCustomizerTest.java @@ -0,0 +1,69 @@ +package io.quarkus.vertx.http.customizers; + +import java.util.concurrent.atomic.AtomicInteger; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.inject.Inject; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.vertx.http.HttpServerOptionsCustomizer; +import io.restassured.RestAssured; +import io.vertx.core.http.HttpServerOptions; +import io.vertx.mutiny.ext.web.Router; + +public class HttpServerOptionsCustomizerTest { + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .withApplicationRoot((jar) -> jar + .addClasses(MyBean.class, MyCustomizer.class)); + + @Inject + MyCustomizer customizer; + + @Test + void test() { + Assertions.assertThat(customizer.count()).isEqualTo(1); + Assertions.assertThat(RestAssured.get("http://localhost:9998").body().asString()).isEqualTo("hello"); + } + + @ApplicationScoped + public static class MyBean { + + public void init(@Observes Router router) { + router.get().handler(rc -> rc.endAndForget("hello")); + } + + } + + @ApplicationScoped + public static class MyCustomizer implements HttpServerOptionsCustomizer { + + AtomicInteger count = new AtomicInteger(); + + @Override + public void customizeHttpServer(HttpServerOptions options) { + count.incrementAndGet(); + options.setPort(9998); + } + + @Override + public void customizeHttpsServer(HttpServerOptions options) { + count.incrementAndGet(); + } + + @Override + public void customizeDomainSocketServer(HttpServerOptions options) { + count.incrementAndGet(); + } + + public int count() { + return count.get(); + } + } +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/HttpServerOptionsCustomizer.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/HttpServerOptionsCustomizer.java new file mode 100644 index 0000000000000..572d33bd03015 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/HttpServerOptionsCustomizer.java @@ -0,0 +1,37 @@ +package io.quarkus.vertx.http; + +import io.vertx.core.http.HttpServerOptions; + +/** + * Interface exposed by beans willing to customizing the HTTP server options. + *

+ * This interface is composed of three methods allowing the customization of the different servers: HTTP, HTTPS and + * domain socket. + *

+ * The passed {@link HttpServerOptions} must be customized in the body of the implementation. The default implementations + * are no-op. + */ +public interface HttpServerOptionsCustomizer { + + /** + * Allows customizing the HTTP server options. + */ + default void customizeHttpServer(HttpServerOptions options) { + // NO-OP + } + + /** + * Allows customizing the HTTPS server options. + */ + default void customizeHttpsServer(HttpServerOptions options) { + // NO-OP + } + + /** + * Allows customizing the server listening on a domain socket if any. + */ + default void customizeDomainSocketServer(HttpServerOptions options) { + // NO-OP + } + +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 00da9c567b72e..4d9de6a6bb077 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -47,6 +47,7 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.quarkus.arc.Arc; +import io.quarkus.arc.InjectableInstance; import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.bootstrap.runner.Timing; import io.quarkus.credentials.CredentialsProvider; @@ -69,6 +70,7 @@ import io.quarkus.runtime.util.ClassPathUtils; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; import io.quarkus.vertx.core.runtime.config.VertxConfiguration; +import io.quarkus.vertx.http.HttpServerOptionsCustomizer; import io.quarkus.vertx.http.runtime.HttpConfiguration.InsecureRequests; import io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler; import io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup; @@ -629,6 +631,7 @@ private void warnIfProxyAddressForwardingAllowedWithMultipleHeaders(HttpConfigur private static void doServerStart(Vertx vertx, HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration, LaunchMode launchMode, Supplier eventLoops, List websocketSubProtocols, boolean auxiliaryApplication) throws IOException { + // Http server configuration HttpServerOptions httpServerOptions = createHttpServerOptions(httpBuildTimeConfig, httpConfiguration, launchMode, websocketSubProtocols); @@ -637,6 +640,25 @@ private static void doServerStart(Vertx vertx, HttpBuildTimeConfig httpBuildTime HttpServerOptions sslConfig = createSslOptions(httpBuildTimeConfig, httpConfiguration, launchMode, websocketSubProtocols); + // Customize + if (Arc.container() != null) { + InjectableInstance instances = Arc.container() + .select(HttpServerOptionsCustomizer.class); + if (!instances.isUnsatisfied()) { + for (HttpServerOptionsCustomizer customizer : instances) { + if (httpServerOptions != null) { + customizer.customizeHttpServer(httpServerOptions); + } + if (sslConfig != null) { + customizer.customizeHttpsServer(sslConfig); + } + if (domainSocketOptions != null) { + customizer.customizeDomainSocketServer(domainSocketOptions); + } + } + } + } + if (httpConfiguration.insecureRequests != HttpConfiguration.InsecureRequests.ENABLED && sslConfig == null) { throw new IllegalStateException("Cannot set quarkus.http.redirect-insecure-requests without enabling SSL."); }