123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- /*
- * 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
- @implementation RCTHTTPRequestHandler
- {
- NSMapTable *_delegates;
- NSURLSession *_session;
- std::mutex _mutex;
- }
- @synthesize bridge = _bridge;
- @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 = [[_bridge networking] methodQueue];
- NSURLSessionConfiguration *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]];
- _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];
- }
- @end
- Class RCTHTTPRequestHandlerCls(void) {
- return RCTHTTPRequestHandler.class;
- }
|