"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpWaitStrategy = void 0;
const undici_1 = require("undici");
const wait_strategy_1 = require("./wait-strategy");
const common_1 = require("../common");
const container_runtime_1 = require("../container-runtime");
class HttpWaitStrategy extends wait_strategy_1.AbstractWaitStrategy {
    path;
    port;
    options;
    protocol = "http";
    method = "GET";
    headers = {};
    predicates = [];
    _allowInsecure = false;
    readTimeout = 1000;
    constructor(path, port, options) {
        super();
        this.path = path;
        this.port = port;
        this.options = options;
    }
    forStatusCode(statusCode) {
        this.predicates.push(async (response) => response.status === statusCode);
        return this;
    }
    forStatusCodeMatching(predicate) {
        this.predicates.push(async (response) => predicate(response.status));
        return this;
    }
    forResponsePredicate(predicate) {
        this.predicates.push(async (response) => predicate(await response.text()));
        return this;
    }
    withMethod(method) {
        this.method = method;
        return this;
    }
    withHeaders(headers) {
        this.headers = { ...this.headers, ...headers };
        return this;
    }
    withBasicCredentials(username, password) {
        const base64Encoded = Buffer.from(`${username}:${password}`).toString("base64");
        this.headers = { ...this.headers, Authorization: `Basic ${base64Encoded}` };
        return this;
    }
    withReadTimeout(readTimeout) {
        this.readTimeout = readTimeout;
        return this;
    }
    usingTls() {
        this.protocol = "https";
        return this;
    }
    allowInsecure() {
        this._allowInsecure = true;
        return this;
    }
    async waitUntilReady(container, boundPorts) {
        common_1.log.debug(`Waiting for HTTP...`, { containerId: container.id });
        const exitStatus = "exited";
        let containerExited = false;
        const client = await (0, container_runtime_1.getContainerRuntimeClient)();
        const { abortOnContainerExit } = this.options;
        await new common_1.IntervalRetry(this.readTimeout).retryUntil(async () => {
            try {
                const url = `${this.protocol}://${client.info.containerRuntime.host}:${boundPorts.getBinding(this.port)}${this.path}`;
                if (abortOnContainerExit) {
                    const containerStatus = (await client.container.inspect(container)).State.Status;
                    if (containerStatus === exitStatus) {
                        containerExited = true;
                        return;
                    }
                }
                return await fetch(url, {
                    method: this.method,
                    signal: AbortSignal.timeout(this.readTimeout),
                    headers: this.headers,
                    dispatcher: this.getAgent(),
                });
            }
            catch {
                return undefined;
            }
        }, async (response) => {
            if (abortOnContainerExit && containerExited) {
                return true;
            }
            if (response === undefined) {
                return false;
            }
            else if (!this.predicates.length) {
                return response.ok;
            }
            else {
                for (const predicate of this.predicates) {
                    const result = await predicate(response);
                    if (!result) {
                        return false;
                    }
                }
                return true;
            }
        }, () => {
            const message = `URL ${this.path} not accessible after ${this.startupTimeout}ms`;
            common_1.log.error(message, { containerId: container.id });
            throw new Error(message);
        }, this.startupTimeout);
        if (abortOnContainerExit && containerExited) {
            return this.handleContainerExit(container);
        }
        common_1.log.debug(`HTTP wait strategy complete`, { containerId: container.id });
    }
    async handleContainerExit(container) {
        const tail = 50;
        const lastLogs = [];
        const client = await (0, container_runtime_1.getContainerRuntimeClient)();
        let message;
        try {
            const stream = await client.container.logs(container, { tail });
            await new Promise((res) => {
                stream.on("data", (d) => lastLogs.push(d.trim())).on("end", res);
            });
            message = `Container exited during HTTP healthCheck, last ${tail} logs: ${lastLogs.join("\n")}`;
        }
        catch (err) {
            message = "Container exited during HTTP healthCheck, failed to get last logs";
        }
        common_1.log.error(message, { containerId: container.id });
        throw new Error(message);
    }
    getAgent() {
        if (this._allowInsecure) {
            return new undici_1.Agent({
                connect: {
                    rejectUnauthorized: false,
                },
            });
        }
    }
}
exports.HttpWaitStrategy = HttpWaitStrategy;
//# sourceMappingURL=http-wait-strategy.js.map