mirror of
https://github.com/openstf/stf
synced 2025-10-04 10:19:30 +02:00
172 lines
3.5 KiB
JavaScript
172 lines
3.5 KiB
JavaScript
var util = require('util')
|
|
var events = require('events')
|
|
var net = require('net')
|
|
|
|
var ForwardReader = require('./reader')
|
|
var ForwardWriter = require('./writer')
|
|
|
|
// Handles multiple ports
|
|
function ForwardManager() {
|
|
var handlersById = Object.create(null)
|
|
|
|
this.has = function(id) {
|
|
return !!handlersById[id]
|
|
}
|
|
|
|
this.add = function(id, conn, options) {
|
|
function endListener() {
|
|
delete handlersById[id]
|
|
this.emit('remove', id, options)
|
|
}
|
|
|
|
if (this.has(id)) {
|
|
this.remove(id)
|
|
}
|
|
|
|
var handler = new ForwardHandler(conn, options)
|
|
handler.on('end', endListener.bind(this))
|
|
|
|
handlersById[id] = handler
|
|
|
|
this.emit('add', id, options)
|
|
}
|
|
|
|
this.remove = function(id) {
|
|
var handler = handlersById[id]
|
|
if (handler) {
|
|
handler.end()
|
|
}
|
|
}
|
|
|
|
this.removeAll = function() {
|
|
Object.keys(handlersById).forEach(function(id) {
|
|
handlersById[id].end()
|
|
})
|
|
}
|
|
|
|
this.listAll = function() {
|
|
return Object.keys(handlersById).map(function(id) {
|
|
var handler = handlersById[id]
|
|
return {
|
|
id: id
|
|
, devicePort: handler.options.devicePort
|
|
, targetHost: handler.options.targetHost
|
|
, targetPort: handler.options.targetPort
|
|
}
|
|
})
|
|
}
|
|
|
|
events.EventEmitter.call(this)
|
|
}
|
|
|
|
util.inherits(ForwardManager, events.EventEmitter)
|
|
|
|
// Handles a single port
|
|
function ForwardHandler(conn, options) {
|
|
var destHandlersById = Object.create(null)
|
|
|
|
function endListener() {
|
|
this.emit('end')
|
|
}
|
|
|
|
function packetEndListener(id) {
|
|
delete destHandlersById[id]
|
|
}
|
|
|
|
function packetListener(id, packet) {
|
|
var dest = destHandlersById[id]
|
|
|
|
if (packet) {
|
|
if (!dest) {
|
|
// Let's create a new connection
|
|
dest = destHandlersById[id] = new DestHandler(id, conn, options)
|
|
dest.on('end', packetEndListener.bind(null, id))
|
|
}
|
|
|
|
dest.write(packet)
|
|
}
|
|
else {
|
|
// It's a simulated fin packet
|
|
if (dest) {
|
|
dest.end()
|
|
}
|
|
}
|
|
}
|
|
|
|
function readableListener() {
|
|
// No-op but must exist so that we get the 'end' event.
|
|
}
|
|
|
|
conn.pipe(new ForwardReader())
|
|
.on('end', endListener.bind(this))
|
|
.on('packet', packetListener)
|
|
.on('readable', readableListener)
|
|
|
|
this.options = options
|
|
|
|
this.end = function() {
|
|
conn.end()
|
|
}
|
|
|
|
events.EventEmitter.call(this)
|
|
}
|
|
|
|
util.inherits(ForwardHandler, events.EventEmitter)
|
|
|
|
// Handles a single connection
|
|
function DestHandler(id, conn, options) {
|
|
function endListener() {
|
|
conn.removeListener('drain', drainListener)
|
|
this.emit('end')
|
|
}
|
|
|
|
function errorListener() {
|
|
writer.end()
|
|
}
|
|
|
|
function readableListener() {
|
|
maybePipeManually()
|
|
}
|
|
|
|
function drainListener() {
|
|
maybePipeManually()
|
|
}
|
|
|
|
// We can't just pipe to conn because we don't want to end it
|
|
// when the dest closes. Instead we'll send a special packet
|
|
// to it (which is handled by the writer).
|
|
function maybePipeManually() {
|
|
var chunk
|
|
while ((chunk = writer.read())) {
|
|
if (!conn.write(chunk)) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
var dest = net.connect({
|
|
host: options.targetHost
|
|
, port: options.targetPort
|
|
})
|
|
.on('error', errorListener)
|
|
|
|
var writer = dest.pipe(new ForwardWriter(id))
|
|
.on('end', endListener.bind(this))
|
|
.on('readable', readableListener)
|
|
|
|
conn.on('drain', drainListener)
|
|
|
|
this.end = function() {
|
|
dest.end()
|
|
}
|
|
|
|
this.write = function(chunk) {
|
|
dest.write(chunk)
|
|
}
|
|
|
|
events.EventEmitter.call(this)
|
|
}
|
|
|
|
util.inherits(DestHandler, events.EventEmitter)
|
|
|
|
module.exports = ForwardManager
|