From 64c5d0f08a8224a56cd32266977e7e928ebf9317 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 12 Dec 2022 14:40:06 +0200 Subject: [PATCH] Ensure that multipart response has a boundary when RestResponse is used Fixes: #29794 --- ...ipartOutputUsingBlockingEndpointsTest.java | 19 +++++++++++-------- .../multipart/MultipartMessageBodyWriter.java | 15 ++++++++++++--- ...ipartOutputUsingBlockingEndpointsTest.java | 19 +++++++++++-------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java index 3e8bb225da17f..62881e6efed67 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java @@ -15,6 +15,7 @@ import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import io.restassured.response.ExtractableResponse; public class MultipartOutputUsingBlockingEndpointsTest extends AbstractMultipartTest { @@ -65,18 +66,20 @@ public void testRestResponse() { @Test public void testWithFormData() { - String response = RestAssured.get("/multipart/output/with-form-data") + ExtractableResponse extractable = RestAssured.get("/multipart/output/with-form-data") .then() - .log().all() .contentType(ContentType.MULTIPART) .statusCode(200) - .extract().asString(); + .extract(); - assertContainsValue(response, "name", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_NAME); - assertContainsValue(response, "custom-surname", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_SURNAME); - assertContainsValue(response, "custom-status", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_STATUS); - assertContainsValue(response, "active", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_ACTIVE); - assertContainsValue(response, "values", MediaType.TEXT_PLAIN, "[one, two]"); + String body = extractable.asString(); + assertContainsValue(body, "name", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_NAME); + assertContainsValue(body, "custom-surname", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_SURNAME); + assertContainsValue(body, "custom-status", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_STATUS); + assertContainsValue(body, "active", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_ACTIVE); + assertContainsValue(body, "values", MediaType.TEXT_PLAIN, "[one, two]"); + + assertThat(extractable.header("Content-Type")).contains("boundary="); } @Test diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java index a7915b23b9349..39b21a7a58bff 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/multipart/MultipartMessageBodyWriter.java @@ -1,5 +1,7 @@ package org.jboss.resteasy.reactive.server.core.multipart; +import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; + import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -136,7 +138,7 @@ private void writeHeaders(String partName, Object partValue, PartItem part, Char throws IOException { part.getHeaders().put(HttpHeaders.CONTENT_DISPOSITION, List.of("form-data; name=\"" + partName + "\"" + getFileNameIfFile(partValue, part.getFilename()))); - part.getHeaders().put(HttpHeaders.CONTENT_TYPE, List.of(part.getMediaType())); + part.getHeaders().put(CONTENT_TYPE, List.of(part.getMediaType())); for (Map.Entry> entry : part.getHeaders().entrySet()) { writeLine(outputStream, entry.getKey() + ": " + entry.getValue().stream().map(String::valueOf) .collect(Collectors.joining("; ")), charset); @@ -206,8 +208,15 @@ private String generateBoundary() { private void appendBoundaryIntoMediaType(ResteasyReactiveRequestContext requestContext, String boundary, MediaType mediaType) { - requestContext.setResponseContentType(new MediaType(mediaType.getType(), mediaType.getSubtype(), - Collections.singletonMap(BOUNDARY_PARAM, boundary))); + MediaType mediaTypeWithBoundary = new MediaType(mediaType.getType(), mediaType.getSubtype(), + Collections.singletonMap(BOUNDARY_PARAM, boundary)); + requestContext.setResponseContentType(mediaTypeWithBoundary); + + // this is a total hack, but it's needed to make RestResponse work properly + requestContext.serverResponse().setResponseHeader(CONTENT_TYPE, mediaTypeWithBoundary.toString()); + if (requestContext.getResponse().isCreated()) { + requestContext.getResponse().get().getHeaders().remove(CONTENT_TYPE); + } } private boolean isNotEmpty(String str) { diff --git a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java index 14c35bfdf92a7..fc27042f39599 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/test/java/org/jboss/resteasy/reactive/server/vertx/test/multipart/MultipartOutputUsingBlockingEndpointsTest.java @@ -14,6 +14,7 @@ import io.restassured.RestAssured; import io.restassured.http.ContentType; +import io.restassured.response.ExtractableResponse; public class MultipartOutputUsingBlockingEndpointsTest extends AbstractMultipartTest { @@ -46,18 +47,20 @@ public void testSimple() { @Test public void testWithFormData() { - String response = RestAssured.get("/multipart/output/with-form-data") + ExtractableResponse extractable = RestAssured.get("/multipart/output/with-form-data") .then() - .log().all() .contentType(ContentType.MULTIPART) .statusCode(200) - .extract().asString(); + .extract(); - assertContainsValue(response, "name", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_NAME); - assertContainsValue(response, "custom-surname", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_SURNAME); - assertContainsValue(response, "custom-status", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_STATUS); - assertContainsValue(response, "active", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_ACTIVE); - assertContainsValue(response, "values", MediaType.TEXT_PLAIN, "[one, two]"); + String body = extractable.asString(); + assertContainsValue(body, "name", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_NAME); + assertContainsValue(body, "custom-surname", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_SURNAME); + assertContainsValue(body, "custom-status", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_STATUS); + assertContainsValue(body, "active", MediaType.TEXT_PLAIN, MultipartOutputResource.RESPONSE_ACTIVE); + assertContainsValue(body, "values", MediaType.TEXT_PLAIN, "[one, two]"); + + assertThat(extractable.header("Content-Type")).contains("boundary="); } @Test