Skip to content

fix(rendezvous): preserve peer port and handle WS heartbeat behind reverse proxy - #27

Merged
YuZhiYuanDev merged 1 commit into
masterfrom
fix/ws-reverse-proxy-einval
Aug 14, 2026
Merged

fix(rendezvous): preserve peer port and handle WS heartbeat behind reverse proxy#27
YuZhiYuanDev merged 1 commit into
masterfrom
fix/ws-reverse-proxy-einval

Conversation

@hashbk

@hashbk hashbk commented Aug 14, 2026

Copy link
Copy Markdown

Summary

Fixes two compounding bugs in WebSocket support that caused WSS clients behind a reverse proxy to fail ~90% of the time and experience frequent spurious disconnects.

Closes #26.

Bug 1: X-Real-IP/X-Forwarded-For handling zeroes the peer's port

The WS handshake callback overwrote addr with ip:0 when a forwarded IP header was present, zeroing the peer's port. This addr was then used as Peer.socket_addr — the UDP relay destination — so sendto() to port 0 failed with EINVAL (os error 22), which propagated up and tore down the entire UDP listening socket, disrupting all connected clients.

Fix: Capture forwarded_ip separately via Arc<Mutex<Option<String>>> shared into the handshake closure, and thread it through handle_tcp as a new forwarded_ip: Option<&str> parameter used only for the reported IP string (Peer.info.ip), never for the routable socket_addr. Also handles X-Forwarded-For chains correctly (takes the first/original client entry).

Bug 2: WS read loop missing heartbeat handling

The WS read loop lacked the empty-message heartbeat handling that the plain-TCP branch has. An empty RendezvousMessage parsed successfully but matched no arm in handle_tcp, returning false and closing the WS connection on the client's next keep-alive.

Fix: Mirror the plain-TCP heartbeat handling (empty binary → echo empty response). Also handle Close frames explicitly (break immediately instead of waiting for the 30s idle timeout) and log read errors instead of silently exiting the while let loop.

…verse proxy

Bug 1: The WS handshake callback overwrote addr with ip:0 when
X-Real-IP/X-Forwarded-For was present, zeroing the peer's port.
This addr was used as Peer.socket_addr, so relaying to the WS peer
failed with EINVAL (os error 22) and tore down the entire UDP socket.
Fix: capture forwarded_ip separately via Arc<Mutex<Option<String>>>
and thread it through handle_tcp as a new parameter used only for the
reported IP string, never for the routable socket_addr. Also handles
X-Forwarded-For chains (takes the first/original client entry).

Bug 2: The WS read loop lacked the empty-message heartbeat handling
that the plain-TCP branch has. An empty RendezvousMessage parsed
successfully but matched no arm in handle_tcp, returning false and
closing the WS connection on the client's next keep-alive.
Fix: mirror the plain-TCP heartbeat handling, handle Close frames
explicitly, and log read errors instead of silently exiting the loop.

Closes #26
@hashbk
hashbk force-pushed the fix/ws-reverse-proxy-einval branch from 0e91fb1 to bef15a4 Compare August 14, 2026 09:36
@YuZhiYuanDev
YuZhiYuanDev merged commit 5da9290 into master Aug 14, 2026
4 checks passed
@YuZhiYuanDev
YuZhiYuanDev deleted the fix/ws-reverse-proxy-einval branch August 14, 2026 09:37
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.

WSS behind reverse proxy error rustdesk-server

2 participants