Skip to content

[Web runtime] Add reconnect-on-close to the Browser SDK - #1569

Open
minggangw wants to merge 3 commits into
RobotWebTools:developfrom
minggangw:feat/web-sdk-reconnect
Open

[Web runtime] Add reconnect-on-close to the Browser SDK#1569
minggangw wants to merge 3 commits into
RobotWebTools:developfrom
minggangw:feat/web-sdk-reconnect

Conversation

@minggangw

@minggangw minggangw commented Aug 7, 2026

Copy link
Copy Markdown
Member

Implements RosClient's previously-reserved reconnect option (web/client.js, _WsLink).

  • {reconnect: true} reopens the WS link with exponential backoff (500ms\u201330s, half-jitter) after a drop that follows a successful connect. The first connect attempt always rejects once rather than retrying forever, even when a connection error is followed by a close event.
  • Active subscriptions are replayed on reopen, reusing the same subId, so existing Subscription handles keep working transparently.
  • RosClient.on/off lifecycle events: 'disconnected' (any unexpected drop), 'reconnecting' ({attempt, delay}), 'reconnected'.
  • In-flight and new requests made during a drop or an active reconnect reject with code: 'connection_lost', with wording that reflects whether reconnecting is actually happening.
  • ros.close() always wins over reconnect and finalizes cleanly, including when called mid-backoff.
  • web/index.d.ts: reconnect documented as implemented; new RosClientEventMap/ReconnectingDetail; on/off declared on RosClient.
  • test/test-web-reconnect.js (new, 8 tests): reconnect + subscription replay, in-flight/new-request rejection during a drop, default behavior with reconnect unset, first-connect-rejects-once, close() during backoff, close() never triggers a reconnect.

Fix: #1510

Copilot AI lite review requested due to automatic review settings August 7, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR enhances the rclnodejs/web Browser SDK’s resilience by adding WebSocket reconnect-on-close with backoff and HTTP retry-with-backoff behavior, plus corresponding public typings and tests.

Changes:

  • Implement WebSocket reconnect-on-close with exponential backoff + jitter, subscription replay, and lifecycle events (disconnected/reconnecting/reconnected).
  • Add HTTP retry-with-backoff for call()/publish() on network errors and 5xx responses via httpRetries.
  • Add a new test suite covering WS reconnect behavior and HTTP retries.

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 4 comments.

File Description
web/index.d.ts Updates public types/docs to expose reconnect, httpRetries, and lifecycle event on/off APIs.
web/client.js Implements reconnect/backoff logic (WS + HTTP), lifecycle event listeners, and wires new options into transports.
test/test-web-reconnect.js Adds coverage for WS reconnect semantics and HTTP retry behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread web/client.js
Comment on lines 131 to 135
const onError = (err) => {
if (this._pending.size === 0 && !this._closed) {
// Ignore post-open: 'close' always follows and _handleClose() owns failing pending requests.
if (!settled) {
settled = true;
reject(err && err.error ? err.error : err);
Comment thread web/client.js
Comment on lines +163 to +178
_handleClose() {
this._failAll(_connectionLostError());
if (this._userClosed) {
this._closed = true;
this._subs.clear();
return;
}
this._onEvent('disconnected', undefined);
if (!this._reconnect) {
this._closed = true;
this._subs.clear();
return;
}
this._reconnecting = true;
this._scheduleReconnect();
}
Comment thread web/client.js
Comment on lines +517 to +520
/**
* Subscribe to an SDK lifecycle event: 'disconnected', 'reconnecting'
* ({attempt, delay}), or 'reconnected'. Only fires with {reconnect: true}.
*/
Comment thread web/client.js Outdated
Comment on lines +244 to +247
if (!this._ws || this._closed) return;
const ws = this._ws;
// Already closed (e.g. mid-backoff) means 'close' won't fire again.
if (ws.readyState === 3) return;
@coveralls

coveralls commented Aug 7, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 91.068% (-0.001%) from 91.069% — minggangw:feat/web-sdk-reconnect into RobotWebTools:develop

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (3)

web/client.js:147

  • When close() aborts a reconnect attempt before its socket opens, opened is false, so this handler only rejects the open promise and never reaches _handleClose()/_finalizeClosed(). The retry stops because _userClosed is set, but _closed remains false and _reconnecting remains true, causing later operations to report connection_lost indefinitely after close() has resolved. Finalize the link when this pre-open close was user initiated.
      const onClose = () => {
        if (opened) {
          this._handleClose();
          return;
        }
        if (!settled) {
          settled = true;
          reject(new Error('connection closed before it was established'));
        }

web/client.js:505

  • The PR promises configurable HTTP retries via httpRetries, but the newly added option plumbing only forwards reconnect; _HttpLink is still constructed without options and performs exactly one fetch() for network errors and 5xx responses. ConnectOptions also exposes no httpRetries, and the added suite contains only WebSocket tests. Implement and type the advertised HTTP retry option (with its retry tests), or remove HTTP retry from this PR's stated scope.
    this._wsOptions = {
      reconnect: !!options.reconnect,
      onEvent: (name, detail) => this._emit(name, detail),
    };

web/client.js:180

  • disconnected listeners run synchronously before the link enters either _reconnecting or _closed. If a listener immediately starts a request, both guards still pass and _sendRaw targets the already-closed socket, producing a transport-specific send error instead of the expected connection_lost/terminal-close result. Transition the state and fail in-flight work before emitting the event, while keeping disconnected before _scheduleReconnect() so lifecycle ordering remains intact.
    this._onEvent('disconnected', undefined);

@minggangw minggangw changed the title [Web runtime] Add reconnect-on-close and HTTP retry to the Browser SDK [Web runtime] Add reconnect-on-close to the Browser SDK Aug 12, 2026
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.

Browser ↔ ROS 2 capability runtime (Web Runtime)

3 participants