Skip to content

Commit c8ab750

Browse files
[0.83] Add support for custom header in devsupport (#55722)
Co-authored-by: Riccardo Cipolleschi <cipolleschi@meta.com> resolved: #55575 resolved: #55576 resolved: #55577 Fix per-tap OkHttpClient allocation in RedBoxContentView (#55578) resolved: #55578 Fix thread-safety race in InspectorNetworkHelper (#55579) resolved: #55579 resolved: #55580 resolved: #55581 resolved: #55582 resolved: #55586 resolved: #55607 resolved: #55608 resolved: #55609 resolved: #55610 resolved: #55611 resolved: #55612 resolved: #55790
1 parent 81223f2 commit c8ab750

24 files changed

Lines changed: 319 additions & 103 deletions

packages/react-native/Libraries/Core/setUpReactDevTools.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,17 +146,34 @@ if (__DEV__) {
146146
? guessHostFromDevServerUrl(devServer.url)
147147
: 'localhost';
148148

149-
// Read the optional global variable for backward compatibility.
150-
// It was added in https://github.com/facebook/react-native/commit/bf2b435322e89d0aeee8792b1c6e04656c2719a0.
151-
const port =
149+
// Derive scheme and port from the dev server URL when possible,
150+
// falling back to ws://host:8097 for local development.
151+
let wsScheme = 'ws';
152+
let port = 8097;
153+
154+
if (
152155
// $FlowFixMe[prop-missing]
153156
// $FlowFixMe[incompatible-use]
154157
window.__REACT_DEVTOOLS_PORT__ != null
155-
? window.__REACT_DEVTOOLS_PORT__
156-
: 8097;
158+
) {
159+
// $FlowFixMe[prop-missing]
160+
port = window.__REACT_DEVTOOLS_PORT__;
161+
} else if (devServer.bundleLoadedFromServer) {
162+
try {
163+
const devUrl = new URL(devServer.url);
164+
if (devUrl.protocol === 'https:') {
165+
wsScheme = 'wss';
166+
}
167+
if (devUrl.port) {
168+
port = parseInt(devUrl.port, 10);
169+
} else if (devUrl.protocol === 'https:') {
170+
port = 443;
171+
}
172+
} catch (e) {}
173+
}
157174

158175
const WebSocket = require('../WebSocket/WebSocket').default;
159-
ws = new WebSocket('ws://' + host + ':' + port);
176+
ws = new WebSocket(wsScheme + '://' + host + ':' + port);
160177
ws.addEventListener('close', event => {
161178
isWebSocketOpen = false;
162179
});

packages/react-native/Libraries/Network/RCTHTTPRequestHandler.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@ typedef NSURLSessionConfiguration * (^NSURLSessionConfigurationProvider)(void);
1414
* app.
1515
*/
1616
RCT_EXTERN void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProvider /*provider*/);
17+
18+
typedef NSURLRequest *_Nullable (^RCTHTTPRequestInterceptor)(NSURLRequest *request);
19+
/**
20+
* The block provided via this function can inspect/modify HTTP requests before
21+
* they are sent. Return a modified request to override, or nil to use the
22+
* original request unchanged.
23+
*/
24+
RCT_EXTERN void RCTSetCustomHTTPRequestInterceptor(RCTHTTPRequestInterceptor /*interceptor*/);
25+
1726
/**
1827
* This is the default RCTURLRequestHandler implementation for HTTP requests.
1928
*/

packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProv
2525
urlSessionConfigurationProvider = provider;
2626
}
2727

28+
static RCTHTTPRequestInterceptor httpRequestInterceptor;
29+
30+
void RCTSetCustomHTTPRequestInterceptor(RCTHTTPRequestInterceptor interceptor)
31+
{
32+
httpRequestInterceptor = interceptor;
33+
}
34+
2835
@implementation RCTHTTPRequestHandler {
2936
NSMapTable *_delegates;
3037
NSURLSession *_session;
@@ -99,7 +106,14 @@ - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request withDelegate:(id<R
99106
valueOptions:NSPointerFunctionsStrongMemory
100107
capacity:0];
101108
}
102-
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
109+
NSURLRequest *finalRequest = request;
110+
if (httpRequestInterceptor != nullptr) {
111+
NSURLRequest *intercepted = httpRequestInterceptor(request);
112+
if (intercepted != nil) {
113+
finalRequest = intercepted;
114+
}
115+
}
116+
NSURLSessionDataTask *task = [_session dataTaskWithRequest:finalRequest];
103117
[_delegates setObject:delegate forKey:task];
104118
[task resume];
105119
return task;

packages/react-native/Libraries/WebSocket/RCTReconnectingWebSocket.m

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#import <React/RCTConvert.h>
1111
#import <React/RCTDefines.h>
12+
#import <React/RCTDevSupportHttpHeaders.h>
1213

1314
#import <SocketRocket/SRWebSocket.h>
1415

@@ -46,7 +47,9 @@ - (void)start
4647
{
4748
[self stop];
4849
_stopped = NO;
49-
_socket = [[SRWebSocket alloc] initWithURL:_url];
50+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
51+
[[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request];
52+
_socket = [[SRWebSocket alloc] initWithURLRequest:request];
5053
_socket.delegate = self;
5154
[_socket setDelegateDispatchQueue:_delegateDispatchQueue];
5255
[_socket open];

packages/react-native/React/Base/RCTBundleURLProvider.mm

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#import "RCTConstants.h"
1111
#import "RCTConvert.h"
1212
#import "RCTDefines.h"
13+
#import "RCTDevSupportHttpHeaders.h"
1314
#import "RCTLog.h"
1415

1516
#import <jsinspector-modern/InspectorFlags.h>
@@ -93,9 +94,10 @@ + (BOOL)isPackagerRunning:(NSString *)hostPort scheme:(NSString *)scheme
9394
NSURL *url = [serverRootWithHostPort(hostPort, scheme) URLByAppendingPathComponent:@"status"];
9495

9596
NSURLSession *session = [NSURLSession sharedSession];
96-
NSURLRequest *request = [NSURLRequest requestWithURL:url
97-
cachePolicy:NSURLRequestUseProtocolCachePolicy
98-
timeoutInterval:10];
97+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
98+
cachePolicy:NSURLRequestUseProtocolCachePolicy
99+
timeoutInterval:10];
100+
[[RCTDevSupportHttpHeaders sharedInstance] applyHeadersToRequest:request];
99101
__block NSURLResponse *response;
100102
__block NSData *data;
101103

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import <Foundation/Foundation.h>
9+
10+
/**
11+
* Thread-safe singleton that holds custom HTTP headers to be applied
12+
* to all devsupport network requests (bundle fetches, packager status
13+
* checks, inspector and HMR WebSocket connections).
14+
*/
15+
@interface RCTDevSupportHttpHeaders : NSObject
16+
17+
+ (instancetype)sharedInstance;
18+
19+
- (void)addRequestHeader:(NSString *)name value:(NSString *)value;
20+
- (void)removeRequestHeader:(NSString *)name;
21+
- (NSDictionary<NSString *, NSString *> *)allHeaders;
22+
- (void)applyHeadersToRequest:(NSMutableURLRequest *)request;
23+
24+
@end
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#import "RCTDevSupportHttpHeaders.h"
9+
10+
@implementation RCTDevSupportHttpHeaders {
11+
NSMutableDictionary<NSString *, NSString *> *_headers;
12+
dispatch_queue_t _queue;
13+
}
14+
15+
+ (instancetype)sharedInstance
16+
{
17+
static RCTDevSupportHttpHeaders *sharedInstance;
18+
static dispatch_once_t onceToken;
19+
dispatch_once(&onceToken, ^{
20+
sharedInstance = [[RCTDevSupportHttpHeaders alloc] init];
21+
});
22+
return sharedInstance;
23+
}
24+
25+
- (instancetype)init
26+
{
27+
if (self = [super init]) {
28+
_headers = [NSMutableDictionary new];
29+
_queue = dispatch_queue_create("com.facebook.react.RCTDevSupportHttpHeaders", DISPATCH_QUEUE_SERIAL);
30+
}
31+
return self;
32+
}
33+
34+
- (void)addRequestHeader:(NSString *)name value:(NSString *)value
35+
{
36+
dispatch_sync(_queue, ^{
37+
self->_headers[name] = value;
38+
});
39+
}
40+
41+
- (void)removeRequestHeader:(NSString *)name
42+
{
43+
dispatch_sync(_queue, ^{
44+
[self->_headers removeObjectForKey:name];
45+
});
46+
}
47+
48+
- (NSDictionary<NSString *, NSString *> *)allHeaders
49+
{
50+
__block NSDictionary<NSString *, NSString *> *snapshot;
51+
dispatch_sync(_queue, ^{
52+
snapshot = [self->_headers copy];
53+
});
54+
return snapshot;
55+
}
56+
57+
- (void)applyHeadersToRequest:(NSMutableURLRequest *)request
58+
{
59+
NSDictionary<NSString *, NSString *> *headers = [self allHeaders];
60+
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *headerName, NSString *headerValue, BOOL *stop) {
61+
[request setValue:headerValue forHTTPHeaderField:headerName];
62+
}];
63+
}
64+
65+
@end

packages/react-native/React/Base/RCTMultipartDataTask.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#import <Foundation/Foundation.h>
99

10+
#import <React/RCTDefines.h>
1011
#import <React/RCTMultipartStreamReader.h>
1112

1213
typedef void (^RCTMultipartDataTaskCallback)(
@@ -16,6 +17,14 @@ typedef void (^RCTMultipartDataTaskCallback)(
1617
NSError *error,
1718
BOOL done);
1819

20+
typedef NSURLRequest * _Nullable (^RCTMultipartDataTaskRequestInterceptor)(NSURLRequest *request);
21+
/**
22+
* The block provided via this function can inspect/modify multipart data task
23+
* requests before they are sent. Return a modified request to override, or nil
24+
* to use the original request unchanged.
25+
*/
26+
RCT_EXTERN void RCTSetCustomMultipartDataTaskRequestInterceptor(RCTMultipartDataTaskRequestInterceptor /*interceptor*/);
27+
1928
@interface RCTMultipartDataTask : NSObject
2029

2130
- (instancetype)initWithURL:(NSURL *)url

packages/react-native/React/Base/RCTMultipartDataTask.m

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77

88
#import "RCTMultipartDataTask.h"
99

10+
static RCTMultipartDataTaskRequestInterceptor multipartRequestInterceptor;
11+
12+
void RCTSetCustomMultipartDataTaskRequestInterceptor(RCTMultipartDataTaskRequestInterceptor interceptor)
13+
{
14+
multipartRequestInterceptor = interceptor;
15+
}
16+
1017
@interface RCTMultipartDataTask () <NSURLSessionDataDelegate, NSURLSessionDataDelegate>
1118

1219
@end
@@ -40,7 +47,15 @@ - (void)startTask
4047
delegateQueue:nil];
4148
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url];
4249
[request addValue:@"multipart/mixed" forHTTPHeaderField:@"Accept"];
43-
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];
50+
NSURLRequest *finalRequest = request;
51+
if (multipartRequestInterceptor != nil) {
52+
NSURLRequest *intercepted = multipartRequestInterceptor(request);
53+
if (intercepted != nil) {
54+
finalRequest = intercepted;
55+
}
56+
}
57+
NSLog(@"[RCTMultipartDataTask] %@ %@", finalRequest.HTTPMethod ?: @"GET", finalRequest.URL.absoluteString);
58+
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:finalRequest];
4459
[dataTask resume];
4560
[session finishTasksAndInvalidate];
4661
}

packages/react-native/React/CoreModules/RCTWebSocketModule.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ NS_ASSUME_NONNULL_BEGIN
1818

1919
@end
2020

21+
@class SRWebSocket;
22+
23+
typedef SRWebSocket * (^SRWebSocketProvider)(NSURLRequest *request);
24+
25+
RCT_EXTERN void RCTSetCustomSRWebSocketProvider(SRWebSocketProvider provider);
26+
2127
@interface RCTWebSocketModule : RCTEventEmitter
2228

2329
// Register a custom handler for a specific websocket. The handler will be strongly held by the WebSocketModule.

0 commit comments

Comments
 (0)