diff --git a/changelog.md b/changelog.md index c6b8402..c0141c5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-05-15 - 18.1.1 - fix(network-proxy/websocket) +Improve WebSocket connection closure and update router integration + +- Wrap WS close logic in try-catch blocks to ensure valid close codes are used for both incoming and outgoing WebSocket connections +- Use explicit numeric close codes (defaulting to 1000 when unavailable) to prevent improper socket termination +- Update NetworkProxy updateRoutes to also refresh the WebSocket handler routes for consistent configuration + ## 2025-05-15 - 18.1.0 - feat(nftables) Add NFTables integration for kernel-level forwarding and update documentation, tests, and helper functions diff --git a/test/test.networkproxy.ts b/test/test.networkproxy.ts index 7ae4f36..9c66cd9 100644 --- a/test/test.networkproxy.ts +++ b/test/test.networkproxy.ts @@ -238,6 +238,10 @@ tap.test('should start the proxy server', async () => { }, tls: { mode: 'terminate' + }, + websocket: { + enabled: true, + subprotocols: ['echo-protocol'] } } } diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 3c0aed9..07a26df 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '18.1.0', + version: '18.1.1', description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' } diff --git a/ts/proxies/network-proxy/network-proxy.ts b/ts/proxies/network-proxy/network-proxy.ts index c208ece..68db24d 100644 --- a/ts/proxies/network-proxy/network-proxy.ts +++ b/ts/proxies/network-proxy/network-proxy.ts @@ -500,6 +500,9 @@ export class NetworkProxy implements IMetricsTracker { this.logger.warn('Router has no recognized configuration method'); } + // Update WebSocket handler with new routes + this.webSocketHandler.setRoutes(routes); + this.logger.info(`Route configuration updated with ${routes.length} routes and ${legacyConfigs.length} proxy configs`); } diff --git a/ts/proxies/network-proxy/websocket-handler.ts b/ts/proxies/network-proxy/websocket-handler.ts index 0de6272..88abbee 100644 --- a/ts/proxies/network-proxy/websocket-handler.ts +++ b/ts/proxies/network-proxy/websocket-handler.ts @@ -419,9 +419,15 @@ export class WebSocketHandler { wsIncoming.on('close', (code, reason) => { this.logger.debug(`WebSocket client connection closed: ${code} ${reason}`); if (wsOutgoing.readyState === wsOutgoing.OPEN) { - const validCode = code || 1000; - const reasonString = toBuffer(reason).toString(); - wsOutgoing.close(validCode, reasonString); + // Ensure code is a valid WebSocket close code number + const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000; + try { + const reasonString = reason ? toBuffer(reason).toString() : ''; + wsOutgoing.close(validCode, reasonString); + } catch (err) { + this.logger.error('Error closing wsOutgoing:', err); + wsOutgoing.close(validCode); + } } // Clean up timers @@ -432,9 +438,15 @@ export class WebSocketHandler { wsOutgoing.on('close', (code, reason) => { this.logger.debug(`WebSocket target connection closed: ${code} ${reason}`); if (wsIncoming.readyState === wsIncoming.OPEN) { - const validCode = code || 1000; - const reasonString = toBuffer(reason).toString(); - wsIncoming.close(validCode, reasonString); + // Ensure code is a valid WebSocket close code number + const validCode = typeof code === 'number' && code >= 1000 && code <= 4999 ? code : 1000; + try { + const reasonString = reason ? toBuffer(reason).toString() : ''; + wsIncoming.close(validCode, reasonString); + } catch (err) { + this.logger.error('Error closing wsIncoming:', err); + wsIncoming.close(validCode); + } } // Clean up timers