From 82dfd2f4d3b28defc6d8a8fb62f79c61b3919b06 Mon Sep 17 00:00:00 2001 From: Fedor Dudinskiy Date: Fri, 3 Jun 2022 15:55:43 +0200 Subject: [PATCH] Add checks for the following issues: https://github.com/quarkusio/quarkus/issues/24402 https://github.com/quarkusio/quarkus/issues/24405 https://github.com/quarkusio/quarkus/issues/24415 https://github.com/quarkusio/quarkus/issues/25028 Additionally: delete temporary files to speed up local execution --- .../http/restclient/reactive/BookClient.java | 4 +- .../reactive/ReactiveClientBookResource.java | 2 +- .../restclient/reactive/files/FileClient.java | 7 +- .../reactive/files/FileClientResource.java | 68 ++++++++++++++++--- .../reactive/files/FileResource.java | 57 +++++++++++++--- .../reactive/files/FileWrapper.java | 11 ++- .../reactive/LargeFileHandlingIT.java | 49 +++++++++++-- .../reactive/ReactiveRestClientIT.java | 5 +- 8 files changed, 167 insertions(+), 36 deletions(-) diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/BookClient.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/BookClient.java index d1d36c3b7d..b63615eedd 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/BookClient.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/BookClient.java @@ -44,8 +44,8 @@ interface AuthorClient { @Produces(MediaType.TEXT_PLAIN) interface ProfessionClient { @GET - @Path("/name") - Uni getName(); + @Path("/title") + Uni getTitle(); @Path("/wage") WageClient getWage(); diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/ReactiveClientBookResource.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/ReactiveClientBookResource.java index d84e28016c..b903fd0944 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/ReactiveClientBookResource.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/ReactiveClientBookResource.java @@ -57,7 +57,7 @@ public Uni getSubResource(@QueryParam("author") String author) { @GET @Path("/profession") public Uni getSubSubResource() { - return bookInterface.getAuthor().getProfession().getName(); + return bookInterface.getAuthor().getProfession().getTitle(); } @GET diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClient.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClient.java index 3bc403ab64..a68f9c087c 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClient.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClient.java @@ -33,7 +33,12 @@ public interface FileClient { @GET @Produces(MediaType.MULTIPART_FORM_DATA) @Path("/download-multipart") - FileWrapper downloadMultipart(); + Uni downloadMultipart(); + + @GET + @Produces(MediaType.MULTIPART_FORM_DATA) + @Path("/download-broken-multipart") + Uni brokenMultipart(); @POST @Consumes(MediaType.APPLICATION_OCTET_STREAM) diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClientResource.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClientResource.java index c579a383ef..4910be109e 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClientResource.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileClientResource.java @@ -2,28 +2,44 @@ import java.io.IOException; import java.nio.file.Files; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; import javax.inject.Inject; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.eclipse.microprofile.rest.client.inject.RestClient; +import org.jboss.resteasy.reactive.RestResponse; +import io.quarkus.logging.Log; import io.smallrye.common.annotation.Blocking; import io.smallrye.mutiny.Uni; @Path("/file-client") public class FileClientResource { private static final String BIGGER_THAN_TWO_GIGABYTES = OsUtils.SIZE_2049MiB; - private final java.nio.file.Path FILE = Files.createTempFile("upload", ".txt").toAbsolutePath(); + + private final java.nio.file.Path file; + private final List deathRow = new LinkedList<>(); private final FileClient client; private final OsUtils utils; @Inject - public FileClientResource(@RestClient FileClient client) throws IOException { + public FileClientResource(@RestClient FileClient client, + @ConfigProperty(name = "client.filepath") Optional folder) { utils = OsUtils.get(); - utils.createFile(FILE.toString(), BIGGER_THAN_TWO_GIGABYTES); + file = folder + .stream() + .map(existing -> java.nio.file.Path.of(existing).resolve("upload.txt").toAbsolutePath()) + .peek(path -> { + utils.createFile(path.toString(), BIGGER_THAN_TWO_GIGABYTES); + }) + .findFirst().orElse(null); this.client = client; } @@ -31,7 +47,7 @@ public FileClientResource(@RestClient FileClient client) throws IOException { @Path("/client-hash") @Blocking public Uni calculateHash() { - return utils.getSum(FILE.toString()); + return utils.getSum(file.toString()); } @GET @@ -43,28 +59,60 @@ public Uni hash() { @GET @Path("/download") public Uni download() { - return client.download().onItem().transformToUni(file -> utils.getSum(file.getAbsolutePath())); + return client.download() + .map(file -> { + java.nio.file.Path path = file.toPath().toAbsolutePath(); + deathRow.add(path); + return path.toString(); + }) + .onItem() + .transformToUni(utils::getSum); } @GET @Path("/download-multipart") public Uni downloadMultipart() { - FileWrapper wrapper = client.downloadMultipart(); - String path = wrapper.file.getAbsolutePath(); - return utils.getSum(path); + return client.downloadMultipart() + .map(wrapper -> wrapper.file.toPath()) + .map(java.nio.file.Path::toAbsolutePath) + .invoke(deathRow::add) + .map(java.nio.file.Path::toString) + .flatMap(utils::getSum); + } + + @GET + @Path("/download-broken-multipart") + public Uni downloadMultipartResponse() { + return client.brokenMultipart() + .map(wrapper -> wrapper.file.getAbsolutePath()) + .flatMap(utils::getSum); } @POST @Path("/multipart") public Uni uploadMultipart() { FileWrapper wrapper = new FileWrapper(); - wrapper.file = FILE.toFile(); + wrapper.file = file.toFile(); + wrapper.name = file.toString(); return client.sendMultipart(wrapper); } @POST @Path("/upload-file") public Uni upload() { - return client.sendFile(FILE.toFile()); + return client.sendFile(file.toFile()); + } + + @DELETE + @Path("/") + public RestResponse drop() { + for (java.nio.file.Path path : deathRow) { + try { + Files.delete(path); + } catch (IOException e) { + Log.warn(e); + } + } + return RestResponse.noContent(); } } diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileResource.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileResource.java index 617effbf7a..a7cf698bc2 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileResource.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileResource.java @@ -3,40 +3,55 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; +import org.eclipse.microprofile.config.inject.ConfigProperty; import org.jboss.resteasy.reactive.MultipartForm; import org.jboss.resteasy.reactive.RestResponse; import io.quarkus.logging.Log; +import io.smallrye.common.annotation.Blocking; import io.smallrye.mutiny.Uni; @Path("/file") public class FileResource { private static final String BIGGER_THAN_TWO_GIGABYTES = OsUtils.SIZE_2049MiB; - private final File FILE = Files.createTempFile("server", ".txt").toAbsolutePath().toFile(); + private final File file; private final OsUtils utils; + private final List deathRow = new LinkedList<>(); - public FileResource() throws IOException { + public FileResource(@ConfigProperty(name = "client.filepath") Optional folder) { utils = OsUtils.get(); - utils.createFile(FILE.getAbsolutePath(), BIGGER_THAN_TWO_GIGABYTES); + file = folder + .stream() + .map(existing -> java.nio.file.Path.of(existing).resolve("server.txt").toAbsolutePath()) + .peek(path -> { + utils.createFile(path.toString(), BIGGER_THAN_TWO_GIGABYTES); + }) + .map(java.nio.file.Path::toFile) + .findFirst().orElse(null); } @GET @Path("/download") public Uni download() { - return Uni.createFrom().item(FILE); + return Uni.createFrom().item(file); } @POST @Path("/upload") public Uni upload(File body) { + deathRow.add(body); return utils.getSum(body.getAbsolutePath()); } @@ -45,23 +60,47 @@ public Uni upload(File body) { @Produces(MediaType.TEXT_PLAIN) @Path("/upload-multipart") public Uni uploadMultipart(@MultipartForm FileWrapper body) { + deathRow.add(body.file); return utils.getSum(body.file.getAbsolutePath()); } @GET @Produces(MediaType.MULTIPART_FORM_DATA) @Path("/download-multipart") - public RestResponse downloadMultipart() { + @Blocking //https://github.com/quarkusio/quarkus/issues/25909 + public Uni downloadMultipart() { FileWrapper wrapper = new FileWrapper(); - wrapper.file = FILE; - return RestResponse.ok(wrapper); + wrapper.file = file; + wrapper.name = file.getName(); + return Uni.createFrom().item(() -> wrapper); + } + + @GET + @Produces(MediaType.MULTIPART_FORM_DATA) + @Path("/download-broken-multipart") + @Blocking //https://github.com/quarkusio/quarkus/issues/25909 + public Uni brokenMultipart() { + return Uni.createFrom().item(() -> RestResponse.ok("Not a multipart message")); } @GET @Path("/hash") @Produces(MediaType.TEXT_PLAIN) public Uni hash() { - Log.info("Hashing path " + FILE.getAbsolutePath()); - return utils.getSum(FILE.getAbsolutePath()); + Log.info("Hashing path " + file.getAbsolutePath()); + return utils.getSum(file.getAbsolutePath()); + } + + @DELETE + @Path("/") + public RestResponse drop() { + for (File path : deathRow) { + try { + Files.delete(path.toPath().toAbsolutePath()); + } catch (IOException e) { + Log.warn(e); + } + } + return RestResponse.noContent(); } } diff --git a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileWrapper.java b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileWrapper.java index 5f9d7c543d..b74a61864e 100644 --- a/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileWrapper.java +++ b/http/rest-client-reactive/src/main/java/io/quarkus/ts/http/restclient/reactive/files/FileWrapper.java @@ -2,13 +2,20 @@ import java.io.File; -import javax.ws.rs.FormParam; import javax.ws.rs.core.MediaType; import org.jboss.resteasy.reactive.PartType; +import org.jboss.resteasy.reactive.RestForm; +import io.quarkus.runtime.annotations.RegisterForReflection; + +@RegisterForReflection public class FileWrapper { - @FormParam("file") + @RestForm("file") @PartType(MediaType.APPLICATION_OCTET_STREAM) public File file; + + @RestForm("name") + @PartType(MediaType.TEXT_PLAIN) + public String name; } diff --git a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/LargeFileHandlingIT.java b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/LargeFileHandlingIT.java index 9ed1bc7dbc..add5675f44 100644 --- a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/LargeFileHandlingIT.java +++ b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/LargeFileHandlingIT.java @@ -6,11 +6,15 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.concurrent.ExecutionException; +import java.util.function.Predicate; import org.apache.http.HttpStatus; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; @@ -18,6 +22,7 @@ import io.quarkus.test.bootstrap.RestService; import io.quarkus.test.scenarios.QuarkusScenario; +import io.quarkus.test.scenarios.annotations.DisabledOnNative; import io.quarkus.test.services.QuarkusApplication; import io.quarkus.ts.http.restclient.reactive.files.OsUtils; import io.restassured.response.Response; @@ -26,17 +31,19 @@ public class LargeFileHandlingIT { private static final String BIGGER_THAN_TWO_GIGABYTES = OsUtils.SIZE_2049MiB; + private static final Path files = getTempDirectory(); private final Path downloaded; private final Path uploaded; private final OsUtils utils = OsUtils.get(); @QuarkusApplication - static RestService app = new RestService().withProperties("modern.properties"); + static RestService app = new RestService() + .withProperty("client.filepath", () -> files.toAbsolutePath().toString()) + .withProperties("modern.properties"); - public LargeFileHandlingIT() throws IOException { - downloaded = Files.createTempFile("downloaded", ".txt").toAbsolutePath(); - Files.delete(downloaded); - uploaded = Files.createTempFile("uploaded", ".txt").toAbsolutePath(); + public LargeFileHandlingIT() { + downloaded = files.resolve("downloaded.txt"); + uploaded = files.resolve("uploaded.txt"); } @Test @@ -65,7 +72,6 @@ public void downloadDirectly() throws IOException, ExecutionException, Interrupt } @Test - @Disabled("https://github.com/quarkusio/quarkus/issues/24402") @DisabledOnOs(value = OS.WINDOWS, disabledReason = "https://github.com/quarkusio/quarkus/issues/24763") public void downloadThroughClient() { Response hashSum = app.given().get("/file/hash"); @@ -123,8 +129,8 @@ public void uploadFile() throws ExecutionException, InterruptedException { } @Test - @Disabled("https://github.com/quarkusio/quarkus/issues/24405") @DisabledOnOs(value = OS.WINDOWS, disabledReason = "https://github.com/quarkusio/quarkus/issues/24763") + @DisabledOnNative(reason = "https://github.com/quarkusio/quarkus/issues/25973") public void uploadFileThroughClient() { Response hashSum = app.given().get("/file-client/client-hash"); assertEquals(HttpStatus.SC_OK, hashSum.statusCode()); @@ -150,4 +156,33 @@ public void uploadMultipart() { assertEquals(before, after); } + + @Test + @DisabledOnOs(value = OS.WINDOWS, disabledReason = "https://github.com/quarkusio/quarkus/issues/24763") + public void multipartError() { + Response download = app.given().get("/file-client/download-broken-multipart"); + assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, download.statusCode()); + Predicate containsError = line -> line.contains("Unable to parse multipart response - No delimiter specified"); + Assertions.assertTrue(app.getLogs().stream().anyMatch(containsError)); + } + + private static Path getTempDirectory() { + try { + return Files.createTempDirectory("large_files"); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @AfterAll + static void afterAll() throws IOException { + try (DirectoryStream paths = Files.newDirectoryStream(files)) { + for (Path path : paths) { + Files.delete(path); + } + } + app.given().delete("/file-client/"); + app.given().delete("/file/"); + Files.delete(files); + } } diff --git a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java index ef5869f1cb..7289d4e0a5 100644 --- a/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java +++ b/http/rest-client-reactive/src/test/java/io/quarkus/ts/http/restclient/reactive/ReactiveRestClientIT.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; @@ -96,7 +95,6 @@ public void deepestLevelDirectly() { } @Test - @Disabled("https://github.com/quarkusio/quarkus/issues/25028") public void subResource() { Response response = app.given().get("/client/book/author/?author=Heller"); assertEquals(HttpStatus.SC_OK, response.statusCode()); @@ -108,11 +106,10 @@ public void subResource() { } @Test - @Disabled("https://github.com/quarkusio/quarkus/issues/25028") public void deepLevel() { Response response = app.given().get("/client/book/currency"); assertEquals(HttpStatus.SC_OK, response.statusCode()); - assertEquals("Heller", response.getBody().asString()); + assertEquals("USD", response.getBody().asString()); } @DisabledOnQuarkusVersion(version = DISABLE_IF_NOT_QUARKUS_2_7_6_OR_2_8_3_OR_HIGHER, reason = FIXED_IN_2_7_6_AND_2_8_3)