diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/pom.xml b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/pom.xml
index bac406ccdbc1c..3807caaaff689 100644
--- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/pom.xml
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/pom.xml
@@ -99,6 +99,11 @@
quarkus-jaxrs-client-reactive-deployment
test
+
+ io.quarkus
+ quarkus-reactive-routes-deployment
+ test
+
diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/headers/VertxHeadersTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/headers/VertxHeadersTest.java
new file mode 100644
index 0000000000000..c2b902c7e2d50
--- /dev/null
+++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/headers/VertxHeadersTest.java
@@ -0,0 +1,63 @@
+package io.quarkus.resteasy.reactive.server.test.headers;
+
+import static io.restassured.RestAssured.when;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.IOException;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.ext.Provider;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.QuarkusUnitTest;
+import io.quarkus.vertx.web.RouteFilter;
+import io.vertx.ext.web.RoutingContext;
+
+public class VertxHeadersTest {
+
+ @RegisterExtension
+ static QuarkusUnitTest TEST = new QuarkusUnitTest()
+ .withApplicationRoot((jar) -> jar.addClasses(VertxFilter.class, JaxRsFilter.class, TestResource.class));
+
+ @Test
+ void testVaryHeaderValues() {
+ var headers = when().get("/test")
+ .then()
+ .statusCode(200)
+ .extract().headers();
+ assertThat(headers.getValues(HttpHeaders.VARY)).containsExactlyInAnyOrder("Origin", "Prefer");
+ }
+
+ public static class VertxFilter {
+ @RouteFilter
+ void addVary(final RoutingContext rc) {
+ rc.response().headers().add(HttpHeaders.VARY, "Origin");
+ rc.next();
+ }
+ }
+
+ @Provider
+ public static class JaxRsFilter implements ContainerResponseFilter {
+ @Override
+ public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
+ throws IOException {
+ responseContext.getHeaders().add(HttpHeaders.VARY, "Prefer");
+ }
+ }
+
+ @Path("test")
+ public static class TestResource {
+
+ @GET
+ public String test() {
+ return "test";
+ }
+ }
+}
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java
index 4ad07b6084ea4..19da861778f6f 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ServerSerialisers.java
@@ -13,6 +13,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -71,7 +72,13 @@ public void accept(ResteasyReactiveRequestContext context) {
}
};
- private static final String CONTENT_TYPE = "Content-Type"; // use this instead of the Vert.x constant because the TCK expects upper case
+ private static final String CONTENT = "Content";
+ private static final String CONTENT_LOWER = "content";
+ private static final String TYPE = "Type";
+ private static final String TYPE_LOWER = "type";
+ private static final String LENGTH = "Length";
+ private static final String LENGTH_LOWER = "length";
+ private static final String CONTENT_TYPE = CONTENT + "-" + TYPE; // use this instead of the Vert.x constant because the TCK expects upper case
static {
primitivesToWrappers.put(boolean.class, Boolean.class);
@@ -484,13 +491,27 @@ public static void encodeResponseHeaders(ResteasyReactiveRequestContext requestC
MultivaluedMap headers = response.getHeaders();
for (Map.Entry> entry : headers.entrySet()) {
if (entry.getValue().size() == 1) {
+ String header = entry.getKey();
+ boolean useSet = requireSingleHeader(header);
Object o = entry.getValue().get(0);
if (o == null) {
- vertxResponse.setResponseHeader(entry.getKey(), "");
+ if (useSet) {
+ vertxResponse.setResponseHeader(header, "");
+ } else {
+ vertxResponse.addResponseHeader(header, "");
+ }
} else if (o instanceof CharSequence) {
- vertxResponse.setResponseHeader(entry.getKey(), (CharSequence) o);
+ if (useSet) {
+ vertxResponse.setResponseHeader(header, (CharSequence) o);
+ } else {
+ vertxResponse.addResponseHeader(header, (CharSequence) o);
+ }
} else {
- vertxResponse.setResponseHeader(entry.getKey(), (CharSequence) HeaderUtil.headerToString(o));
+ if (useSet) {
+ vertxResponse.setResponseHeader(header, (CharSequence) HeaderUtil.headerToString(o));
+ } else {
+ vertxResponse.addResponseHeader(header, (CharSequence) HeaderUtil.headerToString(o));
+ }
}
} else {
List strValues = new ArrayList<>(entry.getValue().size());
@@ -502,4 +523,15 @@ public static void encodeResponseHeaders(ResteasyReactiveRequestContext requestC
}
}
+ private static boolean requireSingleHeader(String header) {
+ if (!(header.startsWith(CONTENT) || header.startsWith(CONTENT_LOWER))) {
+ return false;
+ }
+ if (header.length() < CONTENT.length() + 2) {
+ return false;
+ }
+ String substring = header.substring(CONTENT.length() + 1).toLowerCase(Locale.ROOT);
+ return substring.equals(TYPE_LOWER) || substring.equals(LENGTH_LOWER);
+ }
+
}