fluffos/tools
Yucong Sun 16c57df887
websocket: drain output before driver-initiated close (supersedes #1338) (#1339)
* websocket: drain output before driver-initiated close

* websocket: harden close-after-flush against input, pmd drain, and double-close

Review fixes for the close-after-flush drain (PR #1338), each verified to
fail on the unfixed driver via new ws-smoke coverage:

- LWS_CALLBACK_RECEIVE with a nulled pss->user now DISCARDS input instead
  of returning -1. During the drain window a hard-close on one client
  keystroke truncated the very flush the close was waiting for (both
  subprotocols; smoke now types mid-drain and still requires the full
  burst + close 1000). Deliberately NOT lws_rx_flow_control(): with the
  libevent event lib, flipping POLLIN on a live wsi stalls POLLOUT and
  the drain dies at the deadline (observed directly).

- The buffer-empty close now also requires !lws_send_pipe_choked().
  Choked-with-empty-buffer means permessage-deflate still holds the
  compressed tail of the final message (tx_draining_ext); entering the
  lws close states discards that drain ("defeat tx draining" in
  ops-ws.c), truncating the final message for every pmd client -- i.e.
  every real browser. The smoke client now negotiates pmd (persistent
  raw inflater, RSV1/FIN tracking) and sweeps incompressible burst sizes
  across a full 2048-byte window ring so at least one attempt always
  lands the final drain window over pm-deflate's 1024-byte tx buffer.

- The close is now one-shot (clear close_after_flush before return -1).
  A second already-queued WRITEABLE callback returning -1 again re-enters
  the close path in LRS_RETURNED_CLOSE and degrades the clean handshake
  into an immediate close(fd); with autotuned kernel buffers still
  holding undelivered output, a late client byte then RSTs it all away.
  This was a ~30% flaky truncation on the reused ascii smoke connection
  (fresh sockets' smaller buffers masked it on telnet).

- ws_ascii's u8_truncate hold-back is skipped once the user is gone,
  mirroring ws_telnet's gate: the rest of an incomplete codepoint can
  never arrive post-close, and holding it back busy-spins the writable
  callback until the deadline.

Also widens the permanently-choked smoke check's margin (the 5s deadline
starts when the driver processes the destruct, not when the test sends
it) and documents the four close-after-flush invariants in
src/www/AGENTS.md.

Validation: ws-smoke 22/22 x13 runs (10 instrumented + 3 clean),
19/22 on the unfixed driver (all three new checks fail there); full LPC
testsuite passes ("Checks succeeded.", exit 0) on RelWithDebInfo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014TX65yYUCStrDqworuFvhh

---------

Co-authored-by: devaidendale <xarcos@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-08-02 23:03:33 -07:00
..
ffi docs: rewrite FFI reference around buffer features, add worked libc examples (#1275) 2026-07-15 22:09:25 -07:00
lpc-syntax fix(lpc-syntax): refuse files with broken quoting instead of shredding them 2026-07-23 20:28:21 -07:00
wasm feat(wasm): enable the pcre package on the WebAssembly target 2026-07-23 23:58:50 -07:00
ws-smoke.js websocket: drain output before driver-initiated close (supersedes #1338) (#1339) 2026-08-02 23:03:33 -07:00