"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.StatusHandler = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _lodash = require("lodash");
var _https = require("https");
var _url = require("url");
var _nodeFetch = _interopRequireDefault(require("node-fetch"));
/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */

const statusApiPath = '/api/status';
class StatusHandler {
  static isHealthy(response) {
    return StatusHandler.isSuccess(response) || StatusHandler.isUnauthorized(response);
  }
  static isUnauthorized({
    status,
    headers
  }) {
    return status === 401 && headers.has('www-authenticate');
  }
  static isSuccess({
    status
  }) {
    return status >= 200 && status <= 299 || status === 302;
  }
  constructor(kibanaConfig, logger) {
    (0, _defineProperty2.default)(this, "getAgent", (0, _lodash.memoize)(() => new _https.Agent(this.getAgentConfig())));
    this.kibanaConfig = kibanaConfig;
    this.logger = logger;
    this.handler = this.handler.bind(this);
  }
  async handler(request, toolkit) {
    const body = await this.poll();
    const code = StatusHandler.STATUS_CODE[body.status];
    this.logger.debug(`Returning ${code} response with body: ${JSON.stringify(body)}`);
    return toolkit.response(body).type('application/json').code(code);
  }
  async poll() {
    var _statuses$;
    const hosts = await Promise.all(this.kibanaConfig.hosts.map(this.pollHost.bind(this)));
    const statuses = (0, _lodash.chain)(hosts).map('status').uniq().value();
    const status = statuses.length <= 1 ? (_statuses$ = statuses[0]) !== null && _statuses$ !== void 0 ? _statuses$ : 'healthy' : 'unhealthy';
    return {
      status,
      hosts
    };
  }
  async pollHost(host) {
    this.logger.debug(`Requesting '${host}'`);
    try {
      const response = await this.fetch(host);
      const status = StatusHandler.isHealthy(response) ? 'healthy' : 'unhealthy';
      this.logger.debug(`${(0, _lodash.capitalize)(status)} response from '${host}' with code ${response.status}`);
      return {
        host,
        status,
        code: response.status
      };
    } catch (error) {
      this.logger.error(error);
      if (error.name === 'AbortError') {
        this.logger.error(`Request timeout for '${host}'`);
        return {
          host,
          status: 'timeout'
        };
      }
      this.logger.error(`Failed response from '${host}': ${error.message}`);
      return {
        host,
        status: 'failure'
      };
    }
  }
  async fetch(host) {
    const url = new _url.URL(host);
    appendStatusApiPath(url);
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), this.kibanaConfig.requestTimeout.asMilliseconds());
    try {
      return await (0, _nodeFetch.default)(url, {
        agent: url.protocol === 'https:' ? this.getAgent() : undefined,
        signal: controller.signal,
        redirect: 'manual'
      });
    } finally {
      clearTimeout(timeoutId);
    }
  }
  getAgentConfig() {
    const {
      certificateAuthorities: ca,
      certificate: cert,
      verificationMode
    } = this.kibanaConfig.ssl;
    const options = {
      ca,
      cert
    };
    switch (verificationMode) {
      case 'none':
        options.rejectUnauthorized = false;
        break;
      case 'certificate':
        options.rejectUnauthorized = true;
        // by default, NodeJS is checking the server identify
        options.checkServerIdentity = () => undefined;
        break;
      case 'full':
        options.rejectUnauthorized = true;
        break;
      default:
        throw new Error(`Unknown ssl verificationMode: ${verificationMode}`);
    }
    return options;
  }
}
exports.StatusHandler = StatusHandler;
(0, _defineProperty2.default)(StatusHandler, "STATUS_CODE", {
  healthy: 200,
  unhealthy: 503,
  failure: 502,
  timeout: 504
});
const appendStatusApiPath = url => {
  url.pathname = `${url.pathname}/${statusApiPath}`.replace(/\/{2,}/g, '/');
};