How to Extend Chrome DevTools for React Native Network Debugging

This article explains how the NetEase Cloud Music team built a custom React Native debugging tool by extending Chrome DevTools, covering the devtools frontend module loading, React Native Debugger internals, Chrome DevTools Protocol, and a proxy-based solution to display network requests within the inspector.

NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
NetEase Cloud Music Tech Team
How to Extend Chrome DevTools for React Native Network Debugging

Chrome DevTools Overview

Chrome DevTools is a front‑end debugging tool built into Chrome. Web applications communicate with the DevTools frontend via the Chrome DevTools Protocol (CDP) over a WebSocket connection, allowing the inspected app’s data to be displayed in the DevTools UI.

Chrome DevTools architecture
Chrome DevTools architecture

The open‑source project ChromeDevTools/devtools-frontend provides the DevTools frontend code. The version used in this article is 3964.

DevTools Frontend Module Loading

Start Chrome with a remote‑debugging port: --remote-debugging-port=9222 Open http://localhost:9222/ to list debugging targets, e.g.:

http://localhost:9222/devtools/inspector.html?ws=localhost:9222/devtools/page/7B2421304FE8EF659B264D4F476083DA

The inspector.html loads Runtime.js and inspector.js. inspector.js simply calls: Runtime.startApplication('inspector'); Module loading proceeds as follows:

Read module.json to obtain module metadata.

Instantiate a new Runtime, establishing Runtime → Module → Extension dependencies.

Load core module resources (scripts and CSS).

Start the core module entry point ( Main.Main).

Inspector Startup

The inspector.json file shows that the inspector inherits from devtools_app and adds a screencast module for live page snapshots:

{
  "modules": [
    {"name": "screencast", "type": "autostart"}
  ],
  "extends": "devtools_app",
  "has_html": true
}

The devtools_app definition includes modules such as network, elements, browser_debugger, etc., and extends shell, which loads core modules like bindings, common, protocol, and ui:

{
  "modules": [
    {"name": "bindings", "type": "autostart"},
    {"name": "common", "type": "autostart"},
    {"name": "protocol", "type": "autostart"},
    ...
  ],
  "extends": "shell",
  "has_html": true
}

The inspector establishes a WebSocket connection using the ws query parameter:

SDK._createMainConnection = function() {
  const wsParam = Runtime.queryParam('ws');
  const wssParam = Runtime.queryParam('wss');
  if (wsParam || wssParam) {
    const ws = wsParam ? `ws://${wsParam}` : `wss://${wssParam}`;
    SDK._mainConnection = new SDK.WebSocketConnection(ws, SDK._websocketConnectionLost);
  } else if (InspectorFrontendHost.isHostedMode()) {
    SDK._mainConnection = new SDK.StubConnection();
  } else {
    SDK._mainConnection = new SDK.MainConnection();
  }
  return SDK._mainConnection;
};

React Native Debugger Process

React Native Debugger is an Electron‑based standalone program built on top of the official remote‑debugging feature, with added react-devtools-core and redux-devtools-extension support.

When remote debugging is enabled, the workflow is:

The app sends a /launch-js-devtools request to the server, opening a debugger tab and establishing a socket.

The tab loads the debugger‑ui page and creates a socket connection to /debugger-proxy?role=debugger&name=Chrome.

A worker runs debuggerWorker.js to execute the bundled JavaScript.

The inspector communicates with the worker to debug the React Native app.

Network requests made via fetch or XMLHttpRequest in React Native can be captured in the DevTools frontend, but the Cloud Music app required a custom solution to forward client‑side network data into the DevTools Network panel.

Chrome DevTools Protocol (CDP)

CDP is a WebSocket‑based protocol that groups capabilities into domains. Each domain defines Methods, Events, and Types.

Method : request/response pattern identified by a message ID.

Event : publish/subscribe notification.

Type : data structures used in the protocol.

Example Method call:

request: {"id":1,"method":"Page.canScreencast"}
response: {"id":1,"result":{"result":false}}

Example Event:

{"method":"Network.loadingFinished","params":{"requestId":"14307.143","timestamp":1424097364.31611,"encodedDataLength":0}}

Methods can be sent from Chrome’s “Protocol Monitor” or programmatically from Electron.

Proxy‑Based Cross‑Domain Debugging

When the inspected page and the DevTools frontend are on different networks, a proxy service can forward CDP messages between them. The proxy runs a WebSocket server that:

Queries http://127.0.0.1:9222/json to obtain the webSocketDebuggerUrl of the React Native Debugger page.

Establishes a debugConnection to the debugger.

Forwards messages from the debugger to the frontend and vice‑versa.

Handles client‑side messages (e.g., intercepted network data) and routes them to the debugger.

Key proxy implementation ( proxy.js):

const WS_PROXY_PORT = 9233; // proxy ws port
const CHROME_REMOTE_DEBUG_PORT = 9222; // devtools frontend port

const proxyStart = async () => {
  const server = http.createServer((request, response) => {
    response.writeHead(404);
    response.end();
  });
  server.listen(WS_PROXY_PORT);

  const wss = new WebSocket.Server({ server });

  wss.on('connection', (ws, request, client) => {
    // remote‑debug handling
    if (request.headers['sec-websocket-protocol'] === 'remote-debug') {
      axios.get(`http://127.0.0.1:${CHROME_REMOTE_DEBUG_PORT}/json`).then(res => {
        const { data } = res;
        if (data && data.length > 0) {
          for (let i = 0; i < data.length; i++) {
            const page = data[i];
            if (page.title.indexOf('React Native Debugger') === 0) {
              debugConnection = new WebSocket(page.webSocketDebuggerUrl);
              debugConnection.on('message', message => {
                if (frontendConnection) {
                  if (debugMessageArr.length) {
                    debugMessageArr.forEach(item => frontendConnection.send(item));
                    debugMessageArr = [];
                  }
                  frontendConnection.send(message);
                } else {
                  debugMessageArr.push(message);
                  console.log('Cannot forward to frontend, no connection');
                }
              });
              break;
            }
          }
        }
      }).catch(error => console.log(error));
    }

    // frontend handling
    if (request.url === '/frontend') {
      frontendConnection = ws;
      frontendConnection.on('message', message => {
        if (debugConnection) {
          if (frontendMessageArr.length) {
            frontendMessageArr.forEach(item => debugConnection.send(item));
            frontendMessageArr = [];
          }
          debugConnection.send(message);
        } else {
          frontendMessageArr.push(message);
          console.log('Debugger backend not ready, open the page first');
        }
      });
    }

    // app handling
    if (request.url === '/app') {
      appConnection = ws;
      appConnection.on('message', message => {
        if (frontendConnection) {
          if (debugMessageArr.length) {
            debugMessageArr.forEach(item => frontendConnection.send(item));
            debugMessageArr = [];
          }
          frontendConnection.send(message);
        } else {
          debugMessageArr.push(message);
          console.log('Cannot forward to frontend, no connection');
        }
      });
    }
  });
};

The proxy processes three streams:

remote‑debug : data from the inspected page, obtained via the /json endpoint.

frontend : messages from the DevTools UI, forwarded to the debugger.

app : network data intercepted on the client side, forwarded to the frontend.

Local Frontend Service

Because the DevTools frontend version must match the Chrome version used by Electron, the article retrieves the Chrome version via process.versions.chrome (e.g., 78.0.3904.130) and selects the corresponding DevTools build (3964). The service is started by running devtools-frontend/scripts/server.js, which launches a local HTTP server on port 8090.

http://localhost:8090/front_end/devtools_app.html?ws=localhost:9233/frontend

This URL points the DevTools UI to the proxy WebSocket, enabling the custom network panel.

References

https://github.com/jhen0409/react-native-debugger

https://segmentfault.com/a/1190000013665754

https://github.com/ChromeDevTools/devtools-frontend

https://chromedevtools.github.io/devtools-protocol

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DebuggingProxyReact NativeChrome DevToolsNetwork Inspection
NetEase Cloud Music Tech Team
Written by

NetEase Cloud Music Tech Team

Official account of NetEase Cloud Music Tech Team

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.