diff --git a/packages/react-native/Libraries/Core/setUpReactDevTools.js b/packages/react-native/Libraries/Core/setUpReactDevTools.js index 9bdf66b30184..2c80b23393ad 100644 --- a/packages/react-native/Libraries/Core/setUpReactDevTools.js +++ b/packages/react-native/Libraries/Core/setUpReactDevTools.js @@ -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; }); diff --git a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.h b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.h index ee7fe8be44f7..c3a27e15a045 100644 --- a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.h +++ b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.h @@ -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. */ diff --git a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm index 0303970a2e47..61de2a064155 100644 --- a/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm +++ b/packages/react-native/Libraries/Network/RCTHTTPRequestHandler.mm @@ -25,6 +25,13 @@ void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProv urlSessionConfigurationProvider = provider; } +static RCTHTTPRequestInterceptor httpRequestInterceptor; + +void RCTSetCustomHTTPRequestInterceptor(RCTHTTPRequestInterceptor interceptor) +{ + httpRequestInterceptor = interceptor; +} + @implementation RCTHTTPRequestHandler { NSMapTable *_delegates; NSURLSession *_session; @@ -99,7 +106,14 @@ - (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request withDelegate:(id *_sockets; NSMutableDictionary> *_contentHandlers; @@ -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); diff --git a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm index 36e18415eed3..e2ee27e2d881 100644 --- a/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm +++ b/packages/react-native/React/DevSupport/RCTInspectorDevServerHelper.mm @@ -20,28 +20,33 @@ #import static NSString *const kDebuggerMsgDisable = @"{ \"id\":1,\"method\":\"Debugger.disable\" }"; +static const int kDefaultMetroPort = 8081; static NSString *getServerHost(NSURL *bundleURL) { - NSNumber *port = @8081; - NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"RCT_METRO_PORT"]; - if ((portStr != nullptr) && [portStr length] > 0) { - port = [NSNumber numberWithInt:[portStr intValue]]; - } - if ([bundleURL port] != nullptr) { - port = [bundleURL port]; - } NSString *host = [bundleURL host]; if (host == nullptr) { host = @"localhost"; } - // this is consistent with the Android implementation, where http:// is the - // hardcoded implicit scheme for the debug server. Note, packagerURL - // technically looks like it could handle schemes/protocols other than HTTP, - // so rather than force HTTP, leave it be for now, in case someone is relying - // on that ability when developing against iOS. - return [NSString stringWithFormat:@"%@:%@", host, port]; + // Use explicit port from URL if available + if ([bundleURL port] != nullptr) { + return [NSString stringWithFormat:@"%@:%@", host, [bundleURL port]]; + } + + // Check environment variable + NSString *portStr = [[[NSProcessInfo processInfo] environment] objectForKey:@"RCT_METRO_PORT"]; + if ((portStr != nullptr) && [portStr length] > 0) { + return [NSString stringWithFormat:@"%@:%@", host, portStr]; + } + + // For https, omit port — the scheme implies 443 + if ([[bundleURL scheme] isEqualToString:@"https"]) { + return host; + } + + // Default to 8081 for local development (Metro's default port) + return [NSString stringWithFormat:@"%@:%d", host, kDefaultMetroPort]; } static NSString *getSHA256(NSString *string) @@ -112,13 +117,15 @@ NSString *escapedInspectorDeviceId = [getInspectorDeviceId() stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]; - return [NSURL - URLWithString:[NSString stringWithFormat:@"http://%@/inspector/device?name=%@&app=%@&device=%@&profiling=%@", - getServerHost(bundleURL), - escapedDeviceName, - escapedAppName, - escapedInspectorDeviceId, - isProfilingBuild ? @"true" : @"false"]]; + NSString *scheme = [bundleURL scheme] != nullptr ? [bundleURL scheme] : @"http"; + return + [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/inspector/device?name=%@&app=%@&device=%@&profiling=%@", + scheme, + getServerHost(bundleURL), + escapedDeviceName, + escapedAppName, + escapedInspectorDeviceId, + isProfilingBuild ? @"true" : @"false"]]; } @implementation RCTInspectorDevServerHelper @@ -150,7 +157,9 @@ + (void)openDebugger:(NSURL *)bundleURL withErrorMessage:(NSString *)errorMessag NSString *escapedInspectorDeviceId = [getInspectorDeviceId() stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]; - NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/open-debugger?device=%@", + NSString *scheme = [bundleURL scheme] != nullptr ? [bundleURL scheme] : @"http"; + NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/open-debugger?device=%@", + scheme, getServerHost(bundleURL), escapedInspectorDeviceId]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];