diff --git a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js index 6df32d2ea5acff..b531b8962b996b 100644 --- a/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js +++ b/packages/dev-middleware/src/inspector-proxy/InspectorProxy.js @@ -23,6 +23,8 @@ import type {IncomingMessage, ServerResponse} from 'http'; import type {Timeout} from 'timers'; import Device from './Device'; +import {logger} from '@react-native-community/cli-tools'; +import chalk from 'chalk'; import nullthrows from 'nullthrows'; // Import these from node:timers to get the correct Flow types. // $FlowFixMe[cannot-resolve-module] libdef missing in RN OSS @@ -38,6 +40,7 @@ const PAGES_LIST_JSON_URL = '/json'; const PAGES_LIST_JSON_URL_2 = '/json/list'; const PAGES_LIST_JSON_VERSION_URL = '/json/version'; const MAX_PONG_LATENCY_MS = 5000; +const MAX_MISSED_PONG_COUNT = 3; const DEBUGGER_HEARTBEAT_INTERVAL_MS = 10000; const INTERNAL_ERROR_CODE = 1011; @@ -297,6 +300,7 @@ export default class InspectorProxy implements InspectorProxyQueries { // https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2 #startHeartbeat(socket: WS, intervalMs: number) { let terminateTimeout = null; + let missedPongCount = 0; const pingTimeout: Timeout = setTimeout(() => { if (socket.readyState !== WS.OPEN) { @@ -309,6 +313,16 @@ export default class InspectorProxy implements InspectorProxyQueries { if (socket.readyState !== WS.OPEN) { return; } + if (missedPongCount < MAX_MISSED_PONG_COUNT) { + missedPongCount++; + + logger.warn( + `Temporarily lost connection to the debugger. Reconnecting in ${intervalMs / 1000} seconds (attempt ${missedPongCount} of ${MAX_MISSED_PONG_COUNT})…`, + ); + pingTimeout.refresh(); + return; + } + // We don't use close() here because that initiates a closing handshake, // which will not complete if the other end has gone away - 'close' // would not be emitted. @@ -320,11 +334,24 @@ export default class InspectorProxy implements InspectorProxyQueries { }, intervalMs).unref(); socket.on('pong', () => { + if (missedPongCount > 0) { + logger.info('Connection to the debugger restored.'); + missedPongCount = 0; + } terminateTimeout && clearTimeout(terminateTimeout); pingTimeout.refresh(); }); socket.on('close', () => { + if (missedPongCount > 0) { + logger.error( + `Couldn't reconnect to the debugger after ${missedPongCount} attempt${missedPongCount > 1 ? 's' : ''}.`, + ); + missedPongCount = 0; + } + logger.info( + `The debugger has disconnected. You may restart the debugger by typing ${chalk.bold('j')} in the terminal.`, + ); terminateTimeout && clearTimeout(terminateTimeout); clearTimeout(pingTimeout); });