Skip to content

Proxy WebSocket upgrades across cloud nodes - #628

Open
digitaldan wants to merge 1 commit into
openhab:mainfrom
digitaldan:fix/cross-node-websocket-proxy
Open

Proxy WebSocket upgrades across cloud nodes#628
digitaldan wants to merge 1 commit into
openhab:mainfrom
digitaldan:fix/cross-node-websocket-proxy

Conversation

@digitaldan

Copy link
Copy Markdown
Contributor

The internal proxy had no 'upgrade' listener, so a handshake landing on the wrong node was dropped. Tunnel it instead, and carry staged cookies into the 101 so the CloudServer affinity cookie is refreshed on the handshake.

The internal proxy had no 'upgrade' listener, so a handshake landing on the
wrong node was dropped. Tunnel it instead, and carry staged cookies into the
101 so the CloudServer affinity cookie is refreshed on the handshake.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Dan Cunningham <dan@digitaldan.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves cross-node WebSocket handling in openHAB Cloud’s internal proxy path by explicitly tunneling HTTP upgrade handshakes (101 Switching Protocols) when a client connects to a node that doesn’t own the openHAB connection, and by ensuring staged cookies are included in raw 101 responses so affinity can be refreshed during WebSocket-only traffic.

Changes:

  • Add internal proxy support for http.request(...).on('upgrade', ...) to tunnel WebSocket traffic between cloud nodes.
  • Include staged Set-Cookie headers when ProxyHandler writes a raw 101 response directly to the socket.
  • Factor WebSocket-upgrade detection into isWebSocketUpgrade() and add unit coverage for both direct upgrade events and “hop-by-hop headers stripped” scenarios.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/mocha/unit/socket/proxy-handler.test.ts Adds unit tests asserting staged cookies are preserved in raw 101 responses and CR/LF is stripped to prevent response splitting.
tests/mocha/unit/routes/middleware.test.ts Adds tests proving internal tunneling works for upgrades arriving via server.on('upgrade') and via plain HTTP when hop-by-hop headers are stripped.
src/socket/proxy-handler.ts Carries staged cookies into the raw 101 response written to the client socket.
src/routes/middleware.ts Implements internal WebSocket upgrade tunneling (proxyReq.on('upgrade')) and introduces isWebSocketUpgrade().
src/routes/index.ts Reuses isWebSocketUpgrade() instead of duplicating upgrade detection logic.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/routes/middleware.ts
Comment on lines +75 to +78
export function isWebSocketUpgrade(req: Request): boolean {
return req.headers['upgrade']?.toLowerCase() === 'websocket'
|| (req.headers['sec-websocket-key'] != null && req.headers['sec-websocket-version'] != null);
}
Comment thread src/routes/middleware.ts
Comment on lines +392 to +396
// rawHeaders keeps the origin node's casing and order intact
let rawResponse = `HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage || 'Switching Protocols'}\r\n`;
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
rawResponse += `${proxyRes.rawHeaders[i]}: ${proxyRes.rawHeaders[i + 1]}\r\n`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants