From deb5fae3990a8f329b5a492ebc7dc4495ccb730a Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 14 Jun 2023 15:19:11 +0300 Subject: [PATCH] Cleanup after REST Client multipart request Fixes: #33986 --- .../multipart/MultipartCleanupTest.java | 97 +++++++++++++++++++ .../multipart/QuarkusMultipartFormUpload.java | 14 ++- 2 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartCleanupTest.java diff --git a/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartCleanupTest.java b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartCleanupTest.java new file mode 100644 index 0000000000000..2f2a41fa9a8ee --- /dev/null +++ b/extensions/resteasy-reactive/rest-client-reactive/deployment/src/test/java/io/quarkus/rest/client/reactive/multipart/MultipartCleanupTest.java @@ -0,0 +1,97 @@ +package io.quarkus.rest.client.reactive.multipart; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.MediaType; + +import org.eclipse.microprofile.rest.client.RestClientBuilder; +import org.jboss.resteasy.reactive.MultipartForm; +import org.jboss.resteasy.reactive.PartType; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.quarkus.test.QuarkusUnitTest; +import io.quarkus.test.common.http.TestHTTPResource; + +public class MultipartCleanupTest { + + private static java.nio.file.Path tempDir; + + @TestHTTPResource + URI baseUri; + + @RegisterExtension + static final QuarkusUnitTest TEST = new QuarkusUnitTest() + .withApplicationRoot(jar -> jar.addClasses(Form.class, Client.class, Resource.class)); + + @BeforeAll + static void getTempDir() throws IOException { + java.nio.file.Path tempFilePath = Files.createTempFile("jvm_tempfile_helper_", ".tmp"); // create temp file to get reference to jvm temp dir + tempFilePath.toFile().deleteOnExit(); // cleanup file after test is finished + tempDir = tempFilePath.getParent(); // now we can get the root temp dir + } + + @Test + public void test() throws IOException { + Client client = RestClientBuilder.newBuilder().baseUri(baseUri).build(Client.class); + + File file = File.createTempFile("MultipartTest", ".txt"); + Files.writeString(file.toPath(), "DUMMY".repeat(1000)); + file.deleteOnExit(); + + Form form = new Form(); + form.file = file; + form.dummy = "dummy"; + + long attrFilesBeforeTest = getCountOfNettyAttrTempFiles(); + assertThat(client.send(form)).isEqualTo("test"); + assertThat(getCountOfNettyAttrTempFiles()).isEqualTo(attrFilesBeforeTest); + } + + private long getCountOfNettyAttrTempFiles() { + File[] attrFiles = tempDir.toFile().listFiles((dir, name) -> name.startsWith("Attr_")); + if (attrFiles == null) { + throw new IllegalStateException("could not list Attr_ files because the directory to search was not valid"); + } + return attrFiles.length; + } + + public static class Form { + @FormParam("file") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + private File file; + + @FormParam("dummy") + @PartType(MediaType.TEXT_PLAIN) + private String dummy; + } + + public interface Client { + + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + @Path("/multipart") + String send(Form collectorDto); + } + + @Path("/multipart") + @ApplicationScoped + public static class Resource { + @POST + @Consumes(MediaType.MULTIPART_FORM_DATA) + public String upload(@MultipartForm Form form) { + return "test"; + } + } +} diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java index 21bb2a23db394..dabcbbbb57977 100644 --- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java +++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/multipart/QuarkusMultipartFormUpload.java @@ -146,6 +146,14 @@ private void handleChunk(Object item) { handler.handle(item); } + private void clearEncoder() { + if (encoder == null) { + return; + } + encoder.cleanFiles(); + encoder = null; + } + @Override public void run() { if (Vertx.currentContext() != context) { @@ -161,7 +169,7 @@ public void run() { } else if (chunk == LastHttpContent.EMPTY_LAST_CONTENT || encoder.isEndOfInput()) { ended = true; request = null; - encoder = null; + clearEncoder(); pending.write(InboundBuffer.END_SENTINEL); } else { ByteBuf content = chunk.content(); @@ -179,7 +187,7 @@ public void run() { ByteBuf content = request.content(); Buffer buffer = Buffer.buffer(content); request = null; - encoder = null; + clearEncoder(); pending.write(buffer); ended = true; pending.write(InboundBuffer.END_SENTINEL); @@ -194,7 +202,7 @@ public boolean isChunked() { private void handleError(Throwable e) { ended = true; request = null; - encoder = null; + clearEncoder(); pending.write(e); }