I have 3 components device
, server
and frontend (admin)
.
Starts socket.io server with 2 namespaces /admin
and /client
.
If socket from /admin
namespace sends data, server passes it along to /client
namespace. If socket from /client
namespace sends data, server passes it along to /admin
namespace.
const io = require('socket.io');
const device = io.of('/device');
const admin = io.of('/admin');
device.on('connection', (socket) => {
socket.on('data', (data) => {
console.log("PASSING DATA FROM [DEVICE] TO [ADMIN]")
admin.emit('data', data);
})
});
admin.on('connection', (socket) => {
socket.on('data', (data) => {
console.log("PASSING DATA FROM [ADMIN] TO [DEVICE]")
device.emit('data', data);
});
});
io.listen(80);
Uses socket.io-client
to connect to socket.io
server.
Starts interactive shell session using node-pty
.
const io = require('socket.io-client');
const socket = io('http://localhost:80/client');
const os = require('os');
const pty = require('node-pty');
const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash';
const ptyProcess = pty.spawn(shell, [], {
name: 'xterm-color',
cols: 80,
rows: 30
});
socket.on('connect', () => {
});
// INPUT DATA
socket.on('data', (data) => {
ptyProcess.write(data);
});
// OUTPUTING DATA
ptyProcess.onData = (data) => {
socket.emit('data', data)
}
Finally I have the frontend which uses xterm.js
to create a terminal inside the browser. I am using vue
. The browser client as well connects to socket.io
server on the /admin
namespace. Basically I have this :
<template>
<div id="app">
<div id="terminal" ref="terminal"></div>
</div>
</template>
<script>
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { io } from 'socket.io-client';
export default {
mounted() {
const term = new Terminal({ cursorBlink : true });
term.open(this.$refs.terminal);
const socket = io('http://localhost:80/admin');
socket.on('connect', () => {
term.write('\r\n*** Connected to backend***\r\n');
term.onData((data) => {
socket.emit('data', data);
})
socket.on('data', (data) => {
term.write(data);
});
socket.on('disconnect', () => {
term.write('\r\n*** Disconnected from backend***\r\n');
});
});
}
}
</script>
ā Starting the pty session seems to work, at least there are now errors reported. However it seems the onData
listener callback is never fired, even when I ptyProcess.write()
something.
ā Getting input from xterm
all the way to the device ptyProcess.write
does not seem to work. I can see the data passed along through the socket.io sockets all the way to the device. But from there nothing happens. What do I miss ? Also I don't see my input in the xterm
window as well.
After switching from child_process
to using node-pty
to create an interactive shell session I almost had it right. Following the node-pty
documentation it marked the on('data')
eventhandler as deprecated. Instead I should use .onData
property of the process to register a callback. Like this:
ptyProcess.onData = function(data) {
socket.emit('data', data);
};
But that didn't do anything. So I switched back to the depracated way of adding an event listener:
ptyProcess.on('data', function(data) {
socket.emit('data', data);
});
Now I have a working interactive shell session forwarded from a remote device through websocket inside my browser ā .
UPDATE
Did more digging for onData
property. Realized it's not a property but a method so I used it wrong. This would be the prefered way :
ptyProcess.onData(function(data) {
socket.emit('data', data);
});
Which also works as expected š