"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.OriginalUrlExtractor = void 0;
const global_logger_factory_1 = require("global-logger-factory");
const BadRequestHttpError_1 = require("../../../util/errors/BadRequestHttpError");
const HttpErrorUtil_1 = require("../../../util/errors/HttpErrorUtil");
const InternalServerError_1 = require("../../../util/errors/InternalServerError");
const HeaderUtil_1 = require("../../../util/HeaderUtil");
const PathUtil_1 = require("../../../util/PathUtil");
const TargetExtractor_1 = require("./TargetExtractor");
/**
 * Reconstructs the original URL of an incoming {@link HttpRequest}.
 */
class OriginalUrlExtractor extends TargetExtractor_1.TargetExtractor {
    logger = (0, global_logger_factory_1.getLoggerFor)(this);
    identifierStrategy;
    includeQueryString;
    fixedHost;
    constructor(args) {
        super();
        this.identifierStrategy = args.identifierStrategy;
        this.includeQueryString = args.includeQueryString ?? true;
        if (args.fixedBaseUrl) {
            const url = new URL(args.fixedBaseUrl);
            this.fixedHost = url.host;
            this.logger.warn([
                `The \`fixedBaseUrl\` parameter has been set `,
                `so the host header will be ignored and always assumed to be ${this.fixedHost}.`,
                `Don't use this in production.`,
            ].join(' '));
        }
    }
    async handle({ request: { url, socket, headers } }) {
        if (!url) {
            throw new InternalServerError_1.InternalServerError('Missing URL');
        }
        // Extract host and protocol (possibly overridden by the Forwarded/X-Forwarded-* header)
        let { host } = headers;
        let protocol = socket?.encrypted ? 'https' : 'http';
        // Check Forwarded/X-Forwarded-* headers
        const forwarded = (0, HeaderUtil_1.parseForwarded)(headers);
        if (forwarded.host) {
            ({ host } = forwarded);
        }
        if (forwarded.proto) {
            ({ proto: protocol } = forwarded);
        }
        if (this.fixedHost) {
            host = this.fixedHost;
        }
        // Perform a sanity check on the host
        if (!host) {
            throw new BadRequestHttpError_1.BadRequestHttpError('Missing Host header');
        }
        if (/[/\\*]/u.test(host)) {
            throw new BadRequestHttpError_1.BadRequestHttpError(`The request has an invalid Host header: ${host}`);
        }
        // URL object applies punycode encoding to domain
        const originalUrl = new URL(`${protocol}://${host}`);
        const [, pathname, search] = /^([^?]*)(.*)/u.exec((0, PathUtil_1.toCanonicalUriPath)(url));
        originalUrl.pathname = pathname;
        if (this.includeQueryString && search) {
            originalUrl.search = search;
        }
        // Create ResourceIdentifier instance
        const identifier = { path: originalUrl.href };
        // Check if the configured IdentifierStrategy supports the identifier
        if (!this.identifierStrategy.supportsIdentifier(identifier)) {
            throw new InternalServerError_1.InternalServerError(`The identifier ${identifier.path} is outside the configured identifier space.`, { errorCode: 'E0001', metadata: (0, HttpErrorUtil_1.errorTermsToMetadata)({ path: identifier.path }) });
        }
        return identifier;
    }
}
exports.OriginalUrlExtractor = OriginalUrlExtractor;
//# sourceMappingURL=OriginalUrlExtractor.js.map