"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const events_1 = require("events"); const url_1 = require("url"); const axios_1 = __importDefault(require("axios")); const debug_1 = __importDefault(require("debug")); const TunnelCluster_1 = __importDefault(require("./TunnelCluster")); const debugLog = (0, debug_1.default)('localtunnel:client'); class Tunnel extends events_1.EventEmitter { constructor(opts = {}) { super(); this.opts = opts; this.closed = false; if (!this.opts.host) { this.opts.host = 'https://localtunnel.me'; } } _getInfo(body) { const { id, ip, port, url, cached_url, max_conn_count } = body; const { host, port: local_port, local_host } = this.opts; const { local_https, local_cert, local_key, local_ca, allow_invalid_cert } = this.opts; return { name: id, url, cached_url, max_conn: max_conn_count || 1, remote_host: (0, url_1.parse)(host).hostname, remote_ip: ip, remote_port: port, local_port, local_host, local_https, local_cert, local_key, local_ca, allow_invalid_cert, }; } _init(callback) { const opt = this.opts; const getInfo = this._getInfo.bind(this); const params = { responseType: 'json', }; const baseUri = `${opt.host}/`; // no subdomain at first, maybe use requested domain const assignedDomain = opt.subdomain; // where to quest const uri = baseUri + (assignedDomain || '?new') + (opt.port ? `&port=${opt.port}` : ''); debugLog('请求隧道服务器: %s', uri); const getUrl = () => { axios_1.default .get(uri, params) .then(res => { const body = res.data; debugLog('got tunnel information', body); if (res.status !== 200) { const err = new Error((body && body.message) || 'localtunnel server returned an error, please try again'); return callback(err); } callback(null, getInfo(body)); }) .catch(err => { debugLog(`tunnel server offline: ${err.message}, retry 1s`); return setTimeout(getUrl, 1000); }); }; getUrl(); } _establish(info) { // 为 max_conn 设置默认值 const maxConn = info.max_conn || 1; // 如果 max_conn 未定义,默认为 1 // increase max event listeners so that localtunnel consumers don't get // warning messages as soon as they setup even one listener. See #71 this.setMaxListeners(maxConn + (events_1.EventEmitter.defaultMaxListeners || 10)); this.tunnelCluster = new TunnelCluster_1.default(info); // only emit the url the first time this.tunnelCluster.once('open', () => { this.emit('url', info.url); }); // re-emit socket error this.tunnelCluster.on('error', err => { debugLog('got socket error', err.message); this.emit('error', err); }); let tunnelCount = 0; // track open count this.tunnelCluster.on('open', tunnel => { tunnelCount++; debugLog('tunnel open [total: %d]', tunnelCount); const closeHandler = () => { tunnel.destroy(); }; if (this.closed) { return closeHandler(); } this.once('close', closeHandler); tunnel.once('close', () => { this.removeListener('close', closeHandler); }); }); // when a tunnel dies, open a new one this.tunnelCluster.on('dead', () => { var _a; tunnelCount--; debugLog('tunnel dead [total: %d]', tunnelCount); if (this.closed) { return; } (_a = this.tunnelCluster) === null || _a === void 0 ? void 0 : _a.open(); }); this.tunnelCluster.on('request', req => { this.emit('request', req); }); // establish as many tunnels as allowed for (let count = 0; count < maxConn; ++count) { // 使用 maxConn 变量 this.tunnelCluster.open(); } } open(callback) { this._init((err, info) => { if (err) { return callback(err); } this.clientId = info.name; this.url = info.url; // `cached_url` is only returned by proxy servers that support resource caching. if (info.cached_url) { this.cachedUrl = info.cached_url; } this._establish(info); callback(null); }); } close() { this.closed = true; this.emit('close'); } } exports.default = Tunnel;