From d0400abd58b925e9e1d6b328a26b197ea3fc49b0 Mon Sep 17 00:00:00 2001 From: Pat Date: Fri, 3 Feb 2023 18:54:21 +1100 Subject: [PATCH 1/2] feat: Add Node Websocket Client Connector Allows the package to be used from Node environments as well as browsers --- package.json | 6 ++- ...ButtplugBrowserWebsocketClientConnector.ts | 11 +----- .../ButtplugNodeWebsocketClientConnector.ts | 19 ++++++++++ src/index.ts | 1 + .../ButtplugBrowserWebsocketConnector.ts | 6 ++- yarn.lock | 38 +++++++++++++++---- 6 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 src/client/ButtplugNodeWebsocketClientConnector.ts diff --git a/package.json b/package.json index f699fe4..11f86af 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,9 @@ }, "dependencies": { "class-transformer": "^0.5.1", - "reflect-metadata": "^0.1.13" + "filereader": "^0.10.3", + "reflect-metadata": "^0.1.13", + "ws": "^8.12.0" }, "devDependencies": { "@types/commander": "^2.12.2", @@ -45,7 +47,7 @@ "@types/jest-environment-puppeteer": "^5.0.3", "@types/node": "^18.11.18", "@types/uuid-parse": "^1.0.0", - "@types/ws": "^8.5.3", + "@types/ws": "^8.5.4", "@typescript-eslint/eslint-plugin": "^5.47.1", "@typescript-eslint/parser": "^5.47.1", "copyfiles": "^2.4.1", diff --git a/src/client/ButtplugBrowserWebsocketClientConnector.ts b/src/client/ButtplugBrowserWebsocketClientConnector.ts index ad26fd2..9ff494a 100644 --- a/src/client/ButtplugBrowserWebsocketClientConnector.ts +++ b/src/client/ButtplugBrowserWebsocketClientConnector.ts @@ -19,15 +19,6 @@ export class ButtplugBrowserWebsocketClientConnector implements IButtplugClientConnector { private _sorter: ButtplugMessageSorter = new ButtplugMessageSorter(true); - protected _ws: WebSocket | undefined; - - public constructor(_url: string) { - super(_url); - } - - public get Connected(): boolean { - return this._ws !== undefined; - } public Send = async (msg: ButtplugMessage): Promise => { if (!this.Connected) { @@ -44,7 +35,7 @@ export class ButtplugBrowserWebsocketClientConnector const emitMsgs = this._sorter.ParseIncomingMessages(msgs); this.emit('message', emitMsgs); } else if (event.data instanceof Blob) { - const reader = new FileReader(); + const reader = new (this._filereaderConstructor ?? FileReader)(); reader.addEventListener('load', (ev) => { this.OnReaderLoad(ev); }); diff --git a/src/client/ButtplugNodeWebsocketClientConnector.ts b/src/client/ButtplugNodeWebsocketClientConnector.ts new file mode 100644 index 0000000..098d877 --- /dev/null +++ b/src/client/ButtplugNodeWebsocketClientConnector.ts @@ -0,0 +1,19 @@ +/*! + * Buttplug JS Source Code File - Visit https://buttplug.io for more info about + * the project. Licensed under the BSD 3-Clause license. See LICENSE file in the + * project root for full license information. + * + * @copyright Copyright (c) Nonpolynomial Labs LLC. All rights reserved. + */ + +'use strict'; + +import { ButtplugBrowserWebsocketClientConnector } from './ButtplugBrowserWebsocketClientConnector'; +import { WebSocket as NodeWebSocket } from 'ws'; +import { FileReader } from 'filereader'; + +export class ButtplugNodeWebsocketClientConnector extends ButtplugBrowserWebsocketClientConnector { + protected _websocketConstructor = + NodeWebSocket as unknown as typeof WebSocket; + protected _filereaderConstructor = FileReader; +} diff --git a/src/index.ts b/src/index.ts index 6470006..a79bfe8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ export * from './client/Client'; export * from './client/ButtplugClientDevice'; export * from './client/ButtplugBrowserWebsocketClientConnector'; +export * from './client/ButtplugNodeWebsocketClientConnector'; export * from './client/ButtplugClientConnectorException'; export * from './utils/ButtplugMessageSorter'; export * from './client/IButtplugClientConnector'; diff --git a/src/utils/ButtplugBrowserWebsocketConnector.ts b/src/utils/ButtplugBrowserWebsocketConnector.ts index 8b0dc6f..176c91c 100644 --- a/src/utils/ButtplugBrowserWebsocketConnector.ts +++ b/src/utils/ButtplugBrowserWebsocketConnector.ts @@ -14,6 +14,8 @@ import { FromJSON } from '../core/MessageUtils'; export class ButtplugBrowserWebsocketConnector extends EventEmitter { protected _ws: WebSocket | undefined; + protected _websocketConstructor: typeof WebSocket | null = null; + protected _filereaderConstructor: typeof FileReader | null = null; public constructor(private _url: string) { super(); @@ -24,7 +26,7 @@ export class ButtplugBrowserWebsocketConnector extends EventEmitter { } public Connect = async (): Promise => { - const ws = new WebSocket(this._url); + const ws = new (this._websocketConstructor ?? WebSocket)(this._url); let res; let rej; const p = new Promise((resolve, reject) => { @@ -81,7 +83,7 @@ export class ButtplugBrowserWebsocketConnector extends EventEmitter { const msgs = FromJSON(event.data); this.emit('message', msgs); } else if (event.data instanceof Blob) { - const reader = new FileReader(); + const reader = new (this._filereaderConstructor ?? FileReader)(); reader.addEventListener('load', (ev) => { this.OnReaderLoad(ev); }); diff --git a/yarn.lock b/yarn.lock index 2eb6532..cde6a20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1322,12 +1322,12 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.3": - version: 8.5.3 - resolution: "@types/ws@npm:8.5.3" +"@types/ws@npm:^8.5.4": + version: 8.5.4 + resolution: "@types/ws@npm:8.5.4" dependencies: "@types/node": "*" - checksum: 0ce46f850d41383fcdc2149bcacc86d7232fa7a233f903d2246dff86e31701a02f8566f40af5f8b56d1834779255c04ec6ec78660fe0f9b2a69cf3d71937e4ae + checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 languageName: node linkType: hard @@ -2699,7 +2699,7 @@ __metadata: "@types/jest-environment-puppeteer": ^5.0.3 "@types/node": ^18.11.18 "@types/uuid-parse": ^1.0.0 - "@types/ws": ^8.5.3 + "@types/ws": ^8.5.4 "@typescript-eslint/eslint-plugin": ^5.47.1 "@typescript-eslint/parser": ^5.47.1 class-transformer: ^0.5.1 @@ -2708,6 +2708,7 @@ __metadata: eslint: ^8.30.0 eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.2.1 + filereader: ^0.10.3 fork-ts-checker-webpack-plugin: ^7.2.14 gts: ^3.1.1 jest: ^29.3.1 @@ -2747,6 +2748,7 @@ __metadata: webpack-bundle-analyzer: ^3.6.1 webpack-cli: ^3.3.11 webpack-merge: ^4.2.2 + ws: ^8.12.0 yarn: ^1.22.19 languageName: unknown linkType: soft @@ -4899,6 +4901,13 @@ __metadata: languageName: node linkType: hard +"filereader@npm:^0.10.3": + version: 0.10.3 + resolution: "filereader@npm:0.10.3" + checksum: 870b38a4ea2e77512c642cdc03059171e4792a49610929f14d7e495137de8f4d3c8e408f5e30b22d45c5b18f6813bebb1854d7c8356ab9b7f08f06a6745a2772 + languageName: node + linkType: hard + "filesize@npm:^3.6.1": version: 3.6.1 resolution: "filesize@npm:3.6.1" @@ -11974,11 +11983,11 @@ __metadata: "typescript@patch:typescript@^4.9.4#~builtin": version: 4.9.4 - resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=d73830" + resolution: "typescript@patch:typescript@npm%3A4.9.4#~builtin::version=4.9.4&hash=23ec76" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 37f6e2c3c5e2aa5934b85b0fddbf32eeac8b1bacf3a5b51d01946936d03f5377fe86255d4e5a4ae628fd0cd553386355ad362c57f13b4635064400f3e8e05b9d + checksum: 3e2ab0772908676d9b9cb83398c70003a3b08e1c6b3b122409df9f4b520f2fdaefa20c3d7d57dce283fed760ac94b3ce94d4a7fa875127b67852904425a1f0dc languageName: node linkType: hard @@ -12580,6 +12589,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:^8.12.0": + version: 8.12.0 + resolution: "ws@npm:8.12.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 818ff3f8749c172a95a114cceb8b89cedd27e43a82d65c7ad0f7882b1e96a2ee6709e3746a903c3fa88beec0c8bae9a9fcd75f20858b32a166dfb7519316a5d7 + languageName: node + linkType: hard + "xdg-basedir@npm:^4.0.0": version: 4.0.0 resolution: "xdg-basedir@npm:4.0.0" From 701706b838ee37932eafc28d7a618e92abebb0cb Mon Sep 17 00:00:00 2001 From: Kyle Machulis Date: Sat, 4 Feb 2023 17:08:38 -0800 Subject: [PATCH 2/2] feat: Example of reversing ws Client/Server connections Teledildonics here we come --- example/server.ts | 15 +++++++++++ .../ButtplugBrowserWebsocketConnector.ts | 27 +++++++++++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 example/server.ts diff --git a/example/server.ts b/example/server.ts new file mode 100644 index 0000000..4a3ad0d --- /dev/null +++ b/example/server.ts @@ -0,0 +1,15 @@ +import { + ButtplugBrowserWebsocketClientConnector, + ButtplugClient, +} from '../src/'; +import { WebSocketServer } from 'ws'; + +const wss = new WebSocketServer({ port: 12346 }); + +wss.on('connection', async (ws) => { + const connector = new ButtplugBrowserWebsocketClientConnector(ws as any); + const client = new ButtplugClient('Test Client'); + client.addListener('deviceadded', (device) => console.log(`Device Added: ${device.name}`)); + await client.connect(connector); + await client.startScanning(); +}); diff --git a/src/utils/ButtplugBrowserWebsocketConnector.ts b/src/utils/ButtplugBrowserWebsocketConnector.ts index 176c91c..17118a7 100644 --- a/src/utils/ButtplugBrowserWebsocketConnector.ts +++ b/src/utils/ButtplugBrowserWebsocketConnector.ts @@ -17,7 +17,7 @@ export class ButtplugBrowserWebsocketConnector extends EventEmitter { protected _websocketConstructor: typeof WebSocket | null = null; protected _filereaderConstructor: typeof FileReader | null = null; - public constructor(private _url: string) { + public constructor(private _connectorOptions: WebSocket | string) { super(); } @@ -26,7 +26,30 @@ export class ButtplugBrowserWebsocketConnector extends EventEmitter { } public Connect = async (): Promise => { - const ws = new (this._websocketConstructor ?? WebSocket)(this._url); + if (typeof this._connectorOptions === 'string') { + return this.ConnectUrl(); + } + return this.ConnectWs(); + }; + + private ConnectWs = async (): Promise => { + this._ws = this._connectorOptions as WebSocket; + try { + await this.Initialize(); + this._ws.addEventListener('message', (msg) => { + this.ParseIncomingMessage(msg); + }); + this._ws.addEventListener('close', this.Disconnect); + } catch (e) { + console.log(e); + throw e; + } + }; + + private ConnectUrl = async (): Promise => { + const ws = new (this._websocketConstructor ?? WebSocket)( + this._connectorOptions as string + ); let res; let rej; const p = new Promise((resolve, reject) => {