Files
react-native/Libraries/Network/RCTDownloadTask.m
Nick Lockwood 16a48ae0c3 Fixed "Unrecognized request token" red box
Summary: public

There was a race condition issue in RCTDownLoadTask whereby the request handler would sometimes call one of the delegate methods before setup was complete, causing an error to be logged because the request token had not been set, and causing te request to fail because the class was not yet set up.

This diff fixes that issue by adding an explicit `start` method to RCTDownloadTask, and changing the setup order to allow for the request to call back immediately without this being treated as an error.

Reviewed By: tadeuzagallo

Differential Revision: D2553628

fb-gh-sync-id: 5ca4e791574a632ccbf2e873e28ac88bffdf851d
2015-10-17 09:34:23 -07:00

138 lines
3.3 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDownloadTask.h"
#import "RCTLog.h"
@implementation RCTDownloadTask
{
NSMutableData *_data;
id<RCTURLRequestHandler> _handler;
RCTDownloadTask *_selfReference;
}
- (instancetype)initWithRequest:(NSURLRequest *)request
handler:(id<RCTURLRequestHandler>)handler
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
{
RCTAssertParam(request);
RCTAssertParam(handler);
RCTAssertParam(completionBlock);
static NSUInteger requestID = 0;
if ((self = [super init])) {
_requestID = @(requestID++);
_request = request;
_handler = handler;
_completionBlock = completionBlock;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)invalidate
{
_selfReference = nil;
_completionBlock = nil;
_downloadProgressBlock = nil;
_incrementalDataBlock = nil;
_responseBlock = nil;
_uploadProgressBlock = nil;
}
- (void)start
{
if (_requestToken == nil) {
if ([self validateRequestToken:[_handler sendRequest:_request
withDelegate:self]]) {
_selfReference = self;
}
}
}
- (void)cancel
{
if ([_handler respondsToSelector:@selector(cancelRequest:)]) {
[_handler cancelRequest:_requestToken];
}
[self invalidate];
}
- (BOOL)validateRequestToken:(id)requestToken
{
if (_requestToken == nil) {
if (requestToken == nil) {
return NO;
}
_requestToken = requestToken;
}
if (![requestToken isEqual:_requestToken]) {
if (RCT_DEBUG) {
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
}
if (_completionBlock) {
_completionBlock(_response, _data, [NSError errorWithDomain:RCTErrorDomain code:0
userInfo:@{NSLocalizedDescriptionKey: @"Unrecognized request token."}]);
[self invalidate];
}
return NO;
}
return YES;
}
- (void)URLRequest:(id)requestToken didSendDataWithProgress:(int64_t)bytesSent
{
if ([self validateRequestToken:requestToken]) {
if (_uploadProgressBlock) {
_uploadProgressBlock(bytesSent, _request.HTTPBody.length);
}
}
}
- (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response
{
if ([self validateRequestToken:requestToken]) {
_response = response;
if (_responseBlock) {
_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);
}
if (_downloadProgressBlock && _response.expectedContentLength > 0) {
_downloadProgressBlock(_data.length, _response.expectedContentLength);
}
}
}
- (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error
{
if ([self validateRequestToken:requestToken]) {
if (_completionBlock) {
_completionBlock(_response, _data, error);
[self invalidate];
}
}
}
@end