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