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

io.quarkiverse.web.bundler.runtime.WebBundlerResourceHandler Maven / Gradle / Ivy

package io.quarkiverse.web.bundler.runtime;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;

import org.jboss.logging.Logger;

import io.quarkus.runtime.util.StringUtil;
import io.quarkus.vertx.http.runtime.RouteConstants;
import io.vertx.core.Handler;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.impl.MimeMapping;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.FileSystemAccess;
import io.vertx.ext.web.handler.StaticHandler;

public class WebBundlerResourceHandler implements Handler {

    public static final Set HANDLED_METHODS = Set.of(HttpMethod.HEAD, HttpMethod.OPTIONS, HttpMethod.GET);
    private static final Logger LOG = Logger.getLogger(WebBundlerResourceHandler.class);

    public static final String META_INF_WEB = "META-INF/web";
    public static int DEFAULT_ROUTE_ORDER = RouteConstants.ROUTE_ORDER_BEFORE_DEFAULT + 50;

    private final WebBundlerHandlerConfig config;
    private final Set webResources;
    private final Handler handler;
    private final ClassLoader currentClassLoader;

    WebBundlerResourceHandler(final WebBundlerHandlerConfig config, final String directory, final Set webResources) {
        this.config = config;
        this.webResources = encodeUIResources(webResources);
        handler = createStaticHandler(config, directory);
        currentClassLoader = Thread.currentThread().getContextClassLoader();
    }

    private static Set encodeUIResources(Set uiResources) {
        Set encodedUIResources = new HashSet<>(uiResources.size());
        for (String uiResource : uiResources) {
            encodedUIResources.add(encodeURI(uiResource));
        }
        return encodedUIResources;
    }

    @Override
    public void handle(RoutingContext ctx) {
        if (!shouldHandleMethod(ctx)) {
            next(currentClassLoader, ctx);
            return;
        }
        final String path = resolvePath(ctx);
        final String resourcePath = path.endsWith("/") ? path + config.indexPage : path;
        LOG.debugf("Quinoa is checking: '%s'", resourcePath);
        if (webResources.contains(resourcePath)) {
            LOG.debugf("Quinoa is serving: '%s'", resourcePath);
            compressIfNeeded(config, ctx, resourcePath);
            handler.handle(ctx);
        } else {
            next(currentClassLoader, ctx);
        }
    }

    private static Handler createStaticHandler(WebBundlerHandlerConfig config, String directory) {
        LOG.debugf("Static Index: '%s'", config.indexPage);
        if (StringUtil.isNullOrEmpty(config.indexPage)) {
            throw new IllegalStateException("Static index page is not configured!");
        }
        final StaticHandler staticHandler = directory != null ? StaticHandler.create(FileSystemAccess.ROOT, directory)
                : StaticHandler.create(META_INF_WEB);
        staticHandler.setDefaultContentEncoding(StandardCharsets.UTF_8.name());
        staticHandler.setIndexPage(config.indexPage);
        staticHandler.setCachingEnabled(!config.devMode);
        return staticHandler;
    }

    /**
     * Duplicate code from OmniFaces project under apache license:
     * https://github.com/omnifaces/omnifaces/blob/develop/license.txt
     * 

* URI-encode the given string using UTF-8. URIs (paths and filenames) have different encoding rules as compared to * URL query string parameters. {@link URLEncoder} is actually only for www (HTML) form based query string parameter * values (as used when a webbrowser submits a HTML form). URI encoding has a lot in common with URL encoding, but * the space has to be %20 and some chars doesn't necessarily need to be encoded. * * @param string The string to be URI-encoded using UTF-8. * @return The given string, URI-encoded using UTF-8, or null if null was given. */ private static String encodeURI(String string) { if (string == null) { return null; } return URLEncoder.encode(string, StandardCharsets.UTF_8) .replace("+", "%20") .replace("%21", "!") .replace("%27", "'") .replace("%28", "(") .replace("%29", ")") .replace("%2F", "/") .replace("%7E", "~"); } static String resolvePath(RoutingContext ctx) { return (ctx.mountPoint() == null) ? ctx.normalizedPath() : ctx.normalizedPath().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()); } static void compressIfNeeded(WebBundlerHandlerConfig config, RoutingContext ctx, String path) { if (isCompressed(config, path)) { // VertxHttpRecorder is adding "Content-Encoding: identity" to all requests if // compression is enabled. // Handlers can remove the "Content-Encoding: identity" header to enable // compression. ctx.response().headers().remove(HttpHeaders.CONTENT_ENCODING); } } private static boolean isCompressed(WebBundlerHandlerConfig config, String path) { if (config.compressMediaTypes.isEmpty()) { return false; } String contentType = MimeMapping.getMimeTypeForFilename(path); return contentType != null && config.compressMediaTypes.contains(contentType); } static boolean shouldHandleMethod(RoutingContext ctx) { return HANDLED_METHODS.contains(ctx.request().method()); } static void next(ClassLoader cl, RoutingContext ctx) { // make sure we don't lose the correct TCCL to Vert.x... Thread.currentThread().setContextClassLoader(cl); ctx.next(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy