diff --git a/bom/runtime/pom.xml b/bom/runtime/pom.xml index 32eff33ff677c9..211a464ecf3321 100644 --- a/bom/runtime/pom.xml +++ b/bom/runtime/pom.xml @@ -24,7 +24,7 @@ 0.2.0 0.0.12 0.34.0 - 3.0.0.Final + 3.0.1.Final 1.0.0.Final 1.3 1.0.1 diff --git a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java index f9775d62682b08..3dd6edb748e88f 100644 --- a/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java +++ b/extensions/keycloak-authorization/deployment/src/main/java/io/quarkus/keycloak/pep/deployment/KeycloakPolicyEnforcerBuildStep.java @@ -13,6 +13,7 @@ import io.quarkus.oidc.OIDCException; import io.quarkus.oidc.runtime.OidcBuildTimeConfig; import io.quarkus.oidc.runtime.OidcConfig; +import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem; public class KeycloakPolicyEnforcerBuildStep { @@ -21,6 +22,12 @@ FeatureBuildItem featureBuildItem() { return new FeatureBuildItem(FeatureBuildItem.KEYCLOAK_AUTHORIZATION); } + @BuildStep + RequireBodyHandlerBuildItem requireBody() { + //TODO: this should only be produced if required, ask Pedro about config for this + return new RequireBodyHandlerBuildItem(); + } + @BuildStep public AdditionalBeanBuildItem beans(KeycloakPolicyEnforcerConfig config) { if (config.policyEnforcer.enable) { diff --git a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java index 825d0993b1306e..eabdb995a42a38 100644 --- a/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java +++ b/extensions/keycloak-authorization/runtime/src/main/java/io/quarkus/keycloak/pep/runtime/VertxHttpFacade.java @@ -1,6 +1,5 @@ package io.quarkus.keycloak.pep.runtime; -import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; @@ -11,8 +10,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.security.cert.X509Certificate; -import io.quarkus.vertx.http.runtime.VertxInputStream; -import io.vertx.core.http.HttpHeaders; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.OIDCHttpFacade; import org.keycloak.adapters.spi.AuthenticationError; @@ -25,8 +22,10 @@ import io.quarkus.oidc.AccessTokenCredential; import io.quarkus.security.credential.TokenCredential; import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.vertx.http.runtime.VertxInputStream; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.buffer.Buffer; +import io.vertx.core.http.HttpHeaders; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.core.http.impl.CookieImpl; @@ -133,15 +132,13 @@ public InputStream getInputStream() { @Override public InputStream getInputStream(boolean buffered) { try { - if (routingContext.get("quarkus.request.inputstream") != null) { - return routingContext.get("quarkus.request.inputstream"); + if (routingContext.getBody() != null) { + return new ByteArrayInputStream(routingContext.getBody().getBytes()); } - - BufferedInputStream stream = new BufferedInputStream(new VertxInputStream(request)); - - routingContext.put("quarkus.request.inputstream", stream); - - return stream; + if (routingContext.request().isEnded()) { + return new ByteArrayInputStream(new byte[0]); + } + return new VertxInputStream(request); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java index fb81b825c1ba09..ccd63565410d40 100644 --- a/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java +++ b/extensions/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/standalone/VertxRequestHandler.java @@ -1,5 +1,6 @@ package io.quarkus.resteasy.runtime.standalone; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -7,7 +8,6 @@ import javax.enterprise.inject.spi.CDI; import javax.ws.rs.core.SecurityContext; -import io.quarkus.vertx.http.runtime.VertxInputStream; import org.jboss.logging.Logger; import org.jboss.resteasy.core.ResteasyContext; import org.jboss.resteasy.core.SynchronousDispatcher; @@ -20,6 +20,7 @@ import io.quarkus.arc.runtime.BeanContainer; import io.quarkus.security.identity.CurrentIdentityAssociation; import io.quarkus.vertx.http.runtime.CurrentVertxRequest; +import io.quarkus.vertx.http.runtime.VertxInputStream; import io.quarkus.vertx.http.runtime.security.QuarkusHttpUser; import io.vertx.core.Context; import io.vertx.core.Handler; @@ -64,9 +65,8 @@ public void handle(RoutingContext request) { // otherwise request handlers may not get set up before request ends InputStream is; try { - if (request.get("quarkus.request.inputstream") != null) { - is = request.get("quarkus.request.inputstream"); - is.mark(0); + if (request.getBody() != null) { + is = new ByteArrayInputStream(request.getBody().getBytes()); } else { is = new VertxInputStream(request.request()); } 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 f9e7394440a1bf..d66f8d71f3e487 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 @@ -353,7 +353,8 @@ public void run() { return new Handler() { @Override public void handle(RoutingContext event) { - VertxHttpExchange exchange = new VertxHttpExchange(event.request(), allocator, executorService, event); + VertxHttpExchange exchange = new VertxHttpExchange(event.request(), allocator, executorService, event, + event.getBody()); Optional maxBodySize = httpConfiguration.limits.maxBodySize; if (maxBodySize.isPresent()) { exchange.setMaxEntitySize(maxBodySize.get().asLongValue()); diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java new file mode 100644 index 00000000000000..c5743fcd303850 --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/BodyHandlerBuildItem.java @@ -0,0 +1,17 @@ +package io.quarkus.vertx.http.deployment; + +import io.quarkus.builder.item.SimpleBuildItem; +import io.vertx.core.Handler; +import io.vertx.ext.web.RoutingContext; + +public final class BodyHandlerBuildItem extends SimpleBuildItem { + private final Handler handler; + + public BodyHandlerBuildItem(Handler handler) { + this.handler = handler; + } + + public Handler getHandler() { + return handler; + } +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java new file mode 100644 index 00000000000000..dbfd8541499a3d --- /dev/null +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/RequireBodyHandlerBuildItem.java @@ -0,0 +1,10 @@ +package io.quarkus.vertx.http.deployment; + +import io.quarkus.builder.item.MultiBuildItem; + +/** + * This is a marker that indicates that the body handler should be installed + * on all routes, as an extension requires the request to be fully buffered. + */ +public final class RequireBodyHandlerBuildItem extends MultiBuildItem { +} diff --git a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java index 2dcebfca9208d3..344f6a18cfd907 100644 --- a/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java +++ b/extensions/vertx-http/deployment/src/main/java/io/quarkus/vertx/http/deployment/VertxHttpProcessor.java @@ -35,8 +35,10 @@ import io.quarkus.vertx.http.runtime.VertxHttpRecorder; import io.quarkus.vertx.http.runtime.cors.CORSRecorder; import io.quarkus.vertx.http.runtime.filters.Filter; +import io.vertx.core.Handler; import io.vertx.core.impl.VertxImpl; import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; class VertxHttpProcessor { @@ -97,6 +99,12 @@ VertxWebRouterBuildItem initializeRouter(VertxHttpRecorder recorder, return new VertxWebRouterBuildItem(router); } + @BuildStep + @Record(ExecutionTime.RUNTIME_INIT) + BodyHandlerBuildItem bodyHandler(VertxHttpRecorder recorder, HttpConfiguration httpConfiguration) { + return new BodyHandlerBuildItem(recorder.createBodyHandler(httpConfiguration)); + } + @BuildStep @Record(ExecutionTime.RUNTIME_INIT) ServiceStartBuildItem finalizeRouter( @@ -106,7 +114,9 @@ ServiceStartBuildItem finalizeRouter( List defaultRoutes, List filters, VertxWebRouterBuildItem router, EventLoopCountBuildItem eventLoopCount, HttpBuildTimeConfig httpBuildTimeConfig, HttpConfiguration httpConfiguration, - BuildProducer reflectiveClass, List websocketSubProtocols) + BuildProducer reflectiveClass, List websocketSubProtocols, + List requireBodyHandlerBuildItems, + BodyHandlerBuildItem bodyHandlerBuildItem) throws BuildException, IOException { Optional defaultRoute; if (defaultRoutes == null || defaultRoutes.isEmpty()) { @@ -124,9 +134,13 @@ ServiceStartBuildItem finalizeRouter( .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 bodyHandler = requireBodyHandlerBuildItems.isEmpty() ? bodyHandlerBuildItem.getHandler() : null; + recorder.finalizeRouter(beanContainer.getValue(), defaultRoute.map(DefaultRouteBuildItem::getRoute).orElse(null), - listOfFilters, vertx.getVertx(), router.getRouter(), httpBuildTimeConfig.rootPath, launchMode.getLaunchMode()); + listOfFilters, vertx.getVertx(), router.getRouter(), httpBuildTimeConfig.rootPath, launchMode.getLaunchMode(), + !requireBodyHandlerBuildItems.isEmpty(), bodyHandler); boolean startVirtual = requireVirtual.isPresent() || httpBuildTimeConfig.virtual; if (startVirtual) { diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 4b7b13dfbc37aa..69568666ea191e 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -35,6 +35,7 @@ import io.quarkus.runtime.Timing; import io.quarkus.runtime.annotations.Recorder; import io.quarkus.runtime.configuration.ConfigInstantiator; +import io.quarkus.runtime.configuration.MemorySize; import io.quarkus.vertx.core.runtime.VertxCoreRecorder; import io.quarkus.vertx.core.runtime.config.VertxConfiguration; import io.quarkus.vertx.http.runtime.filters.Filter; @@ -61,6 +62,7 @@ import io.vertx.ext.web.Route; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; +import io.vertx.ext.web.handler.BodyHandler; @Recorder public class VertxHttpRecorder { @@ -169,7 +171,8 @@ public void startServer(RuntimeValue vertxRuntimeValue, ShutdownContext s public void finalizeRouter(BeanContainer container, Consumer defaultRouteHandler, List filterList, RuntimeValue vertx, - RuntimeValue runtimeValue, String rootPath, LaunchMode launchMode) { + RuntimeValue runtimeValue, String rootPath, LaunchMode launchMode, boolean requireBodyHandler, + Handler bodyHandler) { // install the default route at the end Router router = runtimeValue.getValue(); @@ -200,6 +203,18 @@ public void finalizeRouter(BeanContainer container, Consumer defaultRoute container.instance(RouterProducer.class).initialize(resumingRouter); router.route().last().failureHandler(new QuarkusErrorHandler(launchMode.isDevOrTest())); + if (requireBodyHandler) { + //if this is set then everything needs the body handler installed + //TODO: config etc + router.route().order(Integer.MIN_VALUE).handler(new Handler() { + @Override + public void handle(RoutingContext routingContext) { + routingContext.request().resume(); + bodyHandler.handle(routingContext); + } + }); + } + if (rootPath.equals("/")) { if (hotReplacementHandler != null) { router.route().order(-1).handler(hotReplacementHandler); @@ -626,4 +641,24 @@ public static Handler getRootHandler() { return ACTUAL_ROOT; } + public Handler createBodyHandler(HttpConfiguration httpConfiguration) { + BodyHandler bodyHandler = BodyHandler.create(); + Optional maxBodySize = httpConfiguration.limits.maxBodySize; + if (maxBodySize.isPresent()) { + bodyHandler.setBodyLimit(maxBodySize.get().asLongValue()); + } + final BodyConfig bodyConfig = httpConfiguration.body; + bodyHandler.setHandleFileUploads(bodyConfig.handleFileUploads); + bodyHandler.setUploadsDirectory(bodyConfig.uploadsDirectory); + bodyHandler.setDeleteUploadedFilesOnEnd(bodyConfig.deleteUploadedFilesOnEnd); + bodyHandler.setMergeFormAttributes(bodyConfig.mergeFormAttributes); + bodyHandler.setPreallocateBodyBuffer(bodyConfig.preallocateBodyBuffer); + return new Handler() { + @Override + public void handle(RoutingContext event) { + event.request().resume(); + bodyHandler.handle(event); + } + }; + } } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java index 00cd2a23063863..357f77de923aac 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxInputStream.java @@ -81,7 +81,7 @@ public int available() throws IOException { if (finished) { return -1; } - + return exchange.readBytesAvailable(); } @@ -201,13 +201,13 @@ public int readBytesAvailable() { if (input1 != null) { return input1.getByteBuf().readableBytes(); } - + String length = request.getHeader(HttpHeaders.CONTENT_LENGTH); - + if (length == null) { return 0; } - + return Integer.parseInt(length); } } diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java index 715dbafcde91bb..d8e3e968eef7ba 100644 --- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java +++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/BodyHandlerBuildItem.java @@ -4,6 +4,10 @@ import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; +/** + * use {@link io.quarkus.vertx.http.deployment.BodyHandlerBuildItem} instead + */ +@Deprecated public final class BodyHandlerBuildItem extends SimpleBuildItem { private final Handler handler; diff --git a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java index 8b98af3df949c8..ed69c72f5731eb 100644 --- a/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java +++ b/extensions/vertx-web/deployment/src/main/java/io/quarkus/vertx/web/deployment/VertxWebProcessor.java @@ -48,9 +48,9 @@ import io.quarkus.gizmo.MethodDescriptor; import io.quarkus.gizmo.ResultHandle; import io.quarkus.vertx.http.deployment.FilterBuildItem; +import io.quarkus.vertx.http.deployment.RequireBodyHandlerBuildItem; import io.quarkus.vertx.http.deployment.RouteBuildItem; import io.quarkus.vertx.http.runtime.HandlerType; -import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.web.Route; import io.quarkus.vertx.web.RouteBase; import io.quarkus.vertx.web.RouteFilter; @@ -149,9 +149,8 @@ void validateBeanDeployment( } @BuildStep - @Record(ExecutionTime.RUNTIME_INIT) - BodyHandlerBuildItem bodyHandler(VertxWebRecorder recorder, HttpConfiguration httpConfiguration) { - return new BodyHandlerBuildItem(recorder.createBodyHandler(httpConfiguration)); + BodyHandlerBuildItem bodyHandler(io.quarkus.vertx.http.deployment.BodyHandlerBuildItem realOne) { + return new BodyHandlerBuildItem(realOne.getHandler()); } @BuildStep @@ -163,9 +162,10 @@ void addAdditionalRoutes( BuildProducer generatedClass, AnnotationProxyBuildItem annotationProxy, BuildProducer reflectiveClasses, - BodyHandlerBuildItem bodyHandler, + io.quarkus.vertx.http.deployment.BodyHandlerBuildItem bodyHandler, BuildProducer routeProducer, - BuildProducer filterProducer) throws IOException { + BuildProducer filterProducer, + List bodyHandlerRequired) throws IOException { ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClass, true); diff --git a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java index ffc08969aa4fb1..e7b3051717b6b5 100644 --- a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java +++ b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/VertxWebRecorder.java @@ -1,20 +1,15 @@ package io.quarkus.vertx.web.runtime; import java.lang.reflect.InvocationTargetException; -import java.util.Optional; import java.util.function.Function; import io.quarkus.runtime.annotations.Recorder; -import io.quarkus.runtime.configuration.MemorySize; -import io.quarkus.vertx.http.runtime.BodyConfig; -import io.quarkus.vertx.http.runtime.HttpConfiguration; import io.quarkus.vertx.http.runtime.RouterProducer; import io.quarkus.vertx.web.Route; import io.vertx.core.Handler; import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; -import io.vertx.ext.web.handler.BodyHandler; @Recorder public class VertxWebRecorder { @@ -66,33 +61,14 @@ public io.vertx.ext.web.Route apply(Router router) { route.consumes(consumes); } } - route.handler(bodyHandler); + if (bodyHandler != null) { + route.handler(bodyHandler); + } return route; } }; } - public Handler createBodyHandler(HttpConfiguration httpConfiguration) { - BodyHandler bodyHandler = BodyHandler.create(); - Optional maxBodySize = httpConfiguration.limits.maxBodySize; - if (maxBodySize.isPresent()) { - bodyHandler.setBodyLimit(maxBodySize.get().asLongValue()); - } - final BodyConfig bodyConfig = httpConfiguration.body; - bodyHandler.setHandleFileUploads(bodyConfig.handleFileUploads); - bodyHandler.setUploadsDirectory(bodyConfig.uploadsDirectory); - bodyHandler.setDeleteUploadedFilesOnEnd(bodyConfig.deleteUploadedFilesOnEnd); - bodyHandler.setMergeFormAttributes(bodyConfig.mergeFormAttributes); - bodyHandler.setPreallocateBodyBuffer(bodyConfig.preallocateBodyBuffer); - return new Handler() { - @Override - public void handle(RoutingContext event) { - event.request().resume(); - bodyHandler.handle(event); - } - }; - } - private String ensureStartWithSlash(String path) { if (path.startsWith("/")) { return path;