Files
xterm.js/demo/server.js
T

174 lines
5.0 KiB
JavaScript

/**
* WARNING: This demo is a barebones implementation designed for development and evaluation
* purposes only. It is definitely NOT production ready and does not aim to be so. Exposing the
* demo to the public as is would introduce security risks for the host.
**/
var express = require('express');
var expressWs = require('express-ws');
var os = require('os');
var pty = require('node-pty');
// Whether to use binary transport.
const USE_BINARY = os.platform() !== "win32";
function startServer() {
var app = express();
expressWs(app);
var terminals = {},
unsentOutput = {},
temporaryDisposable = {};
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
app.get('/logo.png', (req, res) => {
res.sendFile(__dirname + '/logo.png');
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
});
app.get('/test', (req, res) => {
res.sendFile(__dirname + '/test.html');
});
app.get('/style.css', (req, res) => {
res.sendFile(__dirname + '/style.css');
});
app.use('/dist', express.static(__dirname + '/dist'));
app.use('/src', express.static(__dirname + '/src'));
app.post('/terminals', (req, res) => {
const env = Object.assign({}, process.env);
env['COLORTERM'] = 'truecolor';
var cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
term = pty.spawn(process.platform === 'win32' ? 'pwsh.exe' : 'bash', [], {
name: 'xterm-256color',
cols: cols || 80,
rows: rows || 24,
cwd: process.platform === 'win32' ? undefined : env.PWD,
env: env,
encoding: USE_BINARY ? null : 'utf8'
});
console.log('Created terminal with PID: ' + term.pid);
terminals[term.pid] = term;
unsentOutput[term.pid] = '';
temporaryDisposable[term.pid] = term.onData(function(data) {
unsentOutput[term.pid] += data;
});
res.send(term.pid.toString());
res.end();
});
app.post('/terminals/:pid/size', (req, res) => {
var pid = parseInt(req.params.pid),
cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
term = terminals[pid];
term.resize(cols, rows);
console.log('Resized terminal ' + pid + ' to ' + cols + ' cols and ' + rows + ' rows.');
res.end();
});
app.ws('/terminals/:pid', function (ws, req) {
var term = terminals[parseInt(req.params.pid)];
console.log('Connected to terminal ' + term.pid);
temporaryDisposable[term.pid].dispose();
delete temporaryDisposable[term.pid];
ws.send(unsentOutput[term.pid]);
delete unsentOutput[term.pid];
// unbuffered delivery after user input
let userInput = false;
// string message buffering
function buffer(socket, timeout, maxSize) {
let s = '';
let sender = null;
return (data) => {
s += data;
if (s.length > maxSize || userInput) {
userInput = false;
socket.send(s);
s = '';
if (sender) {
clearTimeout(sender);
sender = null;
}
} else if (!sender) {
sender = setTimeout(() => {
socket.send(s);
s = '';
sender = null;
}, timeout);
}
};
}
// binary message buffering
function bufferUtf8(socket, timeout, maxSize) {
const dataBuffer = new Uint8Array(maxSize);
let sender = null;
let length = 0;
return (data) => {
function flush() {
socket.send(Buffer.from(dataBuffer.buffer, 0, length));
length = 0;
if (sender) {
clearTimeout(sender);
sender = null;
}
}
if (length + data.length > maxSize) {
flush();
}
dataBuffer.set(data, length);
length += data.length;
if (length > maxSize || userInput) {
userInput = false;
flush();
} else if (!sender) {
sender = setTimeout(() => {
sender = null;
flush();
}, timeout);
}
};
}
const send = (USE_BINARY ? bufferUtf8 : buffer)(ws, 5, 262144);
// WARNING: This is a naive implementation that will not throttle the flow of data. This means
// it could flood the communication channel and make the terminal unresponsive. Learn more about
// the problem and how to implement flow control at https://xtermjs.org/docs/guides/flowcontrol/
term.onData(function(data) {
try {
send(data);
} catch (ex) {
// The WebSocket is not open, ignore
}
});
ws.on('message', function(msg) {
term.write(msg);
userInput = true;
});
ws.on('close', function () {
term.kill();
console.log('Closed terminal ' + term.pid);
// Clean things up
delete terminals[term.pid];
});
});
var port = process.env.PORT || 3000,
host = os.platform() === 'win32' ? '127.0.0.1' : '0.0.0.0';
console.log('App listening to http://127.0.0.1:' + port);
app.listen(port, host);
}
module.exports = startServer;