/** * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ #import "RCTHTTPRequestHandler.h" #import #import "RCTNetworking.h" @interface RCTHTTPRequestHandler () @end @implementation RCTHTTPRequestHandler { NSMapTable *_delegates; NSURLSession *_session; std::mutex _mutex; } @synthesize bridge = _bridge; RCT_EXPORT_MODULE() - (void)invalidate { [_session invalidateAndCancel]; _session = nil; } - (BOOL)isValid { // if session == nil and delegates != nil, we've been invalidated return _session || !_delegates; } #pragma mark - NSURLRequestHandler - (BOOL)canHandleRequest:(NSURLRequest *)request { static NSSet *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)delegate { // Lazy setup if (!_session && [self isValid]) { NSOperationQueue *callbackQueue = [NSOperationQueue new]; callbackQueue.maxConcurrentOperationCount = 1; callbackQueue.underlyingQueue = [[_bridge networking] methodQueue]; NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; [configuration setHTTPShouldSetCookies:YES]; [configuration setHTTPCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; [configuration setHTTPCookieStorage:[NSHTTPCookieStorage sharedHTTPCookieStorage]]; _session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:callbackQueue]; std::lock_guard lock(_mutex); _delegates = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:0]; } NSURLSessionDataTask *task = [_session dataTaskWithRequest:request]; { std::lock_guard lock(_mutex); [_delegates setObject:delegate forKey:task]; } [task resume]; return task; } - (void)cancelRequest:(NSURLSessionDataTask *)task { { std::lock_guard 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 delegate; { std::lock_guard lock(_mutex); delegate = [_delegates objectForKey:task]; } [delegate URLRequest:task didSendDataWithProgress:totalBytesSent]; } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { id delegate; { std::lock_guard lock(_mutex); delegate = [_delegates objectForKey:task]; } [delegate URLRequest:task didReceiveResponse:response]; completionHandler(NSURLSessionResponseAllow); } - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data { id delegate; { std::lock_guard lock(_mutex); delegate = [_delegates objectForKey:task]; } [delegate URLRequest:task didReceiveData:data]; } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { id delegate; { std::lock_guard lock(_mutex); delegate = [_delegates objectForKey:task]; [_delegates removeObjectForKey:task]; } [delegate URLRequest:task didCompleteWithError:error]; } @end