Repository navigation
fix: fail a request when the Playwright connection closes - #277
Merged
MrPunyapal merged 2 commits intoOct 25, 2026
Merged
Conversation
Once the websocket closes, receive() returns null. fetch() cast that to an empty string, and a response with neither an `error` nor an `id` never ends execute()'s loop, so it kept asking a closed connection for a response at full CPU instead of failing. fetch() now throws when there is no message, naming the closed connection. A normal response is unchanged.
Member
|
thanks |
12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
What happens
When the Playwright server's websocket closes (the server is killed, or crashes),
WebsocketConnection::receive()returnsnull.Client::fetch()casts that to an empty string:An empty response has neither an
errornor anid, soexecute()never takes its error branch or its exit, and asks the closed connection for another response forever, at full CPU. Reported as pestphp/pest#1801.The fix
fetch()throws aRuntimeExceptionnaming the closed connection whenreceive()returns no message. A normal response is read as before.This is one half of #241, credited as prior art. That pull request was closed by its author for lack of activity, not rejected. Its metadata-timeout half has since landed on
5.xas e84a76d, in v5.0.1. The rest of its remaining half, a per-requestTimeoutCancellationplus a wall-clock deadline, is left out on purpose. In a suite where the browser, the in-process HTTP server and the test share one event loop, a burst of server work can delay response handling past such a deadline with nothing wrong. It was measured failing a passing control-panel smoke test at 144.3s against 102.6s without it. The closed-connection check has no deadline, so it cannot fire that way.Tests
tests/Unit/Playwright/ClientTest.php, with a stubbed websocket connection:idstill completes, with its result returned.Against unchanged
5.xsource, the first test never finishes. It callsreceive()until PHP runs out of memory at 2 GB, which is the same loop pestphp/pest#1801 describes at full CPU.The full suite on this branch passes 341 tests with 27 skipped, including the 2 new ones. Three tests fail: two iframe tests and "it may visit external URLs". All three load external URLs and fail the same way on unchanged
5.xon this machine. PHPStan, type coverage (100%), Rector and Pint on the changed files are clean. Run on Windows 11 with PHP 8.4 and Playwright 1.62.1.pestphp/pest#1781
Not changed here, because it no longer reproduces on
5.x. With Playwright 1.62.1, a click on a locator that matches nothing fails withTimeout 2000ms exceededafter 2.4 to 2.8s, in 3 of 3 runs, with the suite'ssetTimeout(2000). That is e84a76d's metadata timeout at work, so #1781 can likely be closed as fixed by it.Closes pestphp/pest#1801.