Skip to content

Commit 262aa44

Browse files
authored
[ATOD] Add HTTPS/DevSupport improvements for iOS dev server connections (#55905)
1 parent 9a5f907 commit 262aa44

6 files changed

Lines changed: 98 additions & 30 deletions

File tree

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/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.

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ @interface RCTWebSocketModule () <SRWebSocketDelegate, NativeWebSocketModuleSpec
3434

3535
@end
3636

37+
static SRWebSocketProvider srWebSocketProvider;
38+
39+
void RCTSetCustomSRWebSocketProvider(SRWebSocketProvider provider)
40+
{
41+
srWebSocketProvider = provider;
42+
}
43+
3744
@implementation RCTWebSocketModule {
3845
NSMutableDictionary<NSNumber *, SRWebSocket *> *_sockets;
3946
NSMutableDictionary<NSNumber *, id<RCTWebSocketContentHandler>> *_contentHandlers;
@@ -88,7 +95,13 @@ - (void)invalidate
8895
}];
8996
}
9097

91-
SRWebSocket *webSocket = [[SRWebSocket alloc] initWithURLRequest:request protocols:protocols];
98+
SRWebSocket *webSocket;
99+
if (srWebSocketProvider != nullptr) {
100+
webSocket = srWebSocketProvider(request);
101+
}
102+
if (webSocket == nil) {
103+
webSocket = [[SRWebSocket alloc] initWithURLRequest:request protocols:protocols];
104+
}
92105
[webSocket setDelegateDispatchQueue:[self methodQueue]];
93106
webSocket.delegate = self;
94107
webSocket.reactTag = @(socketID);

packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,33 @@
2020
#import <jsinspector-modern/InspectorFlags.h>
2121

2222
static NSString *const kDebuggerMsgDisable = @"{ \"id\":1,\"method\":\"Debugger.disable\" }";
23+
static const int kDefaultMetroPort = 8081;
2324

2425
static NSString *getServerHost(NSURL *bundleURL)
2526
{
26-
NSNumber *port = @8081;
27-
NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"RCT_METRO_PORT"];
28-
if ((portStr != nullptr) && [portStr length] > 0) {
29-
port = [NSNumber numberWithInt:[portStr intValue]];
30-
}
31-
if ([bundleURL port] != nullptr) {
32-
port = [bundleURL port];
33-
}
3427
NSString *host = [bundleURL host];
3528
if (host == nullptr) {
3629
host = @"localhost";
3730
}
3831

39-
// this is consistent with the Android implementation, where http:// is the
40-
// hardcoded implicit scheme for the debug server. Note, packagerURL
41-
// technically looks like it could handle schemes/protocols other than HTTP,
42-
// so rather than force HTTP, leave it be for now, in case someone is relying
43-
// on that ability when developing against iOS.
44-
return [NSString stringWithFormat:@"%@:%@", host, port];
32+
// Use explicit port from URL if available
33+
if ([bundleURL port] != nullptr) {
34+
return [NSString stringWithFormat:@"%@:%@", host, [bundleURL port]];
35+
}
36+
37+
// Check environment variable
38+
NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"RCT_METRO_PORT"];
39+
if ((portStr != nullptr) && [portStr length] > 0) {
40+
return [NSString stringWithFormat:@"%@:%@", host, portStr];
41+
}
42+
43+
// For https, omit port — the scheme implies 443
44+
if ([[bundleURL scheme] isEqualToString:@"https"]) {
45+
return host;
46+
}
47+
48+
// Default to 8081 for local development (Metro's default port)
49+
return [NSString stringWithFormat:@"%@:%d", host, kDefaultMetroPort];
4550
}
4651

4752
static NSString *getSHA256(NSString *string)
@@ -112,13 +117,15 @@
112117
NSString *escapedInspectorDeviceId = [getInspectorDeviceId()
113118
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
114119

115-
return [NSURL
116-
URLWithString:[NSString stringWithFormat:@"http://%@/inspector/device?name=%@&app=%@&device=%@&profiling=%@",
117-
getServerHost(bundleURL),
118-
escapedDeviceName,
119-
escapedAppName,
120-
escapedInspectorDeviceId,
121-
isProfilingBuild ? @"true" : @"false"]];
120+
NSString *scheme = [bundleURL scheme] != nullptr ? [bundleURL scheme] : @"http";
121+
return
122+
[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/inspector/device?name=%@&app=%@&device=%@&profiling=%@",
123+
scheme,
124+
getServerHost(bundleURL),
125+
escapedDeviceName,
126+
escapedAppName,
127+
escapedInspectorDeviceId,
128+
isProfilingBuild ? @"true" : @"false"]];
122129
}
123130

124131
@implementation RCTInspectorDevServerHelper
@@ -150,7 +157,9 @@ + (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessag
150157
NSString *escapedInspectorDeviceId = [getInspectorDeviceId()
151158
stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet];
152159

153-
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/open-debugger?device=%@",
160+
NSString *scheme = [bundleURL scheme] != nullptr ? [bundleURL scheme] : @"http";
161+
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/open-debugger?device=%@",
162+
scheme,
154163
getServerHost(bundleURL),
155164
escapedInspectorDeviceId]];
156165
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

0 commit comments

Comments
 (0)