"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.installWebSocketUpgradeHandler = installWebSocketUpgradeHandler;
const __ΩReturnType = ['T', 'args', '', 'ReturnType', 'l>e"!R"RPde#!P"@2"h"!/#qk#\'QRb!Pde"!p)w$y'];
function __assignType(fn, args) {
    fn.__type = args;
    return fn;
}
const UpgradeClaimedSymbol = Symbol('srpc-upgrade-claimed');
const UpgradeRejectTimerSymbol = Symbol('srpc-upgrade-reject-timer');
const UpgradeOriginalWriteSymbol = Symbol('srpc-upgrade-original-write');
const UpgradeWriteProbeBufferSymbol = Symbol('srpc-upgrade-write-probe-buffer');
const UpgradeClaimHandlingInstalledSymbol = Symbol('srpc-upgrade-claim-handling-installed');
const InstalledUpgradeHandlersSymbol = Symbol('srpc-installed-upgrade-handlers');
const DEFAULT_UNCLAIMED_UPGRADE_REJECTION_DELAY_MS = 1_000;
const __ΩUpgradeRejectionTimer = [() => __ΩReturnType, () => setTimeout, 'UpgradeRejectionTimer', 'i"o!"w#y'];
const __ΩUpgradeHandler = ['req', 'socket', 'head', '', 'UpgradeHandler', 'P!2!!2"!2#$/$w%y'];
const __ΩVerifyClientFn = ['origin', 'secure', 'req', 'info', 'res', 'code', 'message', '', 'cb', 'VerifyClientFn', 'PP&4!)4"!4#M2$P)2%\'2&8&2\'8$/(2)$/(w*y'];
const __ΩUpgradeClaimHandlingOptions = ['unclaimedUpgradeRejectionDelayMs', 'UpgradeClaimHandlingOptions', 'P\'4!8Mw"y'];
const __ΩWebSocketUpgradeHandlerOptions = [() => __ΩUpgradeClaimHandlingOptions, 'httpServer', 'wsPath', 'wsServer', () => __ΩVerifyClientFn, 'verifyClient', 'WebSocketUpgradeHandlerOptions', 'Pn!P!4"&4#!4$n%4&MKw\'y'];
function extractProbeChunk(chunk) {
    if (typeof chunk === 'string')
        return chunk;
    if (Buffer.isBuffer(chunk))
        return chunk.toString('latin1');
    if (chunk instanceof Uint8Array)
        return Buffer.from(chunk).toString('latin1');
    return '';
}
extractProbeChunk.__type = ['chunk', 'extractProbeChunk', 'P#2!&/"'];
function clearUpgradeSuccessProbe(socket) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const anySocket = socket;
    const originalWrite = anySocket[UpgradeOriginalWriteSymbol];
    if (!originalWrite)
        return;
    anySocket.write = originalWrite;
    delete anySocket[UpgradeOriginalWriteSymbol];
    delete anySocket[UpgradeWriteProbeBufferSymbol];
}
clearUpgradeSuccessProbe.__type = ['socket', 'clearUpgradeSuccessProbe', 'P!2!$/"'];
function installUpgradeSuccessProbe(socket) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const anySocket = socket;
    if (anySocket[UpgradeOriginalWriteSymbol])
        return;
    const originalWrite = anySocket.write;
    anySocket[UpgradeOriginalWriteSymbol] = originalWrite;
    anySocket[UpgradeWriteProbeBufferSymbol] = '';
    anySocket.write = __assignType(function (chunk, ...args) {
        if (!anySocket[UpgradeClaimedSymbol]) {
            const probeChunk = extractProbeChunk(chunk);
            if (probeChunk) {
                const previous = String(anySocket[UpgradeWriteProbeBufferSymbol] ?? '');
                const probeBuffer = (previous + probeChunk).slice(0, 256);
                anySocket[UpgradeWriteProbeBufferSymbol] = probeBuffer;
                if (/^HTTP\/1\.[01]\s+101\b/i.test(probeBuffer.trimStart())) {
                    markUpgradeClaimed(socket);
                }
            }
        }
        return originalWrite.apply(this, [chunk, ...args]);
    }, ['this', 'chunk', 'args', '', 'P#2!#2"#@2#"/$']);
    socket.once('close', () => {
        clearUpgradeSuccessProbe(socket);
        clearUpgradeRejectionTimer(socket);
    });
}
installUpgradeSuccessProbe.__type = ['socket', 'installUpgradeSuccessProbe', 'P!2!$/"'];
function clearUpgradeRejectionTimer(socket) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const timer = socket[UpgradeRejectTimerSymbol];
    if (timer) {
        clearTimeout(timer);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        delete socket[UpgradeRejectTimerSymbol];
    }
}
clearUpgradeRejectionTimer.__type = ['socket', 'clearUpgradeRejectionTimer', 'P!2!$/"'];
/**
 * Monkey-patches `httpServer.emit` so that once an upgrade listener claims
 * a socket (by setting UpgradeClaimedSymbol), no further listeners are
 * invoked. This prevents consumer @AutoStart services from destroying
 * sockets that an SrpcServer is already handling.
 *
 * Also installs a low-priority fallback that destroys any socket not
 * claimed by any handler (via delayed timeout).
 */
function installUpgradeClaimHandling(httpServer, options) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const anyServer = httpServer;
    if (anyServer[UpgradeClaimHandlingInstalledSymbol])
        return;
    anyServer[UpgradeClaimHandlingInstalledSymbol] = true;
    const unclaimedUpgradeRejectionDelayMs = Math.max(0, options?.unclaimedUpgradeRejectionDelayMs ?? DEFAULT_UNCLAIMED_UPGRADE_REJECTION_DELAY_MS);
    // Patch emit to stop propagation once a socket is claimed.
    const originalEmit = httpServer.emit;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    httpServer.emit = __assignType(function (event, ...args) {
        if (event !== 'upgrade') {
            return originalEmit.apply(this, [event, ...args]);
        }
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const socket = args[1];
        installUpgradeSuccessProbe(socket);
        const listeners = this.rawListeners('upgrade').slice();
        for (const fn of listeners) {
            fn.apply(this, args);
            if (socket[UpgradeClaimedSymbol])
                break;
        }
        return listeners.length > 0;
    }, ['this', 'event', 'args', '', 'P!2!P&+J2""@2#)/$']);
    // Fallback: destroy sockets not claimed by any handler.
    httpServer.on('upgrade', __assignType((_req, socket) => {
        clearUpgradeRejectionTimer(socket);
        const timer = setTimeout(() => {
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            if (!socket[UpgradeClaimedSymbol] && !socket.destroyed) {
                socket.write('HTTP/1.1 400 Bad Request\r\n\r\n');
                socket.destroy();
            }
            clearUpgradeRejectionTimer(socket);
        }, unclaimedUpgradeRejectionDelayMs);
        timer.unref?.();
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        socket[UpgradeRejectTimerSymbol] = timer;
        socket.once('close', () => {
            clearUpgradeSuccessProbe(socket);
            clearUpgradeRejectionTimer(socket);
        });
    }, ['_req', 'socket', '', 'P"2!!2""/#']));
}
installUpgradeClaimHandling.__type = ['httpServer', () => __ΩUpgradeClaimHandlingOptions, 'options', 'installUpgradeClaimHandling', 'P!2!n"2#8"/$'];
/**
 * Mark a socket as claimed so that `installUpgradeClaimHandling`'s
 * patched emit stops propagating the `'upgrade'` event to subsequent
 * listeners.
 */
function markUpgradeClaimed(socket) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    socket[UpgradeClaimedSymbol] = true;
    clearUpgradeRejectionTimer(socket);
    clearUpgradeSuccessProbe(socket);
}
markUpgradeClaimed.__type = ['socket', 'markUpgradeClaimed', 'P!2!$/"'];
function installWebSocketUpgradeHandler(options) {
    const { httpServer, wsPath, wsServer, verifyClient, unclaimedUpgradeRejectionDelayMs } = options;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const anyServer = httpServer;
    const installedHandlers = 
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    anyServer[InstalledUpgradeHandlersSymbol] ?? new Map();
    anyServer[InstalledUpgradeHandlersSymbol] = installedHandlers;
    const existingHandler = installedHandlers.get(wsPath);
    if (existingHandler) {
        installUpgradeClaimHandling(httpServer, { unclaimedUpgradeRejectionDelayMs });
        return existingHandler;
    }
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const upgradeHandler = __assignType((req, socket, head) => {
        const pathname = req.url?.split('?')[0];
        if (pathname !== wsPath)
            return;
        markUpgradeClaimed(socket);
        verifyClient({ origin: '', secure: false, req }, __assignType((allowed, code, message) => {
            if (!allowed) {
                socket.write(`HTTP/1.1 ${code ?? 403} ${message ?? 'Forbidden'}\r\n\r\n`);
                socket.destroy();
                return;
            }
            wsServer.handleUpgrade(req, socket, head, __assignType(ws => {
                wsServer.emit('connection', ws, req);
            }, ['ws', '', 'P"2!"/"']));
        }, ['allowed', 'code', 'message', '', 'P"2!"2""2#"/$']));
    }, ['req', 'socket', 'head', '', 'P!2!"2"!2#"/$']);
    httpServer.prependListener('upgrade', upgradeHandler);
    installedHandlers.set(wsPath, upgradeHandler);
    installUpgradeClaimHandling(httpServer, { unclaimedUpgradeRejectionDelayMs });
    return upgradeHandler;
}
installWebSocketUpgradeHandler.__type = [() => __ΩWebSocketUpgradeHandlerOptions, 'options', () => __ΩUpgradeHandler, 'installWebSocketUpgradeHandler', 'Pn!2"n#/$'];
//# sourceMappingURL=WebSocketUpgradeHandler.js.map