Skip to content

Commit

Permalink
Merge pull request quarkusio#43209 from xstefank/health-content-type-…
Browse files Browse the repository at this point in the history
…43125

Allow smallrye-health to return DOWN response compliant with rfc9457
  • Loading branch information
geoand authored Sep 13, 2024
2 parents 3e0f8d6 + 636939a commit 888266f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import io.quarkus.smallrye.health.runtime.QuarkusAsyncHealthCheckFactory;
import io.quarkus.smallrye.health.runtime.ShutdownReadinessCheck;
import io.quarkus.smallrye.health.runtime.ShutdownReadinessListener;
import io.quarkus.smallrye.health.runtime.SmallRyeHealthBuildFixedConfig;
import io.quarkus.smallrye.health.runtime.SmallRyeHealthGroupHandler;
import io.quarkus.smallrye.health.runtime.SmallRyeHealthHandler;
import io.quarkus.smallrye.health.runtime.SmallRyeHealthRecorder;
Expand Down Expand Up @@ -459,9 +460,10 @@ void registerHealthUiHandler(
@Consume(SyntheticBeansRuntimeInitBuildItem.class)
void processSmallRyeHealthRuntimeConfig(
SmallRyeHealthRecorder recorder,
SmallRyeHealthRuntimeConfig runtimeConfig) {
SmallRyeHealthRuntimeConfig runtimeConfig,
SmallRyeHealthBuildFixedConfig buildFixedConfig) {

recorder.processSmallRyeHealthRuntimeConfiguration(runtimeConfig);
recorder.processSmallRyeHealthRuntimeConfiguration(runtimeConfig, buildFixedConfig);
}

// Replace health URL in static files
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.smallrye.health.test;

import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;

import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.restassured.parsing.Parser;

class ProblemDetailsConfigOverrideTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(FailingHealthCheck.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"))
.overrideConfigKey("quarkus.smallrye-health.include-problem-details", "true");

@Test
void testProblemDetailsOverride() {
try {
RestAssured.defaultParser = Parser.JSON;
RestAssured.when().get("/q/health/live").then()
.contentType("application/problem+json")
.body("type", is("about:blank"),
"status", is(503),
"title", is("Health Check Failed: /q/health/live"),
"detail", containsString("/q/health/live, invoked at"),
"instance", notNullValue(),
"health.checks.size()", is(1),
"health.checks.status", contains("DOWN"),
"health.checks.name", contains("failing"));
} finally {
RestAssured.reset();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.quarkus.smallrye.health.runtime;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

@ConfigRoot(name = "smallrye-health", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
public class SmallRyeHealthBuildFixedConfig {

/**
* Specify the reported DOWN responses should be aligned with RFC 9457 - Problem Details for HTTP APIs.
*
* @see <a href="https://tools.ietf.org/html/rfc7807">RFC 7807</a>
*/
@ConfigItem(defaultValue = "false")
boolean includeProblemDetails;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@

import java.io.IOException;
import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Collections;
import java.util.Map;

import jakarta.json.JsonObjectBuilder;
import jakarta.json.JsonWriter;
import jakarta.json.JsonWriterFactory;
import jakarta.json.spi.JsonProvider;
import jakarta.json.stream.JsonGenerator;

import io.quarkus.arc.Arc;
import io.quarkus.arc.ManagedContext;
Expand All @@ -22,8 +31,14 @@

public abstract class SmallRyeHealthHandlerBase implements Handler<RoutingContext> {

static volatile boolean problemDetails = false;

protected abstract Uni<SmallRyeHealth> getHealth(SmallRyeHealthReporter reporter, RoutingContext routingContext);

private static final Map<String, ?> JSON_CONFIG = Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true);
private static final JsonProvider JSON_PROVIDER = JsonProvider.provider();
private static final JsonWriterFactory JSON_WRITER_FACTORY = JSON_PROVIDER.createWriterFactory(JSON_CONFIG);

@Override
public void handle(RoutingContext ctx) {
ManagedContext requestContext = Arc.container().requestContext();
Expand Down Expand Up @@ -56,13 +71,33 @@ private void doHandle(RoutingContext ctx, ManagedContext requestContext) {
requestContext.terminate();
}
HttpServerResponse resp = ctx.response();
Buffer buffer = Buffer.buffer(256); // this size seems to cover the basic health checks
if (health.isDown()) {
resp.setStatusCode(503);
if (problemDetails) {
resp.headers().set(HttpHeaders.CONTENT_TYPE, "application/problem+json");
try (BufferOutputStream outputStream = new BufferOutputStream(buffer)) {
JsonObjectBuilder objectBuilder = JSON_PROVIDER.createObjectBuilder();
objectBuilder
.add("type", "about:blank")
.add("status", 503)
.add("title", "Health Check Failed: " + ctx.normalizedPath())
.add("detail", ctx.request().uri() + ", invoked at " + Instant.now())
.add("instance", ctx.request().absoluteURI())
.add("health", health.getPayload());
JsonWriter writer = JSON_WRITER_FACTORY.createWriter(outputStream);
writer.writeObject(objectBuilder.build());
resp.end(buffer);
return;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
resp.headers()
.set(HttpHeaders.CONTENT_TYPE, "application/json; charset=UTF-8")
.set(HttpHeaders.CACHE_CONTROL, "no-store");
Buffer buffer = Buffer.buffer(256); // this size seems to cover the basic health checks

try (BufferOutputStream outputStream = new BufferOutputStream(buffer)) {
reporter.reportHealth(outputStream, health);
resp.end(buffer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,15 @@ public Handler<RoutingContext> uiHandler(String healthUiFinalDestination, String
}
}

public void processSmallRyeHealthRuntimeConfiguration(SmallRyeHealthRuntimeConfig runtimeConfig) {
public void processSmallRyeHealthRuntimeConfiguration(SmallRyeHealthRuntimeConfig runtimeConfig,
SmallRyeHealthBuildFixedConfig buildFixedConfig) {
SmallRyeHealthReporter reporter = Arc.container().select(SmallRyeHealthReporter.class).get();
reporter.setAdditionalProperties(runtimeConfig.additionalProperties);

reporter.setHealthChecksConfigs(runtimeConfig.check.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().enabled)));

SmallRyeHealthHandlerBase.problemDetails = buildFixedConfig.includeProblemDetails;
}

}

0 comments on commit 888266f

Please sign in to comment.