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