fix(dev-middleware): don't let one bad socket kill the dev server - #57794
fix(dev-middleware): don't let one bad socket kill the dev server#57794Milad74f wants to merge 1 commit into
Conversation
InspectorProxy never attached an 'error' listener to the WebSocket connections it accepts. In Node an 'error' event with no listener throws, so a socket-level failure on a single connection took down the whole dev server rather than just that connection. Both #createDeviceConnectionWSServer and #createDebuggerConnectionWSServer listened for 'message' and 'close' only. They now also listen for 'error', attached synchronously before the first await — these handlers are async, so an error arriving while suspended would otherwise still be unhandled. Reported in react#57793, where a normally-connected iOS simulator tripped the maxFragments cap in the vendored ws and exited Metro with 'RangeError: Too many message fragments' (close code 1008). That cap is deliberate hardening and is untouched here; what is fixed is that tripping it was fatal to the process instead of to the connection.
|
Hi @Milad74f! Thank you for your pull request and welcome to our community. Action RequiredIn order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you. ProcessIn order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA. Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks! |
Summary
Fixes #57793.
InspectorProxynever attached an"error"listener to the WebSocket connections it accepts. In Node, an'error'event with no listener throws — so a socket-level failure on one connection takes down the whole dev server rather than just that connection.Both
#createDeviceConnectionWSServerand#createDebuggerConnectionWSServerlistened for"message"and"close"only. They now also listen for"error", log it, and terminate that socket.Two details worth calling out:
await. These connection handlers areasync, so an error arriving while one is suspended would otherwise still have no listener and still be fatal.terminate()rather thanclose():close()waits for a closing handshake, and a socket that failed mid-frame may never complete one, which would leak the connection instead.This does not touch the
maxFragments: 16 * 1024/maxBufferedChunks: 256 * 1024caps in the vendoredws. Those look like deliberate hardening and I'm not proposing they be relaxed. TheRangeErrorthey raise is simply the error this project happened to hit; the bug is that any socket error was fatal to the process.Changelog
[GENERAL] [FIXED] - Dev server no longer exits when a WebSocket connection to the inspector proxy errors
Test Plan
What I verified. Against a pristine
@react-native/dev-middleware@0.81.5from npm, a ~30-line script that connects to/inspector/deviceand sends one message in more fragments than the vendoredwsallows kills the process:With this change applied (as a
patch-packagepatch over the publisheddist/, which is the same edit as thesrc/change here), the same script instead printsPROXY SURVIVED — connection dropped, server still up.The full reproducer is in #57793.I then ran a real Expo/RN 0.81.5 app against the patched middleware: Metro starts, builds its iOS bundle (HTTP 200, 13.8 MB), and a connected simulator registers two pages on
/json/list— i.e. the device socket this wraps still works normally.What I could not verify, and why. I was unable to run this repo's own test suite locally:
yarn installonmainfails on this machine withSo CI will be the first run of the added test (
InspectorProxySocketErrors-test.js). It follows the conventions of the neighbouring inspector-proxy tests (withServerForEachTest,createDeviceMock,wait-for-expect) and asserts that after one connection is broken at the protocol level, the proxy is still serving and a healthy device is still listed. I'm happy to iterate on it if CI disagrees, or to drop it and land the fix alone if you'd prefer a separate test PR.