All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem Maven / Gradle / Ivy

package io.quarkus.vertx.http.deployment;

import java.net.URI;
import java.util.function.Consumer;
import java.util.function.Function;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.util.UriNormalizationUtil;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.quarkus.vertx.http.deployment.devmode.console.ConfiguredPathInfo;
import io.quarkus.vertx.http.runtime.HandlerType;
import io.quarkus.vertx.http.runtime.management.ManagementInterfaceBuildTimeConfig;
import io.vertx.core.Handler;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;

public final class NonApplicationRootPathBuildItem extends SimpleBuildItem {

    // TODO Should be handle the management root path?

    /**
     * Normalized of quarkus.http.root-path.
     * Must end in a slash
     */
    final URI httpRootPath;

    /**
     * Normalized from quarkus.http.non-application-root-path
     */
    final URI nonApplicationRootPath;

    /**
     * Normalized from quarkus.management.root-path
     */
    final URI managementRootPath;

    /**
     * Non-Application root path is distinct from HTTP root path.
     */
    final boolean dedicatedRouterRequired;

    final boolean attachedToMainRouter;

    public NonApplicationRootPathBuildItem(String httpRootPath, String nonApplicationRootPath, String managementRootPath) {
        // Presume value always starts with a slash and is normalized
        this.httpRootPath = UriNormalizationUtil.toURI(httpRootPath, true);

        this.nonApplicationRootPath = UriNormalizationUtil.normalizeWithBase(this.httpRootPath, nonApplicationRootPath,
                true);
        this.managementRootPath = managementRootPath == null ? null
                : UriNormalizationUtil.toURI("/" + managementRootPath, true);

        this.dedicatedRouterRequired = !this.nonApplicationRootPath.getPath().equals(this.httpRootPath.getPath());

        // Is the non-application root path underneath the http root path. Do we add non-application root to main router or not.
        this.attachedToMainRouter = this.nonApplicationRootPath.getPath().startsWith(this.httpRootPath.getPath());
    }

    /**
     * Is a dedicated router required for non-application endpoints.
     *
     * @return boolean
     */
    public boolean isDedicatedRouterRequired() {
        return dedicatedRouterRequired;
    }

    public boolean isAttachedToMainRouter() {
        return attachedToMainRouter;
    }

    /**
     * Path to the Non-application root for use with Vert.x Routers,
     * has a leading slash.
     * 

* If it's under the HTTP Root, return a path relative to HTTP Root. * Otherwise, return an absolute path. * * @return String Path suitable for use with Vert.x Router. It has a leading slash */ String getVertxRouterPath() { if (attachedToMainRouter) { return "/" + UriNormalizationUtil.relativize(httpRootPath.getPath(), nonApplicationRootPath.getPath()); } else { return getNonApplicationRootPath(); } } public String getNormalizedHttpRootPath() { return httpRootPath.getPath(); } /** * Return normalized root path configured from {@literal quarkus.http.root-path} * and {quarkus.http.non-application-root-path}. * This path will always end in a slash. *

* Use {@link #resolvePath(String)} if you need to construct a URI for * a non-application endpoint. * * @return Normalized non-application root path ending with a slash * @see #resolvePath(String) */ public String getNonApplicationRootPath() { return nonApplicationRootPath.getPath(); } /** * @return the normalized root path for the mangement endpoints. {@code getNonApplicationRootPath()} if the * management interface is disabled. */ public String getManagementRootPath() { if (managementRootPath != null) { return managementRootPath.getPath(); } else { return getNonApplicationRootPath(); } } /** * Resolve path into an absolute path. * If path is relative, it will be resolved against `quarkus.http.non-application-root-path`. * An absolute path will be normalized and returned. *

* Given {@literal quarkus.http.root-path=/} and * {@literal quarkus.http.non-application-root-path="q"} *

    *
  • {@code resolvePath("foo")} will return {@literal /q/foo}
  • *
  • {@code resolvePath("/foo")} will return {@literal /foo}
  • *
*

* Given {@literal quarkus.http.root-path=/} and * {@literal quarkus.http.non-application-root-path="/q"} *

    *
  • {@code resolvePath("foo")} will return {@literal /q/foo}
  • *
  • {@code resolvePath("/foo")} will return {@literal /foo}
  • *
* Given {@literal quarkus.http.root-path=/app} and * {@literal quarkus.http.non-application-root-path="q"} *
    *
  • {@code resolvePath("foo")} will return {@literal /app/q/foo}
  • *
  • {@code resolvePath("/foo")} will return {@literal /foo}
  • *
* Given {@literal quarkus.http.root-path=/app} and * {@literal quarkus.http.non-application-root-path="/q"} *
    *
  • {@code resolvePath("foo")} will return {@literal /q/foo}
  • *
  • {@code resolvePath("/foo")} will return {@literal /foo}
  • *
*

* 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 * @throws IllegalArgumentException if path is null or empty * @see UriNormalizationUtil#normalizeWithBase(URI, String, boolean) */ public String resolvePath(String path) { if (path == null || path.trim().isEmpty()) { throw new IllegalArgumentException("Specified path can not be empty"); } return UriNormalizationUtil.normalizeWithBase(nonApplicationRootPath, path, false).getPath(); } public String resolveManagementPath(String path, ManagementInterfaceBuildTimeConfig managementInterfaceBuildTimeConfig, LaunchModeBuildItem mode) { if (path == null || path.trim().isEmpty()) { throw new IllegalArgumentException("Specified path can not be empty"); } if (!managementInterfaceBuildTimeConfig.enabled) { if (managementRootPath != null) { return UriNormalizationUtil.normalizeWithBase(managementRootPath, path, false).getPath(); } return UriNormalizationUtil.normalizeWithBase(nonApplicationRootPath, path, false).getPath(); } else { // Best effort String prefix = getManagementUrlPrefix(mode); if (managementRootPath != null) { return prefix + UriNormalizationUtil.normalizeWithBase(managementRootPath, path, false).getPath(); } else { return prefix + path; } } } /** * Best effort to deduce the URL prefix (scheme, host, port) of the management interface. * * @param mode the mode, influencing the default port * @return the prefix */ public static String getManagementUrlPrefix(LaunchModeBuildItem mode) { Config config = ConfigProvider.getConfig(); var managementHost = config.getOptionalValue("quarkus.management.host", String.class).orElse("0.0.0.0"); var managementPort = config.getOptionalValue("quarkus.management.port", Integer.class).orElse(9000); if (mode.isTest()) { managementPort = config.getOptionalValue("quarkus.management.test-port", Integer.class).orElse(9001); } var isHttps = isTLsConfigured(config); return (isHttps ? "https://" : "http://") + managementHost + ":" + managementPort; } /** * Resolve a base path and a sub-resource against the non-application root. * This will call resolvePath on the base path (to establish a fully-resolved, * absolute path), and then will resolve the subRoute against that resolved path. * This allows a configured subpath to be configured (consistently) * as an absolute URI. *

* Given {@literal quarkus.http.root-path=/} and * {@literal quarkus.http.non-application-root-path="q"} *

    *
  • {@code resolveNestedPath("foo", "a")} will return {@literal /q/foo/a}
  • *
  • {@code resolveNestedPath("foo", "/a)} will return {@literal /a}
  • *
*

* 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 * @throws IllegalArgumentException if path is null or empty * @see UriNormalizationUtil#normalizeWithBase(URI, String, boolean) * @see #resolvePath(String) */ public String resolveNestedPath(String path, String subRoute) { if (path == null || path.trim().isEmpty()) { throw new IllegalArgumentException("Specified path can not be empty"); } URI base = UriNormalizationUtil.normalizeWithBase(nonApplicationRootPath, path, true); return UriNormalizationUtil.normalizeWithBase(base, subRoute, false).getPath(); } public String resolveManagementNestedPath(String path, String subRoute) { if (path == null || path.trim().isEmpty()) { throw new IllegalArgumentException("Specified path can not be empty"); } URI base; if (managementRootPath != null) { base = UriNormalizationUtil.normalizeWithBase(managementRootPath, path, true); } else { base = UriNormalizationUtil.normalizeWithBase(nonApplicationRootPath, path, true); } return UriNormalizationUtil.normalizeWithBase(base, subRoute, false).getPath(); } public Builder routeBuilder() { return new Builder(this); } /** * Per non-application endpoint instance. */ public static class Builder extends RouteBuildItem.Builder { private final NonApplicationRootPathBuildItem buildItem; private RouteBuildItem.RouteType routeType = RouteBuildItem.RouteType.FRAMEWORK_ROUTE; private RouteBuildItem.RouteType routerType = RouteBuildItem.RouteType.FRAMEWORK_ROUTE; private String path; Builder(NonApplicationRootPathBuildItem buildItem) { this.buildItem = buildItem; } @Override public Builder routeFunction(Function routeFunction) { throw new RuntimeException( "This method is not supported using this builder. Use #routeFunction(String, Consumer)"); } public Builder routeFunction(String route, Consumer routeFunction) { if (isManagement && this.buildItem.managementRootPath != null) { // The logic is slightly different when the management interface is enabled, as we have a single // router mounted at the root. if (route.startsWith("/")) { this.path = route; } else { this.path = buildItem.getManagementRootPath() + route; } this.routerType = RouteBuildItem.RouteType.ABSOLUTE_ROUTE; super.routeFunction(this.path, routeFunction); return this; } route = super.absolutePath = buildItem.resolvePath(route); boolean isFrameworkRoute = buildItem.dedicatedRouterRequired && route.startsWith(buildItem.getNonApplicationRootPath()); if (isFrameworkRoute) { // relative non-application root (leading slash for vert.x) this.path = "/" + UriNormalizationUtil.relativize(buildItem.getNonApplicationRootPath(), route); this.routerType = RouteBuildItem.RouteType.FRAMEWORK_ROUTE; } else if (route.startsWith(buildItem.httpRootPath.getPath())) { // relative to http root (leading slash for vert.x route) this.path = "/" + UriNormalizationUtil.relativize(buildItem.httpRootPath.getPath(), route); this.routerType = RouteBuildItem.RouteType.APPLICATION_ROUTE; } else if (route.startsWith("/")) { // absolute path this.path = route; this.routerType = RouteBuildItem.RouteType.ABSOLUTE_ROUTE; } super.routeFunction(this.path, routeFunction); return this; } @Override public Builder route(String route) { routeFunction(route, null); return this; } public Builder nestedRoute(String baseRoute, String subRoute) { if (subRoute.startsWith("/")) { routeFunction(subRoute, null); return this; } baseRoute = baseRoute.endsWith("/") ? baseRoute : baseRoute + "/"; routeFunction(baseRoute + subRoute, null); return this; } @Override public Builder handler(Handler handler) { super.handler(handler); return this; } @Override public Builder handlerType(HandlerType handlerType) { super.handlerType(handlerType); return this; } @Override public Builder blockingRoute() { super.blockingRoute(); return this; } @Override public Builder failureRoute() { super.failureRoute(); return this; } @Override public Builder displayOnNotFoundPage() { super.displayOnNotFoundPage(); return this; } @Override public Builder displayOnNotFoundPage(String notFoundPageTitle) { super.displayOnNotFoundPage(notFoundPageTitle); return this; } @Override public Builder routeConfigKey(String attributeName) { super.routeConfigKey(attributeName); return this; } @Override public RouteBuildItem build() { return new RouteBuildItem(this, routeType, routerType, isManagement); } @Override protected ConfiguredPathInfo getRouteConfigInfo() { return super.getRouteConfigInfo(); } @Override protected NotFoundPageDisplayableEndpointBuildItem getNotFoundEndpoint() { if (!displayOnNotFoundPage) { return null; } if (isManagement && buildItem.managementRootPath != null) { return null; // Exposed on the management interface, so not exposed. } if (notFoundPagePath == null) { throw new RuntimeException("Cannot display " + routeFunction + " on not found page as no explicit path was specified and a route function is in use"); } if (absolutePath != null) { return new NotFoundPageDisplayableEndpointBuildItem(absolutePath, notFoundPageTitle, true); } return new NotFoundPageDisplayableEndpointBuildItem(notFoundPagePath, notFoundPageTitle, false); } @Override public Builder management() { super.management(); return this; } } /** * Best effort to check if the management interface is using {@code https}. * * @param config the config * @return {@code true} if the management interface configuration contains a key or a certificate (indicating TLS) */ private static boolean isTLsConfigured(Config config) { var hasCert = config.getOptionalValue("quarkus.management.ssl.certificate.file", String.class).isPresent(); var hasKey = config.getOptionalValue("quarkus.management.ssl.certificate.key-file", String.class).isPresent(); var hasKeys = config.getOptionalValue("quarkus.management.ssl.certificate.key-files", String.class).isPresent(); var hasCerts = config.getOptionalValue("quarkus.management.ssl.certificate.files", String.class).isPresent(); var hasProvider = config.getOptionalValue("quarkus.management.ssl.certificate.credential-provider", String.class) .isPresent(); var hasProviderName = config .getOptionalValue("quarkus.management.ssl.certificate.credential-provider-name", String.class).isPresent(); var hasKeyStore = config.getOptionalValue("quarkus.management.ssl.certificate.key-store-file", String.class) .isPresent(); return hasCerts || hasKeys || hasCert || hasKey || hasProvider || hasProviderName || hasKeyStore; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy