Skip to content

Commit

Permalink
Test case demonstrating unlimited uploads not working
Browse files Browse the repository at this point in the history
  • Loading branch information
snazy committed Aug 23, 2023
1 parent ca83189 commit ab3ecce
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
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.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 UnlimitedUploadRoute {

/**
* Installs the VertX-Web route to implement non-buffering file uploads.
*
* <p>
* This seems to be the <em>only</em> way to bypass Quarkus/Vert.X-Web's body-handler, which
* would materialize the whole upload in memory.
*
* <p>
* Downside: It is <em>still</em> necessary to set {@code
* quarkus.http.limits.max-body-size=8T}, which is bad (TL;DR DoS attacks). Quarkus adds two
* body-size checks:
*
* <ul>
* <li>{@code HttpServerCommonHandlers.enforceMaxBodySize()} via {@code
* VertxHttpRecorder.finalizeRouter()} and
* <li>{@code BodyHandlerImpl.handle()} via {@code VertxHttpRecorder.configureAndGetBody()}.
* </ul>
*
* The latter is skipped for the route installed in this function, but the first one still kicks
* in. See <a
* href=
* "https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/HTTP.2F413.20req.20entitiy.20too.20large/near/382569720">Quarkus
* Zulip discussion</a>.
*/
void installRoute(@Observes StartupEvent startupEvent, Router router) {
router.post("/unlimited-upload").handler(UploadHandler::newRequest);
}

static class UploadHandler {
final HttpServerRequest req;
final HttpServerResponse resp;

final AtomicLong total = new AtomicLong();

UploadHandler(HttpServerRequest req, HttpServerResponse resp) {
this.req = req;
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(req, resp);
req.handler(uploadHandler::onData)
.endHandler(uploadHandler::end)
.exceptionHandler(uploadHandler::onException);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,6 @@ quarkus.http.filter.just-order.matches=/filter/order
quarkus.http.filter.any-order.order=11
quarkus.http.filter.any-order.header."Cache-Control"=max-age=1
quarkus.http.filter.any-order.matches=/filter/order.*

# For UnlimitedUploadTestCase, value must be smaller than the amount of data sent by the test.
quarkus.http.limits.max-body-size=1k
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
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 org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class UnlimitedUploadTestCase {
@TestHTTPResource(value = "/unlimited-upload")
URL url;

@Test
public void hugeUpload() throws Exception {
HttpURLConnection urlConn = (HttpURLConnection) url.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()) {
// Just a lot of data, nothing kept im memory, so we're good.
output.write(justData);
}
assertEquals(200, urlConn.getResponseCode(), urlConn.getResponseCode() + ": " + urlConn.getResponseMessage());
}
}

0 comments on commit ab3ecce

Please sign in to comment.