Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d5b3cf0
Add shared DevSupportHttpClient singleton (#55575)
cortinico Feb 17, 2026
13703c2
Migrate DevServerHelper to shared OkHttpClient (#55576)
cortinico Feb 18, 2026
f213ba2
Delegate no-arg PackagerStatusCheck constructor to shared OkHttpClien…
cortinico Feb 18, 2026
6f5f2a5
Fix per-tap OkHttpClient allocation in RedBoxContentView (#55578)
cortinico Feb 18, 2026
101b332
Fix thread-safety race in InspectorNetworkHelper (#55579)
cortinico Feb 18, 2026
a0fd163
Migrate CxxInspectorPackagerConnection to shared OkHttpClient (#55580)
cortinico Feb 18, 2026
00313fa
Migrate ReconnectingWebSocket to shared OkHttpClient (#55581)
cortinico Feb 18, 2026
27edb79
Add custom request header support to DevSupportHttpClient (#55582)
cortinico Feb 18, 2026
3277c1f
Add RCTDevSupportHttpHeaders singleton (#55586)
cortinico Feb 18, 2026
cdf963d
Apply custom headers in RCTMultipartDataTask (#55607)
cortinico Feb 18, 2026
13c58dc
Apply custom headers in RCTBundleURLProvider (#55608)
cortinico Feb 18, 2026
879ca9a
Apply custom headers in RCTInspectorDevServerHelper (#55609)
cortinico Feb 18, 2026
3201fc6
Apply custom headers in RCTInspectorNetworkHelper (#55610)
cortinico Feb 19, 2026
583a9ab
Apply custom headers in RCTReconnectingWebSocket (#55611)
cortinico Feb 19, 2026
917eab5
Apply custom headers in RCTCxxInspectorWebSocketAdapter (#55612)
cortinico Feb 19, 2026
c31245b
Fix visibility of DevSupportHttpClient by extracting DevSupportReques…
cortinico Feb 25, 2026
bfd1b9e
Expose DevSupportHttpClient as public API
cortinico Feb 26, 2026
5b26030
Use HTTPS/WSS for dev-support traffic when port is 443 (#55790)
cortinico Feb 27, 2026
31272da
Make DevSupportHttpClient internal
cortinico Mar 3, 2026
2a7d6a3
Remove custom header support from DevSupportHttpClient
cortinico Mar 3, 2026
4e3e80c
WebSocketModule uses OkHttpClientProvider
cortinico Mar 3, 2026
9a5f907
DevSupportHttpClient uses OkHttpClientProvider
cortinico Mar 3, 2026
262aa44
[ATOD] Add HTTPS/DevSupport improvements for iOS dev server connectio…
cipolleschi Mar 4, 2026
a8bb8c3
ATOD] Allow for selectively apply headers to multipart requests
cipolleschi Mar 4, 2026
9bcb317
Skip set-rn-artifacts-version for PRs targeting stable branches
cipolleschi Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/actions/build-android/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ runs:
# already set from the 'create release' commits on the release branch.
# For testing RC.0, though, the version has not been set yet. In that case, we are on Stable branch and
# it is the only case when the version is still 1000.0.0
if: ${{ !endsWith(github.ref_name, '-stable') || endsWith(github.ref_name, '-stable') && steps.read-rn-version.outputs.rn-version == '1000.0.0' }}
# We also skip this when the PR targets a stable branch (github.base_ref ends with '-stable'),
# since the version is already set on the stable branch.
if: ${{ !endsWith(github.ref_name, '-stable') && !endsWith(github.base_ref || '', '-stable') || endsWith(github.ref_name, '-stable') && steps.read-rn-version.outputs.rn-version == '1000.0.0' }}
shell: bash
run: node ./scripts/releases/set-rn-artifacts-version.js --build-type ${{ inputs.release-type }}
- name: Setup gradle
Expand Down
29 changes: 23 additions & 6 deletions packages/react-native/Libraries/Core/setUpReactDevTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,34 @@ if (__DEV__) {
? guessHostFromDevServerUrl(devServer.url)
: 'localhost';

// Read the optional global variable for backward compatibility.
// It was added in https://github.com/facebook/react-native/commit/bf2b435322e89d0aeee8792b1c6e04656c2719a0.
const port =
// Derive scheme and port from the dev server URL when possible,
// falling back to ws://host:8097 for local development.
let wsScheme = 'ws';
let port = 8097;

if (
// $FlowFixMe[prop-missing]
// $FlowFixMe[incompatible-use]
window.__REACT_DEVTOOLS_PORT__ != null
? window.__REACT_DEVTOOLS_PORT__
: 8097;
) {
// $FlowFixMe[prop-missing]
port = window.__REACT_DEVTOOLS_PORT__;
} else if (devServer.bundleLoadedFromServer) {
try {
const devUrl = new URL(devServer.url);
if (devUrl.protocol === 'https:') {
wsScheme = 'wss';
}
if (devUrl.port) {
port = parseInt(devUrl.port, 10);
} else if (devUrl.protocol === 'https:') {
port = 443;
}
} catch (e) {}
}

const WebSocket = require('../WebSocket/WebSocket').default;
ws = new WebSocket('ws://' + host + ':' + port);
ws = new WebSocket(wsScheme + '://' + host + ':' + port);
ws.addEventListener('close', event => {
isWebSocketOpen = false;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ typedef NSURLSessionConfiguration * (^NSURLSessionConfigurationProvider)(void);
* app.
*/
RCT_EXTERN void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProvider /*provider*/);

typedef NSURLRequest *_Nullable (^RCTHTTPRequestInterceptor)(NSURLRequest *request);
/**
* The block provided via this function can inspect/modify HTTP requests before
* they are sent. Return a modified request to override, or nil to use the
* original request unchanged.
*/
RCT_EXTERN void RCTSetCustomHTTPRequestInterceptor(RCTHTTPRequestInterceptor /*interceptor*/);

/**
* This is the default RCTURLRequestHandler implementation for HTTP requests.
*/
Expand Down
16 changes: 15 additions & 1 deletion packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProv
urlSessionConfigurationProvider = provider;
}

static RCTHTTPRequestInterceptor httpRequestInterceptor;

void RCTSetCustomHTTPRequestInterceptor(RCTHTTPRequestInterceptor interceptor)
{
httpRequestInterceptor = interceptor;
}

@implementation RCTHTTPRequestHandler {
NSMapTable *_delegates;
NSURLSession *_session;
Expand Down Expand Up @@ -99,7 +106,14 @@ - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request withDelegate:(id<R
valueOptions:NSPointerFunctionsStrongMemory
capacity:0];
}
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
NSURLRequest *finalRequest = request;
if (httpRequestInterceptor != nullptr) {
NSURLRequest *intercepted = httpRequestInterceptor(request);
if (intercepted != nil) {
finalRequest = intercepted;
}
}
NSURLSessionDataTask *task = [_session dataTaskWithRequest:finalRequest];
[_delegates setObject:delegate forKey:task];
[task resume];
return task;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#import <React/RCTConvert.h>
#import <React/RCTDefines.h>
#import <React/RCTDevSupportHttpHeaders.h>

#import <SocketRocket/SRWebSocket.h>

Expand Down Expand Up @@ -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];
Expand Down
8 changes: 5 additions & 3 deletions packages/react-native/React/Base/RCTBundleURLProvider.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#import "RCTConstants.h"
#import "RCTConvert.h"
#import "RCTDefines.h"
#import "RCTDevSupportHttpHeaders.h"
#import "RCTLog.h"

#import <jsinspector-modern/InspectorFlags.h>
Expand Down Expand Up @@ -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;

Expand Down
24 changes: 24 additions & 0 deletions packages/react-native/React/Base/RCTDevSupportHttpHeaders.h
Original file line number Diff line number Diff line change
@@ -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 <Foundation/Foundation.h>

/**
* 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<NSString *, NSString *> *)allHeaders;
- (void)applyHeadersToRequest:(NSMutableURLRequest *)request;

@end
65 changes: 65 additions & 0 deletions packages/react-native/React/Base/RCTDevSupportHttpHeaders.m
Original file line number Diff line number Diff line change
@@ -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<NSString *, NSString *> *_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<NSString *, NSString *> *)allHeaders
{
__block NSDictionary<NSString *, NSString *> *snapshot;
dispatch_sync(_queue, ^{
snapshot = [self->_headers copy];
});
return snapshot;
}

- (void)applyHeadersToRequest:(NSMutableURLRequest *)request
{
NSDictionary<NSString *, NSString *> *headers = [self allHeaders];
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *headerValue, BOOL *stop) {
[request setValue:headerValue forHTTPHeaderField:headerName];
}];
}

@end
9 changes: 9 additions & 0 deletions packages/react-native/React/Base/RCTMultipartDataTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#import <Foundation/Foundation.h>

#import <React/RCTDefines.h>
#import <React/RCTMultipartStreamReader.h>

typedef void (^RCTMultipartDataTaskCallback)(
Expand All @@ -16,6 +17,14 @@ typedef void (^RCTMultipartDataTaskCallback)(
NSError *error,
BOOL done);

typedef NSURLRequest * _Nullable (^RCTMultipartDataTaskRequestInterceptor)(NSURLRequest *request);
/**
* The block provided via this function can inspect/modify multipart data task
* requests before they are sent. Return a modified request to override, or nil
* to use the original request unchanged.
*/
RCT_EXTERN void RCTSetCustomMultipartDataTaskRequestInterceptor(RCTMultipartDataTaskRequestInterceptor /*interceptor*/);

@interface RCTMultipartDataTask : NSObject

- (instancetype)initWithURL:(NSURL *)url
Expand Down
17 changes: 16 additions & 1 deletion packages/react-native/React/Base/RCTMultipartDataTask.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@

#import "RCTMultipartDataTask.h"

static RCTMultipartDataTaskRequestInterceptor multipartRequestInterceptor;

void RCTSetCustomMultipartDataTaskRequestInterceptor(RCTMultipartDataTaskRequestInterceptor interceptor)
{
multipartRequestInterceptor = interceptor;
}

@interface RCTMultipartDataTask () <NSURLSessionDataDelegate, NSURLSessionDataDelegate>

@end
Expand Down Expand Up @@ -40,7 +47,15 @@ - (void)startTask
delegateQueue:nil];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
[request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
NSURLRequest *finalRequest = request;
if (multipartRequestInterceptor != nil) {
NSURLRequest *intercepted = multipartRequestInterceptor(request);
if (intercepted != nil) {
finalRequest = intercepted;
}
}
NSLog(@"[RCTMultipartDataTask] %@ %@", finalRequest.HTTPMethod ?: @"GET", finalRequest.URL.absoluteString);
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:finalRequest];
[dataTask resume];
[session finishTasksAndInvalidate];
}
Expand Down
6 changes: 6 additions & 0 deletions packages/react-native/React/CoreModules/RCTWebSocketModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ NS_ASSUME_NONNULL_BEGIN

@end

@class SRWebSocket;

typedef SRWebSocket * (^SRWebSocketProvider)(NSURLRequest *request);

RCT_EXTERN void RCTSetCustomSRWebSocketProvider(SRWebSocketProvider provider);

@interface RCTWebSocketModule : RCTEventEmitter

// Register a custom handler for a specific websocket. The handler will be strongly held by the WebSocketModule.
Expand Down
15 changes: 14 additions & 1 deletion packages/react-native/React/CoreModules/RCTWebSocketModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ @interface RCTWebSocketModule () <SRWebSocketDelegate, NativeWebSocketModuleSpec

@end

static SRWebSocketProvider srWebSocketProvider;

void RCTSetCustomSRWebSocketProvider(SRWebSocketProvider provider)
{
srWebSocketProvider = provider;
}

@implementation RCTWebSocketModule {
NSMutableDictionary<NSNumber *, SRWebSocket *> *_sockets;
NSMutableDictionary<NSNumber *, id<RCTWebSocketContentHandler>> *_contentHandlers;
Expand Down Expand Up @@ -88,7 +95,13 @@ - (void)invalidate
}];
}

SRWebSocket *webSocket = [[SRWebSocket alloc] initWithURLRequest:request protocols:protocols];
SRWebSocket *webSocket;
if (srWebSocketProvider != nullptr) {
webSocket = srWebSocketProvider(request);
}
if (webSocket == nil) {
webSocket = [[SRWebSocket alloc] initWithURLRequest:request protocols:protocols];
}
[webSocket setDelegateDispatchQueue:[self methodQueue]];
webSocket.delegate = self;
webSocket.reactTag = @(socketID);
Expand Down
Loading