Skip to content

Commit

Permalink
Merge pull request #21102 from gastaldi/http
Browse files Browse the repository at this point in the history
Introduce `quarkus.http.header`
  • Loading branch information
gastaldi authored Nov 3, 2021
2 parents 6f76614 + 5934870 commit 96025ee
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 10 deletions.
24 changes: 24 additions & 0 deletions docs/src/main/asciidoc/http-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,30 @@ values:

NOTE: if you use `redirect` or `disabled` and have not added a SSL certificate or keystore, your server will not start!

== Additional HTTP Headers

To enable HTTP headers to be sent on every response, add the following properties:

[source, properties]
----
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

HTTP/2 is enabled by default, and will be used by browsers if SSL is in use on JDK11 or higher. JDK8 does not support
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<String>> methods;
}
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ public class HttpConfiguration {
@ConfigItem
public Optional<PayloadHint> unhandledErrorContentTypeDefault;

/**
* Additional HTTP Headers always sent in the response
*/
@ConfigItem
public Map<String, HeaderConfig> header;

public ProxyConfig proxy;

public int determinePort(LaunchMode launchMode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -227,7 +228,8 @@ public static void startServerAfterFailedStart() {
doServerStart(vertx, buildConfig, config, LaunchMode.DEVELOPMENT, new Supplier<Integer>() {
@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) {
Expand Down Expand Up @@ -360,6 +362,38 @@ public void handle(Void e) {
}
});
}
// Headers sent on any request, regardless of the response
Map<String, HeaderConfig> headers = httpConfiguration.header;
if (!headers.isEmpty()) {
// Creates a handler for each header entry
for (Map.Entry<String, HeaderConfig> entry : headers.entrySet()) {
var name = entry.getKey();
var config = entry.getValue();
if (config.methods.isEmpty()) {
httpRouteRouter.route(config.path)
.order(Integer.MIN_VALUE)
.handler(new Handler<RoutingContext>() {
@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<RoutingContext>() {
@Override
public void handle(RoutingContext event) {
event.response().headers().add(name, config.value);
event.next();
}
});
}
}
}
}

Handler<HttpServerRequest> root;
if (rootPath.equals("/")) {
Expand Down Expand Up @@ -564,30 +598,30 @@ public void handle(AsyncResult<Void> event) {
private static void setHttpServerTiming(InsecureRequests insecureRequests, HttpServerOptions httpServerOptions,
HttpServerOptions sslConfig,
HttpServerOptions domainSocketOptions, boolean auxiliaryApplication) {
String serverListeningMessage = "Listening on: ";
StringBuilder serverListeningMessage = new StringBuilder("Listening on: ");
int socketCount = 0;

if (httpServerOptions != null && !InsecureRequests.DISABLED.equals(insecureRequests)) {
serverListeningMessage += String.format(
"http://%s:%s", httpServerOptions.getHost(), actualHttpPort);
serverListeningMessage.append(String.format(
"http://%s:%s", httpServerOptions.getHost(), actualHttpPort));
socketCount++;
}

if (sslConfig != null) {
if (socketCount > 0) {
serverListeningMessage += " and ";
serverListeningMessage.append(" and ");
}
serverListeningMessage += String.format("https://%s:%s", sslConfig.getHost(), actualHttpsPort);
serverListeningMessage.append(String.format("https://%s:%s", sslConfig.getHost(), actualHttpsPort));
socketCount++;
}

if (domainSocketOptions != null) {
if (socketCount > 0) {
serverListeningMessage += " and ";
serverListeningMessage.append(" and ");
}
serverListeningMessage += String.format("unix:%s", domainSocketOptions.getHost());
serverListeningMessage.append(String.format("unix:%s", domainSocketOptions.getHost()));
}
Timing.setHttpServer(serverListeningMessage, auxiliaryApplication);
Timing.setHttpServer(serverListeningMessage.toString(), auxiliaryApplication);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +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.native.additional-build-args=-H:IncludeResources=.*\\.jks,-H:EnableURLProtocols=http\\,https
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class HeadersTestCase {

@Test
void testAdditionalHeaders() {
given()
.get("/headers/any")
.then()
.header("foo", is("bar"))
.header("Pragma", emptyOrNullString())
.body(is("ok"));

}

@Test
void testAdditionalHeadersOverride() {
given()
.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"));

}

}

0 comments on commit 96025ee

Please sign in to comment.