single-tls-tunnel
Version:
A single port TLS tunnel implementation to support a single downstream client proxy
116 lines (105 loc) • 3.45 kB
JavaScript
var tls = require('tls'),
crypto = require('crypto'),
net = require('net'),
http = require('http'),
util = require('util'),
EventEmitter = require('events').EventEmitter,
Valve = require('pipette').Valve,
MultiplexStream = require('multiplex-stream');
function Server(options) {
var self = this,
multiplex,
connections = [],
server = net.createServer({
allowHalfOpen: true
}, function(connection) {
connections.push(connection);
connection.on('close', function() {
connections.splice(connections.indexOf(connection), 1);
});
});
function onConnectionAfterClientAuthenticated(connection) {
// this is required as the server allows connections to be half open
connection.on('end', function() {
connection.end();
});
var valve = new Valve(connection, {paused: true});
var tunnel = multiplex.connect(function() {
valve.pipe(tunnel).pipe(connection);
valve.resume();
});
}
var httpServer = http.createServer(function(request, response) {
response.end('Waiting for a client');
});
httpServer.on('upgrade', function(request, socket, head) {
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n' +
'Upgrade: websocket\r\n' +
'Connection: Upgrade\r\n' +
'\r\n');
var securePair = tls.createSecurePair(
crypto.createCredentials({
key: options.key,
cert: options.cert,
ca: options.ca
}),
true,
options.requireCert,
options.rejectUnauthorized
);
securePair.on('secure', function() {
multiplex = new MultiplexStream();
multiplex.pipe(securePair.cleartext).pipe(multiplex);
server.removeListener('connection', onConnectionWhileWaitingForClient);
server.on('connection', onConnectionAfterClientAuthenticated);
var disconnected = false;
function disconnect() {
if (!disconnected) {
disconnected = true;
server.removeListener('connection', onConnectionAfterClientAuthenticated);
server.on('connection', onConnectionWhileWaitingForClient);
self.emit('disconnected');
}
}
socket.on('close', function() {
disconnect();
});
socket.on('end', function() {
disconnect();
socket.end();
});
self.emit('connected');
});
socket.pipe(securePair.encrypted).pipe(socket);
});
function onConnectionWhileWaitingForClient(connection) {
httpServer.emit('connection', connection);
}
server.on('connection', onConnectionWhileWaitingForClient);
self.listen = function(port, callback) {
if (callback) {
self.once('listening', callback);
}
server.on('listening', function() {
self.emit('listening');
});
server.on('error', function(error) {
self.emit('error', error);
});
server.listen(port);
};
self.close = function(callback) {
connections.forEach(function(connection) {
connection.end();
});
if (callback) {
self.once('close', callback);
}
server.on('close', function() {
self.emit('close');
});
server.close();
};
}
util.inherits(Server, EventEmitter);
module.exports = Server;