From 94afd1b5b660d75413c3a8d69b5a8afd7f14d91e Mon Sep 17 00:00:00 2001 From: Yelzhas Suleimenov Date: Fri, 17 Sep 2021 03:21:08 +0600 Subject: [PATCH 1/4] Response status and Response headers support added --- .../reactive/server/common/Header.java | 7 +++ .../server/common/ResponseHeader.java | 12 ++++ .../reactive/server/common/Status.java | 12 ++++ .../reactive/server/deployment/DotNames.java | 5 ++ .../deployment/ResteasyReactiveProcessor.java | 59 +++++++++++++++++++ .../responseheader/ResponseHeaderTest.java | 50 ++++++++++++++++ .../responsestatus/ResponseStatusTest.java | 41 +++++++++++++ .../responseheader/ResponseHeaderHandler.java | 31 ++++++++++ .../responsestatus/ResponseStatusHandler.java | 30 ++++++++++ 9 files changed, 247 insertions(+) create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java create mode 100644 extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java new file mode 100644 index 0000000000000..a1be59ca4da38 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java @@ -0,0 +1,7 @@ +package io.quarkus.resteasy.reactive.server.common; + +public @interface Header { + public String name(); + + public String value(); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java new file mode 100644 index 0000000000000..fce41d6c1dbcf --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.server.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface ResponseHeader { + public Header[] headers(); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java new file mode 100644 index 0000000000000..62fec747e612d --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java @@ -0,0 +1,12 @@ +package io.quarkus.resteasy.reactive.server.common; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Status { + public int value(); +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java index fbd7828f56ceb..b873184763c96 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java @@ -8,6 +8,9 @@ import org.jboss.jandex.DotName; import org.jboss.resteasy.reactive.multipart.FileUpload; +import io.quarkus.resteasy.reactive.server.common.ResponseHeader; +import io.quarkus.resteasy.reactive.server.common.Status; + final class DotNames { static final String POPULATE_METHOD_NAME = "populate"; @@ -19,6 +22,8 @@ final class DotNames { static final DotName FIELD_UPLOAD_NAME = DotName.createSimple(FileUpload.class.getName()); static final DotName PATH_NAME = DotName.createSimple(Path.class.getName()); static final DotName FILE_NAME = DotName.createSimple(File.class.getName()); + static final DotName RESPONSE_HEADER_ANNOTATION = DotName.createSimple(ResponseHeader.class.getName()); + static final DotName RESPONSE_STATUS_ANNOTATION = DotName.createSimple(Status.class.getName()); private DotNames() { } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 9c893b38c71e3..9423702689ac8 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -51,6 +51,7 @@ import org.jboss.resteasy.reactive.common.processor.ResteasyReactiveDotNames; import org.jboss.resteasy.reactive.common.processor.scanning.ApplicationScanningResult; import org.jboss.resteasy.reactive.common.processor.scanning.ResourceScanningResult; +import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationStore; import org.jboss.resteasy.reactive.common.processor.transformation.AnnotationsTransformer; import org.jboss.resteasy.reactive.common.util.Encode; import org.jboss.resteasy.reactive.server.core.Deployment; @@ -60,6 +61,7 @@ import org.jboss.resteasy.reactive.server.model.ContextResolvers; import org.jboss.resteasy.reactive.server.model.DynamicFeatures; import org.jboss.resteasy.reactive.server.model.Features; +import org.jboss.resteasy.reactive.server.model.FixedHandlerChainCustomizer; import org.jboss.resteasy.reactive.server.model.HandlerChainCustomizer; import org.jboss.resteasy.reactive.server.model.ParamConverterProviders; import org.jboss.resteasy.reactive.server.processor.scanning.MethodScanner; @@ -112,6 +114,8 @@ import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.AuthenticationRedirectExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.ForbiddenExceptionMapper; import io.quarkus.resteasy.reactive.server.runtime.exceptionmappers.UnauthorizedExceptionMapper; +import io.quarkus.resteasy.reactive.server.runtime.responseheader.ResponseHeaderHandler; +import io.quarkus.resteasy.reactive.server.runtime.responsestatus.ResponseStatusHandler; import io.quarkus.resteasy.reactive.server.runtime.security.EagerSecurityHandler; import io.quarkus.resteasy.reactive.server.runtime.security.SecurityContextOverrideHandler; import io.quarkus.resteasy.reactive.server.spi.AnnotationsTransformerBuildItem; @@ -169,6 +173,61 @@ MinNettyAllocatorMaxOrderBuildItem setMinimalNettyMaxOrderSize() { return new MinNettyAllocatorMaxOrderBuildItem(3); } + @BuildStep + MethodScannerBuildItem responseStatusSupport() { + return new MethodScannerBuildItem(new MethodScanner() { + @Override + public List scan(MethodInfo method, ClassInfo actualEndpointClass, + Map methodContext) { + AnnotationStore annotationStore = (AnnotationStore) methodContext + .get(EndpointIndexer.METHOD_CONTEXT_ANNOTATION_STORE); + AnnotationInstance annotationInstance = annotationStore.getAnnotation(method, + DotNames.RESPONSE_STATUS_ANNOTATION); + if (annotationInstance == null) { + return Collections.emptyList(); + } + AnnotationValue responseStatusValue = annotationInstance.value(); + if (responseStatusValue == null) { + return Collections.emptyList(); + } + ResponseStatusHandler handler = new ResponseStatusHandler(); + handler.setStatus(responseStatusValue.asInt()); + return Collections.singletonList(new FixedHandlerChainCustomizer(handler, + HandlerChainCustomizer.Phase.AFTER_RESPONSE_CREATED)); + } + }); + } + + @BuildStep + MethodScannerBuildItem responseHeaderSupport() { + return new MethodScannerBuildItem(new MethodScanner() { + @Override + public List scan(MethodInfo method, ClassInfo actualEndpointClass, + Map methodContext) { + AnnotationStore annotationStore = (AnnotationStore) methodContext + .get(EndpointIndexer.METHOD_CONTEXT_ANNOTATION_STORE); + AnnotationInstance annotationInstance = annotationStore.getAnnotation(method, + DotNames.RESPONSE_HEADER_ANNOTATION); + if (annotationInstance == null) { + return Collections.emptyList(); + } + AnnotationValue responseHeaderValue = annotationInstance.value("headers"); + if (responseHeaderValue == null) { + return Collections.emptyList(); + } + AnnotationInstance[] headersValue = responseHeaderValue.asNestedArray(); + Map headers = new HashMap<>(); + for (AnnotationInstance headerInstance : headersValue) { + headers.put(headerInstance.value("name").asString(), headerInstance.value("value").asString()); + } + ResponseHeaderHandler handler = new ResponseHeaderHandler(); + handler.setHeaders(headers); + return Collections.singletonList(new FixedHandlerChainCustomizer(handler, + HandlerChainCustomizer.Phase.AFTER_RESPONSE_CREATED)); + } + }); + } + @BuildStep void vertxIntegration(BuildProducer writerBuildItemBuildProducer) { writerBuildItemBuildProducer.produce(new MessageBodyWriterBuildItem(ServerVertxBufferMessageBodyWriter.class.getName(), diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java new file mode 100644 index 0000000000000..cdb826e44b098 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java @@ -0,0 +1,50 @@ +package io.quarkus.resteasy.reactive.server.test.responseheader; + +import java.util.Map; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +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.resteasy.reactive.server.common.Header; +import io.quarkus.resteasy.reactive.server.common.ResponseHeader; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; + +public class ResponseHeaderTest { + + @RegisterExtension + static QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void should_return_added_headers() { + Map expectedHeaders = Map.of( + "Access-Control-Allow-Origin", "*", + "Keep-Alive", "timeout=5, max=997"); + RestAssured + .given() + .get("/test") + .then() + .statusCode(200) + .headers(expectedHeaders); + } + + @Path("/test") + public static class TestResource { + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*"), + @Header(name = "Keep-Alive", value = "timeout=5, max=997"), + }) + @GET + public Uni getTest() { + return Uni.createFrom().item("test"); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java new file mode 100644 index 0000000000000..a50c70062b665 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java @@ -0,0 +1,41 @@ +package io.quarkus.resteasy.reactive.server.test.responsestatus; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; + +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.resteasy.reactive.server.common.Status; +import io.quarkus.test.QuarkusUnitTest; +import io.restassured.RestAssured; +import io.smallrye.mutiny.Uni; + +public class ResponseStatusTest { + + @RegisterExtension + static QuarkusUnitTest TEST = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); + + @Test + public void should_return_changed_status() { + int expectedStatus = 201; + RestAssured + .given() + .get("/test") + .then() + .statusCode(expectedStatus); + } + + @Path("/test") + public static class TestResource { + + @Status(201) + @GET + public Uni getTest() { + return Uni.createFrom().item("test"); + } + } +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java new file mode 100644 index 0000000000000..f372eaa6b97e4 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java @@ -0,0 +1,31 @@ +package io.quarkus.resteasy.reactive.server.runtime.responseheader; + +import java.util.Map; + +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; + +public class ResponseHeaderHandler implements ServerRestHandler { + private Map headers; + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public Map getHeaders() { + return headers; + } + + @Override + public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { + Response response = requestContext.getResponse().get(); + if (headers != null) { + for (Map.Entry header : headers.entrySet()) { + response.getHeaders().add(header.getKey(), header.getValue()); + } + } + } + +} diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java new file mode 100644 index 0000000000000..1f4c1b588fc80 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.server.runtime.responsestatus; + +import javax.ws.rs.core.Response; + +import org.jboss.resteasy.reactive.server.core.LazyResponse; +import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; +import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; + +public class ResponseStatusHandler implements ServerRestHandler { + + private int status; + + public void setStatus(int status) { + this.status = status; + } + + public int getStatus() { + return status; + } + + @Override + public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { + Response response = requestContext.getResponse().get(); + + Response.ResponseBuilder responseBuilder = Response.fromResponse(response); + responseBuilder.status(status); + LazyResponse.Existing lazyResponse = new LazyResponse.Existing(responseBuilder.build()); + requestContext.setResponse(lazyResponse); + } +} From d2b98bdc138264d6bc5b7552d921b001d816358f Mon Sep 17 00:00:00 2001 From: Yelzhas Suleimenov Date: Fri, 17 Sep 2021 21:40:01 +0600 Subject: [PATCH 2/4] make @Header applicable to annotation only --- .../io/quarkus/resteasy/reactive/server/common/Header.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java index a1be59ca4da38..71b74fd5eb9a6 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java @@ -1,5 +1,9 @@ package io.quarkus.resteasy.reactive.server.common; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +@Target(ElementType.ANNOTATION_TYPE) public @interface Header { public String name(); From 69ebd69c39746d39b7394f8de393abe7209fcc29 Mon Sep 17 00:00:00 2001 From: Yelzhas Suleimenov Date: Sat, 18 Sep 2021 01:18:23 +0600 Subject: [PATCH 3/4] Annotations moved;Adding headers and status change logic rework --- .../reactive/server/deployment/DotNames.java | 5 +- .../deployment/ResteasyReactiveProcessor.java | 4 +- .../responseheader/ResponseHeaderTest.java | 71 +++++++++++++++++-- .../responsestatus/ResponseStatusTest.java | 62 ++++++++++++++-- .../responseheader/ResponseHeaderHandler.java | 7 +- .../responsestatus/ResponseStatusHandler.java | 10 +-- .../org/jboss/resteasy/reactive}/Header.java | 2 +- .../resteasy/reactive}/ResponseHeader.java | 2 +- .../org/jboss/resteasy/reactive}/Status.java | 2 +- .../core/ResteasyReactiveRequestContext.java | 20 ++++++ .../reactive/server/core/StreamingUtil.java | 12 +++- .../server/handlers/UniResponseHandler.java | 10 +++ 12 files changed, 172 insertions(+), 35 deletions(-) rename {extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common => independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive}/Header.java (79%) rename {extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common => independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive}/ResponseHeader.java (85%) rename {extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common => independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive}/Status.java (84%) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java index b873184763c96..b9e29ad437447 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/DotNames.java @@ -6,11 +6,10 @@ import java.nio.file.Path; import org.jboss.jandex.DotName; +import org.jboss.resteasy.reactive.ResponseHeader; +import org.jboss.resteasy.reactive.Status; import org.jboss.resteasy.reactive.multipart.FileUpload; -import io.quarkus.resteasy.reactive.server.common.ResponseHeader; -import io.quarkus.resteasy.reactive.server.common.Status; - final class DotNames { static final String POPULATE_METHOD_NAME = "populate"; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index 9423702689ac8..d5d2723f8e455 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -193,7 +193,7 @@ public List scan(MethodInfo method, ClassInfo actualEndp ResponseStatusHandler handler = new ResponseStatusHandler(); handler.setStatus(responseStatusValue.asInt()); return Collections.singletonList(new FixedHandlerChainCustomizer(handler, - HandlerChainCustomizer.Phase.AFTER_RESPONSE_CREATED)); + HandlerChainCustomizer.Phase.AFTER_METHOD_INVOKE)); } }); } @@ -223,7 +223,7 @@ public List scan(MethodInfo method, ClassInfo actualEndp ResponseHeaderHandler handler = new ResponseHeaderHandler(); handler.setHeaders(headers); return Collections.singletonList(new FixedHandlerChainCustomizer(handler, - HandlerChainCustomizer.Phase.AFTER_RESPONSE_CREATED)); + HandlerChainCustomizer.Phase.AFTER_METHOD_INVOKE)); } }); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java index cdb826e44b098..42a079e99d66c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java @@ -1,19 +1,23 @@ package io.quarkus.resteasy.reactive.server.test.responseheader; +import static org.junit.jupiter.api.Assertions.*; + import java.util.Map; import javax.ws.rs.GET; import javax.ws.rs.Path; +import org.jboss.resteasy.reactive.Header; +import org.jboss.resteasy.reactive.ResponseHeader; 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.resteasy.reactive.server.common.Header; -import io.quarkus.resteasy.reactive.server.common.ResponseHeader; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; +import io.restassured.http.Headers; +import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; public class ResponseHeaderTest { @@ -23,18 +27,46 @@ public class ResponseHeaderTest { .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); @Test - public void should_return_added_headers() { + public void should_return_added_headers_uni() { + Map expectedHeaders = Map.of( + "Access-Control-Allow-Origin", "*", + "Keep-Alive", "timeout=5, max=997"); + RestAssured + .given() + .get("/test/multi") + .then() + .statusCode(200) + .headers(expectedHeaders); + } + + @Test + public void should_return_added_headers_multi() { Map expectedHeaders = Map.of( "Access-Control-Allow-Origin", "*", "Keep-Alive", "timeout=5, max=997"); RestAssured .given() - .get("/test") + .get("/test/multi") .then() .statusCode(200) .headers(expectedHeaders); } + @Test + public void should_throw_exception_without_headers_uni() { + Headers headers = RestAssured.given().get("/test/exception_uni") + .then().extract().headers(); + assertFalse(headers.hasHeaderWithName("Access-Control-Allow-Origin")); + + } + + @Test + public void should_throw_exception_without_headers_multi() { + Headers headers = RestAssured.given().get("/test/exception_multi") + .then().extract().headers(); + assertFalse(headers.hasHeaderWithName("Access-Control-Allow-Origin")); + } + @Path("/test") public static class TestResource { @@ -43,8 +75,37 @@ public static class TestResource { @Header(name = "Keep-Alive", value = "timeout=5, max=997"), }) @GET - public Uni getTest() { + @Path(("/uni")) + public Uni getTestUni() { return Uni.createFrom().item("test"); } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*"), + @Header(name = "Keep-Alive", value = "timeout=5, max=997"), + }) + @GET + @Path("/multi") + public Multi getTestMulti() { + return Multi.createFrom().item("test"); + } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*") + }) + @GET + @Path(("/exception_uni")) + public Uni throwExceptionUni() { + return Uni.createFrom().failure(new IllegalArgumentException()); + } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*") + }) + @GET + @Path("/exception_multi") + public Multi throwExceptionMulti() { + return Multi.createFrom().failure(new IllegalArgumentException()); + } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java index a50c70062b665..2eb7fc587e0fd 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java @@ -3,14 +3,15 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; +import org.jboss.resteasy.reactive.Status; 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.resteasy.reactive.server.common.Status; import io.quarkus.test.QuarkusUnitTest; import io.restassured.RestAssured; +import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; public class ResponseStatusTest { @@ -20,11 +21,41 @@ public class ResponseStatusTest { .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)); @Test - public void should_return_changed_status() { + public void should_return_changed_status_uni() { int expectedStatus = 201; RestAssured .given() - .get("/test") + .get("/test/uni") + .then() + .statusCode(expectedStatus); + } + + @Test + public void should_return_changed_status_multi() { + int expectedStatus = 201; + RestAssured + .given() + .get("/test/multi") + .then() + .statusCode(expectedStatus); + } + + @Test + public void should_not_change_status_uni() { + int expectedStatus = 500; + RestAssured + .given() + .get("/test/exception_uni") + .then() + .statusCode(expectedStatus); + } + + @Test + public void should_not_change_status_multi() { + int expectedStatus = 500; + RestAssured + .given() + .get("/test/exception_multi") .then() .statusCode(expectedStatus); } @@ -34,8 +65,31 @@ public static class TestResource { @Status(201) @GET - public Uni getTest() { + @Path("/uni") + public Uni getTestUni() { return Uni.createFrom().item("test"); } + + @Status(201) + @GET + @Path("/multi") + public Multi getTestMulti() { + return Multi.createFrom().item("test"); + } + + @Status(201) + @GET + @Path(("/exception_uni")) + public Uni throwExceptionUni() { + return Uni.createFrom().failure(new IllegalArgumentException()); + } + + @Status(201) + @GET + @Path("/exception_multi") + public Multi throwExceptionMulti() { + return Multi.createFrom().failure(new IllegalArgumentException()); + } + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java index f372eaa6b97e4..e0aa8fef30143 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java @@ -2,8 +2,6 @@ import java.util.Map; -import javax.ws.rs.core.Response; - import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; @@ -20,11 +18,8 @@ public Map getHeaders() { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { - Response response = requestContext.getResponse().get(); if (headers != null) { - for (Map.Entry header : headers.entrySet()) { - response.getHeaders().add(header.getKey(), header.getValue()); - } + requestContext.setResponseHeaders(headers); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java index 1f4c1b588fc80..b8d5f6c12695c 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java @@ -1,8 +1,5 @@ package io.quarkus.resteasy.reactive.server.runtime.responsestatus; -import javax.ws.rs.core.Response; - -import org.jboss.resteasy.reactive.server.core.LazyResponse; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; @@ -20,11 +17,6 @@ public int getStatus() { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { - Response response = requestContext.getResponse().get(); - - Response.ResponseBuilder responseBuilder = Response.fromResponse(response); - responseBuilder.status(status); - LazyResponse.Existing lazyResponse = new LazyResponse.Existing(responseBuilder.build()); - requestContext.setResponse(lazyResponse); + requestContext.setResponseStatus(status); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Header.java similarity index 79% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java rename to independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Header.java index 71b74fd5eb9a6..a54c37a0315c0 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Header.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Header.java @@ -1,4 +1,4 @@ -package io.quarkus.resteasy.reactive.server.common; +package org.jboss.resteasy.reactive; import java.lang.annotation.ElementType; import java.lang.annotation.Target; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/ResponseHeader.java similarity index 85% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java rename to independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/ResponseHeader.java index fce41d6c1dbcf..98c22506d325d 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/ResponseHeader.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/ResponseHeader.java @@ -1,4 +1,4 @@ -package io.quarkus.resteasy.reactive.server.common; +package org.jboss.resteasy.reactive; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Status.java similarity index 84% rename from extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java rename to independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Status.java index 62fec747e612d..2d6112b0a6243 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/common-runtime/src/main/java/io/quarkus/resteasy/reactive/server/common/Status.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/Status.java @@ -1,4 +1,4 @@ -package io.quarkus.resteasy.reactive.server.common; +package org.jboss.resteasy.reactive; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java index aaa97a3027348..dc0ec7d651b2c 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java @@ -14,6 +14,7 @@ import java.util.Deque; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; import java.util.regex.Matcher; import javax.ws.rs.core.Cookie; @@ -132,6 +133,9 @@ public abstract class ResteasyReactiveRequestContext */ private List matchedURIs; + private Map responseHeaders; + private Integer responseStatus; + private AsyncResponseImpl asyncResponse; private SseEventSinkImpl sseEventSink; private List pathSegments; @@ -230,6 +234,22 @@ public void setMaxPathParams(int maxPathParams) { } } + public Map getResponseHeaders() { + return responseHeaders; + } + + public void setResponseHeaders(Map responseHeaders) { + this.responseHeaders = responseHeaders; + } + + public Integer getResponseStatus() { + return responseStatus; + } + + public void setResponseStatus(Integer responseStatus) { + this.responseStatus = responseStatus; + } + public String getPathParam(int index) { return doGetPathParam(index, pathParamValues); } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java index bbb7661451690..594ccea581648 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import javax.ws.rs.RuntimeType; @@ -56,7 +57,7 @@ private static byte[] serialiseEntity(ResteasyReactiveRequestContext context, Ob ByteArrayOutputStream baos = new ByteArrayOutputStream(); boolean wrote = false; for (MessageBodyWriter writer : writers) { - // Spec(API) says we should use class/type/mediaType but doesn't talk about annotations + // Spec(API) says we should use class/type/mediaType but doesn't talk about annotations if (writer.isWriteable(entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType)) { // FIXME: spec doesn't really say what headers we should use here writer.writeTo(entity, entityClass, entityType, Serialisers.NO_ANNOTATION, mediaType, @@ -76,10 +77,15 @@ public static void setHeaders(ResteasyReactiveRequestContext context, ServerHttp // FIXME: spec says we should flush the headers when first message is sent or when the resource method returns, whichever // happens first if (!response.headWritten()) { - response.setStatusCode(Response.Status.OK.getStatusCode()); + response.setStatusCode( + context.getResponseStatus() != null ? context.getResponseStatus() : Response.Status.OK.getStatusCode()); response.setResponseHeader(HttpHeaders.CONTENT_TYPE, context.getResponseContentType().toString()); response.setChunked(true); - // FIXME: other headers? + if (context.getResponseHeaders() != null) { + for (Map.Entry header : context.getResponseHeaders().entrySet()) { + response.addResponseHeader(header.getKey(), header.getValue()); + } + } } } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java index b4e720adbc52a..9856ec79b3513 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java @@ -1,7 +1,9 @@ package org.jboss.resteasy.reactive.server.handlers; import io.smallrye.mutiny.Uni; +import java.util.Map; import java.util.function.Consumer; +import javax.ws.rs.core.Response; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; @@ -18,6 +20,14 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti @Override public void accept(Object v) { requestContext.setResult(v); + requestContext.serverResponse().setStatusCode( + requestContext.getResponseStatus() != null ? requestContext.getResponseStatus() + : Response.Status.OK.getStatusCode()); + if (requestContext.getResponseHeaders() != null) { + for (Map.Entry header : requestContext.getResponseHeaders().entrySet()) { + requestContext.serverResponse().addResponseHeader(header.getKey(), header.getValue()); + } + } requestContext.resume(); } }, new Consumer() { From 95ae237a391aa63bf923b4390a7125a6d493113f Mon Sep 17 00:00:00 2001 From: Yelzhas Suleimenov Date: Sat, 18 Sep 2021 14:41:57 +0600 Subject: [PATCH 4/4] Additional headers and status change implementation changed; additional tests added; --- .../runtime/ServletRequestContext.java | 12 +++ .../deployment/ResteasyReactiveProcessor.java | 4 +- .../responseheader/ResponseHeaderTest.java | 79 +++++++++++++++++++ .../responsestatus/ResponseStatusTest.java | 71 +++++++++++++++++ .../responseheader/ResponseHeaderHandler.java | 4 +- .../responsestatus/ResponseStatusHandler.java | 2 +- .../core/ResteasyReactiveRequestContext.java | 20 ----- .../reactive/server/core/StreamingUtil.java | 11 +-- .../CompletionStageResponseHandler.java | 1 + .../server/handlers/ExceptionHandler.java | 1 + .../handlers/PublisherResponseHandler.java | 1 + .../server/handlers/UniResponseHandler.java | 11 +-- .../server/spi/ServerHttpResponse.java | 4 + .../VertxResteasyReactiveRequestContext.java | 10 +++ 14 files changed, 189 insertions(+), 42 deletions(-) diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java index f8f54895427da..88eef5573f02b 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-servlet/runtime/src/main/java/io/quarkus/resteasy/reactive/server/servlet/runtime/ServletRequestContext.java @@ -323,6 +323,11 @@ public ServerHttpResponse setStatusCode(int code) { return this; } + @Override + public int getStatusCode() { + return response.getStatus(); + } + @Override public ServerHttpResponse end() { try { @@ -388,6 +393,13 @@ public ServerHttpResponse setResponseHeader(CharSequence name, Iterable> getAllResponseHeaders() { List> ret = new ArrayList<>(); diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java index d5d2723f8e455..2cd4d8cd01ae5 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/main/java/io/quarkus/resteasy/reactive/server/deployment/ResteasyReactiveProcessor.java @@ -193,7 +193,7 @@ public List scan(MethodInfo method, ClassInfo actualEndp ResponseStatusHandler handler = new ResponseStatusHandler(); handler.setStatus(responseStatusValue.asInt()); return Collections.singletonList(new FixedHandlerChainCustomizer(handler, - HandlerChainCustomizer.Phase.AFTER_METHOD_INVOKE)); + HandlerChainCustomizer.Phase.BEFORE_METHOD_INVOKE)); } }); } @@ -223,7 +223,7 @@ public List scan(MethodInfo method, ClassInfo actualEndp ResponseHeaderHandler handler = new ResponseHeaderHandler(); handler.setHeaders(headers); return Collections.singletonList(new FixedHandlerChainCustomizer(handler, - HandlerChainCustomizer.Phase.AFTER_METHOD_INVOKE)); + HandlerChainCustomizer.Phase.BEFORE_METHOD_INVOKE)); } }); } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java index 42a079e99d66c..92ffc9a9dad52 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responseheader/ResponseHeaderTest.java @@ -3,6 +3,8 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -52,6 +54,32 @@ public void should_return_added_headers_multi() { .headers(expectedHeaders); } + @Test + public void should_return_added_headers_completion() { + Map expectedHeaders = Map.of( + "Access-Control-Allow-Origin", "*", + "Keep-Alive", "timeout=5, max=997"); + RestAssured + .given() + .get("/test/completion") + .then() + .statusCode(200) + .headers(expectedHeaders); + } + + @Test + public void should_return_added_headers_plain() { + Map expectedHeaders = Map.of( + "Access-Control-Allow-Origin", "*", + "Keep-Alive", "timeout=5, max=997"); + RestAssured + .given() + .get("/test/plain") + .then() + .statusCode(200) + .headers(expectedHeaders); + } + @Test public void should_throw_exception_without_headers_uni() { Headers headers = RestAssured.given().get("/test/exception_uni") @@ -67,6 +95,20 @@ public void should_throw_exception_without_headers_multi() { assertFalse(headers.hasHeaderWithName("Access-Control-Allow-Origin")); } + @Test + public void should_throw_exception_without_headers_completion() { + Headers headers = RestAssured.given().get("/test/exception_completion") + .then().extract().headers(); + assertFalse(headers.hasHeaderWithName("Access-Control-Allow-Origin")); + } + + @Test + public void should_throw_exception_without_headers_plain() { + Headers headers = RestAssured.given().get("/test/exception_plain") + .then().extract().headers(); + assertFalse(headers.hasHeaderWithName("Access-Control-Allow-Origin")); + } + @Path("/test") public static class TestResource { @@ -90,6 +132,26 @@ public Multi getTestMulti() { return Multi.createFrom().item("test"); } + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*"), + @Header(name = "Keep-Alive", value = "timeout=5, max=997"), + }) + @GET + @Path("/completion") + public CompletionStage getTestCompletion() { + return CompletableFuture.supplyAsync(() -> "test"); + } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*"), + @Header(name = "Keep-Alive", value = "timeout=5, max=997"), + }) + @GET + @Path("/plain") + public String getTestPlain() { + return "test"; + } + @ResponseHeader(headers = { @Header(name = "Access-Control-Allow-Origin", value = "*") }) @@ -107,5 +169,22 @@ public Uni throwExceptionUni() { public Multi throwExceptionMulti() { return Multi.createFrom().failure(new IllegalArgumentException()); } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*") + }) + @Path("/exception_completion") + public CompletionStage throwExceptionCompletion() { + return CompletableFuture.failedFuture(new IllegalArgumentException()); + } + + @ResponseHeader(headers = { + @Header(name = "Access-Control-Allow-Origin", value = "*") + }) + @GET + @Path("/exception_plain") + public String throwExceptionPlain() { + throw new IllegalArgumentException(); + } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java index 2eb7fc587e0fd..3389d9ce9c23a 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/deployment/src/test/java/io/quarkus/resteasy/reactive/server/test/responsestatus/ResponseStatusTest.java @@ -1,5 +1,8 @@ package io.quarkus.resteasy.reactive.server.test.responsestatus; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + import javax.ws.rs.GET; import javax.ws.rs.Path; @@ -40,6 +43,26 @@ public void should_return_changed_status_multi() { .statusCode(expectedStatus); } + @Test + public void should_return_changed_status_completion() { + int expectedStatus = 201; + RestAssured + .given() + .get("/test/completion") + .then() + .statusCode(expectedStatus); + } + + @Test + public void should_return_changed_status_plain() { + int expectedStatus = 201; + RestAssured + .given() + .get("/test/plain") + .then() + .statusCode(expectedStatus); + } + @Test public void should_not_change_status_uni() { int expectedStatus = 500; @@ -60,6 +83,26 @@ public void should_not_change_status_multi() { .statusCode(expectedStatus); } + @Test + public void should_not_change_status_completion() { + int expectedStatus = 500; + RestAssured + .given() + .get("/test/exception_completion") + .then() + .statusCode(expectedStatus); + } + + @Test + public void should_not_change_status_plain() { + int expectedStatus = 500; + RestAssured + .given() + .get("/test/exception_plain") + .then() + .statusCode(expectedStatus); + } + @Path("/test") public static class TestResource { @@ -77,6 +120,20 @@ public Multi getTestMulti() { return Multi.createFrom().item("test"); } + @Status(201) + @GET + @Path("/completion") + public CompletionStage getTestCompletion() { + return CompletableFuture.supplyAsync(() -> "test"); + } + + @Status(201) + @GET + @Path("/plain") + public String getTestPlain() { + return "test"; + } + @Status(201) @GET @Path(("/exception_uni")) @@ -91,5 +148,19 @@ public Multi throwExceptionMulti() { return Multi.createFrom().failure(new IllegalArgumentException()); } + @Status(201) + @GET + @Path("/exception_completion") + public CompletionStage throwExceptionCompletion() { + return CompletableFuture.failedFuture(new IllegalArgumentException()); + } + + @Status(201) + @GET + @Path("/exception_plain") + public String throwExceptionPlain() { + throw new IllegalArgumentException(); + } + } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java index e0aa8fef30143..e734b32f92741 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responseheader/ResponseHeaderHandler.java @@ -19,7 +19,9 @@ public Map getHeaders() { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { if (headers != null) { - requestContext.setResponseHeaders(headers); + for (Map.Entry header : headers.entrySet()) { + requestContext.serverResponse().setResponseHeader(header.getKey(), header.getValue()); + } } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java index b8d5f6c12695c..bbf672c17bf46 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/responsestatus/ResponseStatusHandler.java @@ -17,6 +17,6 @@ public int getStatus() { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { - requestContext.setResponseStatus(status); + requestContext.serverResponse().setStatusCode(status); } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java index dc0ec7d651b2c..aaa97a3027348 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java @@ -14,7 +14,6 @@ import java.util.Deque; import java.util.LinkedList; import java.util.List; -import java.util.Map; import java.util.concurrent.Executor; import java.util.regex.Matcher; import javax.ws.rs.core.Cookie; @@ -133,9 +132,6 @@ public abstract class ResteasyReactiveRequestContext */ private List matchedURIs; - private Map responseHeaders; - private Integer responseStatus; - private AsyncResponseImpl asyncResponse; private SseEventSinkImpl sseEventSink; private List pathSegments; @@ -234,22 +230,6 @@ public void setMaxPathParams(int maxPathParams) { } } - public Map getResponseHeaders() { - return responseHeaders; - } - - public void setResponseHeaders(Map responseHeaders) { - this.responseHeaders = responseHeaders; - } - - public Integer getResponseStatus() { - return responseStatus; - } - - public void setResponseStatus(Integer responseStatus) { - this.responseStatus = responseStatus; - } - public String getPathParam(int index) { return doGetPathParam(index, pathParamValues); } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java index 594ccea581648..a4983bcd42ddd 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/StreamingUtil.java @@ -4,7 +4,6 @@ import java.io.IOException; import java.lang.reflect.Type; import java.nio.charset.StandardCharsets; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import javax.ws.rs.RuntimeType; @@ -77,15 +76,11 @@ public static void setHeaders(ResteasyReactiveRequestContext context, ServerHttp // FIXME: spec says we should flush the headers when first message is sent or when the resource method returns, whichever // happens first if (!response.headWritten()) { - response.setStatusCode( - context.getResponseStatus() != null ? context.getResponseStatus() : Response.Status.OK.getStatusCode()); + if (response.getStatusCode() == 0) { + response.setStatusCode(Response.Status.OK.getStatusCode()); + } response.setResponseHeader(HttpHeaders.CONTENT_TYPE, context.getResponseContentType().toString()); response.setChunked(true); - if (context.getResponseHeaders() != null) { - for (Map.Entry header : context.getResponseHeaders().entrySet()) { - response.addResponseHeader(header.getKey(), header.getValue()); - } - } } } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java index 78730e984c11d..9be1673bc28c0 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/CompletionStageResponseHandler.java @@ -15,6 +15,7 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti result.handle((v, t) -> { if (t != null) { + requestContext.serverResponse().clearResponseHeaders(); requestContext.handleException(t); } else { requestContext.setResult(v); diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ExceptionHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ExceptionHandler.java index dc6a06aa29c66..4791a382a26e0 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ExceptionHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/ExceptionHandler.java @@ -10,6 +10,7 @@ public class ExceptionHandler implements ServerRestHandler { @Override public void handle(ResteasyReactiveRequestContext requestContext) throws Exception { + requestContext.serverResponse().clearResponseHeaders(); requestContext.mapExceptionIfPresent(); } } diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/PublisherResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/PublisherResponseHandler.java index edb1625f17b8c..84c8d7bf6a726 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/PublisherResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/PublisherResponseHandler.java @@ -147,6 +147,7 @@ public void onError(Throwable t) { protected void handleException(ResteasyReactiveRequestContext requestContext, Throwable t) { // in truth we can only send an exception if we haven't sent the headers yet, otherwise // it will appear to be an SSE value, which is incorrect, so we should only log it and close the connection + requestContext.serverResponse().clearResponseHeaders(); if (requestContext.serverResponse().headWritten()) { log.error("Exception in SSE server handling, impossible to send it to client", t); } else { diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java index 9856ec79b3513..fb05884ba388d 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/handlers/UniResponseHandler.java @@ -1,9 +1,7 @@ package org.jboss.resteasy.reactive.server.handlers; import io.smallrye.mutiny.Uni; -import java.util.Map; import java.util.function.Consumer; -import javax.ws.rs.core.Response; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.spi.ServerRestHandler; @@ -20,19 +18,12 @@ public void handle(ResteasyReactiveRequestContext requestContext) throws Excepti @Override public void accept(Object v) { requestContext.setResult(v); - requestContext.serverResponse().setStatusCode( - requestContext.getResponseStatus() != null ? requestContext.getResponseStatus() - : Response.Status.OK.getStatusCode()); - if (requestContext.getResponseHeaders() != null) { - for (Map.Entry header : requestContext.getResponseHeaders().entrySet()) { - requestContext.serverResponse().addResponseHeader(header.getKey(), header.getValue()); - } - } requestContext.resume(); } }, new Consumer() { @Override public void accept(Throwable t) { + requestContext.serverResponse().clearResponseHeaders(); requestContext.resume(t, true); } }); diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpResponse.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpResponse.java index ae4f0b3b33d45..20b4c290eb934 100644 --- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpResponse.java +++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerHttpResponse.java @@ -10,6 +10,8 @@ public interface ServerHttpResponse { ServerHttpResponse setStatusCode(int code); + int getStatusCode(); + ServerHttpResponse end(); boolean headWritten(); @@ -24,6 +26,8 @@ public interface ServerHttpResponse { ServerHttpResponse setResponseHeader(CharSequence name, Iterable values); + void clearResponseHeaders(); + Iterable> getAllResponseHeaders(); boolean closed(); diff --git a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java index 25934998c399c..5fbafb14760b2 100644 --- a/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java +++ b/independent-projects/resteasy-reactive/server/vertx/src/main/java/org/jboss/resteasy/reactive/server/vertx/VertxResteasyReactiveRequestContext.java @@ -306,6 +306,11 @@ public ServerHttpResponse setStatusCode(int code) { return this; } + @Override + public int getStatusCode() { + return response.getStatusCode(); + } + @Override public ServerHttpResponse end() { if (!response.ended()) { @@ -349,6 +354,11 @@ public ServerHttpResponse setResponseHeader(CharSequence name, Iterable> getAllResponseHeaders() { return response.headers();