@interopio/gateway-server
Version:
## `@glue42/gateway-ent` compatibility
91 lines (84 loc) • 3.17 kB
text/typescript
import * as ws from 'ws';
import getLogger from '../../logger.js';
import {socketKey} from '../../utils.js';
import {HttpServerRequest} from '../../server/exchange.js';
import {IOGateway} from '@interopio/gateway';
import GatewayEncoders = IOGateway.Encoding;
const log = getLogger('ws');
const codec = GatewayEncoders.json<IOGateway.Message>();
function initClient(this: IOGateway.Gateway, key: string, socket: ws.WebSocket, host?: string): IOGateway.GatewayClient<string> | undefined {
const opts = {
key,
host,
codec,
onPing: () => {
socket.ping((err?: Error) => {
if (err) {
log.warn(`failed to ping ${key}`, err);
}
else {
log.info(`ping sent to ${key}`);
}
});
},
onDisconnect: (reason: 'inactive' | 'shutdown') => {
switch (reason) {
case 'inactive': {
log.warn(`no heartbeat (ping) received from ${key}, closing socket`);
socket.close(4001, 'ping expected');
break;
}
case 'shutdown': {
socket.close(1001, 'shutdown');
break;
}
}
}
};
try {
return this.client((data) => socket.send(data), opts);
} catch (err) {
log.warn(`${key} failed to create client`, err);
}
}
async function create(this: IOGateway.Gateway, server: {endpoint: string, wss: ws.WebSocketServer}): Promise<{ close: () => Promise<void> }> {
log.info('start gateway');
await this.start({endpoint: server.endpoint});
server.wss
.on('error', (err: Error) => {
log.error('error starting the gateway websocket server', err);
})
.on('connection', (socket, req) => {
const request = new HttpServerRequest(req);
const key = socketKey(request.socket);
const host = request.host;
log.info(`${key} connected on gw from ${host}`);
const client = initClient.call(this, key, socket);
if (!client) {
log.error(`${key} gw client init failed`);
socket.terminate();
return;
}
socket.on('error', (err: Error) => {
log.error(`${key} websocket error: ${err}`, err);
});
socket.on('message', (data, _isBinary) => {
if (Array.isArray(data)) {
data = Buffer.concat(data);
}
// JSON.parse will invoke abstract operation ToString (coerce it) prior parsing (https://262.ecma-international.org/5.1/#sec-15.12.2)
client.send(data as unknown as string);
});
socket.on('close', (code) => {
log.info(`${key} disconnected from gw. code: ${code}`);
client.close();
});
});
return {
close: async () => {
server.wss.close();
await this.stop();
}
}
}
export default create;