Created
May 8, 2019 15:27
-
-
Save will118/3e3717dcd5e3573b9295f3bc73279df4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const net = require('net'); | |
const readline = require('readline'); | |
const COLUMN_WIDTH = 13; | |
const COLORS = { | |
red: '31', | |
green: '32', | |
cyan: '36', | |
}; | |
const TIMEOUT = 60 * 1000; | |
const PORT = 14000; | |
const LOCALHOST = '172.16.88.19'; | |
const pipes = { | |
1: { | |
port: 64000, | |
server: null | |
}, | |
} | |
const ts = () => Math.floor(new Date() / 1000) | |
// TUI | |
const rl = readline.createInterface({ | |
input: process.stdin, | |
output: process.stdout | |
}); | |
readline.cursorTo(process.stdout, 0, 0); | |
readline.clearScreenDown(process.stdout); | |
rl.setPrompt('\npipes> '); | |
rl.prompt(); | |
const lastActivity = server => { | |
return `${ts() - server.lastActivity} seconds ago` | |
} | |
const printStatus = (done) => { | |
Object.entries(pipes).forEach(([i, pipe], index) => { | |
const log = (status, color) => { | |
const padded = status.padEnd(COLUMN_WIDTH) | |
console.log( | |
`\x1b[${color}m%s\x1b[0m`, | |
`[${i}]: ${pipe.port} => ${PORT} | ${padded} | ${lastActivity(pipe.server)}` | |
) | |
} | |
if (pipe.server) { | |
pipe.server.getConnections((err, count) => { | |
const isListening = pipe.server.listening; | |
log( | |
isListening ? `${count} connection${count === 1 ? '' : 's'}` : 'closed', | |
isListening ? (count === 0 ? COLORS.cyan : COLORS.green) : COLORS.red | |
) | |
if (index === Object.keys(pipes).length - 1) { | |
done() | |
} | |
}); | |
} else if (pipe.server === null) { | |
log('uninitialized', COLORS.red) | |
done() | |
} else { | |
log('unknown', COLORS.red) | |
done() | |
} | |
}); | |
} | |
rl.on('line', line => { | |
const cmd = line.trim(); | |
const matchPipeNumber = startOrStop => { | |
const match = cmd.match(new RegExp(`^${startOrStop}\\s?(\\d)$`)); | |
if (match) { | |
return match[1]; | |
} | |
return null; | |
} | |
const startPipeNumber = matchPipeNumber('start'); | |
const stopPipeNumber = matchPipeNumber('stop'); | |
if (cmd === 'status') { | |
printStatus(() => rl.prompt()); | |
} else if (startPipeNumber) { | |
const pipe = pipes[startPipeNumber]; | |
if (!pipe) { | |
console.log('Pipe not found'); | |
rl.prompt(); | |
return; | |
} | |
const { server, port } = pipe | |
server.listen(port); | |
printStatus(() => rl.prompt()); | |
} else if (stopPipeNumber) { | |
const pipe = pipes[stopPipeNumber]; | |
if (!pipe) { | |
console.log('Pipe not found'); | |
rl.prompt(); | |
return; | |
} | |
const { server, port } = pipe | |
if (server) { | |
server.destroy(); | |
} else { | |
console.log(`No pipe open: ${port}`) | |
rl.prompt(); | |
} | |
} else { | |
console.log('Invalid command') | |
rl.prompt(); | |
} | |
}).on('close', () => { | |
console.log('\n\nBye!'); | |
// This will kill the connections/servers anyway | |
process.exit(0); | |
}); | |
const statusUpdate = (msg) => { | |
console.log(msg || ''); | |
printStatus(() => { | |
console.log(''); | |
rl.prompt() | |
}); | |
} | |
const createServer = port => { | |
const server = net.createServer(socket => { | |
const client = new net.Socket(); | |
client.connect(PORT, LOCALHOST, () => { | |
socket.pipe(client); | |
client.pipe(socket); | |
}); | |
client.on('data', () => { | |
server.lastActivity = ts(); | |
}); | |
socket.on('error', e => { | |
if (e.code === 'ECONNRESET' && e.syscall === 'read') { | |
statusUpdate(); | |
} else { | |
throw e; | |
} | |
}); | |
socket.setTimeout(TIMEOUT, () => { | |
statusUpdate(); | |
socket.end(); | |
}); | |
}); | |
const connections = {} | |
server.on('connection', c => { | |
const { remoteAddress, remotePort } = c; | |
const key = `${remoteAddress}:${remotePort}`; | |
connections[key] = c; | |
statusUpdate(); | |
c.on('close', () => { | |
delete connections[key]; | |
statusUpdate(); | |
}); | |
}); | |
server.destroy = (cb) => { | |
for (const key in connections) { | |
connections[key].destroy(); | |
} | |
server.close(cb); | |
statusUpdate(); | |
}; | |
server.listen(port); | |
return server; | |
} | |
for (const key in pipes) { | |
const pipe = pipes[key] | |
pipe.server = createServer(pipe.port) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment