From 5062b6c74d16c7ba63c79ccf4a7efc959095ff4b Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Thu, 19 Aug 2021 11:52:51 +0200 Subject: [PATCH] Normalize the quarkus.http.root-path and RESTEasy deployment path at config level Make sure both paths always start and end with a '/' so that we don't have any risk of having logic working with '/test' and not with '/test/'. Also simplify a few things thanks to that. Fixes #19492 --- .../NormalizeRootHttpPathConverter.java | 40 +++++++++++++++++++ .../ResteasyServerCommonProcessor.java | 3 ++ .../ResteasyStandaloneBuildStep.java | 27 +++---------- .../deployment/HttpRootPathBuildItem.java | 27 +++++++++++++ .../http/deployment/VertxHttpProcessor.java | 4 +- .../http/runtime/HttpBuildTimeConfig.java | 5 ++- .../http/runtime/StaticResourcesRecorder.java | 4 +- 7 files changed, 86 insertions(+), 24 deletions(-) create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/configuration/NormalizeRootHttpPathConverter.java diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/NormalizeRootHttpPathConverter.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/NormalizeRootHttpPathConverter.java new file mode 100644 index 0000000000000..fcda229d2ec0c --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/NormalizeRootHttpPathConverter.java @@ -0,0 +1,40 @@ +package io.quarkus.runtime.configuration; + +import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; + +import javax.annotation.Priority; + +import org.eclipse.microprofile.config.spi.Converter; + +/** + * A converter to normalize paths that are considered root of something. + *

+ * Any path coming out of this converter will have a leading and ending '/'. + *

+ * Do NOT use this converter for paths that could be relative. + */ +@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) +public class NormalizeRootHttpPathConverter implements Converter { + + private static final String SLASH = "/"; + + @Override + public String convert(String value) throws IllegalArgumentException, NullPointerException { + if (value == null) { + return SLASH; + } + + value = value.trim(); + if (SLASH.equals(value)) { + return value; + } + if (!value.startsWith(SLASH)) { + value = SLASH + value; + } + if (!value.endsWith(SLASH)) { + value = value + SLASH; + } + + return value; + } +} diff --git a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java index ed472ecaf1eea..cf6a1ffebddd1 100755 --- a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java +++ b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerCommonProcessor.java @@ -77,6 +77,8 @@ import io.quarkus.resteasy.server.common.spi.AllowedJaxRsAnnotationPrefixBuildItem; import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.annotations.ConvertWith; +import io.quarkus.runtime.configuration.NormalizeRootHttpPathConverter; /** * Processor that builds the RESTEasy server configuration. @@ -138,6 +140,7 @@ static final class ResteasyConfig { * annotated application classes. */ @ConfigItem(defaultValue = "/") + @ConvertWith(NormalizeRootHttpPathConverter.class) String path; /** diff --git a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java index 3fca6ae74703f..8561b96d185db 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyStandaloneBuildStep.java @@ -23,9 +23,9 @@ import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentBuildItem; import io.quarkus.vertx.core.deployment.CoreVertxBuildItem; import io.quarkus.vertx.http.deployment.DefaultRouteBuildItem; +import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; -import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.http.runtime.VertxHttpRecorder; import io.vertx.core.Handler; @@ -50,32 +50,17 @@ public void staticInit(ResteasyStandaloneRecorder recorder, ResteasyDeploymentBuildItem deployment, ApplicationArchivesBuildItem applicationArchivesBuildItem, ResteasyInjectionReadyBuildItem resteasyInjectionReady, - HttpBuildTimeConfig httpConfig, + HttpRootPathBuildItem httpRootPathBuildItem, BuildProducer standalone) throws Exception { if (capabilities.isPresent(Capability.SERVLET)) { return; } - String deploymentRootPath = null; - // The context path + the resources path - String rootPath = httpConfig.rootPath; - if (deployment != null) { - deploymentRootPath = deployment.getRootPath(); - if (rootPath.endsWith("/")) { - if (deploymentRootPath.startsWith("/")) { - rootPath += deploymentRootPath.substring(1); - } else { - rootPath += deploymentRootPath; - } - } else if (!deploymentRootPath.equals("/")) { - if (!deploymentRootPath.startsWith("/")) { - rootPath += "/"; - } - rootPath += deploymentRootPath; - } - recorder.staticInit(deployment.getDeployment(), rootPath); - standalone.produce(new ResteasyStandaloneBuildItem(deploymentRootPath)); + // the deployment path is always relative to the HTTP root path + recorder.staticInit(deployment.getDeployment(), + httpRootPathBuildItem.relativePath(deployment.getRootPath())); + standalone.produce(new ResteasyStandaloneBuildItem(deployment.getRootPath())); } } diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItem.java index dbdc17f8fb5ec..11f0690f7b6f0 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItem.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItem.java @@ -66,6 +66,33 @@ public String resolvePath(String path) { return UriNormalizationUtil.normalizeWithBase(rootPath, path, false).getPath(); } + /** + * Resolve path that is always relative into an absolute path. + * Whether the path is relative or absolute, it will be resolved against `quarkus.http.root-path`, + * by removing the '/' in the latter case. + *

+ * Given {@literal quarkus.http.root-path=/} + *

+ * Given {@literal quarkus.http.root-path=/app} + * + *

+ * The returned path will not end with a slash. + * + * @param path Path to be resolved to an absolute path. + * @return An absolute path not ending with a slash + * @see UriNormalizationUtil#normalizeWithBase(URI, String, boolean) + */ + public String relativePath(String path) { + String relativePath = path.startsWith("/") ? path.substring(1) : path; + return UriNormalizationUtil.normalizeWithBase(rootPath, relativePath, false).getPath(); + } + public HttpRootPathBuildItem.Builder routeBuilder() { return new HttpRootPathBuildItem.Builder(this); } 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 f2b3ce5928e8a..a1ef76ed0c7c7 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 @@ -198,6 +198,7 @@ ServiceStartBuildItem finalizeRouter( LaunchModeBuildItem launchMode, List defaultRoutes, List filters, VertxWebRouterBuildItem httpRouteRouter, + HttpRootPathBuildItem httpRootPathBuildItem, NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration, List requireBodyHandlerBuildItems, @@ -256,7 +257,8 @@ ServiceStartBuildItem finalizeRouter( recorder.finalizeRouter(beanContainer.getValue(), defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null), - listOfFilters, vertx.getVertx(), lrc, mainRouter, httpRouteRouter.getHttpRouter(), httpBuildTimeConfig.rootPath, + listOfFilters, vertx.getVertx(), lrc, mainRouter, httpRouteRouter.getHttpRouter(), + httpRootPathBuildItem.getRootPath(), launchMode.getLaunchMode(), !requireBodyHandlerBuildItems.isEmpty(), bodyHandler, httpConfiguration, gracefulShutdownFilter, shutdownConfig, executorBuildItem.getExecutorProxy()); diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java index a0a8383110a2a..dbae58949e924 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpBuildTimeConfig.java @@ -5,6 +5,8 @@ import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.annotations.ConvertWith; +import io.quarkus.runtime.configuration.NormalizeRootHttpPathConverter; import io.vertx.core.http.ClientAuth; @ConfigRoot(name = "http", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) @@ -14,6 +16,7 @@ public class HttpBuildTimeConfig { * The HTTP root path. All web content will be served relative to this root path. */ @ConfigItem(defaultValue = "/") + @ConvertWith(NormalizeRootHttpPathConverter.class) public String rootPath; public AuthConfig auth; @@ -43,7 +46,7 @@ public class HttpBuildTimeConfig { * Non-application endpoints will be served from the specified path. * * `${quarkus.http.root-path}` -> Setting this path to the same value as HTTP root path disables * this root path. All extension-provided endpoints will be served from `${quarkus.http.root-path}`. - * + * * @asciidoclet */ @ConfigItem(defaultValue = "q") diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java index 9dbca79a5143c..5c9c0061e125e 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/StaticResourcesRecorder.java @@ -56,7 +56,9 @@ public Consumer start() { StaticHandler staticHandler = StaticHandler.create(META_INF_RESOURCES).setDefaultContentEncoding("UTF-8"); handlers.add(ctx -> { String rel = ctx.mountPoint() == null ? ctx.normalisedPath() - : ctx.normalisedPath().substring(ctx.mountPoint().length()); + : ctx.normalisedPath().substring( + // let's be extra careful here in case Vert.x normalizes the mount points at some point + ctx.mountPoint().endsWith("/") ? ctx.mountPoint().length() - 1 : ctx.mountPoint().length()); if (knownPaths.contains(rel)) { staticHandler.handle(ctx); } else {