From 762137ff8a3316657ef2bce0bc040b811ee54cc9 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 23 Aug 2023 22:32:19 +0200 Subject: [PATCH] Test case demonstrating how to bypass HTTP body size limit Also removes the duplicate addition of the body-size-limit enforcer. --- .../vertx/http/runtime/VertxHttpRecorder.java | 1 - .../java/io/quarkus/it/vertx/UploadRoute.java | 78 +++++++++++++++++++ .../it/vertx/UploadUnlimitedTestCase.java | 66 ++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/UploadRoute.java create mode 100644 integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/UploadUnlimitedTestCase.java 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 77bfd4b69b5ed..b6ab04b678024 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 @@ -555,7 +555,6 @@ public void handle(RoutingContext event) { } HttpServerCommonHandlers.applyHeaders(managementConfiguration.getValue().header(), mr); - HttpServerCommonHandlers.enforceMaxBodySize(managementConfiguration.getValue().limits(), mr); applyCompression(managementBuildTimeConfig.enableCompression(), mr); Handler handler = HttpServerCommonHandlers.enforceDuplicatedContext(mr); diff --git a/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/UploadRoute.java b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/UploadRoute.java new file mode 100644 index 0000000000000..0a06dddc9c87d --- /dev/null +++ b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/UploadRoute.java @@ -0,0 +1,78 @@ +package io.quarkus.it.vertx; + +import java.util.concurrent.atomic.AtomicLong; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; + +import io.quarkus.runtime.StartupEvent; +import io.quarkus.vertx.http.runtime.ServerLimitsConfig; +import io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers; +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.HttpVersion; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +@ApplicationScoped +public class UploadRoute { + + /** + * Installs two POST-routes - one that bypasses the body-length limit using {@code order(-3)} + * ({@link HttpServerCommonHandlers#enforceMaxBodySize(ServerLimitsConfig, Router)} uses {@code order(-2)}) and one that + * does not bypass body-size enforcement. + */ + void installRoute(@Observes StartupEvent startupEvent, Router router) { + router.post("/unlimited-upload").order(-3).handler(UploadHandler::newRequest); + router.post("/limited-upload").handler(UploadHandler::newRequest); + } + + static class UploadHandler { + final HttpServerResponse resp; + + final AtomicLong total = new AtomicLong(); + + UploadHandler(HttpServerResponse resp) { + this.resp = resp; + } + + void end(Void x) { + resp.setStatusCode(200) + .setStatusMessage("OK") + .putHeader("Content-Type", "text/plain") + .end("Got " + total); + } + + void onData(Buffer buffer) { + total.addAndGet(buffer.length()); + } + + void onException(Throwable exception) { + resp.setStatusCode(500) + .setStatusMessage("Internal Server Error") + .end("Failed to process request."); + } + + static void newRequest(RoutingContext routingContext) { + HttpServerRequest req = routingContext.request(); + HttpServerResponse resp = routingContext.response(); + + String expectValue = req.getHeader(HttpHeaders.EXPECT); + if (expectValue != null) { + if (!"100-continue".equals(expectValue)) { + routingContext.fail(417); + } + if (req.version() != HttpVersion.HTTP_1_0) { + resp.writeContinue(); + } + } + + UploadHandler uploadHandler = new UploadHandler(resp); + req.handler(uploadHandler::onData) + .endHandler(uploadHandler::end) + .exceptionHandler(uploadHandler::onException); + } + } +} diff --git a/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/UploadUnlimitedTestCase.java b/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/UploadUnlimitedTestCase.java new file mode 100644 index 0000000000000..63ce48b41cce5 --- /dev/null +++ b/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/UploadUnlimitedTestCase.java @@ -0,0 +1,66 @@ +package io.quarkus.it.vertx; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import io.quarkus.test.common.http.TestHTTPResource; +import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.junit.QuarkusTestProfile; +import io.quarkus.test.junit.TestProfile; + +@QuarkusTest +@TestProfile(UploadUnlimitedTestCase.UploadLimitProfile.class) +public class UploadUnlimitedTestCase { + + /** See {@link io.quarkus.it.vertx.UploadRoute}. */ + @TestHTTPResource(value = "/unlimited-upload") + URL unlimitedUrl; + + /** See {@link io.quarkus.it.vertx.UploadRoute}. */ + @TestHTTPResource(value = "/limited-upload") + URL limitedUrl; + + /** No {@code quarkus.http.limits.max-body-size} via route-config. */ + @Test + public void uploadBypassBodySizeLimit() throws Exception { + HttpURLConnection urlConn = (HttpURLConnection) unlimitedUrl.openConnection(); + byte[] justData = new byte[8192]; + urlConn.setRequestMethod("POST"); + urlConn.setDoOutput(true); + urlConn.setRequestProperty("Content-Type", "multipart/form-data"); + urlConn.setRequestProperty("Content-Length", String.valueOf(justData.length)); + try (OutputStream output = urlConn.getOutputStream()) { + output.write(justData); + } + assertEquals(200, urlConn.getResponseCode(), urlConn.getResponseCode() + ": " + urlConn.getResponseMessage()); + } + + /** Respects {@code quarkus.http.limits.max-body-size}. */ + @Test + public void uploadSizeLimitedByConfig() throws Exception { + HttpURLConnection urlConn = (HttpURLConnection) limitedUrl.openConnection(); + byte[] justData = new byte[8192]; + urlConn.setRequestMethod("POST"); + urlConn.setDoOutput(true); + urlConn.setRequestProperty("Content-Length", String.valueOf(justData.length)); + try (OutputStream output = urlConn.getOutputStream()) { + output.write(justData); + } + assertEquals(413, urlConn.getResponseCode(), urlConn.getResponseCode() + ": " + urlConn.getResponseMessage()); + } + + public static class UploadLimitProfile implements QuarkusTestProfile { + + @Override + public Map getConfigOverrides() { + return Map.of( + "quarkus.http.limits.max-body-size", "1K"); + } + } +}