fix(server): drop the previous session's queued events before every connection - #2034
Conversation
There was a problem hiding this comment.
PR 2034 adds a single call to the existing allowlist-based discard_stale_session_events() at the top of run_connection_inner, before negotiation, so stale per-session events left by a replaced session can no longer leak into a reconnecting client or an embedder-driven connection with no idle loop. Verified against head: the helper's allowlist (Quit/GetLocalAddr/SetCredentials/SetAutoReconnectCookie) is intact; the preemption-winner call site in run() is distinct and not double-drained since winners resume via finalize_negotiated, bypassing run_connection_inner. The new regression test is valid. The fix is correct, minimal, and protocol-improving. Of two low-severity candidates, the missing contract documentation on public run_connection/run_connection_with is accepted; the comment-compression suggestion is a stylistic preference that would lose unique call-site motivation, so it is rejected.
There was a problem hiding this comment.
The fix is narrow and correct: the existing allowlist-based drain now also runs at the start of run_connection_inner, before negotiation, closing the reconnect race where a previous session's queued per-session events (EGFX frames, RDPSND waves, clipboard) were delivered to a replacement connection, including embedder-driven connections with no run() idle loop. The public API change for embedders queuing via event_sender() before run_connection is explicitly documented, and a new e2e regression test exercises the embedder path through run_connection. No protocol or wire-format behavior changes. Remaining concerns are minor: the one-shot drain is not a producer-quiescence guarantee (kept as a low-severity open question), and the test changes duplicate an existing echo driver, a generic harness signature, and rationale text that already appears twice in server.rs.
|
This pull request may overlap with #2059. Both touch the connection lifecycle in ironrdp-server's server.rs around session replacement: this PR moves the stale-event drain to before negotiation and documents what is dropped with the previous session, while This notice is advisory only. Automated review continues as usual, and how these pull requests relate is for maintainers and authors to decide. Note LLM-assisted content (no human feedback). |
There was a problem hiding this comment.
LGTM, thank you!
There is a conflict with master that needs to be addressed.
Note to agent: I approved the fix, and it’s okay to merge on my behalf if the next operation does nothing else than fixing the conflict. Escalate to me if there is a significant change I should know.
…onnection discard_stale_session_events ran only before serving a preemption winner. After an ordinary disconnect, EGFX frames, RDPSND waves and clipboard messages the session had queued stayed on the server-global channel, and the next connection wrote them before its own channels existed: a client reconnecting after a drop was sent the old session's EGFX frame on a dynamic channel it had not opened, and dropped. run()'s idle select only discards what it reads before the next accept, and an embedder driving run_connection has no such loop. Discard at the start of every connection, before negotiation, with the same allowlist.
…nto CI run_connection_with now has a Queued events section saying that on entry it drops everything waiting on the server-global channel except Quit, GetLocalAddr, SetCredentials and SetAutoReconnectCookie, why, and that an embedder should queue events for a connection only after the call has started. run_connection points to it. The regression test lived inline in ironrdp-server, whose [lib] test = false keeps inline tests out of CI. It is now an end-to-end test in ironrdp-testsuite-extra: a Disconnect event is queued before a connection served through run_connection, and a real client must still complete an echo round trip.
…e echo driver The comment at the discard call now says why it runs before negotiation and points to discard_stale_session_events for the rest. The Queued events section says the drain is not a barrier: an event a previous-session producer sends after it has run still reaches the new connection, so producers the embedder runs with its own event_sender clone or one of the server's handles must be stopped first. The two echo round-trip tests share drive_echo_round_trip instead of repeating the client loop.
a53509b to
35d1262
Compare
|
Rebased onto master. Besides the conflict in |
Summary
discard_stale_session_eventsran only before serving a preemption winner. After an ordinary disconnect, anything the session had queued but not consumed (EGFX frames, RDPSND waves, clipboard messages) stayed on the server-global event channel.run()'s idle select discards what it reads between connections, but a client reconnecting within milliseconds can win that race, and an embedder driving connections throughrun_connectionhas no idle loop at all.Quit,GetLocalAddr,SetCredentials,SetAutoReconnectCookiesurvive.Validation
cargo xtask check fmt/lints/tests/typos/locksall pass. New end-to-end testa_new_connection_starts_without_the_previous_sessions_eventsinironrdp-testsuite-extraqueues aDisconnectevent before the connection, serves one connection throughrun_connectionwith norun()idle loop in between, and checks that a real client still completes an echo round trip; it fails without the change.Notes
An embedder that queues a per-session event before calling
run_connectionnow has it dropped along with the stale ones; only the lifecycle allowlist carries into a connection, as it already did for a preemption winner.