websocket cleanups

This commit is contained in:
Koushik Dutta
2021-08-30 21:31:03 -07:00
parent 8ecc05d6fc
commit e62042326e
5 changed files with 199 additions and 40 deletions

View File

@@ -3,6 +3,8 @@ const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json'));
pkg.scripts = {
// alias
"build": "scrypted-webpack",
"prepublishOnly": "NODE_ENV=production scrypted-webpack",
"prescrypted-vscode-launch": "scrypted-webpack",
"scrypted-vscode-launch": "scrypted-deploy-debug",

View File

@@ -79,7 +79,6 @@ module.exports = {
Long: "long",
// browser provide plugin polyfills
_websocket: path.resolve(__dirname, 'polyfill/websocket.js'),
wrtc: path.resolve(__dirname, 'polyfill/nodejs/wrtc'),
mdns: path.resolve(__dirname, 'polyfill/nodejs/mdns'),
serialport: path.resolve(__dirname, 'polyfill/nodejs/serialport'),
@@ -97,9 +96,6 @@ module.exports = {
new webpack.DefinePlugin({
'process.env.SSDP_COV': false,
}),
new webpack.ProvidePlugin({
WebSocket: '_websocket'
}),
],
optimization: {

View File

@@ -379,8 +379,11 @@ async function createREPLServer(events: EventEmitter): Promise<number> {
device
});
delete ctx.console;
delete ctx.window;
const welcome = `JavaScript REPL variables:\n${Object.keys(ctx).map(key => ' ' + key).join('\n')}\n\n`;
const replVariables = Object.keys(ctx).filter(key => key !== 'require');
const welcome = `JavaScript REPL variables:\n${replVariables.map(key => ' ' + key).join('\n')}\n\n`;
socket.write(welcome);
const r = repl.start({

View File

@@ -0,0 +1,159 @@
interface WebSocketEvent {
type: string;
reason?: string;
message?: string;
data?: string|ArrayBufferLike;
source?: any;
}
interface WebSocketEventListener {
(evt: WebSocketEvent): void;
}
// @ts-ignore
class WebSocketEventTarget {
events: { [type: string]: WebSocketEventListener[]} = {};
dispatchEvent(event: WebSocketEvent) {
const list = this.events[event.type];
if (!list) {
return;
}
for (const l of list) {
l(event);
}
}
addEventListener(type: string, f: WebSocketEventListener) {
let list = this.events[type];
if (!list) {
list = this.events[type] = [];
}
list.push(f);
}
removeEventListener(type: string, f: WebSocketEventListener) {
const list = this.events[type];
if (!list) {
return;
}
const index = list.indexOf(f);
if (index > -1) {
list.splice(index, 1);
}
}
}
function defineEventAttribute(p: any, type: string) {
Object.defineProperty(p, 'on' + type, {
get: function () {
throw new Error(`${type} is write only`);
},
set: function (f) {
this.events[type] = [f];
}
});
}
interface WebSocketEndCallback {
(): void;
}
interface WebSocketErrorCallback {
(e: Error): void;
}
interface WebSocketDataCallback {
(data: string | ArrayBufferLike): void;
}
interface WebSocketSend {
(message: string|ArrayBufferLike): void;
}
interface WebSocketConnectCallback {
(e: Error, ws: any, send: WebSocketSend): void;
}
interface WebSocketConnect {
(url: string, protocols: string[],
connect: WebSocketConnectCallback,
end: WebSocketEndCallback,
error: WebSocketErrorCallback,
data: WebSocketDataCallback): void;
}
export function createWebSocketClass(__websocketConnect: WebSocketConnect) {
// @ts-ignore
class WebSocket extends WebSocketEventTarget {
_url: string;
_protocols: string[];
readyState: number;
send: (message: string|ArrayBufferLike) => void;
_ws: any;
constructor(url: string, protocols?: string[]) {
super();
this._url = url;
this._protocols = protocols;
this.readyState = 0;
__websocketConnect(url, protocols, (e, ws, send) => {
// connect
if (e != null) {
this.dispatchEvent({
type: 'error',
message: e.toString(),
});
return;
}
this._ws = ws;
this.send = send;
this.readyState = 1;
this.dispatchEvent({
type: 'open',
});
}, () => {
// end
this.readyState = 3;
this.dispatchEvent({
type: 'close',
reason: 'closed',
});
}, (e: Error) => {
// error
this.readyState = 3;
this.dispatchEvent({
type: 'error',
message: e.toString(),
});
}, (data: string | ArrayBufferLike) => {
// data
this.dispatchEvent({
type: 'message',
data: data,
source: this,
});
});
}
get url() {
return this._url;
}
get extensions() {
return "";
}
close() {
this._ws.close();
}
}
defineEventAttribute(WebSocket.prototype, "close");
defineEventAttribute(WebSocket.prototype, "error");
defineEventAttribute(WebSocket.prototype, "message");
defineEventAttribute(WebSocket.prototype, "open");
return WebSocket;
}

View File

@@ -10,6 +10,7 @@ import { BufferSerializer } from './buffer-serializer';
import { Console } from 'console';
import { EventEmitter, PassThrough } from 'stream';
import { Writable } from 'node:stream';
import { createWebSocketClass } from './plugin-remote-websocket';
class DeviceLogger implements Logger {
nativeId: string;
@@ -405,42 +406,39 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
volume.writeFileSync(name, entry.getData());
}
function websocketConnect(url: string, protocols: any, connect: any, end: any, error: any, data: any) {
if (url.startsWith('io://')) {
const id = url.substring('io://'.length);
ioSockets[id] = {
data,
error,
end
};
connect(undefined, {
close: () => api.ioClose(id),
}, (message: string) => api.ioSend(id, message));
}
else if (url.startsWith('ws://')) {
const id = url.substring('ws://'.length);
ioSockets[id] = {
data,
error,
end
};
connect(undefined, {
close: () => api.ioClose(id),
}, (message: string) => api.ioSend(id, message));
}
else {
throw new Error('unsupported websocket');
}
}
const params: any = {
// legacy
android: {},
__websocketConnect(url: string, protocols: any, connect: any, end: any, error: any, data: any) {
if (url.startsWith('io://')) {
const id = url.substring('io://'.length);
ioSockets[id] = {
data,
error,
end
};
connect(undefined, {
close: () => api.ioClose(id),
}, (message: string) => api.ioSend(id, message));
}
else if (url.startsWith('ws://')) {
const id = url.substring('ws://'.length);
ioSockets[id] = {
data,
error,
end
};
connect(undefined, {
close: () => api.ioClose(id),
}, (message: string) => api.ioSend(id, message));
}
else {
throw new Error('unsupported websocket');
}
},
window,
require: (name: string) => {
if (name === 'fs' && !packageJson.scrypted.realfs) {
@@ -459,6 +457,7 @@ export function attachPluginRemote(peer: RpcPeer, options?: PluginRemoteAttachOp
log,
localStorage,
pluginHostAPI: api,
WebSocket: createWebSocketClass(websocketConnect),
};
if (getDeviceConsole) {