From 86d2025abd5d2a3cad98667e09d6148fcd083d43 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Tue, 17 Feb 2026 10:33:09 -0800 Subject: [PATCH 01/17] Add shared DevSupportHttpClient singleton (#55575) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55575 The devsupport package creates multiple OkHttpClient instances across different files, each with its own connection pool and thread pool. This is wasteful and introduces bugs (thread-safety race in InspectorNetworkHelper, per-tap allocation in RedBoxContentView). This diff introduces DevSupportHttpClient, an internal object singleton with two shared clients derived from the same connection pool: - httpClient: with connect=5s, write=disabled, read=disabled (for HTTP requests) - websocketClient: derived via newBuilder() with connect=10s, write=10s, read=disabled (for WebSocket/inspector use) Subsequent diffs in this stack migrate each consumer to use these shared clients. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93480249 fbshipit-source-id: b489f5946aa0c0a5318f0fb600dec31f373b8c09 --- .../inspector/DevSupportHttpClient.kt | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt new file mode 100644 index 000000000000..997c3c971a55 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +@file:Suppress("DEPRECATION_ERROR") // Conflicting okhttp versions + +package com.facebook.react.devsupport.inspector + +import java.util.concurrent.TimeUnit +import okhttp3.OkHttpClient + +/** + * Shared [OkHttpClient] instances for devsupport networking. Uses a single connection pool and + * dispatcher across all dev support HTTP and WebSocket usage. + */ +internal object DevSupportHttpClient { + /** Client for HTTP requests: connect=5s, write=disabled, read=disabled. */ + val httpClient: OkHttpClient = + OkHttpClient.Builder() + .connectTimeout(5, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.MILLISECONDS) + .readTimeout(0, TimeUnit.MINUTES) + .build() + + /** Client for WebSocket connections: connect=10s, write=10s, read=disabled. */ + val websocketClient: OkHttpClient = + httpClient + .newBuilder() + .connectTimeout(10, TimeUnit.SECONDS) + .writeTimeout(10, TimeUnit.SECONDS) + .build() +} From a3f1e38a1bcb961b3a5eccdef9b049b003d91c6c Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 02/17] Migrate DevServerHelper to shared OkHttpClient (#55576) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55576 Replace the inline OkHttpClient.Builder() in DevServerHelper with DevSupportHttpClient.httpClient. This shares the connection pool and thread pool with all other devsupport HTTP consumers. Remove the now-unused HTTP_CONNECT_TIMEOUT_MS constant and TimeUnit import. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93480251 fbshipit-source-id: b783c2b7a499d20fc12a9fca664651e99b00579d --- .../com/facebook/react/devsupport/DevServerHelper.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt index cfbceaac4dae..6fdcb1c85d28 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt @@ -21,6 +21,7 @@ import com.facebook.react.bridge.ReactContext import com.facebook.react.common.ReactConstants import com.facebook.react.devsupport.InspectorFlags.getFuseboxEnabled import com.facebook.react.devsupport.InspectorFlags.getIsProfilingBuild +import com.facebook.react.devsupport.inspector.DevSupportHttpClient import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener import com.facebook.react.devsupport.interfaces.PackagerStatusCallback import com.facebook.react.modules.debug.interfaces.DeveloperSettings @@ -39,7 +40,6 @@ import java.io.UnsupportedEncodingException import java.security.MessageDigest import java.security.NoSuchAlgorithmException import java.util.Locale -import java.util.concurrent.TimeUnit import okhttp3.Call import okhttp3.Callback import okhttp3.OkHttpClient @@ -86,12 +86,7 @@ public open class DevServerHelper( MAP("map"), } - private val client: OkHttpClient = - OkHttpClient.Builder() - .connectTimeout(HTTP_CONNECT_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS) - .readTimeout(0, TimeUnit.MILLISECONDS) - .writeTimeout(0, TimeUnit.MILLISECONDS) - .build() + private val client: OkHttpClient = DevSupportHttpClient.httpClient private val bundleDownloader: BundleDownloader = BundleDownloader(client) private val packagerStatusCheck: PackagerStatusCheck = PackagerStatusCheck(client) private val packageName: String = applicationContext.packageName @@ -397,7 +392,6 @@ public open class DevServerHelper( } private companion object { - private const val HTTP_CONNECT_TIMEOUT_MS = 5000 private const val DEBUGGER_MSG_DISABLE = "{ \"id\":1,\"method\":\"Debugger.disable\" }" private fun getSHA256(string: String): String { From 18131babc99359506fd550fa1b75b74cdb22fc8f Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 03/17] Delegate no-arg PackagerStatusCheck constructor to shared OkHttpClient (#55577) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55577 PackagerStatusCheck had two constructors: a no-arg one that created its own OkHttpClient, and one that accepted an external client. The no-arg constructor is used by callers in fbandroid/ (FbDevBottomSheetViewController, LocalJsPackagerManager, FbPackagerStatusCheck, FbDevModeNuxController). Instead of removing it, delegate it to `DevSupportHttpClient.httpClient` so that all callers share the same connection pool without needing code changes. Also removes the now-unused `HTTP_CONNECT_TIMEOUT_MS` constant since the timeout configuration is centralized in DevSupportHttpClient. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93480248 fbshipit-source-id: 07b3a6c01cec19a5de1604c560229622974e500e --- .../react/devsupport/PackagerStatusCheck.kt | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.kt index bbb86aa95e6a..02a2a6155d10 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/PackagerStatusCheck.kt @@ -11,10 +11,10 @@ package com.facebook.react.devsupport import com.facebook.common.logging.FLog import com.facebook.react.common.ReactConstants +import com.facebook.react.devsupport.inspector.DevSupportHttpClient import com.facebook.react.devsupport.interfaces.PackagerStatusCallback import java.io.IOException import java.util.Locale -import java.util.concurrent.TimeUnit import okhttp3.Call import okhttp3.Callback import okhttp3.OkHttpClient @@ -22,22 +22,9 @@ import okhttp3.Request import okhttp3.Response /** Use this class to check if the JavaScript packager is running on the provided host. */ -internal class PackagerStatusCheck { +internal class PackagerStatusCheck(private val client: OkHttpClient) { - private val client: OkHttpClient - - constructor() { - client = - OkHttpClient.Builder() - .connectTimeout(HTTP_CONNECT_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS) - .readTimeout(0, TimeUnit.MILLISECONDS) - .writeTimeout(0, TimeUnit.MILLISECONDS) - .build() - } - - constructor(client: OkHttpClient) { - this.client = client - } + constructor() : this(DevSupportHttpClient.httpClient) fun run(host: String, callback: PackagerStatusCallback) { val statusURL = createPackagerStatusURL(host) @@ -92,7 +79,6 @@ internal class PackagerStatusCheck { private companion object { private const val PACKAGER_OK_STATUS = "packager-status:running" - private const val HTTP_CONNECT_TIMEOUT_MS = 5_000 private const val PACKAGER_STATUS_URL_TEMPLATE = "http://%s/status" private fun createPackagerStatusURL(host: String): String = From 0fda0f43e264fa36ba8acde089baa9b4feb7af03 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 04/17] Fix per-tap OkHttpClient allocation in RedBoxContentView (#55578) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55578 RedBoxContentView.OpenStackFrameTask was creating a new OkHttpClient() (with a fresh connection pool and thread pool) every time a user tapped a stack frame in the red box. Replace with the shared DevSupportHttpClient.httpClient singleton. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93480250 fbshipit-source-id: 882c218e709681e5f5fb22b4b35710d23f349c8e --- .../java/com/facebook/react/devsupport/RedBoxContentView.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.kt index 30440cf10d1b..3d7b7626165a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/RedBoxContentView.kt @@ -33,12 +33,12 @@ import android.widget.TextView import com.facebook.common.logging.FLog import com.facebook.react.R import com.facebook.react.common.ReactConstants +import com.facebook.react.devsupport.inspector.DevSupportHttpClient import com.facebook.react.devsupport.interfaces.DevSupportManager import com.facebook.react.devsupport.interfaces.ErrorType import com.facebook.react.devsupport.interfaces.RedBoxHandler import com.facebook.react.devsupport.interfaces.StackFrame import okhttp3.MediaType -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody import org.json.JSONObject @@ -165,7 +165,7 @@ internal class RedBoxContentView( .query(null) .build() .toString() - val client = OkHttpClient() + val client = DevSupportHttpClient.httpClient for (frame in stackFrames) { val payload = stackFrameToJson(checkNotNull(frame)).toString() val body: RequestBody = RequestBody.create(JSON, payload) From 2a2bc7368c778286b5850d28fcad42cba696b535 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 05/17] Fix thread-safety race in InspectorNetworkHelper (#55579) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55579 InspectorNetworkHelper used a lateinit var with an isInitialized check to lazily create an OkHttpClient. This pattern is not thread-safe — concurrent calls to loadNetworkResource could race on the initialization. Replace with the shared DevSupportHttpClient.websocketClient singleton, which is initialized at class load time and is inherently safe. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93480252 fbshipit-source-id: fefb144b3b26670954a77aa27c9b7f637290f21b --- .../devsupport/inspector/InspectorNetworkHelper.kt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt index a7ba76b09e03..1dab02ea9dc4 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/InspectorNetworkHelper.kt @@ -10,26 +10,15 @@ package com.facebook.react.devsupport.inspector import java.io.IOException -import java.util.concurrent.TimeUnit import okhttp3.Call import okhttp3.Callback -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response internal object InspectorNetworkHelper { - private lateinit var client: OkHttpClient @JvmStatic fun loadNetworkResource(url: String, listener: InspectorNetworkRequestListener) { - if (!::client.isInitialized) { - client = - OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build() - } val request = try { @@ -40,7 +29,7 @@ internal object InspectorNetworkHelper { } // TODO(T196951523): Assign cancel function to listener - val call = client.newCall(request) + val call = DevSupportHttpClient.httpClient.newCall(request) call.enqueue( object : Callback { From 6a5160ca6b8b850344acf09c245b0bfdfc1883b3 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 06/17] Migrate CxxInspectorPackagerConnection to shared OkHttpClient (#55580) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55580 Replace the OkHttpClient.Builder() in CxxInspectorPackagerConnection.DelegateImpl with DevSupportHttpClient.websocketClient. This shares the connection pool and thread pool with all other devsupport WebSocket consumers. Remove the now-unused TimeUnit and OkHttpClient imports. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93480223 fbshipit-source-id: ff3d742ccb4f484f4906496b1a081377a1b3fa8d --- .../react/devsupport/CxxInspectorPackagerConnection.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt index a92d4ac26c62..ef98c3df2ec2 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/CxxInspectorPackagerConnection.kt @@ -12,10 +12,9 @@ import android.os.Looper import com.facebook.jni.HybridData import com.facebook.proguard.annotations.DoNotStrip import com.facebook.proguard.annotations.DoNotStripAny +import com.facebook.react.devsupport.inspector.DevSupportHttpClient import com.facebook.soloader.SoLoader import java.io.Closeable -import java.util.concurrent.TimeUnit -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.WebSocket @@ -78,12 +77,7 @@ internal class CxxInspectorPackagerConnection( /** Java implementation of the C++ InspectorPackagerConnectionDelegate interface. */ private class DelegateImpl { - private val httpClient = - OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build() + private val httpClient = DevSupportHttpClient.websocketClient private val handler = Handler(Looper.getMainLooper()) From 3c6afbb5c769f132b3470c416fc6dc5af05a13fe Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 07/17] Migrate ReconnectingWebSocket to shared OkHttpClient (#55581) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55581 Replace the inline OkHttpClient.Builder() in ReconnectingWebSocket with DevSupportHttpClient.websocketClient. This shares the connection pool and thread pool with all other devsupport WebSocket consumers. DevSupportHttpClient is changed from internal to public to be accessible from the packagerconnection module, and the packagerconnection BUCK target gains a dependency on the inspector target. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93481092 fbshipit-source-id: b0d6a14e59310e2f975ce92d6bab68f6f6a231da --- .../react/packagerconnection/ReconnectingWebSocket.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt index d122467eca71..87d80827a336 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/ReconnectingWebSocket.kt @@ -10,10 +10,9 @@ package com.facebook.react.packagerconnection import android.os.Handler import android.os.Looper import com.facebook.common.logging.FLog +import com.facebook.react.devsupport.inspector.DevSupportHttpClient import java.io.IOException import java.nio.channels.ClosedChannelException -import java.util.concurrent.TimeUnit -import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okhttp3.WebSocket @@ -40,12 +39,7 @@ public class ReconnectingWebSocket( } private val handler = Handler(Looper.getMainLooper()) - private val okHttpClient: OkHttpClient = - OkHttpClient.Builder() - .connectTimeout(10, TimeUnit.SECONDS) - .writeTimeout(10, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.MINUTES) // Disable timeouts for read - .build() + private val okHttpClient = DevSupportHttpClient.websocketClient private var closed = false private var suppressConnectionErrors = false private var webSocket: WebSocket? = null From 671dddc6a358c1daeba35f808b749829216ca6f3 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 07:38:50 -0800 Subject: [PATCH 08/17] Add custom request header support to DevSupportHttpClient (#55582) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55582 Add addRequestHeader/removeRequestHeader methods to DevSupportHttpClient. These allow callers to register custom HTTP headers that will be included in all requests made through both the httpClient and websocketClient. Implemented via a shared OkHttp Interceptor that reads from a ConcurrentHashMap, so headers can be safely added/removed from any thread. Changelog: [Internal] Reviewed By: cipolleschi Differential Revision: D93481539 fbshipit-source-id: e8d6bc4c9d21c65a0f4d95065eaadff776933c55 --- .../inspector/DevSupportHttpClient.kt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt index 997c3c971a55..2ec285daf84a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt @@ -9,7 +9,9 @@ package com.facebook.react.devsupport.inspector +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit +import okhttp3.Interceptor import okhttp3.OkHttpClient /** @@ -17,9 +19,20 @@ import okhttp3.OkHttpClient * dispatcher across all dev support HTTP and WebSocket usage. */ internal object DevSupportHttpClient { + private val customHeaders = ConcurrentHashMap() + + private val headerInterceptor = Interceptor { chain -> + val builder = chain.request().newBuilder() + for ((name, value) in customHeaders) { + builder.header(name, value) + } + chain.proceed(builder.build()) + } + /** Client for HTTP requests: connect=5s, write=disabled, read=disabled. */ val httpClient: OkHttpClient = OkHttpClient.Builder() + .addInterceptor(headerInterceptor) .connectTimeout(5, TimeUnit.SECONDS) .writeTimeout(0, TimeUnit.MILLISECONDS) .readTimeout(0, TimeUnit.MINUTES) @@ -32,4 +45,14 @@ internal object DevSupportHttpClient { .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build() + + /** Add a custom header to be included in all requests made through both clients. */ + fun addRequestHeader(name: String, value: String) { + customHeaders[name] = value + } + + /** Remove a previously added custom header. */ + fun removeRequestHeader(name: String) { + customHeaders.remove(name) + } } From 160265e785fdb2ddbfc275c0da2ba7986cd6e13d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 10:57:25 -0800 Subject: [PATCH 09/17] Add RCTDevSupportHttpHeaders singleton (#55586) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55586 Add a thread-safe singleton that holds custom HTTP headers to be applied to all iOS devsupport network requests. This mirrors the Android `DevSupportHttpClient` from D93481539. Uses a GCD concurrent queue with barrier writes for reader-writer synchronization. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93490954 fbshipit-source-id: addb2f11025a046eedd0e296a7c398eef7211dc4 --- .../React/Base/RCTDevSupportHttpHeaders.h | 24 +++++++ .../React/Base/RCTDevSupportHttpHeaders.m | 65 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 packages/react-native/React/Base/RCTDevSupportHttpHeaders.h create mode 100644 packages/react-native/React/Base/RCTDevSupportHttpHeaders.m diff --git a/packages/react-native/React/Base/RCTDevSupportHttpHeaders.h b/packages/react-native/React/Base/RCTDevSupportHttpHeaders.h new file mode 100644 index 000000000000..e73cdd3c4b88 --- /dev/null +++ b/packages/react-native/React/Base/RCTDevSupportHttpHeaders.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import + +/** + * Thread-safe singleton that holds custom HTTP headers to be applied + * to all devsupport network requests (bundle fetches, packager status + * checks, inspector and HMR WebSocket connections). + */ +@interface RCTDevSupportHttpHeaders : NSObject + ++ (instancetype)sharedInstance; + +- (void)addRequestHeader:(NSString *)name value:(NSString *)value; +- (void)removeRequestHeader:(NSString *)name; +- (NSDictionary *)allHeaders; +- (void)applyHeadersToRequest:(NSMutableURLRequest *)request; + +@end diff --git a/packages/react-native/React/Base/RCTDevSupportHttpHeaders.m b/packages/react-native/React/Base/RCTDevSupportHttpHeaders.m new file mode 100644 index 000000000000..0717537f8424 --- /dev/null +++ b/packages/react-native/React/Base/RCTDevSupportHttpHeaders.m @@ -0,0 +1,65 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#import "RCTDevSupportHttpHeaders.h" + +@implementation RCTDevSupportHttpHeaders { + NSMutableDictionary *_headers; + dispatch_queue_t _queue; +} + ++ (instancetype)sharedInstance +{ + static RCTDevSupportHttpHeaders *sharedInstance; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[RCTDevSupportHttpHeaders alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init +{ + if (self = [super init]) { + _headers = [NSMutableDictionary new]; + _queue = dispatch_queue_create("com.facebook.react.RCTDevSupportHttpHeaders", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (void)addRequestHeader:(NSString *)name value:(NSString *)value +{ + dispatch_sync(_queue, ^{ + self->_headers[name] = value; + }); +} + +- (void)removeRequestHeader:(NSString *)name +{ + dispatch_sync(_queue, ^{ + [self->_headers removeObjectForKey:name]; + }); +} + +- (NSDictionary *)allHeaders +{ + __block NSDictionary *snapshot; + dispatch_sync(_queue, ^{ + snapshot = [self->_headers copy]; + }); + return snapshot; +} + +- (void)applyHeadersToRequest:(NSMutableURLRequest *)request +{ + NSDictionary *headers = [self allHeaders]; + [headers enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *headerValue, BOOL *stop) { + [request setValue:headerValue forHTTPHeaderField:headerName]; + }]; +} + +@end From e6ec94982c7d75520d6e99700cb9c5b4035e558d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 10:57:25 -0800 Subject: [PATCH 10/17] Apply custom headers in RCTMultipartDataTask (#55607) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55607 Inject custom devsupport headers into bundle download requests. This ensures that multipart bundle fetches include any headers registered via `RCTDevSupportHttpHeaders`. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596721 fbshipit-source-id: 358b0de351677b843a70204caa101531941789b0 --- packages/react-native/React/Base/RCTMultipartDataTask.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/react-native/React/Base/RCTMultipartDataTask.m b/packages/react-native/React/Base/RCTMultipartDataTask.m index 3ac07683eb93..9f5ff5995535 100644 --- a/packages/react-native/React/Base/RCTMultipartDataTask.m +++ b/packages/react-native/React/Base/RCTMultipartDataTask.m @@ -7,6 +7,8 @@ #import "RCTMultipartDataTask.h" +#import "RCTDevSupportHttpHeaders.h" + @interface RCTMultipartDataTask () @end @@ -40,6 +42,7 @@ - (void)startTask delegateQueue:nil]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url]; [request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"]; + [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request]; [dataTask resume]; [session finishTasksAndInvalidate]; From 4b8e99ebe8f6b7cbd455f2eeb23a1ef02a639b5c Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 10:57:25 -0800 Subject: [PATCH 11/17] Apply custom headers in RCTBundleURLProvider (#55608) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55608 Inject custom devsupport headers into the packager status check request. Changes NSURLRequest to NSMutableURLRequest to allow header mutation before the request is sent. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596717 fbshipit-source-id: 94ab3df95317cb3697d26d18b6a27750bfcf4bff --- packages/react-native/React/Base/RCTBundleURLProvider.mm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-native/React/Base/RCTBundleURLProvider.mm b/packages/react-native/React/Base/RCTBundleURLProvider.mm index 89ccd0c7ad7a..dd708d4a0d6b 100644 --- a/packages/react-native/React/Base/RCTBundleURLProvider.mm +++ b/packages/react-native/React/Base/RCTBundleURLProvider.mm @@ -10,6 +10,7 @@ #import "RCTConstants.h" #import "RCTConvert.h" #import "RCTDefines.h" +#import "RCTDevSupportHttpHeaders.h" #import "RCTLog.h" #import @@ -93,9 +94,10 @@ + (BOOL)isPackagerRunning:(NSString *)hostPort scheme:(NSString *)scheme NSURL *url = [serverRootWithHostPort(hostPort, scheme) URLByAppendingPathComponent:@"status"]; NSURLSession *session = [NSURLSession sharedSession]; - NSURLRequest *request = [NSURLRequest requestWithURL:url - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:10]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:10]; + [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request]; __block NSURLResponse *response; __block NSData *data; From 0a7e3df13cc7f4606baeb9e7ffaa88fbda71b818 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Wed, 18 Feb 2026 10:57:25 -0800 Subject: [PATCH 12/17] Apply custom headers in RCTInspectorDevServerHelper (#55609) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55609 Inject custom devsupport headers into the open-debugger POST request. This ensures the debugger launch request includes any registered custom headers. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596719 fbshipit-source-id: d4ed0a67b586b8b4e6bbd1817b7cfdf0bd75065f --- .../React/DevSupport/RCTInspectorDevServerHelper.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm index 9230dd3b3322..36e18415eed3 100644 --- a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm +++ b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm @@ -14,6 +14,7 @@ #import #import +#import #import #import @@ -154,6 +155,7 @@ + (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessag escapedInspectorDeviceId]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; [request setHTTPMethod:@"POST"]; + [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request]; [[[NSURLSession sharedSession] dataTaskWithRequest:request From 1abddbe6e7cb1433e252747dc2878c6e7076512e Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 19 Feb 2026 07:26:33 -0800 Subject: [PATCH 13/17] Apply custom headers in RCTInspectorNetworkHelper (#55610) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55610 Inject custom devsupport headers into inspector resource loading requests. This ensures that inspector network fetches include any registered custom headers. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596720 fbshipit-source-id: bda4690a2d9c78729c874977357358a432e46046 --- .../react-native/React/DevSupport/RCTInspectorNetworkHelper.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm b/packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm index 8e775d3185df..f0fa55333445 100644 --- a/packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm +++ b/packages/react-native/React/DevSupport/RCTInspectorNetworkHelper.mm @@ -6,6 +6,7 @@ */ #import "RCTInspectorNetworkHelper.h" +#import #import using ListenerBlock = void (^)(RCTInspectorNetworkListener *); @@ -47,6 +48,7 @@ - (void)loadNetworkResourceWithParams:(const RCTInspectorLoadNetworkResourceRequ NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url]; [urlRequest setHTTPMethod:@"GET"]; + [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:urlRequest]; NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:urlRequest]; __weak NSURLSessionDataTask *weakDataTask = dataTask; From 0ea26d2b4dcf3f8a390aa0d00745a17899df9d36 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 19 Feb 2026 07:26:33 -0800 Subject: [PATCH 14/17] Apply custom headers in RCTReconnectingWebSocket (#55611) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55611 Inject custom devsupport headers into the HMR WebSocket connection. Changes from `initWithURL:` to `initWithURLRequest:` so headers can be set on the HTTP upgrade handshake. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596718 fbshipit-source-id: ab028d0e03e11f0efde000d19943693439236dbc --- .../Libraries/WebSocket/RCTReconnectingWebSocket.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m b/packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m index 1bd4de74a724..ce64e0af0aca 100644 --- a/packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m +++ b/packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m @@ -9,6 +9,7 @@ #import #import +#import #import @@ -46,7 +47,9 @@ - (void)start { [self stop]; _stopped = NO; - _socket = [[SRWebSocket alloc] initWithURL:_url]; + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url]; + [[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request]; + _socket = [[SRWebSocket alloc] initWithURLRequest:request]; _socket.delegate = self; [_socket setDelegateDispatchQueue:_delegateDispatchQueue]; [_socket open]; From 3b2b74cd6a5f81d88dfcbd2377e568ac2653eca1 Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 19 Feb 2026 10:24:08 -0800 Subject: [PATCH 15/17] Apply custom headers in RCTCxxInspectorWebSocketAdapter (#55612) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/55612 Inject custom devsupport headers into the inspector CDP WebSocket connection. Changes from `initWithURL:` to `initWithURLRequest:` so headers can be set on the HTTP upgrade handshake. Changelog: [Internal] [Changed] - Reviewed By: cipolleschi Differential Revision: D93596715 fbshipit-source-id: 12dc5c991027654fdd5559daafbc27144a0fdb6b --- .../React/Inspector/RCTCxxInspectorWebSocketAdapter.mm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/react-native/React/Inspector/RCTCxxInspectorWebSocketAdapter.mm b/packages/react-native/React/Inspector/RCTCxxInspectorWebSocketAdapter.mm index 4b0754b26ae1..ad9db108e20e 100644 --- a/packages/react-native/React/Inspector/RCTCxxInspectorWebSocketAdapter.mm +++ b/packages/react-native/React/Inspector/RCTCxxInspectorWebSocketAdapter.mm @@ -9,6 +9,7 @@ #if RCT_DEV || RCT_REMOTE_PROFILE +#import #import #import #import @@ -36,7 +37,10 @@ - (instancetype)initWithURL:(const std::string &)url delegate:(std::weak_ptr Date: Wed, 25 Feb 2026 03:48:43 -0800 Subject: [PATCH 16/17] Fix visibility of DevSupportHttpClient by extracting DevSupportRequestHeaders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Extract header management from the `internal` `DevSupportHttpClient` into a new public `DevSupportRequestHeaders` singleton in `devsupport.interfaces`. This allows external consumers (e.g. Expo Dev Launcher) to register custom HTTP headers that will be applied to all React Native dev-support network traffic without depending on internal APIs. `DevSupportHttpClient` now delegates to `DevSupportRequestHeaders.allHeaders()` instead of maintaining its own `ConcurrentHashMap`. A separate `request_headers` Buck target is introduced to avoid a dependency cycle (`bridge` → `inspector` → `interfaces` → `bridge`). Changelog: [Android][Added] - Public DevSupportRequestHeaders API for registering custom dev-support HTTP headers Differential Revision: D94354168 --- .../ReactAndroid/api/ReactAndroid.api | 7 ++++ .../inspector/DevSupportHttpClient.kt | 31 +++++++-------- .../interfaces/DevSupportRequestHeaders.kt | 38 +++++++++++++++++++ 3 files changed, 58 insertions(+), 18 deletions(-) create mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 5500caf93403..030142f3856e 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2212,6 +2212,13 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp public abstract fun onResume ()V } +public final class com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders { + public static final field INSTANCE Lcom/facebook/react/devsupport/interfaces/DevSupportRequestHeaders; + public static final fun addRequestHeader (Ljava/lang/String;Ljava/lang/String;)V + public static final fun allHeaders ()Ljava/util/Map; + public static final fun removeRequestHeader (Ljava/lang/String;)V +} + public abstract interface class com/facebook/react/devsupport/interfaces/ErrorCustomizer { public abstract fun customizeErrorInfo (Landroid/util/Pair;)Landroid/util/Pair; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt index 2ec285daf84a..1bf6b402731a 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt @@ -9,24 +9,29 @@ package com.facebook.react.devsupport.inspector -import java.util.concurrent.ConcurrentHashMap +import com.facebook.react.devsupport.interfaces.DevSupportRequestHeaders import java.util.concurrent.TimeUnit import okhttp3.Interceptor import okhttp3.OkHttpClient /** * Shared [OkHttpClient] instances for devsupport networking. Uses a single connection pool and - * dispatcher across all dev support HTTP and WebSocket usage. + * dispatcher across all dev support HTTP and WebSocket usage. Injects custom request headers + * registered via [DevSupportRequestHeaders] into every outgoing request. */ internal object DevSupportHttpClient { - private val customHeaders = ConcurrentHashMap() - private val headerInterceptor = Interceptor { chain -> - val builder = chain.request().newBuilder() - for ((name, value) in customHeaders) { - builder.header(name, value) + val originalRequest = chain.request() + val headers = DevSupportRequestHeaders.allHeaders() + if (headers.isEmpty()) { + chain.proceed(originalRequest) + } else { + val builder = originalRequest.newBuilder() + for ((name, value) in headers) { + builder.header(name, value) + } + chain.proceed(builder.build()) } - chain.proceed(builder.build()) } /** Client for HTTP requests: connect=5s, write=disabled, read=disabled. */ @@ -45,14 +50,4 @@ internal object DevSupportHttpClient { .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build() - - /** Add a custom header to be included in all requests made through both clients. */ - fun addRequestHeader(name: String, value: String) { - customHeaders[name] = value - } - - /** Remove a previously added custom header. */ - fun removeRequestHeader(name: String) { - customHeaders.remove(name) - } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt new file mode 100644 index 000000000000..c0adbcda3121 --- /dev/null +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.devsupport.interfaces + +import java.util.concurrent.ConcurrentHashMap + +/** + * Thread-safe singleton for registering custom HTTP headers that will be applied to all React + * Native dev-support network traffic (bundle fetches, packager status checks, inspector + * connections, HMR WebSocket upgrades). + * + * Headers are stored globally and survive across React Host re-creations, so they can be set before + * the first app load. + */ +public object DevSupportRequestHeaders { + + private val headers = ConcurrentHashMap() + + /** Adds (or replaces) a custom request header. */ + @JvmStatic + public fun addRequestHeader(name: String, value: String) { + headers[name] = value + } + + /** Removes a previously added custom request header. */ + @JvmStatic + public fun removeRequestHeader(name: String) { + headers.remove(name) + } + + /** Returns a snapshot of all currently registered headers. */ + @JvmStatic public fun allHeaders(): Map = HashMap(headers) +} From ad9cbeb00564da7eb2d0ebde00982ff217ee700b Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 26 Feb 2026 05:47:39 -0800 Subject: [PATCH 17/17] Expose DevSupportHttpClient as public API Summary: Revert the `DevSupportRequestHeaders` extraction from D94354168 and instead make `DevSupportHttpClient` itself `public`. This gives external consumers (e.g. Expo Dev Launcher) direct access to the shared OkHttpClient instances and the `addRequestHeader`/`removeRequestHeader` methods without needing a separate intermediary singleton. Changes: - Remove `DevSupportRequestHeaders` class and its `request_headers` Buck target - Change `DevSupportHttpClient` from `internal` to `public` - Restore `addRequestHeader`/`removeRequestHeader` methods in `DevSupportHttpClient` - Revert `interfaces` Buck target to include all `interfaces/*.kt` files - Revert inspector Buck target to remove the `request_headers` dependency This looks breaking from the API prespective but `DevSupportRequestHeaders` was never shipped, so it's just an addittive change. Changelog: [Android][Added] - Make DevSupportHttpClient public for custom dev-support HTTP header registration Differential Revision: D94515394 --- .../ReactAndroid/api/ReactAndroid.api | 15 +++---- .../inspector/DevSupportHttpClient.kt | 40 +++++++++++-------- .../interfaces/DevSupportRequestHeaders.kt | 38 ------------------ 3 files changed, 32 insertions(+), 61 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index 030142f3856e..39975762a523 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2129,6 +2129,14 @@ public final class com/facebook/react/devsupport/StackTraceHelper$StackFrameImpl public fun toJSON ()Lorg/json/JSONObject; } +public final class com/facebook/react/devsupport/inspector/DevSupportHttpClient { + public static final field INSTANCE Lcom/facebook/react/devsupport/inspector/DevSupportHttpClient; + public static final fun addRequestHeader (Ljava/lang/String;Ljava/lang/String;)V + public final fun getHttpClient ()Lokhttp3/OkHttpClient; + public final fun getWebsocketClient ()Lokhttp3/OkHttpClient; + public static final fun removeRequestHeader (Ljava/lang/String;)V +} + public abstract interface class com/facebook/react/devsupport/interfaces/BundleLoadCallback { public fun onError (Ljava/lang/Exception;)V public abstract fun onSuccess ()V @@ -2212,13 +2220,6 @@ public abstract interface class com/facebook/react/devsupport/interfaces/DevSupp public abstract fun onResume ()V } -public final class com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders { - public static final field INSTANCE Lcom/facebook/react/devsupport/interfaces/DevSupportRequestHeaders; - public static final fun addRequestHeader (Ljava/lang/String;Ljava/lang/String;)V - public static final fun allHeaders ()Ljava/util/Map; - public static final fun removeRequestHeader (Ljava/lang/String;)V -} - public abstract interface class com/facebook/react/devsupport/interfaces/ErrorCustomizer { public abstract fun customizeErrorInfo (Landroid/util/Pair;)Landroid/util/Pair; } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt index 1bf6b402731a..3c9f9ff87646 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/inspector/DevSupportHttpClient.kt @@ -9,33 +9,29 @@ package com.facebook.react.devsupport.inspector -import com.facebook.react.devsupport.interfaces.DevSupportRequestHeaders +import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.TimeUnit import okhttp3.Interceptor import okhttp3.OkHttpClient /** * Shared [OkHttpClient] instances for devsupport networking. Uses a single connection pool and - * dispatcher across all dev support HTTP and WebSocket usage. Injects custom request headers - * registered via [DevSupportRequestHeaders] into every outgoing request. + * dispatcher across all dev support HTTP and WebSocket usage. Supports injecting custom request + * headers that are applied to every outgoing request via an OkHttp interceptor. */ -internal object DevSupportHttpClient { +public object DevSupportHttpClient { + private val customHeaders = ConcurrentHashMap() + private val headerInterceptor = Interceptor { chain -> - val originalRequest = chain.request() - val headers = DevSupportRequestHeaders.allHeaders() - if (headers.isEmpty()) { - chain.proceed(originalRequest) - } else { - val builder = originalRequest.newBuilder() - for ((name, value) in headers) { - builder.header(name, value) - } - chain.proceed(builder.build()) + val builder = chain.request().newBuilder() + for ((name, value) in customHeaders) { + builder.header(name, value) } + chain.proceed(builder.build()) } /** Client for HTTP requests: connect=5s, write=disabled, read=disabled. */ - val httpClient: OkHttpClient = + public val httpClient: OkHttpClient = OkHttpClient.Builder() .addInterceptor(headerInterceptor) .connectTimeout(5, TimeUnit.SECONDS) @@ -44,10 +40,22 @@ internal object DevSupportHttpClient { .build() /** Client for WebSocket connections: connect=10s, write=10s, read=disabled. */ - val websocketClient: OkHttpClient = + public val websocketClient: OkHttpClient = httpClient .newBuilder() .connectTimeout(10, TimeUnit.SECONDS) .writeTimeout(10, TimeUnit.SECONDS) .build() + + /** Add a custom header to be included in all requests made through both clients. */ + @JvmStatic + public fun addRequestHeader(name: String, value: String) { + customHeaders[name] = value + } + + /** Remove a previously added custom header. */ + @JvmStatic + public fun removeRequestHeader(name: String) { + customHeaders.remove(name) + } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt deleted file mode 100644 index c0adbcda3121..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/interfaces/DevSupportRequestHeaders.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.devsupport.interfaces - -import java.util.concurrent.ConcurrentHashMap - -/** - * Thread-safe singleton for registering custom HTTP headers that will be applied to all React - * Native dev-support network traffic (bundle fetches, packager status checks, inspector - * connections, HMR WebSocket upgrades). - * - * Headers are stored globally and survive across React Host re-creations, so they can be set before - * the first app load. - */ -public object DevSupportRequestHeaders { - - private val headers = ConcurrentHashMap() - - /** Adds (or replaces) a custom request header. */ - @JvmStatic - public fun addRequestHeader(name: String, value: String) { - headers[name] = value - } - - /** Removes a previously added custom request header. */ - @JvmStatic - public fun removeRequestHeader(name: String) { - headers.remove(name) - } - - /** Returns a snapshot of all currently registered headers. */ - @JvmStatic public fun allHeaders(): Map = HashMap(headers) -}