From 1312d5522e25ab0a006f4206c2e65bfa5bedbcbc Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Wed, 19 Aug 2026 07:51:48 -0700 Subject: [PATCH] Expose InspectorNetworkReporter integration point to Frameworks (Android) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: **Motivation** Third-party networking stacks that replace React Native's networking modules have no supported way to report CDP and Web Performance network events, so their requests are invisible in the React Native DevTools Network panel. **This diff** On Android, the `InspectorNetworkReporter` integration point (our intended public API) is closed — change `InspectorNetworkReporter`'s visibility to `public`, and annotate with`UnstableReactNativeAPI`. On iOS no visibility change is needed — `RCTInspectorNetworkReporter` already ships via `React-Core`. **🚧 Unstable API** These APIs are exposed, but marked with `UnstableReactNativeAPI` (on Android) until we consolidate their use by an external integrator (Expo). This leaves open the potential for minor changes, although unlikely. **Changes** - `InspectorNetworkReporter` is now `public`, annotated `UnstableReactNativeAPI`, requiring explicit opt-in. `NetworkEventUtil` and `WebSocketModule` opt in via `Silvochka:OptIn`. - `react/networking/` joins the prefab header export in `build.gradle.kts`, matching iOS's `React-networking` pod parity. - Doc comments on `NetworkReporter`, `RCTInspectorNetworkReporter` and `InspectorNetworkReporter` drop `[Experimental]`, naming each as the third-party integration point. Changelog: [Android][Added] - **Network Inspection**: Expose network event reporting to third-party networking stacks as an unstable API Differential Revision: D116472113 --- .../Network/RCTInspectorNetworkReporter.h | 11 ++-- .../network/InspectorNetworkReporter.kt | 60 +++++++++++-------- .../react/modules/network/NetworkEventUtil.kt | 2 + .../modules/websocket/WebSocketModule.kt | 2 + 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/packages/react-native/Libraries/Network/RCTInspectorNetworkReporter.h b/packages/react-native/Libraries/Network/RCTInspectorNetworkReporter.h index 03e55518e6f1..d3ea35f66e13 100644 --- a/packages/react-native/Libraries/Network/RCTInspectorNetworkReporter.h +++ b/packages/react-native/Libraries/Network/RCTInspectorNetworkReporter.h @@ -10,13 +10,16 @@ #import /** - * [Experimental] An interface for reporting network events to the modern - * debugger server and Web Performance APIs. + * An interface for reporting network events to the modern debugger server and + * Web Performance APIs. * * In a production (non dev or profiling) build, CDP reporting is disabled. * - * This is a helper class wrapping - * `facebook::react::jsinspector_modern::NetworkReporter`. + * This is a helper class wrapping `facebook::react::NetworkReporter`, and is + * the integration point for third-party networking stacks that replace React + * Native's networking modules. + * + * This is an unstable API. Its exact location and shape may change over time. */ @interface RCTInspectorNetworkReporter : NSObject diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt index 653da920f6ac..3d7a98b377d9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/InspectorNetworkReporter.kt @@ -10,24 +10,26 @@ package com.facebook.react.modules.network import com.facebook.proguard.annotations.DoNotStripAny +import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.soloader.SoLoader import okio.ByteString /** - * [Experimental] An interface for reporting network events to the modern debugger server and Web - * Performance APIs. + * An interface for reporting network events to the modern debugger server and Web Performance APIs. * * In a production (non dev or profiling) build, CDP reporting is disabled. * - * This is a helper class wrapping `facebook::react::jsinspector_modern::NetworkReporter`. + * This is a helper class wrapping `facebook::react::NetworkReporter`, and is the integration point + * for third-party networking stacks that replace React Native's networking modules. */ @DoNotStripAny -internal object InspectorNetworkReporter { +@UnstableReactNativeAPI +public object InspectorNetworkReporter { init { SoLoader.loadLibrary("react_devsupportjni") } - @JvmStatic external fun isDebuggingEnabled(): Boolean + @JvmStatic public external fun isDebuggingEnabled(): Boolean /** * Report a network request that is about to be sent. @@ -36,7 +38,7 @@ internal object InspectorNetworkReporter { * native request was initiated). */ @JvmStatic - external fun reportRequestStart( + public external fun reportRequestStart( requestId: String, requestUrl: String, requestMethod: String, @@ -51,7 +53,8 @@ internal object InspectorNetworkReporter { * - Corresponds to `PerformanceResourceTiming.domainLookupStart`, * `PerformanceResourceTiming.connectStart`. */ - @JvmStatic external fun reportConnectionTiming(requestId: String, headers: Map) + @JvmStatic + public external fun reportConnectionTiming(requestId: String, headers: Map) /** * Report when HTTP response headers have been received, corresponding to when the first byte of @@ -60,7 +63,7 @@ internal object InspectorNetworkReporter { * - Corresponds to `PerformanceResourceTiming.responseStart`. */ @JvmStatic - external fun reportResponseStart( + public external fun reportResponseStart( requestId: String, requestUrl: String, responseStatus: Int, @@ -74,35 +77,35 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.dataReceived` in CDP. */ @JvmStatic - fun reportDataReceived(requestId: String, data: String) { + public fun reportDataReceived(requestId: String, data: String) { // Guard call to CDP-only reporting method (avoid encodeToByteArray calculation) if (isDebuggingEnabled()) { reportDataReceivedImpl(requestId, data.encodeToByteArray().size) } } - @JvmStatic external fun reportDataReceivedImpl(requestId: String, dataLength: Int) + @JvmStatic public external fun reportDataReceivedImpl(requestId: String, dataLength: Int) /** * Report when a network request is complete and we are no longer receiving response data. * - Corresponds to `Network.loadingFinished` in CDP. * - Corresponds to `PerformanceResourceTiming.responseEnd`. */ - @JvmStatic external fun reportResponseEnd(requestId: String, encodedDataLength: Long) + @JvmStatic public external fun reportResponseEnd(requestId: String, encodedDataLength: Long) /** * Report when a network request has failed. * * Corresponds to `Network.loadingFailed` in CDP. */ - @JvmStatic external fun reportRequestFailed(requestId: String, cancelled: Boolean) + @JvmStatic public external fun reportRequestFailed(requestId: String, cancelled: Boolean) /** * Store response body preview. This is an optional reporting method, and is a no-op if CDP * debugging is disabled. */ @JvmStatic - fun maybeStoreResponseBody(requestId: String, body: String, base64Encoded: Boolean) { + public fun maybeStoreResponseBody(requestId: String, body: String, base64Encoded: Boolean) { // Guard call to CDP-only reporting method (avoid sending string over JNI) if (isDebuggingEnabled()) { maybeStoreResponseBodyImpl(requestId, body, base64Encoded) @@ -110,7 +113,11 @@ internal object InspectorNetworkReporter { } @JvmStatic - external fun maybeStoreResponseBodyImpl(requestId: String, body: String, base64Encoded: Boolean) + public external fun maybeStoreResponseBodyImpl( + requestId: String, + body: String, + base64Encoded: Boolean, + ) /** * Incrementally store a response body preview, when a string response is received in chunks. @@ -120,21 +127,22 @@ internal object InspectorNetworkReporter { * is disabled. */ @JvmStatic - fun maybeStoreResponseBodyIncremental(requestId: String, data: String) { + public fun maybeStoreResponseBodyIncremental(requestId: String, data: String) { // Guard call to CDP-only reporting method (avoid sending string over JNI) if (isDebuggingEnabled()) { maybeStoreResponseBodyIncrementalImpl(requestId, data) } } - @JvmStatic external fun maybeStoreResponseBodyIncrementalImpl(requestId: String, data: String) + @JvmStatic + public external fun maybeStoreResponseBodyIncrementalImpl(requestId: String, data: String) /** * Report that a WebSocket connection is about to be created. * * Corresponds to `Network.webSocketCreated` in CDP. */ - @JvmStatic external fun reportWebSocketCreated(requestId: String, url: String) + @JvmStatic public external fun reportWebSocketCreated(requestId: String, url: String) /** * Report that a WebSocket handshake (HTTP upgrade) request is about to be sent, along with its @@ -143,7 +151,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketWillSendHandshakeRequest` in CDP. */ @JvmStatic - external fun reportWebSocketWillSendHandshakeRequest( + public external fun reportWebSocketWillSendHandshakeRequest( requestId: String, headers: Map, ) @@ -154,7 +162,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketHandshakeResponseReceived` in CDP. */ @JvmStatic - external fun reportWebSocketHandshakeResponseReceived( + public external fun reportWebSocketHandshakeResponseReceived( requestId: String, statusCode: Int, headers: Map, @@ -166,7 +174,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketFrameSent` in CDP. */ @JvmStatic - fun reportWebSocketMessageSent(requestId: String, message: String) { + public fun reportWebSocketMessageSent(requestId: String, message: String) { if (isDebuggingEnabled()) { reportWebSocketMessageSentImpl(requestId, message, false) } @@ -178,7 +186,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketFrameSent` in CDP. */ @JvmStatic - fun reportWebSocketMessageSent(requestId: String, message: ByteString) { + public fun reportWebSocketMessageSent(requestId: String, message: ByteString) { // Guard call to CDP-only reporting method (avoid base64 encoding) if (isDebuggingEnabled()) { reportWebSocketMessageSentImpl(requestId, message.base64(), true) @@ -186,7 +194,7 @@ internal object InspectorNetworkReporter { } @JvmStatic - external fun reportWebSocketMessageSentImpl( + public external fun reportWebSocketMessageSentImpl( requestId: String, payloadData: String, isBinary: Boolean, @@ -198,7 +206,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketFrameReceived` in CDP. */ @JvmStatic - fun reportWebSocketMessageReceived(requestId: String, message: String) { + public fun reportWebSocketMessageReceived(requestId: String, message: String) { if (isDebuggingEnabled()) { reportWebSocketMessageReceivedImpl(requestId, message, false) } @@ -210,7 +218,7 @@ internal object InspectorNetworkReporter { * Corresponds to `Network.webSocketFrameReceived` in CDP. */ @JvmStatic - fun reportWebSocketMessageReceived(requestId: String, message: ByteString) { + public fun reportWebSocketMessageReceived(requestId: String, message: ByteString) { // Guard call to CDP-only reporting method (avoid base64 encoding) if (isDebuggingEnabled()) { reportWebSocketMessageReceivedImpl(requestId, message.base64(), true) @@ -218,7 +226,7 @@ internal object InspectorNetworkReporter { } @JvmStatic - external fun reportWebSocketMessageReceivedImpl( + public external fun reportWebSocketMessageReceivedImpl( requestId: String, payloadData: String, isBinary: Boolean, @@ -229,5 +237,5 @@ internal object InspectorNetworkReporter { * * Corresponds to `Network.webSocketClosed` in CDP. */ - @JvmStatic external fun reportWebSocketClosed(requestId: String) + @JvmStatic public external fun reportWebSocketClosed(requestId: String) } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt index 39518f7342c1..3edd4781341d 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkEventUtil.kt @@ -6,6 +6,7 @@ */ @file:Suppress("DEPRECATION_ERROR") // Conflicting okhttp versions +@file:OptIn(UnstableReactNativeAPI::class) package com.facebook.react.modules.network @@ -15,6 +16,7 @@ import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.buildReadableArray +import com.facebook.react.common.annotations.UnstableReactNativeAPI import java.io.IOException import java.net.SocketTimeoutException import okhttp3.Headers diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt index e6b127058f2c..175772f3232a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/websocket/WebSocketModule.kt @@ -6,6 +6,7 @@ */ @file:Suppress("DEPRECATION_ERROR") // Conflicting okhttp versions +@file:OptIn(UnstableReactNativeAPI::class) package com.facebook.react.modules.websocket @@ -19,6 +20,7 @@ import com.facebook.react.bridge.ReadableType import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.buildReadableMap import com.facebook.react.common.ReactConstants +import com.facebook.react.common.annotations.UnstableReactNativeAPI import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags import com.facebook.react.module.annotations.ReactModule import com.facebook.react.modules.network.CustomClientBuilder