mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
58444c74f5
Summary: While it is possible in the React Native implementation for Android to provide a custom configuration for HTTP requests, the iOS implementation does not allow for the same customization. As the NSURLSession used for HTTP requests on iOS is configured internally, one may for instance not supply an ephemeral configuration for HTTP requests. Other concerns related to the given problem have been addressed in the community: https://github.com/react-native-community/discussions-and-proposals/issues/166. I did make a PR with an RFC in the community repo, but after some discussion in the said repo, I figured I might as well make a PR with a suggestion :) ## Changelog [iOS] [Added] - Allow for configuring the NSURLSessionConfiguration Implement a C function `RCTSetCustomNSURLSessionConfigurationProvider` which gives the app programmer the ability to provide a block which provides an NSURLSessionConfiguration that will be used for all HTTP requests instead of the default configuration. The provided block will be called when the session configuration is needed. Pull Request resolved: https://github.com/facebook/react-native/pull/27701 Test Plan: Unsure if this can be tested in any other way than uncommenting the example code in `RNTester/RNTester/AppDelegate.mm`. Reviewed By: yungsters Differential Revision: D28680384 Pulled By: JoshuaGross fbshipit-source-id: ae24399955581a1cc9f4202f0f6f497bfe067a5c
200 lines
6.6 KiB
Plaintext
200 lines
6.6 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 <React/RCTHTTPRequestHandler.h>
|
|
|
|
#import <mutex>
|
|
|
|
#import <React/RCTNetworking.h>
|
|
#import <ReactCommon/RCTTurboModule.h>
|
|
|
|
#import "RCTNetworkPlugins.h"
|
|
|
|
@interface RCTHTTPRequestHandler () <NSURLSessionDataDelegate, RCTTurboModule>
|
|
|
|
@end
|
|
|
|
static NSURLSessionConfigurationProvider urlSessionConfigurationProvider;
|
|
|
|
void RCTSetCustomNSURLSessionConfigurationProvider(NSURLSessionConfigurationProvider provider) {
|
|
urlSessionConfigurationProvider = provider;
|
|
}
|
|
|
|
@implementation RCTHTTPRequestHandler
|
|
{
|
|
NSMapTable *_delegates;
|
|
NSURLSession *_session;
|
|
std::mutex _mutex;
|
|
}
|
|
|
|
@synthesize moduleRegistry = _moduleRegistry;
|
|
@synthesize methodQueue = _methodQueue;
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
- (void)invalidate
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
[self->_session invalidateAndCancel];
|
|
self->_session = nil;
|
|
}
|
|
|
|
// Needs to lock before call this method.
|
|
- (BOOL)isValid
|
|
{
|
|
// if session == nil and delegates != nil, we've been invalidated
|
|
return _session || !_delegates;
|
|
}
|
|
|
|
#pragma mark - NSURLRequestHandler
|
|
|
|
- (BOOL)canHandleRequest:(NSURLRequest *)request
|
|
{
|
|
static NSSet<NSString *> *schemes = nil;
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
// technically, RCTHTTPRequestHandler can handle file:// as well,
|
|
// but it's less efficient than using RCTFileRequestHandler
|
|
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
|
|
});
|
|
return [schemes containsObject:request.URL.scheme.lowercaseString];
|
|
}
|
|
|
|
- (NSURLSessionDataTask *)sendRequest:(NSURLRequest *)request
|
|
withDelegate:(id<RCTURLRequestDelegate>)delegate
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
// Lazy setup
|
|
if (!_session && [self isValid]) {
|
|
// You can override default NSURLSession instance property allowsCellularAccess (default value YES)
|
|
// by providing the following key to your RN project (edit ios/project/Info.plist file in Xcode):
|
|
// <key>ReactNetworkForceWifiOnly</key> <true/>
|
|
// This will set allowsCellularAccess to NO and force Wifi only for all network calls on iOS
|
|
// If you do not want to override default behavior, do nothing or set key with value false
|
|
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
|
|
NSNumber *useWifiOnly = [infoDictionary objectForKey:@"ReactNetworkForceWifiOnly"];
|
|
|
|
NSOperationQueue *callbackQueue = [NSOperationQueue new];
|
|
callbackQueue.maxConcurrentOperationCount = 1;
|
|
callbackQueue.underlyingQueue = [[_moduleRegistry moduleForName:"Networking"] methodQueue];
|
|
NSURLSessionConfiguration *configuration;
|
|
if (urlSessionConfigurationProvider) {
|
|
configuration = urlSessionConfigurationProvider();
|
|
} else {
|
|
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
// Set allowsCellularAccess to NO ONLY if key ReactNetworkForceWifiOnly exists AND its value is YES
|
|
if (useWifiOnly) {
|
|
configuration.allowsCellularAccess = ![useWifiOnly boolValue];
|
|
}
|
|
[configuration setHTTPShouldSetCookies:YES];
|
|
[configuration setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];
|
|
[configuration setHTTPCookieStorage:[NSHTTPCookieStorage sharedHTTPCookieStorage]];
|
|
}
|
|
assert(configuration != nil);
|
|
_session = [NSURLSession sessionWithConfiguration:configuration
|
|
delegate:self
|
|
delegateQueue:callbackQueue];
|
|
|
|
_delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory
|
|
valueOptions:NSPointerFunctionsStrongMemory
|
|
capacity:0];
|
|
}
|
|
NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
|
|
[_delegates setObject:delegate forKey:task];
|
|
[task resume];
|
|
return task;
|
|
}
|
|
|
|
- (void)cancelRequest:(NSURLSessionDataTask *)task
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
[_delegates removeObjectForKey:task];
|
|
}
|
|
[task cancel];
|
|
}
|
|
|
|
#pragma mark - NSURLSession delegate
|
|
|
|
- (void)URLSession:(NSURLSession *)session
|
|
task:(NSURLSessionTask *)task
|
|
didSendBodyData:(int64_t)bytesSent
|
|
totalBytesSent:(int64_t)totalBytesSent
|
|
totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
|
|
{
|
|
id<RCTURLRequestDelegate> delegate;
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
delegate = [_delegates objectForKey:task];
|
|
}
|
|
[delegate URLRequest:task didSendDataWithProgress:totalBytesSent];
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session
|
|
task:(NSURLSessionTask *)task
|
|
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
|
|
newRequest:(NSURLRequest *)request
|
|
completionHandler:(void (^)(NSURLRequest *))completionHandler
|
|
{
|
|
// Reset the cookies on redirect.
|
|
// This is necessary because we're not letting iOS handle cookies by itself
|
|
NSMutableURLRequest *nextRequest = [request mutableCopy];
|
|
|
|
NSArray<NSHTTPCookie *> *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:request.URL];
|
|
nextRequest.allHTTPHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
|
|
completionHandler(nextRequest);
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session
|
|
dataTask:(NSURLSessionDataTask *)task
|
|
didReceiveResponse:(NSURLResponse *)response
|
|
completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
|
|
{
|
|
id<RCTURLRequestDelegate> delegate;
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
delegate = [_delegates objectForKey:task];
|
|
}
|
|
[delegate URLRequest:task didReceiveResponse:response];
|
|
completionHandler(NSURLSessionResponseAllow);
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session
|
|
dataTask:(NSURLSessionDataTask *)task
|
|
didReceiveData:(NSData *)data
|
|
{
|
|
id<RCTURLRequestDelegate> delegate;
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
delegate = [_delegates objectForKey:task];
|
|
}
|
|
[delegate URLRequest:task didReceiveData:data];
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
{
|
|
id<RCTURLRequestDelegate> delegate;
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
delegate = [_delegates objectForKey:task];
|
|
[_delegates removeObjectForKey:task];
|
|
}
|
|
[delegate URLRequest:task didCompleteWithError:error];
|
|
}
|
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
@end
|
|
|
|
Class RCTHTTPRequestHandlerCls(void) {
|
|
return RCTHTTPRequestHandler.class;
|
|
}
|