156 lines
5.4 KiB
JavaScript
156 lines
5.4 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.WebSocketHandler = void 0;
|
|
const WebSocket = require("isomorphic-ws");
|
|
const protocols = ['v4.channel.k8s.io', 'v3.channel.k8s.io', 'v2.channel.k8s.io', 'channel.k8s.io'];
|
|
class WebSocketHandler {
|
|
// factory is really just for test injection
|
|
constructor(config, socketFactory) {
|
|
this.config = config;
|
|
this.socketFactory = socketFactory;
|
|
}
|
|
static handleStandardStreams(streamNum, buff, stdout, stderr) {
|
|
if (buff.length < 1) {
|
|
return null;
|
|
}
|
|
if (stdout && streamNum === WebSocketHandler.StdoutStream) {
|
|
stdout.write(buff);
|
|
}
|
|
else if (stderr && streamNum === WebSocketHandler.StderrStream) {
|
|
stderr.write(buff);
|
|
}
|
|
else if (streamNum === WebSocketHandler.StatusStream) {
|
|
// stream closing.
|
|
if (stdout && stdout !== process.stdout) {
|
|
stdout.end();
|
|
}
|
|
if (stderr && stderr !== process.stderr) {
|
|
stderr.end();
|
|
}
|
|
return JSON.parse(buff.toString('utf8'));
|
|
}
|
|
else {
|
|
throw new Error('Unknown stream: ' + streamNum);
|
|
}
|
|
return null;
|
|
}
|
|
static handleStandardInput(ws, stdin, streamNum = 0) {
|
|
stdin.on('data', (data) => {
|
|
const buff = Buffer.alloc(data.length + 1);
|
|
buff.writeInt8(streamNum, 0);
|
|
if (data instanceof Buffer) {
|
|
data.copy(buff, 1);
|
|
}
|
|
else {
|
|
buff.write(data, 1);
|
|
}
|
|
ws.send(buff);
|
|
});
|
|
stdin.on('end', () => {
|
|
ws.close();
|
|
});
|
|
// Keep the stream open
|
|
return true;
|
|
}
|
|
static async processData(data, ws, createWS, streamNum = 0, retryCount = 3) {
|
|
const buff = Buffer.alloc(data.length + 1);
|
|
buff.writeInt8(streamNum, 0);
|
|
if (data instanceof Buffer) {
|
|
data.copy(buff, 1);
|
|
}
|
|
else {
|
|
buff.write(data, 1);
|
|
}
|
|
let i = 0;
|
|
for (; i < retryCount; ++i) {
|
|
if (ws !== null && ws.readyState === WebSocket.OPEN) {
|
|
ws.send(buff);
|
|
break;
|
|
}
|
|
else {
|
|
ws = await createWS();
|
|
}
|
|
}
|
|
// This throw doesn't go anywhere.
|
|
// TODO: Figure out the right way to return an error.
|
|
if (i >= retryCount) {
|
|
throw new Error("can't send data to ws");
|
|
}
|
|
return ws;
|
|
}
|
|
static restartableHandleStandardInput(createWS, stdin, streamNum = 0, retryCount = 3) {
|
|
if (retryCount < 0) {
|
|
throw new Error("retryCount can't be lower than 0.");
|
|
}
|
|
let queue = Promise.resolve();
|
|
let ws = null;
|
|
stdin.on('data', (data) => {
|
|
queue = queue.then(async () => {
|
|
ws = await WebSocketHandler.processData(data, ws, createWS, streamNum, retryCount);
|
|
});
|
|
});
|
|
stdin.on('end', () => {
|
|
if (ws) {
|
|
ws.close();
|
|
}
|
|
});
|
|
return () => ws;
|
|
}
|
|
/**
|
|
* Connect to a web socket endpoint.
|
|
* @param path The HTTP Path to connect to on the server.
|
|
* @param textHandler Callback for text over the web socket.
|
|
* Returns true if the connection should be kept alive, false to disconnect.
|
|
* @param binaryHandler Callback for binary data over the web socket.
|
|
* Returns true if the connection should be kept alive, false to disconnect.
|
|
*/
|
|
async connect(path, textHandler, binaryHandler) {
|
|
const cluster = this.config.getCurrentCluster();
|
|
if (!cluster) {
|
|
throw new Error('No cluster is defined.');
|
|
}
|
|
const server = cluster.server;
|
|
const ssl = server.startsWith('https://');
|
|
const target = ssl ? server.substr(8) : server.substr(7);
|
|
const proto = ssl ? 'wss' : 'ws';
|
|
const uri = `${proto}://${target}${path}`;
|
|
const opts = {};
|
|
await this.config.applytoHTTPSOptions(opts);
|
|
return await new Promise((resolve, reject) => {
|
|
const client = this.socketFactory
|
|
? this.socketFactory(uri, opts)
|
|
: new WebSocket(uri, protocols, opts);
|
|
let resolved = false;
|
|
client.onopen = () => {
|
|
resolved = true;
|
|
resolve(client);
|
|
};
|
|
client.onerror = (err) => {
|
|
if (!resolved) {
|
|
reject(err);
|
|
}
|
|
};
|
|
client.onmessage = ({ data }) => {
|
|
// TODO: support ArrayBuffer and Buffer[] data types?
|
|
if (typeof data === 'string') {
|
|
if (textHandler && !textHandler(data)) {
|
|
client.close();
|
|
}
|
|
}
|
|
else if (data instanceof Buffer) {
|
|
const streamNum = data.readInt8(0);
|
|
if (binaryHandler && !binaryHandler(streamNum, data.slice(1))) {
|
|
client.close();
|
|
}
|
|
}
|
|
};
|
|
});
|
|
}
|
|
}
|
|
exports.WebSocketHandler = WebSocketHandler;
|
|
WebSocketHandler.StdinStream = 0;
|
|
WebSocketHandler.StdoutStream = 1;
|
|
WebSocketHandler.StderrStream = 2;
|
|
WebSocketHandler.StatusStream = 3;
|
|
WebSocketHandler.ResizeStream = 4;
|
|
//# sourceMappingURL=web-socket-handler.js.map
|