fix: use websocket to test server liveness before client reload
Description
- Closes https://github.com/vitejs/vite/issues/13125
- Closes https://github.com/vitejs/vite/issues/16536
- Supersedes/Closes https://github.com/vitejs/vite/pull/17768
- Related (but this is a separate feature request) https://github.com/vitejs/vite/issues/5675
This PR makes waitForSuccessfulPing more robust by testing Vite server liveness based on websocket connection as suggested in https://github.com/vitejs/vite/issues/5675#issuecomment-1345588419
Testing https://github.com/vitejs/vite/issues/13125
- run playground e.g.
pnpm -C playground/tailwind dev - run proxy e.g.
cloudflared tunnel --url http://localhost:5173/ - open a page from proxy e.g. https://arrow-lonely-looks-counsel.trycloudflare.com/
- stop playground server and see client keeps pinging (previously client full reloads here and ends up 502 page)
- start playground server and see client reloads automatically
Testing https://github.com/vitejs/vite/issues/16536 (this one is added as a test case)
- run playground
pnpm -C playground/tailwind devwith a following server configserver: { // different port for hmr websocket hmr: { port: 12345 }, // cross origin isolation headers headers: { "Cross-Origin-Embedder-Policy": "require-corp", "Cross-Origin-Opener-Policy": "same-origin" } - stop playground server and see client keeps pinging
- start playground server and see client reloads (previously client fails to automatically full reload here)
Run & review this pull request in StackBlitz Codeflow.
My team applied these changes to vite locally and tried it with 5 different Nuxt 3 based applications running behind a traefik based https reverse proxy. This change massively improves the DX for us as we no longer have to constantly full-page reload our apps especially when vite is still booting.
I'd love to see this merged β€οΈ
Thanks @hi-ogawa, this gives us back so much productivity!
Can confirm, this also fixes my issue. I use vite for SSR by adding it to my express server (pretty much exactly this code in app.use('*': https://vitejs.dev/guide/ssr#setting-up-the-dev-server), but at the end I add these CORS headers to my SSR rendered response to get access to performance.now()
res.status(200).set({
'Content-Type': 'text/html',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
}).end(html);
Before this patch, after a server stop/start or reload, the HMR ping over HTTP would loop forever and fail with a 426 status (what Firefox shows me anyway) and console logs show the request failed due to CORS. I couldn't find any easy way to configure the HMR server to add CORS headers after a solid hour of searching.
Eventually landed here, tried this fix, and it works. Pinging over WS instead of over HTTP certainly makes more sense IMO especially since the target connection is WS anyway; why ping over HTTP when the goal is to get a WS connection set up? Browsers have different rules for HTTP vs WS regarding CORS so pinging over WS removes that entire class of issues.
New test is flaky but I'm not sure what's going on. Locally, this command can fail if I run a few times.
pnpm test-serve playground/client-reload
Though tests could be still flaky, but I added some workaround by skipping some tests and retry.
It passes on my PC even if I enable all tests. Did you try pnpm debug-serve?
/ecosystem-ci run
π Ran ecosystem CI on 9a68df7: Open
| suite | result | latest scheduled |
|---|---|---|
| histoire | :x: failure | :x: failure |
| remix | :x: failure | :x: failure |
| sveltekit | :x: failure | :x: failure |
| vike | :x: failure | :x: failure |
| vitest | :white_check_mark: success | :x: failure |
:white_check_mark: analogjs, astro, ladle, laravel, marko, nuxt, previewjs, quasar, qwik, rakkas, redwoodjs, storybook, unocss, vite-environment-examples, vite-plugin-pwa, vite-plugin-react, vite-plugin-react-swc, vite-plugin-svelte, vite-plugin-vue, vite-setup-catalogue, vitepress, vuepress
It passes on my PC even if I enable all tests. Did you try
pnpm debug-serve?
On my PC, it happens quite consistently (like 1 in 2 or 3 runs) by running it while sleep 1; do pnpm test-serve playground/client-reload/; done. Also there a few fails on CI before the commit test: retry.
I couldn't figure out a way to debug the issue. What does debug-serve do?
What does
debug-servedo?
It'll run playwright in non-headless mode. Maybe you can find out what's happening. Although, it may not reproduce with it.
Hmm, unfortunately it doesn't reproduce with debug-serve. I think I've only seen this fails on ubuntu CI, so it's possible that this is something due to os + headless specific quirks.
Let me tweak the test to only skip on linux.
Well, it just failed on macos as well. I'll re-run a few times and probably I'll put back skip and retry.
I fixed the test fail in 91e7edf4bf9705b67e344142824eea5a0a0f23f9. It seems the HTTP server was sending an empty response because the WS server listens before the HTTP server.
/ecosystem-ci run
π Ran ecosystem CI on 1c7a479: Open
| suite | result | latest scheduled |
|---|---|---|
| analogjs | :x: failure | :white_check_mark: success |
| histoire | :x: failure | :x: failure |
| marko | :x: failure | :white_check_mark: success |
| redwoodjs | :x: failure | :white_check_mark: success |
| remix | :x: failure | :x: failure |
| sveltekit | :white_check_mark: success | :x: failure |
| unocss | :x: failure | :white_check_mark: success |
| vike | :x: failure | :x: failure |
| vite-plugin-svelte | :x: failure | :white_check_mark: success |
| vitepress | :x: failure | :white_check_mark: success |
:white_check_mark: astro, ladle, laravel, nuxt, previewjs, quasar, qwik, rakkas, storybook, vite-environment-examples, vite-plugin-pwa, vite-plugin-react, vite-plugin-react-swc, vite-plugin-vue, vite-setup-catalogue, vitest, vuepress
Hmm, it seems my change broke a bunch of tests.
/ecosystem-ci run
π Ran ecosystem CI on f22ae96: Open
| suite | result | latest scheduled |
|---|---|---|
| histoire | :x: failure | :x: failure |
| nuxt | :x: failure | :white_check_mark: success |
| remix | :x: failure | :x: failure |
| sveltekit | :x: failure | :x: failure |
| vike | :x: failure | :x: failure |
:white_check_mark: analogjs, astro, ladle, laravel, marko, previewjs, quasar, qwik, rakkas, redwoodjs, storybook, unocss, vite-environment-examples, vite-plugin-pwa, vite-plugin-react, vite-plugin-react-swc, vite-plugin-svelte, vite-plugin-vue, vite-setup-catalogue, vitepress, vitest, vuepress
/ecosystem-ci run nuxt
Wow, awesome finding. I still cannot see it reproduced in the browser, but the fix makes sense!
I did a unsophisticated push and CI debugπ https://github.com/sapphi-red/vite/commits/fix/test-fix-robost-client-ping-reolad/
Great work! I think it would be good to have this in Vite 6. I added it to the milestone.
@patak-dev why not in v5.x? This is a great bug fix IMO.
It because this PR is not small enough or urgent enough to release in a patch and we don't have a plan to release a minor in v5.
/ecosystem-ci run
π Ran ecosystem CI on 38942b1: Open
| suite | result | latest scheduled |
|---|---|---|
| astro | :x: failure | :x: failure |
| nuxt | :x: failure | :white_check_mark: success |
| redwoodjs | :x: failure | :x: failure |
| remix | :x: failure | :x: failure |
| sveltekit | :x: failure | :x: failure |
| vike | :x: failure | :x: failure |
| vite-plugin-svelte | :x: failure | :x: failure |
| vitest | :x: failure | :x: failure |
:white_check_mark: analogjs, histoire, ladle, laravel, marko, previewjs, quasar, qwik, rakkas, storybook, unocss, vite-environment-examples, vite-plugin-pwa, vite-plugin-react, vite-plugin-react-swc, vite-plugin-vue, vite-setup-catalogue, vitepress, vuepress
nuxt is failing on main branch and the fails have the same errors with the latest scheduled ones so should be fine.