Skip to content

Commit e5f9a12

Browse files
Added ping mechanism for keeping websocket alive (#17) (#18)
Makes sure the websocket stays open in case behind a webserver * Added ping mechanism for keeping websocket alive (#17) * Better implementation of ping (#17) * Added support for setting ping interval (#17) The ping interval is configurable via `src/client/config.js`: ``` { xterm: { ping: 30000 } } ```
1 parent 7a068a6 commit e5f9a12

File tree

3 files changed

+98
-4
lines changed

3 files changed

+98
-4
lines changed

attach.js

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// This is a custom AttachAddon implementation
2+
3+
function addSocketListener(socket, type, handler) {
4+
socket.addEventListener(type, handler);
5+
return {
6+
dispose: () => {
7+
if (!handler) {
8+
// Already disposed
9+
return;
10+
}
11+
socket.removeEventListener(type, handler);
12+
}
13+
};
14+
}
15+
16+
export class AttachAddon {
17+
constructor(socket, options) {
18+
this._socket = socket;
19+
// always set binary type to arraybuffer, we do not handle blobs
20+
this._socket.binaryType = 'arraybuffer';
21+
this._bidirectional = (options && options.bidirectional === false) ? false : true;
22+
this._disposables = [];
23+
}
24+
25+
activate(terminal) {
26+
this._disposables.push(
27+
addSocketListener(this._socket, 'message', ev => {
28+
const data = ev.data;
29+
if (typeof data === 'string') {
30+
const message = JSON.parse(data);
31+
if (message.content) {
32+
terminal.write(message.content);
33+
}
34+
} else {
35+
terminal.write(new Uint8Array(data));
36+
}
37+
})
38+
);
39+
40+
if (this._bidirectional) {
41+
this._disposables.push(terminal.onData(data => this._sendData(data)));
42+
this._disposables.push(terminal.onBinary(data => this._sendBinary(data)));
43+
}
44+
45+
this._disposables.push(addSocketListener(this._socket, 'close', () => this.dispose()));
46+
this._disposables.push(addSocketListener(this._socket, 'error', () => this.dispose()));
47+
}
48+
49+
dispose() {
50+
this._disposables.forEach(d => d.dispose());
51+
}
52+
53+
_sendData(data) {
54+
// TODO: do something better than just swallowing
55+
// the data if the socket is not in a working condition
56+
if (this._socket.readyState !== 1) {
57+
return;
58+
}
59+
this._socket.send(JSON.stringify({data}));
60+
}
61+
62+
_sendBinary(data) {
63+
if (this._socket.readyState !== 1) {
64+
return;
65+
}
66+
const buffer = new Uint8Array(data.length);
67+
for (let i = 0; i < data.length; ++i) {
68+
buffer[i] = data.charCodeAt(i) & 255;
69+
}
70+
this._socket.send(buffer);
71+
}
72+
}

index.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030

3131
import {Terminal} from 'xterm';
3232
import {FitAddon} from 'xterm-addon-fit';
33-
import {AttachAddon} from 'xterm-addon-attach';
3433
import * as clipboard from 'clipboard-polyfill';
34+
import {AttachAddon} from './attach.js';
3535

3636
import './index.scss';
3737
import osjs from 'osjs';
@@ -54,6 +54,7 @@ const createConnection = async (core, proc, win, term) => {
5454
term.writeln('Requesting connection....');
5555

5656
const {uuid} = await proc.request('/create', {method: 'post', body: params});
57+
const pingInterval = core.config('xterm.ping', 30000);
5758

5859
const ws = proc.socket('/socket', {
5960
socket: {
@@ -64,11 +65,18 @@ const createConnection = async (core, proc, win, term) => {
6465
const attachAddon = new AttachAddon(ws.connection);
6566
term.loadAddon(attachAddon);
6667

68+
let pinger;
6769
let closed = false;
6870
let pinged = false;
6971
let pid = -1;
7072

71-
ws.on('open', () => ws.send(uuid));
73+
ws.on('open', () => {
74+
ws.send(uuid);
75+
76+
pinger = setInterval(() => {
77+
ws.send(JSON.stringify({action: 'ping'}));
78+
}, pingInterval);
79+
});
7280

7381
ws.on('message', (ev) => {
7482
if (!pinged) {
@@ -80,6 +88,7 @@ const createConnection = async (core, proc, win, term) => {
8088
ws.on('close', () => {
8189
term.writeln('... Disconnected. Press any key to close terminal ...');
8290
closed = true;
91+
clearInterval(pinger);
8392
});
8493

8594
term.onKey(() => {

server.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,25 @@ const createTerminal = (core, ws, options = {}, args = []) => {
7676

7777
term.onData((data) => {
7878
try {
79-
ws.send(data);
79+
// This is a workaround for ping wrapper
80+
ws.send(JSON.stringify({content: data}));
8081
} catch (e) {
8182
console.warn(e);
8283
}
8384
});
84-
ws.on('message', (data) => term.write(data));
85+
86+
ws.on('message', (data) => {
87+
// This is a workaround for ping wrapper
88+
if (typeof data === 'string') {
89+
const message = JSON.parse(data);
90+
if (message.data) {
91+
term.write(message.data);
92+
}
93+
} else {
94+
term.write(data);
95+
}
96+
});
97+
8598
ws.on('close', () => kill());
8699

87100
terminals.push({

0 commit comments

Comments
 (0)