From c311c111bdd1b3c20d056f6f46a7c3c12610f8ce Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Fri, 5 Jul 2024 16:41:39 +0200 Subject: [PATCH 1/4] convert ws to native WebSocket --- examples/client-server/client-benchmark.js | 16 +++---- examples/client-server/client-periodic.js | 16 +++---- examples/client-server/client.js | 16 +++---- examples/client-server/package-lock.json | 27 ----------- examples/client-server/package.json | 1 - examples/client-server/signaling-server.js | 54 ++++++++++++++-------- 6 files changed, 56 insertions(+), 74 deletions(-) diff --git a/examples/client-server/client-benchmark.js b/examples/client-server/client-benchmark.js index cbc17fb..8d9a2be 100644 --- a/examples/client-server/client-benchmark.js +++ b/examples/client-server/client-benchmark.js @@ -6,7 +6,6 @@ const require = createRequire(import.meta.url); const yargs = require('yargs/yargs'); const { hideBin } = require('yargs/helpers'); -import WebSocket from 'ws'; import readline from 'readline'; import nodeDataChannel from '../../lib/index.js'; @@ -60,23 +59,22 @@ const rl = readline.createInterface({ output: process.stdout, }); -const ws = new WebSocket(wsUrl + '/' + id, { - perMessageDeflate: false, -}); +const ws = new nodeDataChannel.WebSocket(); +ws.open(wsUrl + '/' + id); console.log(`The local ID is: ${id}`); console.log(`Waiting for signaling to be connected...`); -ws.on('open', () => { +ws.onOpen(() => { console.log('WebSocket connected, signaling ready'); readUserInput(); }); -ws.on('error', (err) => { +ws.onError((err) => { console.log('WebSocket Error: ', err); }); -ws.on('message', (msgStr) => { +ws.onMessage((msgStr) => { let msg = JSON.parse(msgStr); switch (msg.type) { case 'offer': @@ -184,10 +182,10 @@ function createPeerConnection(peerId) { console.log('GatheringState: ', state); }); peerConnection.onLocalDescription((description, type) => { - ws.send(JSON.stringify({ id: peerId, type, description })); + ws.sendMessage(JSON.stringify({ id: peerId, type, description })); }); peerConnection.onLocalCandidate((candidate, mid) => { - ws.send(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); + ws.sendMessage(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); }); peerConnection.onDataChannel((dc) => { rl.close(); diff --git a/examples/client-server/client-periodic.js b/examples/client-server/client-periodic.js index 95bf602..c97a92b 100644 --- a/examples/client-server/client-periodic.js +++ b/examples/client-server/client-periodic.js @@ -1,4 +1,3 @@ -import WebSocket from 'ws'; import readline from 'readline'; import nodeDataChannel from '../../lib/index.js'; @@ -25,23 +24,22 @@ const rl = readline.createInterface({ // Signaling Server const WS_URL = process.env.WS_URL || 'ws://localhost:8000'; -const ws = new WebSocket(WS_URL + '/' + id, { - perMessageDeflate: false, -}); +const ws = new nodeDataChannel.WebSocket(); +ws.open(WS_URL + '/' + id); console.log(`The local ID is: ${id}`); console.log(`Waiting for signaling to be connected...`); -ws.on('open', () => { +ws.onOpen(() => { console.log('WebSocket connected, signaling ready'); readUserInput(); }); -ws.on('error', (err) => { +ws.onError((err) => { console.log('WebSocket Error: ', err); }); -ws.on('message', (msgStr) => { +ws.onMessage((msgStr) => { let msg = JSON.parse(msgStr); switch (msg.type) { case 'offer': @@ -122,10 +120,10 @@ function createPeerConnection(peerId) { console.log('GatheringState: ', state); }); peerConnection.onLocalDescription((description, type) => { - ws.send(JSON.stringify({ id: peerId, type, description })); + ws.sendMessage(JSON.stringify({ id: peerId, type, description })); }); peerConnection.onLocalCandidate((candidate, mid) => { - ws.send(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); + ws.sendMessage(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); }); peerConnection.onDataChannel((dc) => { rl.close(); diff --git a/examples/client-server/client.js b/examples/client-server/client.js index 95a4841..2046519 100644 --- a/examples/client-server/client.js +++ b/examples/client-server/client.js @@ -1,4 +1,3 @@ -import WebSocket from 'ws'; import readline from 'readline'; import nodeDataChannel from '../../lib/index.js'; @@ -13,23 +12,22 @@ const id = randomId(4); // Signaling Server const WS_URL = process.env.WS_URL || 'ws://localhost:8000'; -const ws = new WebSocket(WS_URL + '/' + id, { - perMessageDeflate: false, -}); +const ws = new nodeDataChannel.WebSocket(); +ws.open(WS_URL + '/' + id); console.log(`The local ID is: ${id}`); console.log(`Waiting for signaling to be connected...`); -ws.on('open', () => { +ws.onOpen(() => { console.log('WebSocket connected, signaling ready'); readUserInput(); }); -ws.on('error', (err) => { +ws.onError((err) => { console.log('WebSocket Error: ', err); }); -ws.on('message', (msgStr) => { +ws.onMessage((msgStr) => { let msg = JSON.parse(msgStr); switch (msg.type) { case 'offer': @@ -86,10 +84,10 @@ function createPeerConnection(peerId) { console.log('GatheringState: ', state); }); peerConnection.onLocalDescription((description, type) => { - ws.send(JSON.stringify({ id: peerId, type, description })); + ws.sendMessage(JSON.stringify({ id: peerId, type, description })); }); peerConnection.onLocalCandidate((candidate, mid) => { - ws.send(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); + ws.sendMessage(JSON.stringify({ id: peerId, type: 'candidate', candidate, mid })); }); peerConnection.onDataChannel((dc) => { console.log('DataChannel from ' + peerId + ' received with label "', dc.getLabel() + '"'); diff --git a/examples/client-server/package-lock.json b/examples/client-server/package-lock.json index 9f13652..ef520ef 100644 --- a/examples/client-server/package-lock.json +++ b/examples/client-server/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "ws": "^7.5.3", "yargs": "^16.2.0" } }, @@ -138,26 +137,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -282,12 +261,6 @@ "strip-ansi": "^6.0.0" } }, - "ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "requires": {} - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/examples/client-server/package.json b/examples/client-server/package.json index 63861d6..99e665f 100644 --- a/examples/client-server/package.json +++ b/examples/client-server/package.json @@ -11,7 +11,6 @@ "author": "", "license": "ISC", "dependencies": { - "ws": "^7.5.3", "yargs": "^16.2.0" } } diff --git a/examples/client-server/signaling-server.js b/examples/client-server/signaling-server.js index b5145ed..85123dc 100644 --- a/examples/client-server/signaling-server.js +++ b/examples/client-server/signaling-server.js @@ -1,28 +1,44 @@ -import WebSocket from 'ws'; +import nodeDataChannel from '../../lib/index.js'; + +// Init Logger +nodeDataChannel.initLogger('Debug'); const clients = {}; -const wss = new WebSocket.Server({ port: 8000 }); +const wsServer = new nodeDataChannel.WebSocketServer({ bindAddress: '127.0.0.1', port: 8000 }); + +wsServer.onClient((ws) => { + // path workaround + setTimeout(() => { + const id = ws.path().replace('/', ''); + console.log(`New Connection from ${id}`); -wss.on('connection', (ws, req) => { - const id = req.url.replace('/', ''); - console.log(`New Connection from ${id}`); + clients[id] = ws; + ws.onMessage((buffer) => { + let msg = JSON.parse(buffer); + let peerId = msg.id; + let peerWs = clients[peerId]; - clients[id] = ws; - ws.on('message', (buffer) => { - let msg = JSON.parse(buffer); - let peerId = msg.id; - let peerWs = clients[peerId]; + console.log(`Message from ${id} to ${peerId} : ${buffer}`); + if (!peerWs) return console.error(`Can not find peer with ID ${peerId}`); - console.log(`Message from ${id} to ${peerId} : ${buffer}`); - if (!peerWs) return console.error(`Can not find peer with ID ${peerId}`); + msg.id = id; + peerWs.sendMessage(JSON.stringify(msg)); + }); - msg.id = id; - peerWs.send(JSON.stringify(msg)); - }); + ws.onClosed(() => { + console.log(`${id} disconnected`); + delete clients[id]; + }); - ws.on('close', () => { - console.log(`${id} disconected`); - delete clients[id]; - }); + ws.onError((err) => { + console.error(err); + }); + }, 100); }); + +// There is a bug in the library that causes the WebSocketServer to be garbage collected +// This is a workaround to keep it alive +setInterval(() => { + console.log(wsServer); +}, 60 * 60 * 60); From 7b78800ee4a3eb06eb14ab7624058f81e9451a91 Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Sat, 6 Jul 2024 12:26:25 +0200 Subject: [PATCH 2/4] delete old comments --- test/connectivity.js | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/test/connectivity.js b/test/connectivity.js index e9b9eed..1639e8e 100644 --- a/test/connectivity.js +++ b/test/connectivity.js @@ -1,30 +1,16 @@ import nodeDataChannel from '../lib/index.js'; nodeDataChannel.initLogger('Debug'); -nodeDataChannel.preload(); let dc1 = null; let dc2 = null; -// Config options -// export interface RtcConfig { -// iceServers: string[]; -// proxyServer?: ProxyServer; -// bindAddress?: string; -// enableIceTcp?: boolean; -// portRangeBegin?: number; -// portRangeEnd?: number; -// maxMessageSize?: number; -// iceTransportPolicy?: TransportPolicy; -// } -// // "iceServers" option is an array of stun/turn server urls // Examples; // STUN Server Example : stun:stun.l.google.com:19302 // TURN Server Example : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT // TURN Server Example (TCP) : turn:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT?transport=tcp // TURN Server Example (TLS) : turns:USERNAME:PASSWORD@TURN_IP_OR_ADDRESS:PORT - let peer1 = new nodeDataChannel.PeerConnection('Peer1', { iceServers: ['stun:stun.l.google.com:19302'] }); // Set Callbacks @@ -78,15 +64,7 @@ peer2.onDataChannel((dc) => { }); }); -// DataChannel Options -// export interface DataChannelInitConfig { -// protocol?: string; -// negotiated?: boolean; -// id?: number; -// ordered?: boolean; -// maxPacketLifeTime?: number; -// maxRetransmits?: number; -// } +// Create DataChannel dc1 = peer1.createDataChannel('test'); dc1.onOpen(() => { dc1.sendMessage('Hello from Peer1'); From 0df4b4a9d8de22dfa3065d99ade53e16d6df0f42 Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Sat, 6 Jul 2024 13:01:46 +0200 Subject: [PATCH 3/4] move path read to onOpen --- examples/client-server/signaling-server.js | 51 +++++++++++----------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/examples/client-server/signaling-server.js b/examples/client-server/signaling-server.js index 85123dc..1a6aceb 100644 --- a/examples/client-server/signaling-server.js +++ b/examples/client-server/signaling-server.js @@ -8,33 +8,34 @@ const clients = {}; const wsServer = new nodeDataChannel.WebSocketServer({ bindAddress: '127.0.0.1', port: 8000 }); wsServer.onClient((ws) => { - // path workaround - setTimeout(() => { - const id = ws.path().replace('/', ''); - console.log(`New Connection from ${id}`); + let id = ''; + ws.onOpen(() => { + id = ws.path().replace('/', ''); + console.log(`New Connection from ${id}`); clients[id] = ws; - ws.onMessage((buffer) => { - let msg = JSON.parse(buffer); - let peerId = msg.id; - let peerWs = clients[peerId]; - - console.log(`Message from ${id} to ${peerId} : ${buffer}`); - if (!peerWs) return console.error(`Can not find peer with ID ${peerId}`); - - msg.id = id; - peerWs.sendMessage(JSON.stringify(msg)); - }); - - ws.onClosed(() => { - console.log(`${id} disconnected`); - delete clients[id]; - }); - - ws.onError((err) => { - console.error(err); - }); - }, 100); + }); + + ws.onMessage((buffer) => { + let msg = JSON.parse(buffer); + let peerId = msg.id; + let peerWs = clients[peerId]; + + console.log(`Message from ${id} to ${peerId} : ${buffer}`); + if (!peerWs) return console.error(`Can not find peer with ID ${peerId}`); + + msg.id = id; + peerWs.sendMessage(JSON.stringify(msg)); + }); + + ws.onClosed(() => { + console.log(`${id} disconnected`); + delete clients[id]; + }); + + ws.onError((err) => { + console.error(err); + }); }); // There is a bug in the library that causes the WebSocketServer to be garbage collected From e5d386af21e8051cd9bac93b3ed1be55cf5c83d8 Mon Sep 17 00:00:00 2001 From: Murat Dogan Date: Fri, 19 Jul 2024 15:30:32 +0200 Subject: [PATCH 4/4] websocket class --- examples/client-server/signaling-server.js | 6 ---- lib/index.js | 37 +++++++++++++--------- lib/node-datachannel.js | 7 ++++ lib/websocket-server.js | 34 ++++++++++++++++++++ 4 files changed, 63 insertions(+), 21 deletions(-) create mode 100644 lib/node-datachannel.js create mode 100644 lib/websocket-server.js diff --git a/examples/client-server/signaling-server.js b/examples/client-server/signaling-server.js index 1a6aceb..c64bcd5 100644 --- a/examples/client-server/signaling-server.js +++ b/examples/client-server/signaling-server.js @@ -37,9 +37,3 @@ wsServer.onClient((ws) => { console.error(err); }); }); - -// There is a bug in the library that causes the WebSocketServer to be garbage collected -// This is a workaround to keep it alive -setInterval(() => { - console.log(wsServer); -}, 60 * 60 * 60); diff --git a/lib/index.js b/lib/index.js index 3035b61..3019cc0 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,9 +1,6 @@ -// createRequire is native in node version >= 12 -import { createRequire } from 'module'; -const require = createRequire(import.meta.url); - -const nodeDataChannel = require('../build/Release/node_datachannel.node'); +import nodeDataChannel from './node-datachannel.js'; import DataChannelStream from './datachannel-stream.js'; +import WebSocketServer from './websocket-server.js'; const { initLogger, @@ -17,9 +14,16 @@ const { DataChannel, PeerConnection, WebSocket, - WebSocketServer, } = nodeDataChannel; +export const DescriptionType = { + Unspec: 'unspec', + Offer: 'offer', + Answer: 'answer', + Pranswer: 'pranswer', + Rollback: 'rollback', +}; + export { initLogger, cleanup, @@ -38,14 +42,17 @@ export { }; export default { - ...nodeDataChannel, + initLogger, + cleanup, + preload, + setSctpSettings, + RtcpReceivingSession, + Track, + Video, + Audio, + DataChannel, + PeerConnection, + WebSocket, + WebSocketServer, DataChannelStream, }; - -export const DescriptionType = { - Unspec: 'unspec', - Offer: 'offer', - Answer: 'answer', - Pranswer: 'pranswer', - Rollback: 'rollback', -}; diff --git a/lib/node-datachannel.js b/lib/node-datachannel.js new file mode 100644 index 0000000..62435f1 --- /dev/null +++ b/lib/node-datachannel.js @@ -0,0 +1,7 @@ +// createRequire is native in node version >= 12 +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); + +const nodeDataChannel = require('../build/Release/node_datachannel.node'); + +export default nodeDataChannel; diff --git a/lib/websocket-server.js b/lib/websocket-server.js new file mode 100644 index 0000000..70f93ca --- /dev/null +++ b/lib/websocket-server.js @@ -0,0 +1,34 @@ +import { EventEmitter } from 'events'; +import nodeDataChannel from './node-datachannel.js'; + +export default class WebSocketServer extends EventEmitter { + #server; + #clients = []; + + constructor(options) { + super(); + this.#server = new nodeDataChannel.WebSocketServer(options); + + this.#server.onClient((client) => { + this.emit('client', client); + this.#clients.push(client); + }); + } + + port() { + return this.#server?.port() || 0; + } + + stop() { + this.#clients.forEach((client) => { + client?.close(); + }); + this.#server?.stop(); + this.#server = null; + this.removeAllListeners(); + } + + onClient(cb) { + if (this.#server) this.on('client', cb); + } +}