1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * 4 * This source code is licensed under the MIT license found in the 5 * LICENSE file in the root directory of this source tree. 6 */ 7 8#import <mutex> 9 10#import <ABI47_0_0React/ABI47_0_0RCTLog.h> 11#import <ABI47_0_0React/ABI47_0_0RCTNetworkTask.h> 12#import <ABI47_0_0React/ABI47_0_0RCTUtils.h> 13 14@implementation ABI47_0_0RCTNetworkTask 15{ 16 NSMutableData *_data; 17 id<ABI47_0_0RCTURLRequestHandler> _handler; 18 dispatch_queue_t _callbackQueue; 19 std::mutex _mutex; 20 21 ABI47_0_0RCTNetworkTask *_selfReference; 22} 23 24- (instancetype)initWithRequest:(NSURLRequest *)request 25 handler:(id<ABI47_0_0RCTURLRequestHandler>)handler 26 callbackQueue:(dispatch_queue_t)callbackQueue 27{ 28 ABI47_0_0RCTAssertParam(request); 29 ABI47_0_0RCTAssertParam(handler); 30 ABI47_0_0RCTAssertParam(callbackQueue); 31 32 static NSUInteger requestID = 0; 33 34 if ((self = [super init])) { 35 _requestID = @(requestID++); 36 _request = request; 37 _handler = handler; 38 _callbackQueue = callbackQueue; 39 _status = ABI47_0_0RCTNetworkTaskPending; 40 41 dispatch_queue_set_specific(callbackQueue, (__bridge void *)self, (__bridge void *)self, NULL); 42 } 43 return self; 44} 45 46ABI47_0_0RCT_NOT_IMPLEMENTED(- (instancetype)init) 47 48- (void)invalidate 49{ 50 _selfReference = nil; 51 _completionBlock = nil; 52 _downloadProgressBlock = nil; 53 _incrementalDataBlock = nil; 54 _responseBlock = nil; 55 _uploadProgressBlock = nil; 56 _requestToken = nil; 57} 58 59- (void)dispatchCallback:(dispatch_block_t)callback 60{ 61 if (dispatch_get_specific((__bridge void *)self) == (__bridge void *)self) { 62 callback(); 63 } else { 64 dispatch_async(_callbackQueue, callback); 65 } 66} 67 68- (void)start 69{ 70 if (_status != ABI47_0_0RCTNetworkTaskPending) { 71 ABI47_0_0RCTLogError(@"ABI47_0_0RCTNetworkTask was already started or completed"); 72 return; 73 } 74 75 if (_requestToken == nil) { 76 id token = [_handler sendRequest:_request withDelegate:self]; 77 if ([self validateRequestToken:token]) { 78 _selfReference = self; 79 _status = ABI47_0_0RCTNetworkTaskInProgress; 80 } 81 } 82} 83 84- (void)cancel 85{ 86 if (_status == ABI47_0_0RCTNetworkTaskFinished) { 87 return; 88 } 89 90 _status = ABI47_0_0RCTNetworkTaskFinished; 91 id token = _requestToken; 92 if (token && [_handler respondsToSelector:@selector(cancelRequest:)]) { 93 [_handler cancelRequest:token]; 94 } 95 [self invalidate]; 96} 97 98- (BOOL)validateRequestToken:(id)requestToken 99{ 100 BOOL valid = YES; 101 if (_requestToken == nil) { 102 _requestToken = requestToken; 103 } else if (![requestToken isEqual:_requestToken]) { 104 if (ABI47_0_0RCT_DEBUG) { 105 ABI47_0_0RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken); 106 } 107 valid = NO; 108 } 109 110 if (!valid) { 111 _status = ABI47_0_0RCTNetworkTaskFinished; 112 if (_completionBlock) { 113 ABI47_0_0RCTURLRequestCompletionBlock completionBlock = _completionBlock; 114 [self dispatchCallback:^{ 115 completionBlock(self->_response, nil, ABI47_0_0RCTErrorWithMessage(@"Invalid request token.")); 116 }]; 117 } 118 [self invalidate]; 119 } 120 return valid; 121} 122 123- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent 124{ 125 if (![self validateRequestToken:requestToken]) { 126 return; 127 } 128 129 if (_uploadProgressBlock) { 130 ABI47_0_0RCTURLRequestProgressBlock uploadProgressBlock = _uploadProgressBlock; 131 int64_t length = _request.HTTPBody.length; 132 [self dispatchCallback:^{ 133 uploadProgressBlock(bytesSent, length); 134 }]; 135 } 136} 137 138- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response 139{ 140 if (![self validateRequestToken:requestToken]) { 141 return; 142 } 143 144 _response = response; 145 if (_responseBlock) { 146 ABI47_0_0RCTURLRequestResponseBlock responseBlock = _responseBlock; 147 [self dispatchCallback:^{ 148 responseBlock(response); 149 }]; 150 } 151} 152 153- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data 154{ 155 if (![self validateRequestToken:requestToken]) { 156 return; 157 } 158 159 int64_t length = 0; 160 161 { 162 // NSData is not thread-safe and this method could be called from different threads as 163 // ABI47_0_0RCTURLRequestHandlers does not provide any guarantee of which thread we are called on. 164 std::lock_guard<std::mutex> lock(_mutex); 165 if (!_data) { 166 _data = [NSMutableData new]; 167 } 168 [_data appendData:data]; 169 length = _data.length; 170 } 171 172 int64_t total = _response.expectedContentLength; 173 174 if (_incrementalDataBlock) { 175 ABI47_0_0RCTURLRequestIncrementalDataBlock incrementalDataBlock = _incrementalDataBlock; 176 [self dispatchCallback:^{ 177 incrementalDataBlock(data, length, total); 178 }]; 179 } 180 if (_downloadProgressBlock && total > 0) { 181 ABI47_0_0RCTURLRequestProgressBlock downloadProgressBlock = _downloadProgressBlock; 182 [self dispatchCallback:^{ 183 downloadProgressBlock(length, total); 184 }]; 185 } 186} 187 188- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error 189{ 190 if (![self validateRequestToken:requestToken]) { 191 return; 192 } 193 194 _status = ABI47_0_0RCTNetworkTaskFinished; 195 if (_completionBlock) { 196 ABI47_0_0RCTURLRequestCompletionBlock completionBlock = _completionBlock; 197 NSData *dataCopy = nil; 198 { 199 std::lock_guard<std::mutex> lock(self->_mutex); 200 dataCopy = _data; 201 _data = nil; 202 } 203 [self dispatchCallback:^{ 204 completionBlock(self->_response, dataCopy, error); 205 }]; 206 } 207 [self invalidate]; 208} 209 210@end 211