Files
react-native/React/Base/RCTBundleURLProvider.mm
T
Jimmy Zhang 12543d557f Remove port from JSLocation when returning packager host
Summary:
Changelog:
Some callsites may rely on the assumption that address return by this method does not contain port information. JSLocation saved in NSUserDefault might contain port information, removing it here can prevent the issue for callsites.

Differential Revision: D23240495

fbshipit-source-id: a2edf4abb086fd951dd089331407bd659aad1729
2020-08-20 09:25:18 -07:00

324 lines
10 KiB
Plaintext

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTBundleURLProvider.h"
#import "RCTConvert.h"
#import "RCTDefines.h"
#if __has_include("hermes.h") || __has_include(<hermes/hermes.h>)
#import <hermes/hermes.h>
#define HAS_BYTECODE_VERSION
#endif
NSString *const RCTBundleURLProviderUpdatedNotification = @"RCTBundleURLProviderUpdatedNotification";
const NSUInteger kRCTBundleURLProviderDefaultPort = RCT_METRO_PORT;
static NSString *const kRCTJsLocationKey = @"RCT_jsLocation";
// This option is no longer exposed in the dev menu UI.
// It was renamed in D15958697 so it doesn't get stuck with no way to turn it off:
static NSString *const kRCTEnableLiveReloadKey = @"RCT_enableLiveReload_LEGACY";
static NSString *const kRCTEnableDevKey = @"RCT_enableDev";
static NSString *const kRCTEnableMinificationKey = @"RCT_enableMinification";
@implementation RCTBundleURLProvider
- (instancetype)init
{
self = [super init];
if (self) {
[self setDefaults];
}
return self;
}
- (NSDictionary *)defaults
{
return @{
kRCTEnableLiveReloadKey : @NO,
kRCTEnableDevKey : @YES,
kRCTEnableMinificationKey : @NO,
};
}
- (void)settingsUpdated
{
[[NSNotificationCenter defaultCenter] postNotificationName:RCTBundleURLProviderUpdatedNotification object:self];
}
- (void)setDefaults
{
[[NSUserDefaults standardUserDefaults] registerDefaults:[self defaults]];
}
- (void)resetToDefaults
{
for (NSString *key in [[self defaults] allKeys]) {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
[self setDefaults];
[self settingsUpdated];
}
static NSURL *serverRootWithHostPort(NSString *hostPort)
{
if ([hostPort rangeOfString:@":"].location != NSNotFound) {
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", hostPort]];
}
return [NSURL
URLWithString:[NSString
stringWithFormat:@"http://%@:%lu/", hostPort, (unsigned long)kRCTBundleURLProviderDefaultPort]];
}
#if RCT_DEV_MENU
+ (BOOL)isPackagerRunning:(NSString *)host
{
NSURL *url = [serverRootWithHostPort(host) URLByAppendingPathComponent:@"status"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
__block NSURLResponse *response;
__block NSData *data;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[session dataTaskWithRequest:request
completionHandler:^(NSData *d, NSURLResponse *res, __unused NSError *err) {
data = d;
response = res;
dispatch_semaphore_signal(semaphore);
}] resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSString *status = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return [status isEqualToString:@"packager-status:running"];
}
- (NSString *)guessPackagerHost
{
static NSString *ipGuess;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *ipPath = [[NSBundle mainBundle] pathForResource:@"ip" ofType:@"txt"];
ipGuess =
[[NSString stringWithContentsOfFile:ipPath encoding:NSUTF8StringEncoding
error:nil] stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
});
NSString *host = ipGuess ?: @"localhost";
if ([RCTBundleURLProvider isPackagerRunning:host]) {
return host;
}
return nil;
}
#else
+ (BOOL)isPackagerRunning:(NSString *)host
{
return false;
}
#endif
- (NSString *)packagerServerHost
{
NSString *location = [self jsLocation];
if (location) {
NSInteger index = [location rangeOfString:@":"].location;
if (index != NSNotFound) {
location = [location substringToIndex:index];
}
}
#if RCT_DEV_MENU
if ([location length] && ![RCTBundleURLProvider isPackagerRunning:location]) {
location = nil;
}
#endif
if (location != nil) {
return location;
}
#if RCT_DEV
NSString *host = [self guessPackagerHost];
if (host) {
return host;
}
#endif
return nil;
}
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackURLProvider:(NSURL * (^)(void))fallbackURLProvider
{
NSString *packagerServerHost = [self packagerServerHost];
if (!packagerServerHost) {
return fallbackURLProvider();
} else {
return [RCTBundleURLProvider jsBundleURLForBundleRoot:bundleRoot
packagerHost:packagerServerHost
enableDev:[self enableDev]
enableMinification:[self enableMinification]];
}
}
- (NSURL *)jsBundleURLForSplitBundleRoot:(NSString *)bundleRoot
{
return [RCTBundleURLProvider jsBundleURLForBundleRoot:bundleRoot
packagerHost:[self packagerServerHost]
enableDev:[self enableDev]
enableMinification:[self enableMinification]
modulesOnly:YES
runModule:NO];
}
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
fallbackResource:(NSString *)resourceName
fallbackExtension:(NSString *)extension
{
return [self jsBundleURLForBundleRoot:bundleRoot
fallbackURLProvider:^NSURL * {
return [self jsBundleURLForFallbackResource:resourceName fallbackExtension:extension];
}];
}
- (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot fallbackResource:(NSString *)resourceName
{
return [self jsBundleURLForBundleRoot:bundleRoot fallbackResource:resourceName fallbackExtension:nil];
}
- (NSURL *)jsBundleURLForFallbackResource:(NSString *)resourceName fallbackExtension:(NSString *)extension
{
resourceName = resourceName ?: @"main";
extension = extension ?: @"jsbundle";
return [[NSBundle mainBundle] URLForResource:resourceName withExtension:extension];
}
- (NSURL *)resourceURLForResourceRoot:(NSString *)root
resourceName:(NSString *)name
resourceExtension:(NSString *)extension
offlineBundle:(NSBundle *)offlineBundle
{
NSString *packagerServerHost = [self packagerServerHost];
if (!packagerServerHost) {
// Serve offline bundle (local file)
NSBundle *bundle = offlineBundle ?: [NSBundle mainBundle];
return [bundle URLForResource:name withExtension:extension];
}
NSString *path = [NSString stringWithFormat:@"/%@/%@.%@", root, name, extension];
return [[self class] resourceURLForResourcePath:path packagerHost:packagerServerHost query:nil];
}
+ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
packagerHost:(NSString *)packagerHost
enableDev:(BOOL)enableDev
enableMinification:(BOOL)enableMinification
{
return [self jsBundleURLForBundleRoot:bundleRoot
packagerHost:packagerHost
enableDev:enableDev
enableMinification:enableMinification
modulesOnly:NO
runModule:YES];
}
+ (NSURL *)jsBundleURLForBundleRoot:(NSString *)bundleRoot
packagerHost:(NSString *)packagerHost
enableDev:(BOOL)enableDev
enableMinification:(BOOL)enableMinification
modulesOnly:(BOOL)modulesOnly
runModule:(BOOL)runModule
{
NSString *path = [NSString stringWithFormat:@"/%@.bundle", bundleRoot];
#ifdef HAS_BYTECODE_VERSION
NSString *runtimeBytecodeVersion =
[NSString stringWithFormat:@"&runtimeBytecodeVersion=%u", facebook::hermes::HermesRuntime::getBytecodeVersion()];
#else
NSString *runtimeBytecodeVersion = @"";
#endif
// When we support only iOS 8 and above, use queryItems for a better API.
NSString *query = [NSString stringWithFormat:@"platform=ios&dev=%@&minify=%@&modulesOnly=%@&runModule=%@%@",
enableDev ? @"true" : @"false",
enableMinification ? @"true" : @"false",
modulesOnly ? @"true" : @"false",
runModule ? @"true" : @"false",
runtimeBytecodeVersion];
NSString *bundleID = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleIdentifierKey];
if (bundleID) {
query = [NSString stringWithFormat:@"%@&app=%@", query, bundleID];
}
return [[self class] resourceURLForResourcePath:path packagerHost:packagerHost query:query];
}
+ (NSURL *)resourceURLForResourcePath:(NSString *)path packagerHost:(NSString *)packagerHost query:(NSString *)query
{
NSURLComponents *components = [NSURLComponents componentsWithURL:serverRootWithHostPort(packagerHost)
resolvingAgainstBaseURL:NO];
components.path = path;
if (query != nil) {
components.query = query;
}
return components.URL;
}
- (void)updateValue:(id)object forKey:(NSString *)key
{
[[NSUserDefaults standardUserDefaults] setObject:object forKey:key];
[[NSUserDefaults standardUserDefaults] synchronize];
[self settingsUpdated];
}
- (BOOL)enableDev
{
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableDevKey];
}
- (BOOL)enableLiveReload
{
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableLiveReloadKey];
}
- (BOOL)enableMinification
{
return [[NSUserDefaults standardUserDefaults] boolForKey:kRCTEnableMinificationKey];
}
- (NSString *)jsLocation
{
return [[NSUserDefaults standardUserDefaults] stringForKey:kRCTJsLocationKey];
}
- (void)setEnableDev:(BOOL)enableDev
{
[self updateValue:@(enableDev) forKey:kRCTEnableDevKey];
}
- (void)setEnableLiveReload:(BOOL)enableLiveReload
{
[self updateValue:@(enableLiveReload) forKey:kRCTEnableLiveReloadKey];
}
- (void)setJsLocation:(NSString *)jsLocation
{
[self updateValue:jsLocation forKey:kRCTJsLocationKey];
}
- (void)setEnableMinification:(BOOL)enableMinification
{
[self updateValue:@(enableMinification) forKey:kRCTEnableMinificationKey];
}
+ (instancetype)sharedSettings
{
static RCTBundleURLProvider *sharedInstance;
static dispatch_once_t once_token;
dispatch_once(&once_token, ^{
sharedInstance = [RCTBundleURLProvider new];
});
return sharedInstance;
}
@end