tasq/node_modules/pipenet/dist/TunnelCluster.js

106 lines
4.1 KiB
JavaScript

import debug from 'debug';
import { EventEmitter } from 'events';
import fs from 'fs';
import net from 'net';
import tls from 'tls';
import { HeaderHostTransformer } from './HeaderHostTransformer.js';
const log = debug('pipenet:client');
export class TunnelCluster extends EventEmitter {
opts;
constructor(opts = {}) {
super();
this.opts = opts;
}
open() {
const opt = this.opts;
const remoteHostOrIp = opt.remoteIp || opt.remoteHost;
const remotePort = opt.remotePort;
const localHost = opt.localHost || 'localhost';
const localPort = opt.localPort;
const localProtocol = opt.localHttps ? 'https' : 'http';
const allowInvalidCert = opt.allowInvalidCert;
log('establishing tunnel %s://%s:%s <> %s:%s', localProtocol, localHost, localPort, remoteHostOrIp, remotePort);
const remote = net.connect({
host: remoteHostOrIp,
port: remotePort,
});
remote.setKeepAlive(true);
remote.on('error', (err) => {
log('got remote connection error', err.message);
if (err.code === 'ECONNREFUSED') {
this.emit('error', new Error(`connection refused: ${remoteHostOrIp}:${remotePort} (check your firewall settings)`));
}
remote.end();
});
const connLocal = () => {
if (remote.destroyed) {
log('remote destroyed');
this.emit('dead');
return;
}
log('connecting locally to %s://%s:%d', localProtocol, localHost, localPort);
remote.pause();
if (allowInvalidCert) {
log('allowing invalid certificates');
}
const getLocalCertOpts = () => allowInvalidCert
? { rejectUnauthorized: false }
: {
ca: opt.localCa ? [fs.readFileSync(opt.localCa)] : undefined,
cert: fs.readFileSync(opt.localCert),
key: fs.readFileSync(opt.localKey),
};
const local = opt.localHttps
? tls.connect({ host: localHost, port: localPort, ...getLocalCertOpts() })
: net.connect({ host: localHost, port: localPort });
const remoteClose = () => {
log('remote close');
this.emit('dead');
local.end();
};
remote.once('close', remoteClose);
local.once('error', (err) => {
log('local error %s', err.message);
local.end();
remote.removeListener('close', remoteClose);
if (err.code !== 'ECONNREFUSED' && err.code !== 'ECONNRESET') {
remote.end();
return;
}
setTimeout(connLocal, 1000);
});
local.once('connect', () => {
log('connected locally');
remote.resume();
let stream = remote;
if (opt.localHost) {
log('transform Host header to %s', opt.localHost);
stream = remote.pipe(new HeaderHostTransformer({ host: opt.localHost }));
}
stream.pipe(local).pipe(remote);
local.once('close', (hadError) => {
log('local connection closed [%s]', hadError);
});
});
};
remote.on('data', (data) => {
const match = data.toString().match(/^(\w+) (\S+)/);
if (match) {
this.emit('request', {
method: match[1],
path: match[2],
});
}
});
remote.once('connect', () => {
// Send client ID as the first message for shared tunnel server mode
if (opt.sharedTunnel && opt.name) {
log('sending client ID for shared tunnel: %s', opt.name);
remote.write(opt.name + '\n');
}
this.emit('open', remote);
connLocal();
});
}
}
//# sourceMappingURL=TunnelCluster.js.map