"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.RemotePortForwarder = void 0;
const net = require("net");
const dev_tunnels_ssh_1 = require("@microsoft/dev-tunnels-ssh");
const streamForwarder_1 = require("./streamForwarder");
const remotePortConnector_1 = require("./remotePortConnector");
/**
 * Receives SSH channels forwarded from a remote port and forwards them on to a local port.
 */
class RemotePortForwarder extends remotePortConnector_1.RemotePortConnector {
    /* @internal */
    constructor(pfs, session, remoteIPAddress, remotePort, localHost, localPort) {
        super(session, remoteIPAddress, remotePort);
        this.pfs = pfs;
        this.localHost = localHost;
        this.localPort = localPort;
    }
    /* @internal */
    async onPortChannelOpening(request, cancellation) {
        await RemotePortForwarder.forwardChannel(this.pfs, request, this.localHost, this.localPort, this.remotePort, this.trace, cancellation);
    }
    /* @internal */
    static async forwardChannel(pfs, request, localHost, localPort, remotePort, trace, cancellation) {
        const channel = request.channel;
        const forwardedStream = await pfs.forwardedPortConnecting(remotePort !== null && remotePort !== void 0 ? remotePort : localPort, true, new dev_tunnels_ssh_1.SshStream(channel), cancellation);
        if (!forwardedStream) {
            // The event handler rejected the connection.
            request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.connectFailed;
            return;
        }
        const socket = net.createConnection({
            host: localHost,
            port: localPort,
            // This option enables connection attempts for multiple resolved IP addresses,
            // aka "Happy Eyeballs" as described in https://datatracker.ietf.org/doc/html/rfc8305.
            // Effectively this enables fast connections to either 127.0.0.1 or ::1 when 'localhost'
            // is specified as the hostname. Note this option is available starting with Node.js
            // v18.13 and is enabled by default starting with Node.js v20.0.
            autoSelectFamily: true,
            // Use the minimum supported connection attempt delay when connecting to 'localhost'.
            // See https://nodejs.org/api/net.html#socketconnectoptions-connectlistener
            autoSelectFamilyAttemptTimeout: localHost === 'localhost' ? 10 : 250,
        });
        const connectCompletion = new dev_tunnels_ssh_1.PromiseCompletionSource();
        const cancellationRegistration = cancellation
            ? cancellation.onCancellationRequested(() => socket.destroy(new Error('Cancelled.')))
            : null;
        try {
            socket.once('connect', () => {
                connectCompletion.resolve();
            });
            socket.once('error', (e) => {
                connectCompletion.reject(e);
            });
            await connectCompletion.promise;
        }
        catch (e) {
            if (!(e instanceof Error) || (cancellation === null || cancellation === void 0 ? void 0 : cancellation.isCancellationRequested)) {
                throw e;
            }
            trace(dev_tunnels_ssh_1.TraceLevel.Warning, dev_tunnels_ssh_1.SshTraceEventIds.portForwardConnectionFailed, `PortForwardingService connection ` +
                `to ${localHost}:${localPort} failed: ${e.message}`, e);
            request.failureReason = dev_tunnels_ssh_1.SshChannelOpenFailureReason.connectFailed;
            request.failureDescription = e.message;
        }
        finally {
            cancellationRegistration === null || cancellationRegistration === void 0 ? void 0 : cancellationRegistration.dispose();
        }
        // TODO: Set socket options?
        const streamForwarder = new streamForwarder_1.StreamForwarder(socket, forwardedStream, channel.session.trace);
        trace(dev_tunnels_ssh_1.TraceLevel.Info, dev_tunnels_ssh_1.SshTraceEventIds.portForwardConnectionOpened, `${channel.session} PortForwardingService forwarded channel ` +
            `#${channel.channelId} connection to ${localHost}:${localPort}.`);
        pfs.streamForwarders.push(streamForwarder);
    }
}
exports.RemotePortForwarder = RemotePortForwarder;
//# sourceMappingURL=https://github.com/VSCodium/sourcemaps/releases/download/stable-824c4c46a288b839f13b24022655329c2aeb9f81/node_modules-@microsoft-dev-tunnels-ssh-tcp-services-remotePortForwarder.js.map