2
0
mirror of https://github.com/ad1217/PrinterStatus synced 2024-11-21 23:13:49 -05:00

server: Actively check for loss of connection to octoprint websocket

Periodically `ping` the server, and check for returning `pong`s
This commit is contained in:
Adam Goldsmith 2021-10-28 02:34:28 -04:00
parent 51d3cbf41e
commit 009612898a

View File

@ -13,6 +13,10 @@ import * as octoprint from '../types/octoprint';
const PORT = process.env.PORT || 1234; const PORT = process.env.PORT || 1234;
const PING_TIME = 10000;
type Timeout = ReturnType<typeof setTimeout>;
type configuration = { type configuration = {
printers: { printers: {
[key: string]: { address: string; apikey: string }; [key: string]: { address: string; apikey: string };
@ -69,9 +73,6 @@ class PrinterStatus {
webcamURL?: URL; webcamURL?: URL;
name?: string; name?: string;
session_key?: string;
websocket?: WebSocket;
lastStatus?: messages.ExtendedMessage; lastStatus?: messages.ExtendedMessage;
constructor(slug: string, address: string, apikey: string) { constructor(slug: string, address: string, apikey: string) {
@ -79,42 +80,43 @@ class PrinterStatus {
this.address = address; this.address = address;
this.apikey = apikey; this.apikey = apikey;
// async init this.try_connect_websocket();
}
try_connect_websocket() {
this.connect_websocket().catch((e) => { this.connect_websocket().catch((e) => {
console.error('Failed to initialize "${this.slug}"'); console.error(
throw e; `Failed to connect to "${this.slug}", attempting reconnection in 5 seconds`
);
console.error(e);
setTimeout(() => this.try_connect_websocket(), 5000);
}); });
} }
async connect_websocket() { async connect_websocket() {
// initial setup const settings = await this.api_get('settings');
if (!this.session_key) { this.webcamURL = new URL(settings.webcam.streamUrl, this.address);
try { this.name = settings.appearance.name;
const settings = await this.api_get('settings');
this.webcamURL = new URL(settings.webcam.streamUrl, this.address);
this.name = settings.appearance.name;
// do passive login to get a session key from the API key // do passive login to get a session key from the API key
const login: octoprint.LoginResponse = await this.api_post('login', { const login: octoprint.LoginResponse = await this.api_post('login', {
passive: 'true', passive: 'true',
}); });
this.session_key = login.name + ':' + login.session; const session_key = login.name + ':' + login.session;
} catch {
console.log( let pingSender: ReturnType<typeof setInterval>;
`Failed to connect to "${this.slug}" attempting reconnection in 5 seconds` let pongTimeout: Timeout;
);
setTimeout(() => this.connect_websocket(), 5000);
return;
}
}
const url = new URL('/sockjs/websocket', this.address); const url = new URL('/sockjs/websocket', this.address);
url.protocol = 'ws'; url.protocol = 'ws';
this.websocket = new WebSocket(url.toString()); let websocket = new WebSocket(url.toString());
this.websocket websocket
.on('open', () => { .on('open', () => {
pingSender = setInterval(() => websocket.ping(), PING_TIME);
pongTimeout = this.heartbeat(websocket, pongTimeout);
console.log(`Connected to "${this.slug}"`); console.log(`Connected to "${this.slug}"`);
this.websocket!.send(JSON.stringify({ auth: this.session_key })); websocket.send(JSON.stringify({ auth: session_key }));
}) })
.on('message', (data: WebSocket.Data) => { .on('message', (data: WebSocket.Data) => {
const event: octoprint.Message = JSON.parse(data as string); const event: octoprint.Message = JSON.parse(data as string);
@ -130,14 +132,28 @@ class PrinterStatus {
this.lastStatus = ext_event; this.lastStatus = ext_event;
} }
}) })
.on('pong', () => {
pongTimeout = this.heartbeat(websocket, pongTimeout);
})
.on('close', () => { .on('close', () => {
clearInterval(pingSender);
clearTimeout(pongTimeout);
console.log( console.log(
`Lost connection to "${this.slug}" attempting reconnection in 5 seconds` `Lost connection to "${this.slug}", attempting reconnection in 5 seconds`
); );
setTimeout(() => this.connect_websocket(), 5000); setTimeout(() => this.try_connect_websocket(), 5000);
}); });
} }
heartbeat(websocket: WebSocket, pongTimeout: Timeout): Timeout {
clearTimeout(pongTimeout);
return setTimeout(() => {
console.log(`Missed 2 heartbeats for "${this.slug}", disconnecting`);
websocket.terminate();
}, PING_TIME * 2);
}
async api_get(endpoint: string): Promise<any> { async api_get(endpoint: string): Promise<any> {
const r = await fetch(new URL('/api/' + endpoint, this.address), { const r = await fetch(new URL('/api/' + endpoint, this.address), {
headers: { 'X-Api-Key': this.apikey }, headers: { 'X-Api-Key': this.apikey },