Commit dc132e0
feat(ws): native WebSocket transport, drop raw TCP listeners (#1093)
* feat(ws): native WebSocket transport, drop raw TCP listeners
The lobby server now accepts WebSocket connections directly on /ws
(default port 8003) instead of raw TCP behind the ws_bridge_rs sidecar.
This removes a transport-level hop that broke client reconnects, and
keeps everything on HTTP so the deployment can stay fully behind
DDoS-protected HTTPS ingress.
- New WebSocketProtocol (aiohttp WebSocketResponse / ClientWebSocketResponse)
using one JSON object per text frame.
- ServerContext replaces asyncio.start_server with an aiohttp app, dropping
the proxyprotocol detection path.
- Config: LISTEN list replaced with WS_HOST / WS_PORT / WS_PATH.
- Pipfile: proxy-protocol removed.
- Tests rewritten to drive the server over real WebSocket clients;
proxy-mode tests removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ws): lint, lockfile, metric double-count, untrusted forwarded IP
- Remove unused aiohttp.web import and unused get_session test import
(flake8 F401).
- Regenerate Pipfile.lock after dropping proxy-protocol.
- WebSocketProtocol.write_messages no longer increments sent_messages
itself — write_raw already does it per message.
- ServerContext only honors a forwarded-IP header when the new
WS_FORWARDED_IP_HEADER config is set (e.g. "CF-Connecting-IP"); otherwise
uses the direct TCP peer, so clients cannot spoof their peername by
sending X-Forwarded-For themselves.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style: address codacy docstring findings
- websocket.py module docstring uses D212 style (summary on line 2).
- Add one-line __init__ docstring (D107).
- test_multiple_contexts docstring has a one-line summary ending with a
period (D205/D212/D415).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style: swap docstring summary placement (D212 vs D213)
Codacy enforces D212 on modules (summary on first line) and D213 on
functions (summary on second line) — I had them reversed in the previous
commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* style: collapse docstrings to single-line to satisfy D212+D213
Codacy has both D212 and D213 enabled, which is mutually contradictory
for multi-line docstrings — whichever style we pick, the other fires.
Single-line docstrings dodge both rules.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(ws): default WS_FORWARDED_IP_HEADER to X-Real-IP
Cloudflare (and our Traefik ingress) sets X-Real-IP to the real client
address, so it's the right default. Still configurable to "" for
deployments where the server is exposed to untrusted clients directly
(spoofable headers).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: raise WebSocketProtocol and SimpleJson coverage
- WebSocketProtocol now reaches 100% line coverage in isolation:
added tests for binary frames, write_message/write_messages paths,
empty-pending drain, drain-failure -> DisconnectedError, and abort
with/without an already-closed ws.
- SimpleJsonProtocol regains coverage of decode_message / read_message /
empty-readline DisconnectedError; these were previously hit through
the qstream-vs-json integration parametrize that this PR removed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ws): default path to '/' to match existing clients
The Java client (faf-commons-lobby) connects to wss://lobby.{base}
with no path component, so the WS upgrade GET hits '/'. Serving on
'/ws' caused aiohttp to fail routing and return 'Invalid method
encountered' (the parser confusing itself on the unmatched URL).
Switch the default WS_PATH and the path= function defaults to '/'
so out of the box the server matches what the client sends. Existing
deployments can still pin WS_PATH to '/ws' (or anything else) via
config.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(ws): terminate outgoing frames with '\n' for client framing
faf-commons-lobby (the Kotlin lobby client) splits the inbound WS
byte stream on '\n' rather than treating each frame as a complete
message. Without a trailing newline the client buffers the response
indefinitely and times out the login flow after 30 s.
The previous SimpleJsonProtocol already encoded messages this way,
which is why it worked through ws_bridge_rs unchanged. Mirror that
behavior in WebSocketProtocol so existing clients keep working.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test: assert_awaited for ws.close in abort test
CodeRabbit nit: assert_called only confirms the close coroutine was
scheduled, not that it actually ran to completion. assert_awaited is
the right check for an AsyncMock.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent b6b1850 commit dc132e0
17 files changed
Lines changed: 573 additions & 382 deletions
File tree
- server
- protocol
- tests
- integration_tests
- unit_tests
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
25 | 24 | | |
26 | 25 | | |
27 | 26 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | 31 | | |
33 | 32 | | |
34 | 33 | | |
| |||
115 | 114 | | |
116 | 115 | | |
117 | 116 | | |
118 | | - | |
119 | | - | |
120 | | - | |
121 | | - | |
122 | | - | |
123 | | - | |
124 | | - | |
125 | | - | |
126 | | - | |
127 | | - | |
128 | | - | |
129 | | - | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
145 | 121 | | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
146 | 127 | | |
147 | 128 | | |
148 | 129 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
| 12 | + | |
| 13 | + | |
16 | 14 | | |
17 | 15 | | |
18 | 16 | | |
| |||
44 | 42 | | |
45 | 43 | | |
46 | 44 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
| 45 | + | |
| 46 | + | |
51 | 47 | | |
52 | 48 | | |
53 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
133 | 133 | | |
134 | 134 | | |
135 | 135 | | |
136 | | - | |
137 | 136 | | |
138 | 137 | | |
139 | 138 | | |
| |||
258 | 257 | | |
259 | 258 | | |
260 | 259 | | |
261 | | - | |
262 | | - | |
| 260 | + | |
263 | 261 | | |
264 | 262 | | |
265 | | - | |
| 263 | + | |
266 | 264 | | |
267 | 265 | | |
268 | 266 | | |
269 | | - | |
270 | | - | |
271 | | - | |
272 | | - | |
273 | | - | |
| 267 | + | |
| 268 | + | |
274 | 269 | | |
275 | 270 | | |
276 | 271 | | |
277 | 272 | | |
278 | 273 | | |
279 | | - | |
| 274 | + | |
280 | 275 | | |
281 | 276 | | |
282 | | - | |
283 | 277 | | |
284 | | - | |
| 278 | + | |
285 | 279 | | |
286 | 280 | | |
287 | 281 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
63 | 56 | | |
64 | 57 | | |
65 | 58 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
| 16 | + | |
16 | 17 | | |
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
23 | | - | |
| 24 | + | |
| 25 | + | |
24 | 26 | | |
0 commit comments