Search in sources :

Example 1 with GracefulShutdownFilter

use of io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter in project quarkus by quarkusio.

the class VertxHttpProcessor method finalizeRouter.

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
ServiceStartBuildItem finalizeRouter(VertxHttpRecorder recorder, BeanContainerBuildItem beanContainer, CoreVertxBuildItem vertx, LaunchModeBuildItem launchMode, List<DefaultRouteBuildItem> defaultRoutes, List<FilterBuildItem> filters, VertxWebRouterBuildItem httpRouteRouter, HttpRootPathBuildItem httpRootPathBuildItem, NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem, HttpBuildTimeConfig httpBuildTimeConfig, List<RequireBodyHandlerBuildItem> requireBodyHandlerBuildItems, BodyHandlerBuildItem bodyHandlerBuildItem, BuildProducer<ShutdownListenerBuildItem> shutdownListenerBuildItemBuildProducer, ShutdownConfig shutdownConfig, LiveReloadConfig lrc, // Injected to be sure that Vert.x has been produced before calling this method.
CoreVertxBuildItem core, ExecutorBuildItem executorBuildItem) throws BuildException, IOException {
    Optional<DefaultRouteBuildItem> defaultRoute;
    if (defaultRoutes == null || defaultRoutes.isEmpty()) {
        defaultRoute = Optional.empty();
    } else {
        if (defaultRoutes.size() > 1) {
            // this should never happen
            throw new BuildException("Too many default routes.", Collections.emptyList());
        } else {
            defaultRoute = Optional.of(defaultRoutes.get(0));
        }
    }
    HttpRemoteDevClientProvider.liveReloadConfig = lrc;
    GracefulShutdownFilter gracefulShutdownFilter = recorder.createGracefulShutdownHandler();
    shutdownListenerBuildItemBuildProducer.produce(new ShutdownListenerBuildItem(gracefulShutdownFilter));
    List<Filter> listOfFilters = filters.stream().filter(f -> f.getHandler() != null).map(FilterBuildItem::toFilter).collect(Collectors.toList());
    // if the body handler is required then we know it is installed for all routes, so we don't need to register it here
    Handler<RoutingContext> bodyHandler = !requireBodyHandlerBuildItems.isEmpty() ? bodyHandlerBuildItem.getHandler() : null;
    Optional<RuntimeValue<Router>> mainRouter = httpRouteRouter.getMainRouter() != null ? Optional.of(httpRouteRouter.getMainRouter()) : Optional.empty();
    if (httpRouteRouter.getFrameworkRouter() != null) {
        if (nonApplicationRootPathBuildItem.isAttachedToMainRouter()) {
            // Mount nested framework router
            recorder.mountFrameworkRouter(httpRouteRouter.getHttpRouter(), httpRouteRouter.getFrameworkRouter(), nonApplicationRootPathBuildItem.getVertxRouterPath());
        } else {
            // Create main router, not mounted under application router
            if (!mainRouter.isPresent()) {
                mainRouter = Optional.of(recorder.initializeRouter(vertx.getVertx()));
            }
            // Mount independent framework router under new main router
            recorder.mountFrameworkRouter(mainRouter.get(), httpRouteRouter.getFrameworkRouter(), nonApplicationRootPathBuildItem.getVertxRouterPath());
        }
    }
    recorder.finalizeRouter(beanContainer.getValue(), defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null), listOfFilters, vertx.getVertx(), lrc, mainRouter, httpRouteRouter.getHttpRouter(), httpRouteRouter.getMutinyRouter(), httpRootPathBuildItem.getRootPath(), launchMode.getLaunchMode(), !requireBodyHandlerBuildItems.isEmpty(), bodyHandler, gracefulShutdownFilter, shutdownConfig, executorBuildItem.getExecutorProxy());
    return new ServiceStartBuildItem("vertx-http");
}
Also used : RoutingContext(io.vertx.ext.web.RoutingContext) ServiceStartBuildItem(io.quarkus.deployment.builditem.ServiceStartBuildItem) GracefulShutdownFilter(io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter) Filter(io.quarkus.vertx.http.runtime.filters.Filter) ShutdownListenerBuildItem(io.quarkus.deployment.builditem.ShutdownListenerBuildItem) GracefulShutdownFilter(io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter) RuntimeValue(io.quarkus.runtime.RuntimeValue) BuildException(io.quarkus.builder.BuildException) BuildStep(io.quarkus.deployment.annotations.BuildStep) Record(io.quarkus.deployment.annotations.Record)

Example 2 with GracefulShutdownFilter

use of io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter in project quarkus by quarkusio.

the class VertxHttpRecorder method finalizeRouter.

public void finalizeRouter(BeanContainer container, Consumer<Route> defaultRouteHandler, List<Filter> filterList, Supplier<Vertx> vertx, LiveReloadConfig liveReloadConfig, Optional<RuntimeValue<Router>> mainRouterRuntimeValue, RuntimeValue<Router> httpRouterRuntimeValue, RuntimeValue<io.vertx.mutiny.ext.web.Router> mutinyRouter, String rootPath, LaunchMode launchMode, boolean requireBodyHandler, Handler<RoutingContext> bodyHandler, GracefulShutdownFilter gracefulShutdownFilter, ShutdownConfig shutdownConfig, Executor executor) {
    HttpConfiguration httpConfiguration = this.httpConfiguration.getValue();
    // install the default route at the end
    Router httpRouteRouter = httpRouterRuntimeValue.getValue();
    // allow the router to be modified programmatically
    Event<Object> event = Arc.container().beanManager().getEvent();
    // First, fire an event with the filter collector
    Filters filters = new Filters();
    event.select(Filters.class).fire(filters);
    filterList.addAll(filters.getFilters());
    // Then, fire the resuming router
    event.select(Router.class).fire(httpRouteRouter);
    // Also fires the Mutiny one
    event.select(io.vertx.mutiny.ext.web.Router.class).fire(mutinyRouter.getValue());
    for (Filter filter : filterList) {
        if (filter.getHandler() != null) {
            // Filters with high priority gets called first.
            httpRouteRouter.route().order(-1 * filter.getPriority()).handler(filter.getHandler());
        }
    }
    if (defaultRouteHandler != null) {
        defaultRouteHandler.accept(httpRouteRouter.route().order(DEFAULT_ROUTE_ORDER));
    }
    if (httpBuildTimeConfig.enableCompression) {
        httpRouteRouter.route().order(0).handler(new Handler<RoutingContext>() {

            @Override
            public void handle(RoutingContext ctx) {
                // Add "Content-Encoding: identity" header that disables the compression
                // This header can be removed to enable the compression
                ctx.response().putHeader(HttpHeaders.CONTENT_ENCODING, HttpHeaders.IDENTITY);
                ctx.next();
            }
        });
    }
    httpRouteRouter.route().last().failureHandler(new QuarkusErrorHandler(launchMode.isDevOrTest(), httpConfiguration.unhandledErrorContentTypeDefault));
    if (requireBodyHandler) {
        // if this is set then everything needs the body handler installed
        // TODO: config etc
        httpRouteRouter.route().order(Integer.MIN_VALUE + 1).handler(new Handler<RoutingContext>() {

            @Override
            public void handle(RoutingContext routingContext) {
                routingContext.request().resume();
                bodyHandler.handle(routingContext);
            }
        });
    }
    if (httpConfiguration.limits.maxBodySize.isPresent()) {
        long limit = httpConfiguration.limits.maxBodySize.get().asLongValue();
        Long limitObj = limit;
        httpRouteRouter.route().order(-2).handler(new Handler<RoutingContext>() {

            @Override
            public void handle(RoutingContext event) {
                String lengthString = event.request().headers().get(HttpHeaderNames.CONTENT_LENGTH);
                if (lengthString != null) {
                    long length = Long.parseLong(lengthString);
                    if (length > limit) {
                        event.response().headers().add(HttpHeaderNames.CONNECTION, "close");
                        event.response().setStatusCode(HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE.code());
                        event.response().endHandler(new Handler<Void>() {

                            @Override
                            public void handle(Void e) {
                                event.request().connection().close();
                            }
                        });
                        event.response().end();
                        return;
                    }
                } else {
                    event.put(MAX_REQUEST_SIZE_KEY, limitObj);
                }
                event.next();
            }
        });
    }
    // Filter Configuration per path
    var filtersInConfig = httpConfiguration.filter;
    if (!filtersInConfig.isEmpty()) {
        for (var entry : filtersInConfig.entrySet()) {
            var filterConfig = entry.getValue();
            var matches = filterConfig.matches;
            var order = filterConfig.order.orElse(Integer.MIN_VALUE);
            var methods = filterConfig.methods;
            var headers = filterConfig.header;
            if (methods.isEmpty()) {
                httpRouteRouter.routeWithRegex(matches).order(order).handler(new Handler<RoutingContext>() {

                    @Override
                    public void handle(RoutingContext event) {
                        event.response().headers().setAll(headers);
                        event.next();
                    }
                });
            } else {
                for (var method : methods.get()) {
                    httpRouteRouter.routeWithRegex(HttpMethod.valueOf(method.toUpperCase(Locale.ROOT)), matches).order(order).handler(new Handler<RoutingContext>() {

                        @Override
                        public void handle(RoutingContext event) {
                            event.response().headers().setAll(headers);
                            event.next();
                        }
                    });
                }
            }
        }
    }
    // Headers sent on any request, regardless of the response
    Map<String, HeaderConfig> headers = httpConfiguration.header;
    if (!headers.isEmpty()) {
        // Creates a handler for each header entry
        for (Map.Entry<String, HeaderConfig> entry : headers.entrySet()) {
            var name = entry.getKey();
            var config = entry.getValue();
            if (config.methods.isEmpty()) {
                httpRouteRouter.route(config.path).order(Integer.MIN_VALUE).handler(new Handler<RoutingContext>() {

                    @Override
                    public void handle(RoutingContext event) {
                        event.response().headers().set(name, config.value);
                        event.next();
                    }
                });
            } else {
                for (String method : config.methods.get()) {
                    httpRouteRouter.route(HttpMethod.valueOf(method.toUpperCase(Locale.ROOT)), config.path).order(Integer.MIN_VALUE).handler(new Handler<RoutingContext>() {

                        @Override
                        public void handle(RoutingContext event) {
                            event.response().headers().add(name, config.value);
                            event.next();
                        }
                    });
                }
            }
        }
    }
    Handler<HttpServerRequest> root;
    if (rootPath.equals("/")) {
        if (hotReplacementHandler != null) {
            // recorders are always executed in the current CL
            ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
            httpRouteRouter.route().order(Integer.MIN_VALUE).handler(new Handler<RoutingContext>() {

                @Override
                public void handle(RoutingContext event) {
                    Thread.currentThread().setContextClassLoader(currentCl);
                    hotReplacementHandler.handle(event);
                }
            });
        }
        root = httpRouteRouter;
    } else {
        Router mainRouter = mainRouterRuntimeValue.isPresent() ? mainRouterRuntimeValue.get().getValue() : Router.router(vertx.get());
        mainRouter.mountSubRouter(rootPath, httpRouteRouter);
        if (hotReplacementHandler != null) {
            ClassLoader currentCl = Thread.currentThread().getContextClassLoader();
            mainRouter.route().order(Integer.MIN_VALUE).handler(new Handler<RoutingContext>() {

                @Override
                public void handle(RoutingContext event) {
                    Thread.currentThread().setContextClassLoader(currentCl);
                    hotReplacementHandler.handle(event);
                }
            });
        }
        root = mainRouter;
    }
    warnIfDeprecatedHttpConfigPropertiesPresent(httpConfiguration);
    warnIfProxyAddressForwardingAllowedWithMultipleHeaders(httpConfiguration);
    ForwardingProxyOptions forwardingProxyOptions = ForwardingProxyOptions.from(httpConfiguration);
    if (forwardingProxyOptions.proxyAddressForwarding) {
        Handler<HttpServerRequest> delegate = root;
        root = new Handler<HttpServerRequest>() {

            @Override
            public void handle(HttpServerRequest event) {
                delegate.handle(new ForwardedServerRequestWrapper(event, forwardingProxyOptions));
            }
        };
    }
    boolean quarkusWrapperNeeded = false;
    if (shutdownConfig.isShutdownTimeoutSet()) {
        gracefulShutdownFilter.next(root);
        root = gracefulShutdownFilter;
        quarkusWrapperNeeded = true;
    }
    AccessLogConfig accessLog = httpConfiguration.accessLog;
    if (accessLog.enabled) {
        AccessLogReceiver receiver;
        if (accessLog.logToFile) {
            File outputDir = accessLog.logDirectory.isPresent() ? new File(accessLog.logDirectory.get()) : new File("");
            receiver = new DefaultAccessLogReceiver(executor, outputDir, accessLog.baseFileName, accessLog.logSuffix, accessLog.rotate);
        } else {
            receiver = new JBossLoggingAccessLogReceiver(accessLog.category);
        }
        AccessLogHandler handler = new AccessLogHandler(receiver, accessLog.pattern, getClass().getClassLoader(), accessLog.excludePattern);
        httpRouteRouter.route().order(Integer.MIN_VALUE).handler(handler);
        quarkusWrapperNeeded = true;
    }
    BiConsumer<Cookie, HttpServerRequest> cookieFunction = null;
    if (!httpConfiguration.sameSiteCookie.isEmpty()) {
        cookieFunction = processSameSiteConfig(httpConfiguration.sameSiteCookie);
        quarkusWrapperNeeded = true;
    }
    BiConsumer<Cookie, HttpServerRequest> cookieConsumer = cookieFunction;
    if (quarkusWrapperNeeded) {
        Handler<HttpServerRequest> old = root;
        root = new Handler<HttpServerRequest>() {

            @Override
            public void handle(HttpServerRequest event) {
                old.handle(new QuarkusRequestWrapper(event, cookieConsumer));
            }
        };
    }
    Handler<HttpServerRequest> delegate = root;
    root = new Handler<HttpServerRequest>() {

        @Override
        public void handle(HttpServerRequest event) {
            if (!VertxContext.isOnDuplicatedContext()) {
                // Vert.x should call us on a duplicated context.
                // But in the case of pipelined requests, it does not.
                // See https://github.com/quarkusio/quarkus/issues/24626.
                Context context = VertxContext.createNewDuplicatedContext();
                context.runOnContext(new Handler<Void>() {

                    @Override
                    public void handle(Void x) {
                        setCurrentContextSafe(true);
                        delegate.handle(new ResumingRequestWrapper(event));
                    }
                });
            } else {
                setCurrentContextSafe(true);
                delegate.handle(new ResumingRequestWrapper(event));
            }
        }
    };
    if (httpConfiguration.recordRequestStartTime) {
        httpRouteRouter.route().order(Integer.MIN_VALUE).handler(new Handler<RoutingContext>() {

            @Override
            public void handle(RoutingContext event) {
                event.put(REQUEST_START_TIME, System.nanoTime());
                event.next();
            }
        });
    }
    if (launchMode == LaunchMode.DEVELOPMENT && liveReloadConfig.password.isPresent() && hotReplacementContext.getDevModeType() == DevModeType.REMOTE_SERVER_SIDE) {
        root = remoteSyncHandler = new RemoteSyncHandler(liveReloadConfig.password.get(), root, hotReplacementContext);
    }
    rootHandler = root;
}
Also used : QuarkusRequestWrapper(io.quarkus.vertx.http.runtime.filters.QuarkusRequestWrapper) DefaultAccessLogReceiver(io.quarkus.vertx.http.runtime.filters.accesslog.DefaultAccessLogReceiver) Filters(io.quarkus.vertx.http.runtime.filters.Filters) RoutingContext(io.vertx.ext.web.RoutingContext) DefaultAccessLogReceiver(io.quarkus.vertx.http.runtime.filters.accesslog.DefaultAccessLogReceiver) AccessLogReceiver(io.quarkus.vertx.http.runtime.filters.accesslog.AccessLogReceiver) JBossLoggingAccessLogReceiver(io.quarkus.vertx.http.runtime.filters.accesslog.JBossLoggingAccessLogReceiver) ShutdownContext(io.quarkus.runtime.ShutdownContext) EventLoopContext(io.vertx.core.impl.EventLoopContext) RoutingContext(io.vertx.ext.web.RoutingContext) Context(io.vertx.core.Context) VertxContext(io.smallrye.common.vertx.VertxContext) HotReplacementContext(io.quarkus.dev.spi.HotReplacementContext) Cookie(io.vertx.core.http.Cookie) ShutdownContext(io.quarkus.runtime.ShutdownContext) EventLoopContext(io.vertx.core.impl.EventLoopContext) RoutingContext(io.vertx.ext.web.RoutingContext) Context(io.vertx.core.Context) VertxContext(io.smallrye.common.vertx.VertxContext) HotReplacementContext(io.quarkus.dev.spi.HotReplacementContext) HttpServerRequest(io.vertx.core.http.HttpServerRequest) Router(io.vertx.ext.web.Router) AccessLogHandler(io.quarkus.vertx.http.runtime.filters.accesslog.AccessLogHandler) BodyHandler(io.vertx.ext.web.handler.BodyHandler) VertxHandler(io.vertx.core.net.impl.VertxHandler) RemoteSyncHandler(io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler) Handler(io.vertx.core.Handler) JBossLoggingAccessLogReceiver(io.quarkus.vertx.http.runtime.filters.accesslog.JBossLoggingAccessLogReceiver) AccessLogHandler(io.quarkus.vertx.http.runtime.filters.accesslog.AccessLogHandler) RemoteSyncHandler(io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler) GracefulShutdownFilter(io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter) Filter(io.quarkus.vertx.http.runtime.filters.Filter) Map(java.util.Map) TreeMap(java.util.TreeMap) HashMap(java.util.HashMap) File(java.io.File)

Aggregations

Filter (io.quarkus.vertx.http.runtime.filters.Filter)2 GracefulShutdownFilter (io.quarkus.vertx.http.runtime.filters.GracefulShutdownFilter)2 RoutingContext (io.vertx.ext.web.RoutingContext)2 BuildException (io.quarkus.builder.BuildException)1 BuildStep (io.quarkus.deployment.annotations.BuildStep)1 Record (io.quarkus.deployment.annotations.Record)1 ServiceStartBuildItem (io.quarkus.deployment.builditem.ServiceStartBuildItem)1 ShutdownListenerBuildItem (io.quarkus.deployment.builditem.ShutdownListenerBuildItem)1 HotReplacementContext (io.quarkus.dev.spi.HotReplacementContext)1 RuntimeValue (io.quarkus.runtime.RuntimeValue)1 ShutdownContext (io.quarkus.runtime.ShutdownContext)1 RemoteSyncHandler (io.quarkus.vertx.http.runtime.devmode.RemoteSyncHandler)1 Filters (io.quarkus.vertx.http.runtime.filters.Filters)1 QuarkusRequestWrapper (io.quarkus.vertx.http.runtime.filters.QuarkusRequestWrapper)1 AccessLogHandler (io.quarkus.vertx.http.runtime.filters.accesslog.AccessLogHandler)1 AccessLogReceiver (io.quarkus.vertx.http.runtime.filters.accesslog.AccessLogReceiver)1 DefaultAccessLogReceiver (io.quarkus.vertx.http.runtime.filters.accesslog.DefaultAccessLogReceiver)1 JBossLoggingAccessLogReceiver (io.quarkus.vertx.http.runtime.filters.accesslog.JBossLoggingAccessLogReceiver)1 VertxContext (io.smallrye.common.vertx.VertxContext)1 Context (io.vertx.core.Context)1