diff --git a/docs/web-install.md b/docs/web-install.md index f078a74..d77dfd7 100644 --- a/docs/web-install.md +++ b/docs/web-install.md @@ -148,3 +148,11 @@ The manifest's file URL must then be `http://localhost:8000/…` or `frame_webinstall.dispatch()` and deletes the folder. - The app registers the scheme each time it starts, so the last Frame Control started (e.g. a development checkout) handles the links. + +**Quitting during a stalled download.** On macOS and Linux, quitting stops a +download at once (`shutdown()` on its socket wakes the blocked read). On +Windows that doesn't wake a read in another thread, and closing the handle +under a TLS read isn't safe, so a download that has stalled holds the quit for +the 4-second grace period until the app stops the server; the partial file is +removed on the next start. Downloads that are still moving stop at their next +read either way. diff --git a/tests/test_webinstall.py b/tests/test_webinstall.py index 53917b7..1858297 100644 --- a/tests/test_webinstall.py +++ b/tests/test_webinstall.py @@ -328,6 +328,10 @@ class ServerJobs(unittest.TestCase): self.assertEqual((job["phase"], job["message"]), ("done", "ok")) def stall_then_shutdown(self, scheme, reply): + if os.name == "nt": + # shutdown() from another thread doesn't wake a blocked recv on Windows, and + # closing the handle under a TLS read isn't safe; see web-install.md. + self.skipTest("Windows: a stalled download is only dropped when the app stops the server") """Start a download from a server that stalls after sending reply; shutdown must stop it quickly.""" stall = socket.socket() stall.bind(("127.0.0.1", 0))