Skip to content

Commit

Permalink
Fix dev-mode CL issue with multipart form handling
Browse files Browse the repository at this point in the history
Fixes: quarkusio#16794
(cherry picked from commit 3dda470)
  • Loading branch information
geoand authored and gsmet committed Apr 26, 2021
1 parent 9967783 commit e44ee87
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 2 deletions.
@@ -0,0 +1,79 @@
package io.quarkus.resteasy.reactive.server.test.multipart;

import static org.hamcrest.CoreMatchers.equalTo;

import java.io.File;
import java.util.function.Function;
import java.util.function.Supplier;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.resteasy.reactive.server.test.multipart.other.OtherPackageFormDataBase;
import io.quarkus.test.QuarkusDevModeTest;
import io.restassured.RestAssured;

public class MultipartFormInputDevModeTest {

@RegisterExtension
static QuarkusDevModeTest TEST = new QuarkusDevModeTest()
.setArchiveProducer(new Supplier<JavaArchive>() {
@Override
public JavaArchive get() {
return ShrinkWrap.create(JavaArchive.class)
.addClasses(FormDataBase.class, OtherPackageFormDataBase.class, FormData.class, Status.class,
OtherFormData.class, OtherFormDataBase.class, MultipartResource.class);
}

});

private final File HTML_FILE = new File("./src/test/resources/test.html");
private final File XML_FILE = new File("./src/test/resources/test.html");
private final File TXT_FILE = new File("./src/test/resources/lorem.txt");

@Test
public void test() {
doTest("simple");
doTest("simple");

TEST.modifySourceFile(MultipartResource.class.getSimpleName() + ".java", new Function<String, String>() {
@Override
public String apply(String s) {
return s.replace("/simple", "/simple2");
}
});

doTest("simple2");
doTest("simple2");

TEST.modifySourceFile(MultipartResource.class.getSimpleName() + ".java", new Function<String, String>() {
@Override
public String apply(String s) {
return s.replace("/simple2", "/simple");
}
});

doTest("simple");
doTest("simple");
}

private void doTest(String path) {
RestAssured.given()
.multiPart("name", "Alice")
.multiPart("active", "true")
.multiPart("num", "25")
.multiPart("status", "WORKING")
.multiPart("htmlFile", HTML_FILE, "text/html")
.multiPart("xmlFile", XML_FILE, "text/xml")
.multiPart("txtFile", TXT_FILE, "text/plain")
.accept("text/plain")
.when()
.post("/multipart/" + path + "/2")
.then()
.statusCode(200)
.body(equalTo("Alice - true - 50 - WORKING - text/html - true - true"));
}

}
Expand Up @@ -47,12 +47,15 @@ public class MultipartFormHandler implements RuntimeConfigurableServerRestHandle
private volatile String uploadsDirectory;
private volatile boolean deleteUploadedFilesOnEnd;
private volatile Optional<Long> maxBodySize;
private volatile ClassLoader tccl;

@Override
public void configure(RuntimeConfiguration configuration) {
uploadsDirectory = configuration.body().uploadsDirectory();
deleteUploadedFilesOnEnd = configuration.body().deleteUploadedFilesOnEnd();
maxBodySize = configuration.limits().maxBodySize();
// capture the proper TCCL in order to avoid losing it to Vert.x in dev-mode
tccl = Thread.currentThread().getContextClassLoader();

try {
Files.createDirectories(Paths.get(uploadsDirectory));
Expand Down Expand Up @@ -80,7 +83,7 @@ public void handle(ResteasyReactiveRequestContext context) throws Exception {
httpServerRequest.setExpectMultipart(true);
httpServerRequest.pause();
context.suspend();
MultipartFormVertxHandler handler = new MultipartFormVertxHandler(context, uploadsDirectory,
MultipartFormVertxHandler handler = new MultipartFormVertxHandler(context, tccl, uploadsDirectory,
deleteUploadedFilesOnEnd, maxBodySize);
httpServerRequest.handler(handler);
httpServerRequest.endHandler(new Handler<Void>() {
Expand All @@ -96,6 +99,7 @@ public void handle(Void event) {
private static class MultipartFormVertxHandler implements Handler<Buffer> {
private final ResteasyReactiveRequestContext rrContext;
private final RoutingContext context;
private final ClassLoader tccl;

private final String uploadsDirectory;
private final boolean deleteUploadedFilesOnEnd;
Expand All @@ -107,10 +111,11 @@ private static class MultipartFormVertxHandler implements Handler<Buffer> {
boolean ended;
long uploadSize = 0L;

public MultipartFormVertxHandler(ResteasyReactiveRequestContext rrContext, String uploadsDirectory,
public MultipartFormVertxHandler(ResteasyReactiveRequestContext rrContext, ClassLoader tccl, String uploadsDirectory,
boolean deleteUploadedFilesOnEnd, Optional<Long> maxBodySize) {
this.rrContext = rrContext;
this.context = rrContext.serverRequest().unwrap(RoutingContext.class);
this.tccl = tccl;
this.uploadsDirectory = uploadsDirectory;
this.deleteUploadedFilesOnEnd = deleteUploadedFilesOnEnd;
this.maxBodySize = maxBodySize;
Expand All @@ -134,6 +139,7 @@ public void handle(HttpServerFileUpload upload) {
long size = uploadSize + upload.size();
if (size > MultipartFormVertxHandler.this.maxBodySize.get()) {
failed = true;
restoreProperTCCL();
rrContext.resume(new WebApplicationException(Response.Status.REQUEST_ENTITY_TOO_LARGE));
return;
}
Expand Down Expand Up @@ -183,10 +189,15 @@ public void handle(Void v) {
MultipartFormVertxHandler.this.deleteFileUploads();
}
});
restoreProperTCCL();
rrContext.resume(new WebApplicationException(Response.Status.REQUEST_ENTITY_TOO_LARGE));
}
}

private void restoreProperTCCL() {
Thread.currentThread().setContextClassLoader(tccl);
}

void uploadEnded() {
int count = uploadCount.decrementAndGet();
// only if parsing is done and count is 0 then all files have been processed
Expand Down Expand Up @@ -214,6 +225,7 @@ void doEnd() {
context.addBodyEndHandler(x -> deleteFileUploads());
}
rrContext.setInputStream(NO_BYTES_INPUT_STREAM);
restoreProperTCCL();
rrContext.resume();
}

Expand Down

0 comments on commit e44ee87

Please sign in to comment.