rustdesk / rustdesk/rustdesk-server

WebSocket connection (port 21118/21119) is unilaterally closed after exactly ~30 seconds of idle time, even with no reverse proxy involved

Open
#704 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Rust
Stars
10.4k
Forks
2.6k
PR merge metrics
No merged PRs in 30d

Description

hbbs WebSocket connection (port 21118/21119) is unilaterally closed after exactly ~30 seconds of idle time, even with no reverse proxy involved

Environment:

  • hbbs/hbbr version: 1.1.16 (latest available at time of writing)
  • OS: Debian 12, x86_64
  • Deployment: systemd services, standard binaries in /opt/rustdesk-server/
  • hbbs started with: hbbs -r <domain>:21117 -k _
  • hbbr started with: hbbr -k _
  • RustDesk Desktop Client: v1.4.9 (Windows), allow-websocket = 'Y' set in RustDesk2.toml

Description

When a WebSocket client connects to hbbs on port 21118 (or hbbr on 21119) and completes the handshake but does not send further data immediately, the server closes the connection unilaterally after exactly ~30 seconds, regardless of whether any reverse proxy, firewall, or network component is involved.

This makes the documented allow-websocket=Y client feature effectively unusable against an OSS server whenever the client's registration/heartbeat interval is close to or exceeds this window: the client repeatedly gets disconnected before or right as it would send its next heartbeat, and never reaches a stable "ready" state.

Steps to reproduce (server-local, no proxy/network involved)

Run directly on the machine hosting hbbs:

exec 3<>/dev/tcp/127.0.0.1/21118
printf 'GET / HTTP/1.1\r\nHost: 127.0.0.1\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: SGVsbG9XZWJTb2NrZXQ=\r\nSec-WebSocket-Version: 13\r\n\r\n' >&3

date
( cat <&3; echo "--- connection closed by server ---"; date ) &
CATPID=$!
sleep 60
if kill -0 $CATPID 2>/dev/null; then
  echo "--- still open after 60s ---"
  kill $CATPID
fi
exec 3<&- ; exec 3>&-

Actual output

Mo 7. Sep 13:31:03 CEST 2026
HTTP/1.1 101 Switching Protocols
connection: Upgrade
upgrade: websocket
sec-websocket-accept: rzNTH42nbDwCim35ggSltPhhUV0=
--- connection closed by server ---
Mo 7. Sep 13:31:33 CEST 2026

The server accepts the handshake correctly (101 Switching Protocols), then closes the connection exactly 30.0 seconds later, with zero data sent on either side after the handshake. This is fully reproducible.

Expected behavior

A WebSocket connection with a completed handshake should remain open indefinitely while idle (or at least significantly longer than 30s), consistent with how the raw TCP/UDP rendezvous protocol on ports 21115–21117 behaves.

Real-world symptom (Desktop Client v1.4.9, allow-websocket=Y, connecting via wss:// through a reverse proxy)

[...] INFO [src\rendezvous_mediator.rs:425] start tcp: wss://<domain>/ws/id
[...] DEBUG [...tungstenite-0.26.2\src\handshake\client.rs:101] Client handshake done.
[...] ERROR [...libs\hbb_common\src\websocket.rs:282] WebSocket protocol error: Connection reset without closing handshake
[...] ERROR [src\rendezvous_mediator.rs:177] rendezvous mediator error: WebSocket protocol error: WebSocket protocol error: Connection reset without closing handshake
[...] INFO [src\rendezvous_mediator.rs:482] start rendezvous mediator of <domain>
[...] INFO [src\rendezvous_mediator.rs:425] start tcp: wss://<domain>/ws/id

This reconnect loop repeats indefinitely at ~30-second intervals; the client never reaches a stable "ready" state. On one occasion the reconnect also produced WebSocket protocol error: Reserved bits are non-zero.

What has been ruled out

Extensive testing was done before filing this report:

  • Reverse proxy (Apache 2.4.68) — tested with explicit ProxyTimeout 3600, timeout=3600 on the ProxyPass directive, and ProxyWebsocketFallbackToProxyHttp Off to force legacy mod_proxy_wstunnel handling. No change.
  • Two independent client networks — identical failure in both, ruling out client-side firewalls/NAT/DPI.
  • Server-side firewall/conntracknf_conntrack_tcp_timeout_established at default (432000s), no relevant iptables/UFW rules.
  • systemdhbbs/hbbr had continuous uptime throughout the failures; Restart=always was never triggered.
  • No CLI flag or environment variable exists to configure this (confirmed via hbbs --help / hbbr --help and the full argument reference).

Additional context

Given allow-websocket is documented as officially supported starting with Client ≥1.4.0, it would be helpful to know whether this 30s idle-close is an intentional design limit (e.g., tied to Pro-server-only usage) or an unintended default in the OSS hbbs/hbbr WebSocket listener implementation. If intentional, documenting it explicitly under the allow-websocket docs would save others the multi-hour debugging process this took.

Happy to provide further logs, tcpdump captures, or run additional tests against hbbs/hbbr if that helps narrow this down.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the hbbs and hbbr WebSocket listener paths and reproduce the server-local handshake on ports 21118 and 21119 without a proxy. Trace where the idle connection is closed and check the behavior against the 30-second interval. Done means an idle completed WebSocket handshake remains open beyond 60 seconds and the client no longer enters the reported reconnect loop.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
backend, networking
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.