From 6c034f2838bcdc6ef7ce481c94246acaa917adac Mon Sep 17 00:00:00 2001 From: ChK Date: Sun, 1 Feb 2026 14:38:11 +0100 Subject: [PATCH] Update: changes in 3DInput mit WSS --- 3DInput.js | 22 ++++++++++++ programs/driver.js | 71 ++++++++++++++++++++++++++++++++++++ public/WebService.js | 86 +++++++++++++++++++++++++++++++------------- 3 files changed, 155 insertions(+), 24 deletions(-) create mode 100755 programs/driver.js diff --git a/3DInput.js b/3DInput.js index e30cc5b..411e1f2 100755 --- a/3DInput.js +++ b/3DInput.js @@ -2,6 +2,7 @@ const fs = require('fs'); const https = require('https'); const WebSocket = require('ws'); const url = require('url'); +const driverWS = require("./programs/driver"); const httpsOptions = { "enable": true, @@ -34,6 +35,27 @@ server = https.createServer(httpsOptions, (req, res) => { }) }); +// vvvvvvvvvvvvvvvvvvvvv Forward WebSocket: Eigener WSS auf Driver vvvvvvvvvvvvvvvvvvvvvvvv +const wssInput = new WebSocket.Server({ noServer: true }); +const targetServer = process.env.TARGET_SERVER || 'wss://localhost:2095'; +process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; +driverWS.setupCommandForwarding(wssInput, targetServer); +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Upgrade handler: accept all WebSocket upgrades and forward to the forwarding server (wssInput) +server.on('upgrade', (req, socket, head) => { + const pathname = url.parse(req.url).pathname; + console.log('Upgrade request for', pathname); + try { + // Forward the upgrade to the forwarding server + wssInput.handleUpgrade(req, socket, head, (ws) => { + wssInput.emit('connection', ws, req); + }); + } catch (err) { + console.error('Upgrade handling error:', err && err.message || err); + socket.destroy(); + } +}); server.listen(10010); console.log("Connect to: https://localhost:10010") diff --git a/programs/driver.js b/programs/driver.js new file mode 100755 index 0000000..0ffdbed --- /dev/null +++ b/programs/driver.js @@ -0,0 +1,71 @@ +const WebSocket = require("ws"); + +/** + * Forwards WebSocket messages between browser clients (/robot) + * and a target WebSocket server (behind a firewall). + * + * @param {WebSocket.Server} wssInput - Local WebSocket server for browser clients + * @param {string} targetUrl - URL of target WebSocket server, e.g. "wss://internal.local:8080" + */ +function setupCommandForwarding(wssInput, targetUrl) { + let targetSocket; + const clients = new Set(); + + function connectTarget() { + console.log(`🔌 Connecting to target server: ${targetUrl}`); + targetSocket = new WebSocket(targetUrl); + + targetSocket.on("open", () => { + console.log("✅ Connected to target server"); + }); + + targetSocket.on("message", (msg) => { + const data = msg.toString(); + console.log("⬅️ Message from target:", data); + // Broadcast to all connected browsers + for (const client of clients) { + if (client.readyState === WebSocket.OPEN) { + client.send(data); + } + } + }); + + targetSocket.on("close", () => { + console.warn("⚠️ Target connection closed. Reconnecting in 5s..."); + setTimeout(connectTarget, 5000); + }); + + targetSocket.on("error", (err) => { + console.error("❌ Target connection error:", err.message); + }); + } + + connectTarget(); + + // When a browser connects to /robot + wssInput.on("connection", (ws, req) => { + console.log("🤖 Browser connected:", req.socket.remoteAddress); + clients.add(ws); + + ws.on("message", (msg) => { + const data = msg.toString(); + console.log("➡️ From browser → target:", data); + if (targetSocket?.readyState === WebSocket.OPEN) { + targetSocket.send(data); + } else { + console.warn("⚠️ Target not connected. Message dropped."); + } + }); + + ws.on("close", () => { + clients.delete(ws); + console.log("🔌 Browser disconnected"); + }); + + ws.on("error", (err) => { + console.error("❌ Browser socket error:", err.message); + }); + }); +} + +module.exports = { setupCommandForwarding }; \ No newline at end of file diff --git a/public/WebService.js b/public/WebService.js index 50493c9..0f89a02 100755 --- a/public/WebService.js +++ b/public/WebService.js @@ -2,33 +2,71 @@ var startTime = Date.now() ; console.log("Meine Document-Location: " + document.location); var lastPingRequest; -//var socket = new WebSocket(String(document.location).replace("https://","wss://").replace("10010","2095").replace("index.html","") + "echo"); -var socket = new WebSocket("wss://thinkcentre.local:2095/echo"); -socket.onopen = () => console.log('Connected') || setInterval(() => { lastPingRequest = Date.now(); - socket.send("Ping"); - }, 5000); -socket.onclose = (event) => console.log((event.wasClean) ? 'Disconnected' : 'Connection break: ' + (event.reason || event.code)); +// Use explicit '/echo' path and a small wrapper to guard sends from other modules +var socketUrl = (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + '/echo'; -socket.onmessage = (event) => { - if(event.data == "Ping"){ - console.log("Ping: " + (Date.now() - lastPingRequest).toString() + " ms"); +// socket wrapper keeps the same global identifier but guards sends when underlying ws is not open +var socket = { + _ws: null, + send: function(msg) { + if (this._ws && this._ws.readyState === WebSocket.OPEN) { + this._ws.send(msg); + } else { + console.warn('Socket not open, dropping message:', msg); + } } - else if(event.data.toString().includes("position")){ - console.log("Position: " + event.data); - } - else if(event.data.toString().includes("XYZ__FShow__XYZ")){ - const content = event.data.toString().split("XYZ__FShow__XYZ")[1]; - console.log("File Content: " + content); - console.log(document.getElementById("GCodeWindow")) - - console.log(document.querySelectorAll("textarea#GCodeWindow.editor-look")[0]); +}; - document.querySelectorAll("textarea#GCodeWindow.editor-look")[0].value = content; - } - else{ - console.log('DATA SinceStartup: ' + (Date.now() - startTime).toString() +': ', event.data); - } +let pingIntervalId = null; +let reconnectDelay = 1000; + +function connect() { + const ws = new WebSocket(socketUrl); + socket._ws = ws; + + ws.onopen = () => { + console.log('Connected'); + reconnectDelay = 1000; + if (pingIntervalId) clearInterval(pingIntervalId); + pingIntervalId = setInterval(() => { + if (socket._ws && socket._ws.readyState === WebSocket.OPEN) { + lastPingRequest = Date.now(); + socket.send('Ping'); + } + }, 5000); + }; + + ws.onclose = (event) => { + console.log((event.wasClean) ? 'Disconnected' : 'Connection break: ' + (event.reason || event.code)); + if (pingIntervalId) { clearInterval(pingIntervalId); pingIntervalId = null; } + setTimeout(() => { + console.log('Reconnecting...'); + connect(); + }, reconnectDelay); + reconnectDelay = Math.min(30000, reconnectDelay * 2); + }; + + ws.onmessage = (event) => { + if(event.data == 'Ping'){ + console.log('Ping: ' + (Date.now() - lastPingRequest).toString() + ' ms'); + } + else if(event.data.toString().includes('position')){ + console.log('Position: ' + event.data); + } + else if(event.data.toString().includes('XYZ__FShow__XYZ')){ + const content = event.data.toString().split('XYZ__FShow__XYZ')[1]; + console.log('File Content: ' + content); + const el = document.querySelector('textarea#GCodeWindow.editor-look'); + if (el) el.value = content; + } + else{ + console.log('DATA SinceStartup: ' + (Date.now() - startTime).toString() +': ', event.data); + } + }; + + ws.onerror = (err) => console.error('WebSocket error:', err || err.message); } -socket.onerror = (err) => console.error(err.message); + +connect();