Repository navigation
fix(vite-plugin): don't destroy WebSockets owned by other upgrade listeners - #15659
dedsec-terminal wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: 3ffcca1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
@cloudflare/autoconfig
@cloudflare/build-output-utils
@cloudflare/codemods
@cloudflare/config
@cloudflare/containers-shared
create-cloudflare
@cloudflare/deploy-helpers
@cloudflare/kv-asset-handler
miniflare
@cloudflare/pages-functions
@cloudflare/pages-shared
@cloudflare/runtime-types
@cloudflare/unenv-preset
@cloudflare/vite-plugin
@cloudflare/vitest-plugin
@cloudflare/workers-auth
@cloudflare/workers-editor-shared
@cloudflare/workers-utils
wrangler
commit: |
|
Hi @cjol — friendly bump, this is waiting on human review (review also requested from @workers-devprod via existing request). CI is still running on latest head \3aef22f; will confirm once green. Let me know if you need anything else from my side. Thanks! |
|
@dedsec-terminal please stop adding automated commits if you would like the PR to be reviewed; each bump puts it to the back of the queue, no matter how minor. |
|
Apologies for the repeated updates — they came across as automated queue bumps. I was working through the review suggestions and pushed fixes as I addressed them, but I understand that each update moved the PR back in the queue. That wasn't my intention. I'm consolidating the remaining review fixes and local verification into one final update before pushing again. |
|
Consolidated update in
Local verification:
|
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
703ac4a to
8f79dbc
Compare
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
1 similar comment
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
petebacondarwin
left a comment
There was a problem hiding this comment.
Thank you @dedsec-terminal for this excellent PR. I really appreciated the comments in each test that helped me understand exactly what was being tests in each case.
We've discussed this as a team and are concerned by the nature of having to guess/infer who the owner of the Websocket is.
Ideally there should be some mechanism in the Vite eco-system that plugins can use to register their interest in incoming sockets, but this doesn't appear to be likely.
Since the main problem, that triggered this issue is quite well defined, we think it would be better to provide a simple more focussed fix that just addresses that case (perhaps by looking at the URL path), and avoiding trying to solve all cases.
It might be worth keeping this PR as it is, but closing it, and replacing with the simpler approach.
Node dispatches upgrade to every registered listener. The dev-server handler destroyed the raw socket when the Worker returned no route, even when another listener had already completed the handshake. Snapshot the socket write count before awaiting dispatchFetch, and only tear the socket down when no other listener has written a response. Keep the existing teardown for sole-listener upgrades and defer rejection teardown one tick so another listener can claim the socket first. Signed-off-by: dedsec-terminal <dedsec.terminal@gmail.com>
81bcb0a to
2e5c30c
Compare
|
Codeowners approval required for this PR:
Show detailed file reviewers
|
|
💬 Approved with comments — The fix works for upgrade listeners that answer synchronously, and the pull request description says delayed owners are out of scope. The only findings are one WARNING about whether the new regression test can actually fail, and one SUGGESTION about a no-op line. Findings: 1 warning · 1 suggestion The decision is advisory: this comment never approves, blocks or requests changes on the pull request. 🔧 Fix with your agentPaste the following into your coding agent to investigate and fix these issues. 📋 Code Quality:
|
|
Thanks for the steer, Pete! I have reworked this into a small focused fix: it no longer tries to infer who owns a socket, it just checks whether another listener has already written a response before deciding to close anything, and leaves the socket alone if it has. The timing-based tests are gone as well. Could you take another look when you have a moment? |
|
clanker review |
|
Can you address the two concerns in the AI review above: #15659 (comment) Otherwise, this is looking promising. |
Drop a no-op header cleanup in the claimed-socket early return and assert on the server socket directly, so the regression test fails when the isClaimed guard is removed. Signed-off-by: dedsec-terminal <dedsec.terminal@gmail.com>
|
Addressed both findings in 9df5a7d:
|
The stream destroy method returns the Duplex, so the mock must preserve the return value for the type check. Signed-off-by: dedsec-terminal <dedsec.terminal@gmail.com>
|
Checks is green on 3ffcca1. The Windows runtime partition hit an unrelated miniflare flake — |
Fixes #15654
Node dispatches
upgradeto every registered listener. The dev-server handler forwarded the request to the Worker, then destroyed the raw socket when the Worker returned no route — even if another listener (for example Vite DevTools at/__devtools/__ws) had already completed the handshake. Clients sawonopenfollowed by close code1006.This replaces the earlier ownership-inference implementation with a focused check:
socket.bytesWrittenbefore other listeners run.handleUpgradeif another listener claimed the socket whiledispatchFetchwas in flight.dispatchFetchrejection one tick so another listener can claim the socket first. A sole unclaimed upgrade is still destroyed.This deliberately does not try to identify delayed or one-shot owners.
Tests in
src/__tests__/websockets.spec.tspass 11/11, including the DevTools-style owner case and the sole-listener unrouted teardown. Changeset included.