Skip to content

fix(vm_service): reconnect when the connection to the VM service drops - #566

Open
Catherine1401 wants to merge 1 commit into
nvim-flutter:mainfrom
Catherine1401:fix/vm-service-reconnect
Open

Catherine1401 wants to merge 1 commit into
nvim-flutter:mainfrom
Catherine1401:fix/vm-service-reconnect

Conversation

@Catherine1401

Copy link
Copy Markdown

Problem

flutter-tools.vm_service is the client behind the widget inspector navigation (it listens to the Debug stream and reacts to Inspect events). Once its WebSocket drops (the server closes it, an adb forward hiccup, ...) the client calls disconnect(), which also forgets every registered stream handler, and never reconnects. From then on selecting a widget in the inspector silently does nothing until the app is restarted.

The hand-rolled WebSocket handling also loses data:

  • bytes that arrive in the same TCP chunk as the HTTP/1.1 101 response are discarded, so a first event sent right after the handshake is lost;
  • continuation frames are ignored, so a message split over several frames is lost.

Changes

  • vm_service.lua now sits on top of flutter-tools.utils.websocket (already used by the DTD client), which buffers the handshake, keeps the bytes after it, assembles fragmented messages and answers ping/close frames.
  • When an established connection drops, the client reconnects with a backoff (300 ms up to 5 s, 10 attempts) and listens again to every stream registered with stream_listen. It notifies when the connection is lost and when it is back, and calls on_error after giving up.
  • A connection that was never established still reports on_error immediately and is not retried, as before.
  • disconnect() cancels any pending reconnect, and a generation counter makes callbacks of an old socket no-ops.
  • debugger_runner now always calls vm_service.disconnect() when the debug session ends, so a pending reconnect does not outlive the session.
  • New tests/vm_service_spec.lua (7 tests) runs the client against an in-process WebSocket server.

Verification

  • tests/vm_service_spec.lua: 7/7 pass with this change. Against current main three of them fail: the event sent together with the handshake response, the fragmented message, and the reconnect.
  • Against a real Dart VM service: a script calling dart:developer inspect() every second, and a TCP proxy between the client and the VM that is cut after the first events.
    • main: 3 Inspect events, then is_connected() stays false and no further event arrives.
    • this branch: the client reconnects, listens to Debug again and receives 3 more Inspect events.
  • Full test suite: the only failures are the ones already present on main (ui_spec progress resend, 2 tests, and debugger_runner_spec "reports adapter progress", 1 test). stylua --check . is clean.

Notes

While testing I noticed that vm_service.request(method, {}) serializes empty params as [], which makes the Dart VM service isolate throw (type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>') and stop. No current caller does this, so I left it out of this change.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant