fix: clear _timeoutInfo in _onclose() and scope .finally() abort controller cleanup #1462
+170
−2
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes two related cleanup gaps in
Protocol._onclose():Bug 1:
_timeoutInfonot cleared in_onclose()_onclose()methodically clears_responseHandlers,_progressHandlers,_taskProgressTokens,_pendingDebouncedNotifications, and_requestHandlerAbortControllers, but does not clear_timeoutInfo. PendingsetTimeoutcallbacks from old requests surviveclose()and can firecancel()against a new transport afterclose()+connect(), sending spuriousnotifications/cancelledfor request IDs the new client never sent.Fix: Iterate
_timeoutInfoentries callingclearTimeout()for each, then clear the map in_onclose().Bug 2: Stale
.finally()can delete new connection's abort controllerIn
_onrequest, the fire-and-forget promise chain's.finally()doesthis._requestHandlerAbortControllers.delete(request.id). Therequest.idis captured from the closure, but there is no check that the stored controller matches. After reconnect, if a new client reuses the same request ID (common since IDs start from 0), the old handler's.finally()deletes the new connection's abort controller.Fix: Capture a reference to the specific
AbortControllerin the.finally()closure and only delete from the map if the stored value matches.Impact
Both issues are pre-existing on
v1.x. Practical impact is low because (1) spurious cancellations use stale request IDs that would be ignored, (2) Protocol reuse across different clients is uncommon. However, the issues become more relevant asclose()+connect()becomes a supported reconnect pattern.Found by bughunter.
Fixes #1459