Skip to content

Commit

Permalink
Normalize the quarkus.http.root-path and RESTEasy deployment path at …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
gsmet committed Aug 19, 2021
1 parent 0770982 commit 5062b6c
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -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.
* <p>
* Any path coming out of this converter will have a leading and ending '/'.
* <p>
* Do NOT use this converter for paths that could be relative.
*/
@Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY)
public class NormalizeRootHttpPathConverter implements Converter<String> {

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -138,6 +140,7 @@ static final class ResteasyConfig {
* annotated application classes.
*/
@ConfigItem(defaultValue = "/")
@ConvertWith(NormalizeRootHttpPathConverter.class)
String path;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -50,32 +50,17 @@ public void staticInit(ResteasyStandaloneRecorder recorder,
ResteasyDeploymentBuildItem deployment,
ApplicationArchivesBuildItem applicationArchivesBuildItem,
ResteasyInjectionReadyBuildItem resteasyInjectionReady,
HttpBuildTimeConfig httpConfig,
HttpRootPathBuildItem httpRootPathBuildItem,
BuildProducer<ResteasyStandaloneBuildItem> 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()));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <p>
* Given {@literal quarkus.http.root-path=/}
* <ul>
* <li>{@code resolvePath("foo")} will return {@literal /foo}</li>
* <li>{@code resolvePath("/foo")} will return {@literal /foo}</li>
* </ul>
* Given {@literal quarkus.http.root-path=/app}
* <ul>
* <li>{@code resolvePath("foo")} will return {@literal /app/foo}</li>
* <li>{@code resolvePath("/foo")} will return {@literal /app/foo}</li>
* </ul>
* <p>
* 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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ ServiceStartBuildItem finalizeRouter(
LaunchModeBuildItem launchMode,
List<DefaultRouteBuildItem> defaultRoutes, List<FilterBuildItem> filters,
VertxWebRouterBuildItem httpRouteRouter,
HttpRootPathBuildItem httpRootPathBuildItem,
NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration,
List<RequireBodyHandlerBuildItem> requireBodyHandlerBuildItems,
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand Down Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ public Consumer<Route> 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 {
Expand Down

0 comments on commit 5062b6c

Please sign in to comment.