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