From c234c9a04393568c1dc71325bfed048c7a621701 Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Fri, 20 Aug 2021 00:24:22 +0200 Subject: [PATCH] Additional work on path normalization --- .../deployment/util/UriNormalizationUtil.java | 8 +++- .../ResteasyDeploymentBuildItem.java | 4 +- .../ResteasyServerConfigBuildItem.java | 2 +- .../deployment/ResteasyServletProcessor.java | 5 +-- .../undertow/deployment/ServletConfig.java | 37 +++++++++++++++++++ .../deployment/UndertowBuildStep.java | 10 ++--- .../runtime/UndertowDeploymentRecorder.java | 13 +------ .../deployment/HttpRootPathBuildItemTest.java | 2 +- 8 files changed, 55 insertions(+), 26 deletions(-) diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/UriNormalizationUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/UriNormalizationUtil.java index 3a629bf968b169..77ef04d84404f7 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/util/UriNormalizationUtil.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/UriNormalizationUtil.java @@ -91,14 +91,18 @@ public static URI toURI(String path, boolean trailingSlash) { * * * @param base URI to resolve relative paths. Use {@link #toURI(String, boolean)} to construct this parameter. - * + * * @param segment Relative or absolute path * @param trailingSlash true if resulting URI must end with a '/' * @throws IllegalArgumentException if the path contains invalid characters or path segments. */ public static URI normalizeWithBase(URI base, String segment, boolean trailingSlash) { if (segment == null || segment.trim().isEmpty()) { - return base; + if ("/".equals(base.getPath())) { + return base; + } + // otherwise, make sure trailingSlash is honored + return toURI(base.getPath(), trailingSlash); } URI segmentUri = toURI(segment, trailingSlash); URI resolvedUri = base.resolve(segmentUri); diff --git a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java index ad88ad4e845966..86b7141d67d4c2 100644 --- a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java +++ b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyDeploymentBuildItem.java @@ -5,12 +5,12 @@ import io.quarkus.builder.item.SimpleBuildItem; public final class ResteasyDeploymentBuildItem extends SimpleBuildItem { - private ResteasyDeployment deployment; private String rootPath; + private ResteasyDeployment deployment; public ResteasyDeploymentBuildItem(String rootPath, ResteasyDeployment deployment) { + this.rootPath = rootPath.startsWith("/") ? rootPath : "/" + rootPath; this.deployment = deployment; - this.rootPath = rootPath; } public ResteasyDeployment getDeployment() { diff --git a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java index 7705fd62b6b532..a092917849ee6e 100644 --- a/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java +++ b/extensions/resteasy-classic/resteasy-server-common/deployment/src/main/java/io/quarkus/resteasy/server/common/deployment/ResteasyServerConfigBuildItem.java @@ -21,7 +21,7 @@ public final class ResteasyServerConfigBuildItem extends SimpleBuildItem { */ public ResteasyServerConfigBuildItem(String rootPath, String path, Map initParameters) { this.rootPath = rootPath; - this.path = path; + this.path = path.startsWith("/") ? path : "/" + path; this.initParameters = initParameters; } diff --git a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java index 8a2f3435ef55e1..1bac1984897650 100644 --- a/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java +++ b/extensions/resteasy-classic/resteasy/deployment/src/main/java/io/quarkus/resteasy/deployment/ResteasyServletProcessor.java @@ -53,9 +53,8 @@ public void jaxrsConfig( BuildProducer resteasyJaxrsConfig, HttpRootPathBuildItem httpRootPathBuildItem) { if (resteasyServerConfig.isPresent()) { - String rp = resteasyServerConfig.get().getRootPath(); - String rootPath = httpRootPathBuildItem.resolvePath(rp.startsWith("/") ? rp.substring(1) : rp); - String defaultPath = httpRootPathBuildItem.resolvePath(resteasyServerConfig.get().getPath()); + String rootPath = httpRootPathBuildItem.relativePath(resteasyServerConfig.get().getRootPath()); + String defaultPath = resteasyServerConfig.get().getPath(); deprecatedResteasyJaxrsConfig.produce(new ResteasyJaxrsConfigBuildItem(defaultPath)); resteasyJaxrsConfig diff --git a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/ServletConfig.java b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/ServletConfig.java index b14400aaff5a7d..a152a8aea284df 100644 --- a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/ServletConfig.java +++ b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/ServletConfig.java @@ -1,10 +1,17 @@ package io.quarkus.undertow.deployment; +import static io.quarkus.runtime.configuration.ConverterSupport.DEFAULT_QUARKUS_CONVERTER_PRIORITY; + import java.util.Optional; +import javax.annotation.Priority; + +import org.eclipse.microprofile.config.spi.Converter; + import io.quarkus.runtime.annotations.ConfigItem; import io.quarkus.runtime.annotations.ConfigPhase; import io.quarkus.runtime.annotations.ConfigRoot; +import io.quarkus.runtime.annotations.ConvertWith; @ConfigRoot(phase = ConfigPhase.BUILD_TIME) public class ServletConfig { @@ -17,6 +24,7 @@ public class ServletConfig { * is /bar and the http root is /foo then the actual Servlet path will be /foo/bar. */ @ConfigItem + @ConvertWith(ContextPathConverter.class) Optional contextPath; /** @@ -25,4 +33,33 @@ public class ServletConfig { @ConfigItem(defaultValue = "UTF-8") public String defaultCharset; + /** + * This converter adds a '/' at the beginning of the context path but does not add one at the end, given we want to support + * binding to a context without an ending '/'. + *

+ * See ContextPathTestCase for an example. + */ + @Priority(DEFAULT_QUARKUS_CONVERTER_PRIORITY) + public static class ContextPathConverter 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; + } + + return value; + } + } + } diff --git a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java index 633596dc8b88a1..fa8f71c8f185f5 100644 --- a/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java +++ b/extensions/undertow/deployment/src/main/java/io/quarkus/undertow/deployment/UndertowBuildStep.java @@ -116,6 +116,7 @@ import io.quarkus.undertow.runtime.UndertowDeploymentRecorder; import io.quarkus.undertow.runtime.UndertowHandlersConfServletExtension; import io.quarkus.vertx.http.deployment.DefaultRouteBuildItem; +import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig; import io.quarkus.vertx.http.runtime.HttpConfiguration; @@ -319,11 +320,7 @@ public ServletContextPathBuildItem contextPath( WebMetadataBuildItem webMetadataBuildItem) { String contextPath; if (servletConfig.contextPath.isPresent()) { - if (!servletConfig.contextPath.get().startsWith("/")) { - contextPath = "/" + servletConfig.contextPath; - } else { - contextPath = servletConfig.contextPath.get(); - } + contextPath = servletConfig.contextPath.get(); } else if (webMetadataBuildItem.getWebMetaData().getDefaultContextPath() != null) { contextPath = webMetadataBuildItem.getWebMetaData().getDefaultContextPath(); } else { @@ -351,6 +348,7 @@ public ServletDeploymentManagerBuildItem build(List servlets, ShutdownContextBuildItem shutdownContext, KnownPathsBuildItem knownPaths, HttpBuildTimeConfig httpBuildTimeConfig, + HttpRootPathBuildItem httpRootPath, ServletConfig servletConfig) throws Exception { ObjectSubstitutionBuildItem.Holder holder = new ObjectSubstitutionBuildItem.Holder(ServletSecurityInfo.class, @@ -365,7 +363,7 @@ public ServletDeploymentManagerBuildItem build(List servlets, String contextPath = servletContextPathBuildItem.getServletContextPath(); RuntimeValue deployment = recorder.createDeployment("test", knownPaths.knownFiles, knownPaths.knownDirectories, - launchMode.getLaunchMode(), shutdownContext, contextPath, httpBuildTimeConfig.rootPath, + launchMode.getLaunchMode(), shutdownContext, httpRootPath.relativePath(contextPath), servletConfig.defaultCharset, webMetaData.getRequestCharacterEncoding(), webMetaData.getResponseCharacterEncoding(), httpBuildTimeConfig.auth.proactive, webMetaData.getWelcomeFileList() != null ? webMetaData.getWelcomeFileList().getWelcomeFiles() : null); diff --git a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java index aa6532a2f53f3b..730b858eea0931 100644 --- a/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java +++ b/extensions/undertow/runtime/src/main/java/io/quarkus/undertow/runtime/UndertowDeploymentRecorder.java @@ -156,18 +156,9 @@ public static void setHotDeploymentResources(List resources) { } public RuntimeValue createDeployment(String name, Set knownFile, Set knownDirectories, - LaunchMode launchMode, ShutdownContext context, String contextPath, String httpRootPath, String defaultCharset, + LaunchMode launchMode, ShutdownContext context, String mountPoint, String defaultCharset, String requestCharacterEncoding, String responseCharacterEncoding, boolean proactiveAuth, List welcomeFiles) { - String realMountPoint; - if (contextPath.equals("/")) { - realMountPoint = httpRootPath; - } else if (httpRootPath.equals("/")) { - realMountPoint = contextPath; - } else { - realMountPoint = httpRootPath + contextPath; - } - DeploymentInfo d = new DeploymentInfo(); d.setDefaultRequestEncoding(requestCharacterEncoding); d.setDefaultResponseEncoding(responseCharacterEncoding); @@ -175,7 +166,7 @@ public RuntimeValue createDeployment(String name, Set kn d.setSessionIdGenerator(new QuarkusSessionIdGenerator()); d.setClassLoader(getClass().getClassLoader()); d.setDeploymentName(name); - d.setContextPath(realMountPoint); + d.setContextPath(mountPoint); d.setEagerFilterInit(true); ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItemTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItemTest.java index 456675376bf6b3..1e98718b35c87a 100644 --- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItemTest.java +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/deployment/HttpRootPathBuildItemTest.java @@ -18,7 +18,7 @@ void testResolvePathWithSlash() { void testResolvePathWithSlashApp() { HttpRootPathBuildItem buildItem = new HttpRootPathBuildItem("/app"); - Assertions.assertEquals("/app/", buildItem.resolvePath("")); + Assertions.assertEquals("/app", buildItem.resolvePath("")); Assertions.assertEquals("/app/foo", buildItem.resolvePath("foo")); Assertions.assertEquals("/foo", buildItem.resolvePath("/foo")); }