All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.ext.web.healthchecks.impl.HealthCheckHandlerImpl Maven / Gradle / Ivy

package io.vertx.ext.web.healthchecks.impl;

import io.vertx.core.*;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.authentication.AuthenticationProvider;
import io.vertx.ext.healthchecks.CheckResult;
import io.vertx.ext.healthchecks.HealthChecks;
import io.vertx.ext.healthchecks.Status;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.healthchecks.HealthCheckHandler;
import io.vertx.ext.web.impl.Utils;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;

import static io.vertx.ext.healthchecks.CheckResult.isUp;

public class HealthCheckHandlerImpl implements HealthCheckHandler {

  private final Logger log = LoggerFactory.getLogger(HealthCheckHandler.class);

  private final HealthChecks healthChecks;
  private final AuthenticationProvider authProvider;
  private volatile Function> resultMapper;

  public HealthCheckHandlerImpl(Vertx vertx, AuthenticationProvider provider) {
    this.healthChecks = HealthChecks.create(vertx);
    this.authProvider = provider;
  }

  public HealthCheckHandlerImpl(HealthChecks hc, AuthenticationProvider provider) {
    this.healthChecks = Objects.requireNonNull(hc);
    this.authProvider = provider;
  }

  @Override
  public HealthCheckHandler register(String name, Handler> procedure) {
    healthChecks.register(name, procedure);
    return this;
  }

  @Override
  public HealthCheckHandler register(String name, long timeout, Handler> procedure) {
    healthChecks.register(name, timeout, procedure);
    return this;
  }

  @Override
  public HealthCheckHandler resultMapper(Function> resultMapper) {
    this.resultMapper = resultMapper;
    return this;
  }

  @Override
  public void handle(RoutingContext rc) {
    // ensure that we get the right offset to the router, either root or sub-router
    String path = Utils.pathOffset(rc.normalizedPath(), rc);

    String id;

    // remove the leading slash to extract the id
    if (path.length() > 0) {
      id = path.substring(1);
    } else {
      id = path;
    }

    if (authProvider != null) {
      // Copy all HTTP header in a json array and params
      JsonObject authData = new JsonObject();
      rc.request().headers().forEach(entry -> authData.put(entry.getKey(), entry.getValue()));
      rc.request().params().forEach(entry -> authData.put(entry.getKey(), entry.getValue()));
      if (rc.request().method() == HttpMethod.POST
        && rc.request().getHeader(HttpHeaders.CONTENT_TYPE) != null
        && rc.request().getHeader(HttpHeaders.CONTENT_TYPE).contains("application/json")) {
        try {
          JsonObject body = rc.body().asJsonObject();
          if (body != null) {
            authData.mergeIn(body);
          }
        } catch (Exception err) {
          log.error("Invalid authentication json body", err);
        }
      }
      authProvider.authenticate(new JsonCredentials(authData))
        .onFailure(err -> rc.response().setStatusCode(403).end())
        .onSuccess(user -> {
          healthChecks.checkStatus(id).onComplete(healthReportHandler(rc));
        });
    } else {
      healthChecks.checkStatus(id).onComplete(healthReportHandler(rc));
    }
  }

  private Completable healthReportHandler(RoutingContext rc) {
    Completable handler = (json,err) -> {
      HttpServerResponse response = rc.response()
        .putHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8");
      if (err != null) {
        if (err.getMessage().toLowerCase().contains("not found")) {
          response.setStatusCode(404);
        } else {
          response.setStatusCode(400);
        }
        response.end("{\"message\": \"" + err.getMessage() + "\"}");
      } else {
        buildResponse(json, response);
      }
    };
    if (this.resultMapper != null) {
      Promise promise = Promise.promise();
      promise.future().flatMap(resultMapper).onComplete(handler);
      return promise;
    }
    return handler;
  }

  private void buildResponse(CheckResult json, HttpServerResponse response) {
    int status = isUp(json) ? 200 : 503;

    if (status == 503 && hasProcedureError(json)) {
      status = 500;
    }

    List checks = json.getChecks();
    if (status == 200 && checks != null && checks.isEmpty()) {
      // Special case, no procedure installed.
      response.setStatusCode(204).end();
      return;
    }

    response
      .setStatusCode(status)
      .end(json.toJson().encode());
  }

  @Override
  public synchronized HealthCheckHandler unregister(String name) {
    healthChecks.unregister(name);
    return this;
  }

  private boolean hasProcedureError(CheckResult json) {
    JsonObject data = json.getData();
    if (data != null && data.getBoolean("procedure-execution-failure", false)) {
      return true;
    }

    List checks = json.getChecks();
    if (checks != null) {
      for (CheckResult check : checks) {
        if (hasProcedureError(check)) {
          return true;
        }
      }
    }

    return false;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy