diff --git a/docs/src/main/asciidoc/http-reference.adoc b/docs/src/main/asciidoc/http-reference.adoc index 3f281b8f220fef..902960b0967245 100644 --- a/docs/src/main/asciidoc/http-reference.adoc +++ b/docs/src/main/asciidoc/http-reference.adoc @@ -149,11 +149,23 @@ To enable HTTP headers to be sent on every response, add the following propertie [source, properties] ---- -quarkus.http.header."X-Content-Type-Options"=nosniff +quarkus.http.header."X-Content-Type-Options".value=nosniff ---- This will include the `X-Content-Type-Options: nosniff` HTTP Header on responses for requests performed on any resource in the application. +You can also specify a `path` pattern and the HTTP `methods` the header needs to be applied: + +[source, properties] +---- +quarkus.http.header.Pragma.value=no-cache +quarkus.http.header.Pragma.path=/headers/pragma +quarkus.http.header.Pragma.methods=GET,HEAD +---- + +This will apply the `Pragma` header only when the `/headers/pragma` resource is called with a `GET` or a `HEAD` method + +include::{generated-dir}/config/quarkus-vertx-http-config-group-header-config.adoc[leveloffset=+1, opts=optional] == HTTP/2 Support diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HeaderConfig.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HeaderConfig.java new file mode 100644 index 00000000000000..98a247bf2f0cd5 --- /dev/null +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HeaderConfig.java @@ -0,0 +1,32 @@ +package io.quarkus.vertx.http.runtime; + +import java.util.List; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigGroup; +import io.quarkus.runtime.annotations.ConfigItem; + +/** + * Configuration that allows for setting an HTTP header + */ +@ConfigGroup +public class HeaderConfig { + + /** + * The path this header should be applied + */ + @ConfigItem(defaultValue = "/*") + public String path; + + /** + * The value for this header configuration + */ + @ConfigItem + public String value; + + /** + * The HTTP methods for this header configuration + */ + @ConfigItem + public Optional> methods; +} diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java index cfce23031bd7d3..8c95dad8119ba8 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/HttpConfiguration.java @@ -249,7 +249,7 @@ public class HttpConfiguration { * Additional HTTP Headers always sent in the response */ @ConfigItem - public Map header; + public Map header; public ProxyConfig proxy; diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index 52599772a388db..c0b7628fe2ab6f 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -13,6 +13,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -227,7 +228,8 @@ public static void startServerAfterFailedStart() { doServerStart(vertx, buildConfig, config, LaunchMode.DEVELOPMENT, new Supplier() { @Override public Integer get() { - return ProcessorInfo.availableProcessors() * 2; //this is dev mode, so the number of IO threads not always being 100% correct does not really matter in this case + return ProcessorInfo.availableProcessors() + * 2; //this is dev mode, so the number of IO threads not always being 100% correct does not really matter in this case } }, null, false); } catch (Exception e) { @@ -361,13 +363,32 @@ public void handle(Void e) { }); } // Headers sent on any request, regardless of the response - Map headers = httpConfiguration.header; + Map headers = httpConfiguration.header; if (!headers.isEmpty()) { - httpRouteRouter.route().order(Integer.MIN_VALUE).handler(new Handler() { - @Override - public void handle(RoutingContext event) { - event.response().headers().addAll(headers); - event.next(); + // Creates a handler for each header entry + headers.forEach((name, config) -> { + if (!config.methods.isPresent()) { + httpRouteRouter.route(config.path) + .order(Integer.MIN_VALUE) + .handler(new Handler() { + @Override + public void handle(RoutingContext event) { + event.response().headers().add(name, config.value); + event.next(); + } + }); + } else { + for (String method : config.methods.get()) { + httpRouteRouter.route(HttpMethod.valueOf(method.toUpperCase(Locale.ROOT)), config.path) + .order(Integer.MIN_VALUE) + .handler(new Handler() { + @Override + public void handle(RoutingContext event) { + event.response().headers().add(name, config.value); + event.next(); + } + }); + } } }); } diff --git a/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/HeaderResource.java b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/HeaderResource.java new file mode 100644 index 00000000000000..df732a7549672a --- /dev/null +++ b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/HeaderResource.java @@ -0,0 +1,28 @@ +package io.quarkus.it.vertx; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.core.Response; + +@Path("/headers") +public class HeaderResource { + + @GET + @Path("/any") + public String headers() { + return "ok"; + } + + @GET + @Path("/pragma") + public String pragmaHeaderMustBeSet() { + return "ok"; + } + + @GET + @Path("/override") + public Response headersOverride() { + return Response.ok("ok").header("foo", "abc").build(); + } + +} diff --git a/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/SimpleResource.java b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/SimpleResource.java index 32c1fb948c8e19..39571998edd90f 100644 --- a/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/SimpleResource.java +++ b/integration-tests/vertx-http/src/main/java/io/quarkus/it/vertx/SimpleResource.java @@ -2,7 +2,6 @@ import javax.ws.rs.GET; import javax.ws.rs.Path; -import javax.ws.rs.core.Response; /** * @@ -14,16 +13,4 @@ public class SimpleResource { public String accessLogTest() { return "passed"; } - - @GET - @Path("/headers") - public String headers() { - return "ok"; - } - - @GET - @Path("/headers-override") - public Response headersOverride() { - return Response.ok("ok").header("foo", "abc").build(); - } } diff --git a/integration-tests/vertx-http/src/main/resources/application.properties b/integration-tests/vertx-http/src/main/resources/application.properties index b6628c76011861..4e777d7cfd38f1 100644 --- a/integration-tests/vertx-http/src/main/resources/application.properties +++ b/integration-tests/vertx-http/src/main/resources/application.properties @@ -11,5 +11,8 @@ quarkus.http.access-log.enabled=true quarkus.http.access-log.log-to-file=true quarkus.http.access-log.base-file-name=quarkus-access-log quarkus.http.access-log.log-directory=target -quarkus.http.header.foo=bar +quarkus.http.header.foo.value=bar +quarkus.http.header.Pragma.value=no-cache +quarkus.http.header.Pragma.path=/headers/pragma +quarkus.http.header.Pragma.methods=GET,HEAD quarkus.native.additional-build-args=-H:IncludeResources=.*\\.jks,-H:EnableURLProtocols=http\\,https diff --git a/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/HeadersTestCase.java b/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/HeadersTestCase.java index ef8d611d5aaae2..a048262f568a9b 100644 --- a/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/HeadersTestCase.java +++ b/integration-tests/vertx-http/src/test/java/io/quarkus/it/vertx/HeadersTestCase.java @@ -1,6 +1,7 @@ package io.quarkus.it.vertx; import static io.restassured.RestAssured.given; +import static org.hamcrest.Matchers.emptyOrNullString; import static org.hamcrest.Matchers.is; import org.junit.jupiter.api.Test; @@ -13,9 +14,10 @@ public class HeadersTestCase { @Test void testAdditionalHeaders() { given() - .get("/simple/headers") + .get("/headers/any") .then() .header("foo", is("bar")) + .header("Pragma", emptyOrNullString()) .body(is("ok")); } @@ -23,10 +25,22 @@ void testAdditionalHeaders() { @Test void testAdditionalHeadersOverride() { given() - .get("/simple/headers-override") + .get("/headers/override") .then() .header("foo", is("abc")) + .header("Pragma", emptyOrNullString()) + .body(is("ok")); + } + + @Test + void testPragmaIsSent() { + given() + .get("/headers/pragma") + .then() + .header("foo", is("bar")) + .header("Pragma", is("no-cache")) .body(is("ok")); } + }