From fe79da7c20057736b1129e52212161a1c6e719d7 Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Wed, 5 Jan 2022 12:16:34 +0200 Subject: [PATCH] Ensure that UTF8 is used as the default encoding in RESTEasy Reactive This was already being done when producing text/plain, but in cases where application/json was being used and the actual entity was a String, the encoding was not being specified Fixes: #21700 --- .../response/ResponseStringNonAsciiTest.java | 49 +++++++++++++++++++ .../BasicServerJacksonMessageBodyWriter.java | 3 +- ...eaturedServerJacksonMessageBodyWriter.java | 3 +- .../KotlinSerializationMessageBodyWriter.kt | 8 +-- .../jackson/JacksonMessageBodyWriterUtil.java | 3 +- .../server/jsonb/JsonbMessageBodyWriter.java | 5 +- 6 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/response/ResponseStringNonAsciiTest.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/response/ResponseStringNonAsciiTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/response/ResponseStringNonAsciiTest.java new file mode 100644 index 0000000000000..ae0d65a6bdbee --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/deployment/src/test/java/io/quarkus/resteasy/reactive/jackson/deployment/test/response/ResponseStringNonAsciiTest.java @@ -0,0 +1,49 @@ +package io.quarkus.resteasy.reactive.jackson.deployment.test.response; + +import java.util.function.Supplier; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.hamcrest.Matchers; +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.test.QuarkusUnitTest; +import io.restassured.RestAssured; + +public class ResponseStringNonAsciiTest { + + @RegisterExtension + static QuarkusUnitTest test = new QuarkusUnitTest() + .setArchiveProducer(new Supplier<>() { + @Override + public JavaArchive get() { + return ShrinkWrap.create(JavaArchive.class) + .addClasses(HelloResource.class); + } + }); + + @Test + public void test() { + RestAssured.get("/hello") + .then().statusCode(200) + .and().body(Matchers.equalTo("{\"message\": \"Καλημέρα κόσμε\"}")) + .and().contentType("application/json"); + } + + @Path("hello") + public static class HelloResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response hello() { + return Response.ok("{\"message\": \"Καλημέρα κόσμε\"}").build(); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/BasicServerJacksonMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/BasicServerJacksonMessageBodyWriter.java index ca23123a25882..9ea88c452de67 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/BasicServerJacksonMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/BasicServerJacksonMessageBodyWriter.java @@ -8,6 +8,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.inject.Inject; import javax.ws.rs.WebApplicationException; @@ -35,7 +36,7 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte setContentTypeIfNecessary(context); OutputStream stream = context.getOrCreateOutputStream(); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - stream.write(((String) o).getBytes()); + stream.write(((String) o).getBytes(StandardCharsets.UTF_8)); } else { defaultWriter.writeValue(stream, o); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/FullyFeaturedServerJacksonMessageBodyWriter.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/FullyFeaturedServerJacksonMessageBodyWriter.java index 49962c813b721..ed1afa6ad0893 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/FullyFeaturedServerJacksonMessageBodyWriter.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-jackson/runtime/src/main/java/io/quarkus/resteasy/reactive/jackson/runtime/serialisers/FullyFeaturedServerJacksonMessageBodyWriter.java @@ -9,6 +9,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.BiFunction; @@ -46,7 +47,7 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte setContentTypeIfNecessary(context); OutputStream stream = context.getOrCreateOutputStream(); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - stream.write(((String) o).getBytes()); + stream.write(((String) o).getBytes(StandardCharsets.UTF_8)); } else { // First test the names to see if JsonView is used. We do this to avoid doing reflection for the common case // where JsonView is not used diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/runtime/src/main/kotlin/io/quarkus/kotlin/serialization/KotlinSerializationMessageBodyWriter.kt b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/runtime/src/main/kotlin/io/quarkus/kotlin/serialization/KotlinSerializationMessageBodyWriter.kt index 903eb29f6f227..ead259f72e5ff 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/runtime/src/main/kotlin/io/quarkus/kotlin/serialization/KotlinSerializationMessageBodyWriter.kt +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-kotlin-serialization/runtime/src/main/kotlin/io/quarkus/kotlin/serialization/KotlinSerializationMessageBodyWriter.kt @@ -1,7 +1,6 @@ package io.quarkus.kotlin.serialization import kotlinx.serialization.ExperimentalSerializationApi -import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.serializer @@ -11,6 +10,7 @@ import org.jboss.resteasy.reactive.server.spi.ServerMessageBodyWriter.AllWriteab import org.jboss.resteasy.reactive.server.spi.ServerRequestContext import java.io.OutputStream import java.lang.reflect.Type +import java.nio.charset.StandardCharsets import javax.inject.Inject import javax.ws.rs.Produces import javax.ws.rs.core.MediaType @@ -25,7 +25,7 @@ class KotlinSerializationMessageBodyWriter(@Inject var json: Json) : AllWriteabl ) { JsonMessageBodyWriterUtil.setContentTypeIfNecessary(httpHeaders) if (o is String) { // YUK: done in order to avoid adding extra quotes... - entityStream.write(o.toByteArray()) + entityStream.write(o.toByteArray(StandardCharsets.UTF_8)) } else { json.encodeToStream(o, entityStream) } @@ -37,7 +37,7 @@ class KotlinSerializationMessageBodyWriter(@Inject var json: Json) : AllWriteabl val stream: OutputStream = NoopCloseAndFlushOutputStream(originalStream) if (o is String) { // YUK: done in order to avoid adding extra quotes... - stream.write(o.toByteArray()) + stream.write(o.toByteArray(StandardCharsets.UTF_8)) } else { json.encodeToStream(serializer(genericType), o, stream) } @@ -60,4 +60,4 @@ class KotlinSerializationMessageBodyWriter(@Inject var json: Json) : AllWriteabl delegate.write(b, off, len) } } -} \ No newline at end of file +} diff --git a/independent-projects/resteasy-reactive/server/jackson/src/main/java/org/jboss/resteasy/reactive/server/jackson/JacksonMessageBodyWriterUtil.java b/independent-projects/resteasy-reactive/server/jackson/src/main/java/org/jboss/resteasy/reactive/server/jackson/JacksonMessageBodyWriterUtil.java index c1702422bde33..9de661d986ffa 100644 --- a/independent-projects/resteasy-reactive/server/jackson/src/main/java/org/jboss/resteasy/reactive/server/jackson/JacksonMessageBodyWriterUtil.java +++ b/independent-projects/resteasy-reactive/server/jackson/src/main/java/org/jboss/resteasy/reactive/server/jackson/JacksonMessageBodyWriterUtil.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.io.OutputStream; import java.lang.annotation.Annotation; +import java.nio.charset.StandardCharsets; import javax.ws.rs.core.MultivaluedMap; public final class JacksonMessageBodyWriterUtil { @@ -43,7 +44,7 @@ public static void doLegacyWrite(Object o, Annotation[] annotations, Multivalued OutputStream entityStream, ObjectWriter defaultWriter) throws IOException { setContentTypeIfNecessary(httpHeaders); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - entityStream.write(((String) o).getBytes()); + entityStream.write(((String) o).getBytes(StandardCharsets.UTF_8)); } else { if (annotations != null) { for (Annotation annotation : annotations) { diff --git a/independent-projects/resteasy-reactive/server/jsonb/src/main/java/org/jboss/resteasy/reactive/server/jsonb/JsonbMessageBodyWriter.java b/independent-projects/resteasy-reactive/server/jsonb/src/main/java/org/jboss/resteasy/reactive/server/jsonb/JsonbMessageBodyWriter.java index 1f6b3b90b4432..5be8a9a9058b3 100644 --- a/independent-projects/resteasy-reactive/server/jsonb/src/main/java/org/jboss/resteasy/reactive/server/jsonb/JsonbMessageBodyWriter.java +++ b/independent-projects/resteasy-reactive/server/jsonb/src/main/java/org/jboss/resteasy/reactive/server/jsonb/JsonbMessageBodyWriter.java @@ -4,6 +4,7 @@ import java.io.OutputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; import javax.inject.Inject; import javax.json.bind.Jsonb; import javax.ws.rs.WebApplicationException; @@ -28,7 +29,7 @@ public void writeTo(Object o, Class type, Type genericType, Annotation[] anno MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException { JsonMessageBodyWriterUtil.setContentTypeIfNecessary(httpHeaders); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - entityStream.write(((String) o).getBytes()); + entityStream.write(((String) o).getBytes(StandardCharsets.UTF_8)); } else { json.toJson(o, type, entityStream); } @@ -41,7 +42,7 @@ public void writeResponse(Object o, Type genericType, ServerRequestContext conte OutputStream originalStream = context.getOrCreateOutputStream(); OutputStream stream = new NoopCloseAndFlushOutputStream(originalStream); if (o instanceof String) { // YUK: done in order to avoid adding extra quotes... - stream.write(((String) o).getBytes()); + stream.write(((String) o).getBytes(StandardCharsets.UTF_8)); } else { json.toJson(o, stream); }