Perform all callbacks from RCTNetworkTasks on a given queue
Reviewed By: mmmulani Differential Revision: D3690022 fbshipit-source-id: 55c0121f7a99cc2186e68d0bb3ebfe5355af98fc
This commit is contained in:
parent
e4ac66b0ee
commit
66bea7d1e5
|
@ -30,8 +30,8 @@ typedef NS_ENUM(NSInteger, RCTNetworkTaskStatus) {
|
|||
@property (nonatomic, readonly) NSNumber *requestID;
|
||||
@property (nonatomic, readonly, weak) id requestToken;
|
||||
@property (nonatomic, readonly) NSURLResponse *response;
|
||||
@property (nonatomic, readonly) RCTURLRequestCompletionBlock completionBlock;
|
||||
|
||||
@property (nonatomic, copy) RCTURLRequestCompletionBlock completionBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestProgressBlock downloadProgressBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestIncrementalDataBlock incrementalDataBlock;
|
||||
@property (nonatomic, copy) RCTURLRequestResponseBlock responseBlock;
|
||||
|
@ -41,7 +41,7 @@ typedef NS_ENUM(NSInteger, RCTNetworkTaskStatus) {
|
|||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
handler:(id<RCTURLRequestHandler>)handler
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock NS_DESIGNATED_INITIALIZER;
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)start;
|
||||
- (void)cancel;
|
||||
|
|
|
@ -10,21 +10,24 @@
|
|||
#import "RCTNetworkTask.h"
|
||||
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTNetworkTask
|
||||
{
|
||||
NSMutableData *_data;
|
||||
id<RCTURLRequestHandler> _handler;
|
||||
dispatch_queue_t _callbackQueue;
|
||||
|
||||
RCTNetworkTask *_selfReference;
|
||||
}
|
||||
|
||||
- (instancetype)initWithRequest:(NSURLRequest *)request
|
||||
handler:(id<RCTURLRequestHandler>)handler
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
callbackQueue:(dispatch_queue_t)callbackQueue
|
||||
{
|
||||
RCTAssertParam(request);
|
||||
RCTAssertParam(handler);
|
||||
RCTAssertParam(completionBlock);
|
||||
RCTAssertParam(callbackQueue);
|
||||
|
||||
static NSUInteger requestID = 0;
|
||||
|
||||
|
@ -32,7 +35,7 @@
|
|||
_requestID = @(requestID++);
|
||||
_request = request;
|
||||
_handler = handler;
|
||||
_completionBlock = completionBlock;
|
||||
_callbackQueue = callbackQueue;
|
||||
_status = RCTNetworkTaskPending;
|
||||
}
|
||||
return self;
|
||||
|
@ -53,8 +56,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
- (void)start
|
||||
{
|
||||
if (_requestToken == nil) {
|
||||
if ([self validateRequestToken:[_handler sendRequest:_request
|
||||
withDelegate:self]]) {
|
||||
id token = [_handler sendRequest:_request withDelegate:self];
|
||||
if ([self validateRequestToken:token]) {
|
||||
_selfReference = self;
|
||||
_status = RCTNetworkTaskInProgress;
|
||||
}
|
||||
|
@ -88,11 +91,14 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
valid = NO;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
_status = RCTNetworkTaskFinished;
|
||||
if (_completionBlock) {
|
||||
_completionBlock(_response, nil, [NSError errorWithDomain:RCTErrorDomain code:0
|
||||
userInfo:@{NSLocalizedDescriptionKey: @"Invalid request token."}]);
|
||||
RCTURLRequestCompletionBlock completionBlock = _completionBlock;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
completionBlock(self->_response, nil, RCTErrorWithMessage(@"Invalid request token."));
|
||||
});
|
||||
}
|
||||
[self invalidate];
|
||||
}
|
||||
|
@ -101,48 +107,76 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
|
||||
- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent
|
||||
{
|
||||
if ([self validateRequestToken:requestToken]) {
|
||||
if (_uploadProgressBlock) {
|
||||
_uploadProgressBlock(bytesSent, _request.HTTPBody.length);
|
||||
}
|
||||
if (![self validateRequestToken:requestToken]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_uploadProgressBlock) {
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = _uploadProgressBlock;
|
||||
int64_t length = _request.HTTPBody.length;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
uploadProgressBlock(bytesSent, length);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
|
||||
{
|
||||
if ([self validateRequestToken:requestToken]) {
|
||||
_response = response;
|
||||
if (_responseBlock) {
|
||||
_responseBlock(response);
|
||||
}
|
||||
if (![self validateRequestToken:requestToken]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_response = response;
|
||||
if (_responseBlock) {
|
||||
RCTURLRequestResponseBlock responseBlock = _responseBlock;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
responseBlock(response);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data
|
||||
{
|
||||
if ([self validateRequestToken:requestToken]) {
|
||||
if (!_data) {
|
||||
_data = [NSMutableData new];
|
||||
}
|
||||
[_data appendData:data];
|
||||
if (_incrementalDataBlock) {
|
||||
_incrementalDataBlock(data, _data.length, _response.expectedContentLength);
|
||||
}
|
||||
if (_downloadProgressBlock && _response.expectedContentLength > 0) {
|
||||
_downloadProgressBlock(_data.length, _response.expectedContentLength);
|
||||
}
|
||||
if (![self validateRequestToken:requestToken]) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_data) {
|
||||
_data = [NSMutableData new];
|
||||
}
|
||||
[_data appendData:data];
|
||||
|
||||
int64_t length = _data.length;
|
||||
int64_t total = _response.expectedContentLength;
|
||||
|
||||
if (_incrementalDataBlock) {
|
||||
RCTURLRequestIncrementalDataBlock incrementalDataBlock = _incrementalDataBlock;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
incrementalDataBlock(data, length, total);
|
||||
});
|
||||
}
|
||||
if (_downloadProgressBlock && total > 0) {
|
||||
RCTURLRequestProgressBlock downloadProgressBlock = _downloadProgressBlock;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
downloadProgressBlock(length, total);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
|
||||
{
|
||||
if ([self validateRequestToken:requestToken]) {
|
||||
_status = RCTNetworkTaskFinished;
|
||||
if (_completionBlock) {
|
||||
_completionBlock(_response, _data, error);
|
||||
}
|
||||
[self invalidate];
|
||||
if (![self validateRequestToken:requestToken]) {
|
||||
return;
|
||||
}
|
||||
|
||||
_status = RCTNetworkTaskFinished;
|
||||
if (_completionBlock) {
|
||||
RCTURLRequestCompletionBlock completionBlock = _completionBlock;
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
completionBlock(self->_response, self->_data, error);
|
||||
});
|
||||
}
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -382,30 +382,25 @@ RCT_EXPORT_MODULE()
|
|||
RCTAssertThread(_methodQueue, @"sendRequest: must be called on method queue");
|
||||
|
||||
__block RCTNetworkTask *task;
|
||||
|
||||
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
|
||||
dispatch_async(self->_methodQueue, ^{
|
||||
NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)];
|
||||
[self sendEventWithName:@"didSendNetworkData" body:responseJSON];
|
||||
});
|
||||
NSArray *responseJSON = @[task.requestID, @((double)progress), @((double)total)];
|
||||
[self sendEventWithName:@"didSendNetworkData" body:responseJSON];
|
||||
};
|
||||
|
||||
RCTURLRequestResponseBlock responseBlock = ^(NSURLResponse *response) {
|
||||
dispatch_async(self->_methodQueue, ^{
|
||||
NSDictionary<NSString *, NSString *> *headers;
|
||||
NSInteger status;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
headers = httpResponse.allHeaderFields ?: @{};
|
||||
status = httpResponse.statusCode;
|
||||
} else {
|
||||
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
|
||||
status = 200;
|
||||
}
|
||||
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
|
||||
NSArray<id> *responseJSON = @[task.requestID, @(status), headers, responseURL];
|
||||
[self sendEventWithName:@"didReceiveNetworkResponse" body:responseJSON];
|
||||
});
|
||||
NSDictionary<NSString *, NSString *> *headers;
|
||||
NSInteger status;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
||||
headers = httpResponse.allHeaderFields ?: @{};
|
||||
status = httpResponse.statusCode;
|
||||
} else {
|
||||
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
|
||||
status = 200;
|
||||
}
|
||||
id responseURL = response.URL ? response.URL.absoluteString : [NSNull null];
|
||||
NSArray<id> *responseJSON = @[task.requestID, @(status), headers, responseURL];
|
||||
[self sendEventWithName:@"didReceiveNetworkResponse" body:responseJSON];
|
||||
};
|
||||
|
||||
// XHR does not allow you to peek at xhr.response before the response is
|
||||
|
@ -417,44 +412,38 @@ RCT_EXPORT_MODULE()
|
|||
if (incrementalUpdates) {
|
||||
if ([responseType isEqualToString:@"text"]) {
|
||||
incrementalDataBlock = ^(NSData *data, int64_t progress, int64_t total) {
|
||||
dispatch_async(self->_methodQueue, ^{
|
||||
NSString *responseString = [RCTNetworking decodeTextData:data fromResponse:task.response];
|
||||
if (!responseString) {
|
||||
RCTLogWarn(@"Received data was not a string, or was not a recognised encoding.");
|
||||
return;
|
||||
}
|
||||
NSArray<id> *responseJSON = @[task.requestID, responseString, @(progress), @(total)];
|
||||
[self sendEventWithName:@"didReceiveNetworkIncrementalData" body:responseJSON];
|
||||
});
|
||||
NSString *responseString = [RCTNetworking decodeTextData:data fromResponse:task.response];
|
||||
if (!responseString) {
|
||||
RCTLogWarn(@"Received data was not a string, or was not a recognised encoding.");
|
||||
return;
|
||||
}
|
||||
NSArray<id> *responseJSON = @[task.requestID, responseString, @(progress), @(total)];
|
||||
[self sendEventWithName:@"didReceiveNetworkIncrementalData" body:responseJSON];
|
||||
};
|
||||
} else {
|
||||
downloadProgressBlock = ^(int64_t progress, int64_t total) {
|
||||
dispatch_async(self->_methodQueue, ^{
|
||||
NSArray<id> *responseJSON = @[task.requestID, @(progress), @(total)];
|
||||
[self sendEventWithName:@"didReceiveNetworkDataProgress" body:responseJSON];
|
||||
});
|
||||
NSArray<id> *responseJSON = @[task.requestID, @(progress), @(total)];
|
||||
[self sendEventWithName:@"didReceiveNetworkDataProgress" body:responseJSON];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RCTURLRequestCompletionBlock completionBlock =
|
||||
^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
dispatch_async(self->_methodQueue, ^{
|
||||
// Unless we were sending incremental (text) chunks to JS, all along, now
|
||||
// is the time to send the request body to JS.
|
||||
if (!(incrementalUpdates && [responseType isEqualToString:@"text"])) {
|
||||
[self sendData:data
|
||||
responseType:responseType
|
||||
forTask:task];
|
||||
}
|
||||
NSArray *responseJSON = @[task.requestID,
|
||||
RCTNullIfNil(error.localizedDescription),
|
||||
error.code == kCFURLErrorTimedOut ? @YES : @NO
|
||||
];
|
||||
// Unless we were sending incremental (text) chunks to JS, all along, now
|
||||
// is the time to send the request body to JS.
|
||||
if (!(incrementalUpdates && [responseType isEqualToString:@"text"])) {
|
||||
[self sendData:data
|
||||
responseType:responseType
|
||||
forTask:task];
|
||||
}
|
||||
NSArray *responseJSON = @[task.requestID,
|
||||
RCTNullIfNil(error.localizedDescription),
|
||||
error.code == kCFURLErrorTimedOut ? @YES : @NO
|
||||
];
|
||||
|
||||
[self sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON];
|
||||
[self->_tasksByRequestID removeObjectForKey:task.requestID];
|
||||
});
|
||||
[self sendEventWithName:@"didCompleteNetworkResponse" body:responseJSON];
|
||||
[self->_tasksByRequestID removeObjectForKey:task.requestID];
|
||||
};
|
||||
|
||||
task = [self networkTaskWithRequest:request completionBlock:completionBlock];
|
||||
|
@ -476,8 +465,7 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
#pragma mark - Public API
|
||||
|
||||
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
|
||||
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request completionBlock:(RCTURLRequestCompletionBlock)completionBlock
|
||||
{
|
||||
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
|
||||
if (!handler) {
|
||||
|
@ -485,9 +473,11 @@ RCT_EXPORT_MODULE()
|
|||
return nil;
|
||||
}
|
||||
|
||||
return [[RCTNetworkTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
completionBlock:completionBlock];
|
||||
RCTNetworkTask *task = [[RCTNetworkTask alloc] initWithRequest:request
|
||||
handler:handler
|
||||
callbackQueue:_methodQueue];
|
||||
task.completionBlock = completionBlock;
|
||||
return task;
|
||||
}
|
||||
|
||||
#pragma mark - JS API
|
||||
|
@ -499,7 +489,6 @@ RCT_EXPORT_METHOD(sendRequest:(NSDictionary *)query
|
|||
// no way to invoke it, if, for example the request is cancelled while
|
||||
// loading a large file to build the request body
|
||||
[self buildRequest:query completionBlock:^(NSURLRequest *request) {
|
||||
|
||||
NSString *responseType = [RCTConvert NSString:query[@"responseType"]];
|
||||
BOOL incrementalUpdates = [RCTConvert BOOL:query[@"incrementalUpdates"]];
|
||||
[self sendRequest:request
|
||||
|
|
Loading…
Reference in New Issue