From 5cd988ae6a21ffe01e6576768ba6e47bcabf1f62 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 29 Oct 2024 06:51:50 +0100 Subject: [PATCH 1/6] doc: proceed with RequestFilter and CompletableFuture --- ...terCompletableFutureFutureProceedTest.java | 75 +++++++++++++++++++ .../http/annotation/RequestFilter.java | 2 + .../filtermethods/filtermethodproceed.adoc | 7 ++ src/main/docs/guide/toc.yml | 1 + 4 files changed, 85 insertions(+) create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java create mode 100644 src/main/docs/guide/httpServer/filters/filtermethods/filtermethodproceed.adoc diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java new file mode 100644 index 0000000000..2eb707ca02 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java @@ -0,0 +1,75 @@ +package io.micronaut.http.server.tck.tests.filter; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.*; +import io.micronaut.http.tck.AssertionUtils; +import io.micronaut.http.tck.HttpResponseAssertion; +import io.micronaut.http.tck.TestScenario; +import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" +}) +public class RequestFilterCompletableFutureFutureProceedTest { + public static final String SPEC_NAME = "RequestFilterCompleteableFutureProceedTest"; + + @Test + public void requestFilterProceedWithCompletableFuture() throws IOException { + TestScenario.builder() + .specName(SPEC_NAME) + .request(HttpRequest.GET("/foobar").header("X-FOOBAR", "123")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.ACCEPTED) + .build())) + .run(); + + TestScenario.builder() + .specName(SPEC_NAME) + .request(HttpRequest.GET("/foobar")) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.UNAUTHORIZED) + .build())) + .run(); + } + + /* + //tag::clazz[] + @ServerFilter(ServerFilter.MATCH_ALL_PATTERN) + class FooBarFilter { + //end::clazz[] + */ + @Requires(property = "spec.name", value = SPEC_NAME) + @ServerFilter(ServerFilter.MATCH_ALL_PATTERN) + static class FooBarFilter { + //tag::methods[] + @RequestFilter + CompletableFuture<@Nullable HttpResponse> filter(@NonNull HttpRequest request) { + if (request.getHeaders().contains("X-FOOBAR")) { + // proceed + return CompletableFuture.completedFuture(null); + } else { + return CompletableFuture.completedFuture(HttpResponse.unauthorized()); + } + } + } + //end::methods[] + + @Requires(property = "spec.name", value = SPEC_NAME) + @Controller("/foobar") + static class FooBarController { + @Get + @Status(HttpStatus.ACCEPTED) + void index() { + // no-op + } + } +} diff --git a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java index de7001c63c..d77ee1f722 100644 --- a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java +++ b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java @@ -18,6 +18,7 @@ import io.micronaut.context.annotation.AliasFor; import io.micronaut.context.annotation.Executable; import io.micronaut.core.annotation.EntryPoint; +import io.micronaut.core.annotation.Nullable; import io.micronaut.core.io.buffer.ByteBuffer; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; @@ -69,6 +70,7 @@ *
  • A {@link HttpResponse} to skip execution of the request
  • *
  • A {@link Publisher} (or other reactive type) that produces any of these return types, to * delay further execution
  • + *
  • A {@link java.util.concurrent.CompletableFuture}. Suppose you must write a filter that proceeds with the request in some scenarios. You can use {@code CompletableFuture<@ Nullable HttpResponse>} as the return type. Then, to proceed with the request, return {@code CompletableFuture.completedFuture(null)}.
  • * * * @since 4.0.0 diff --git a/src/main/docs/guide/httpServer/filters/filtermethods/filtermethodproceed.adoc b/src/main/docs/guide/httpServer/filters/filtermethods/filtermethodproceed.adoc new file mode 100644 index 0000000000..0dfa2eaa95 --- /dev/null +++ b/src/main/docs/guide/httpServer/filters/filtermethods/filtermethodproceed.adoc @@ -0,0 +1,7 @@ +If you need to write a filter, e.g., security-related, which needs to proceed with requests in some scenarios or stop the request execution +and return an HTTP Response directly in the filter; you can use, for example, a `CompletableFuture` as the filter method's response type. + +[source, java] +---- +include::http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java[tags=clazz;methods,indent=0] +---- \ No newline at end of file diff --git a/src/main/docs/guide/toc.yml b/src/main/docs/guide/toc.yml index 4aa35573b5..c40b29a1df 100644 --- a/src/main/docs/guide/toc.yml +++ b/src/main/docs/guide/toc.yml @@ -140,6 +140,7 @@ httpServer: filtermethods: title: Filter Methods filtermethodsexample: Server Filter with Filter Methods + filtermethodproceed: Proceeding with Filter Methods errorStates: Error States continuations: Continuations order: Filter Order From ec87ebf869b6ceb6a765599fd7e30fd3bdacaec3 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 29 Oct 2024 07:46:18 +0100 Subject: [PATCH 2/6] Update http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java --- ...tFilterCompletableFutureFutureProceedTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java index 2eb707ca02..d401405ff6 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletableFutureFutureProceedTest.java @@ -1,3 +1,18 @@ +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.micronaut.http.server.tck.tests.filter; import io.micronaut.context.annotation.Requires; From 5faed4b4417faa1099d47dfe3b3b13416e22f123 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 29 Oct 2024 09:32:05 +0100 Subject: [PATCH 3/6] remove unused import --- .../main/java/io/micronaut/http/annotation/RequestFilter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java index d77ee1f722..2afcf1a07a 100644 --- a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java +++ b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java @@ -18,7 +18,6 @@ import io.micronaut.context.annotation.AliasFor; import io.micronaut.context.annotation.Executable; import io.micronaut.core.annotation.EntryPoint; -import io.micronaut.core.annotation.Nullable; import io.micronaut.core.io.buffer.ByteBuffer; import io.micronaut.http.HttpMethod; import io.micronaut.http.HttpRequest; From d70ee12c4cf9ff20a5c11b4bd258b47b3ea7db3a Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 29 Oct 2024 09:34:38 +0100 Subject: [PATCH 4/6] added CompletionStage as a possible return type --- ...ilterCompletionStageFutureProceedTest.java | 77 +++++++++++++++++++ .../http/annotation/RequestFilter.java | 1 + 2 files changed, 78 insertions(+) create mode 100644 http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java new file mode 100644 index 0000000000..aecd6b8390 --- /dev/null +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java @@ -0,0 +1,77 @@ +package io.micronaut.http.server.tck.tests.filter; + +import io.micronaut.context.annotation.Requires; +import io.micronaut.core.annotation.NonNull; +import io.micronaut.core.annotation.Nullable; +import io.micronaut.http.HttpRequest; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.annotation.*; +import io.micronaut.http.tck.AssertionUtils; +import io.micronaut.http.tck.HttpResponseAssertion; +import io.micronaut.http.tck.TestScenario; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +@SuppressWarnings({ + "java:S5960", // We're allowed assertions, as these are used in tests only + "checkstyle:MissingJavadocType", + "checkstyle:DesignForExtension" +}) +public class RequestFilterCompletionStageFutureProceedTest { + public static final String SPEC_NAME = "RequestFilterCompletionStageFutureProceedTest"; + + @Test + public void requestFilterProceedWithCompletableFuture() throws IOException { + TestScenario.builder() + .specName(SPEC_NAME) + .request(HttpRequest.GET("/foobar").header("X-FOOBAR", "123")) + .assertion((server, request) -> AssertionUtils.assertDoesNotThrow(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.ACCEPTED) + .build())) + .run(); + + TestScenario.builder() + .specName(SPEC_NAME) + .request(HttpRequest.GET("/foobar")) + .assertion((server, request) -> AssertionUtils.assertThrows(server, request, HttpResponseAssertion.builder() + .status(HttpStatus.UNAUTHORIZED) + .build())) + .run(); + } + + /* + //tag::clazz[] + @ServerFilter(ServerFilter.MATCH_ALL_PATTERN) + class FooBarFilter { + //end::clazz[] + */ + @Requires(property = "spec.name", value = SPEC_NAME) + @ServerFilter(ServerFilter.MATCH_ALL_PATTERN) + static class FooBarFilter { + //tag::methods[] + @RequestFilter + CompletionStage<@Nullable HttpResponse> filter(@NonNull HttpRequest request) { + if (request.getHeaders().contains("X-FOOBAR")) { + // proceed + return CompletableFuture.completedFuture(null); + } else { + return CompletableFuture.completedFuture(HttpResponse.unauthorized()); + } + } + } + //end::methods[] + + @Requires(property = "spec.name", value = SPEC_NAME) + @Controller("/foobar") + static class FooBarController { + @Get + @Status(HttpStatus.ACCEPTED) + void index() { + // no-op + } + } +} diff --git a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java index 2afcf1a07a..e69b94808f 100644 --- a/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java +++ b/http/src/main/java/io/micronaut/http/annotation/RequestFilter.java @@ -69,6 +69,7 @@ *
  • A {@link HttpResponse} to skip execution of the request
  • *
  • A {@link Publisher} (or other reactive type) that produces any of these return types, to * delay further execution
  • + *
  • A {@link java.util.concurrent.CompletionStage}. *
  • A {@link java.util.concurrent.CompletableFuture}. Suppose you must write a filter that proceeds with the request in some scenarios. You can use {@code CompletableFuture<@ Nullable HttpResponse>} as the return type. Then, to proceed with the request, return {@code CompletableFuture.completedFuture(null)}.
  • * * From 789f6e366d0d523752a8324b92ff2e3ea28fc72f Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Tue, 29 Oct 2024 10:14:24 +0100 Subject: [PATCH 5/6] Update http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java --- ...tFilterCompletionStageFutureProceedTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java index aecd6b8390..1928ce3390 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java @@ -1,4 +1,19 @@ -package io.micronaut.http.server.tck.tests.filter; +/* + * Copyright 2017-2024 original authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package io.micronaut.http.server.tck.tests.filter; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.NonNull; From beeb6e22002f23338a6f991dee083862d7ce3c61 Mon Sep 17 00:00:00 2001 From: Sergio del Amo Date: Wed, 30 Oct 2024 12:22:25 +0100 Subject: [PATCH 6/6] fix license --- .../filter/RequestFilterCompletionStageFutureProceedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java index 1928ce3390..c5f9c074a3 100644 --- a/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java +++ b/http-server-tck/src/main/java/io/micronaut/http/server/tck/tests/filter/RequestFilterCompletionStageFutureProceedTest.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package io.micronaut.http.server.tck.tests.filter; +package io.micronaut.http.server.tck.tests.filter; import io.micronaut.context.annotation.Requires; import io.micronaut.core.annotation.NonNull;