mirror of
https://github.com/status-im/react-native.git
synced 2025-01-10 01:25:39 +00:00
235749ba19
Summary: Under rare and as-yet-to-be determined circumstances, images can sometimes fail to load/download and get "stuck", without producing an error. Because the `RCTNetworkTask` for these images is stuck in the "in progress" state, they clog up the RCTImageLoader task queue, which has a limit of 4 concurrent in-progress tasks. This was previously masked by the fact that we automatically cancelled image requests when the RCTImageView moved offscreen, but we no longer do that. This diff adds logic to detect some types of stuck task and remove them, thereby unblocking the queue. I've also restored the functionality of cancelling downloads for offscreen images (but not unloading the image itself) so that stuck images will be cancelled when you move to another screen, instead of using up space in the queue forever. Reviewed By: fkgozali Differential Revision: D3398105 fbshipit-source-id: 75ee40d06a872ae8e1cb57f02f9cad57c459143c
149 lines
3.7 KiB
Objective-C
149 lines
3.7 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 "RCTNetworkTask.h"
|
|
|
|
#import "RCTLog.h"
|
|
|
|
@implementation RCTNetworkTask
|
|
{
|
|
NSMutableData *_data;
|
|
id<RCTURLRequestHandler> _handler;
|
|
RCTNetworkTask *_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;
|
|
_status = RCTNetworkTaskPending;
|
|
}
|
|
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;
|
|
_status = RCTNetworkTaskInProgress;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)cancel
|
|
{
|
|
_status = RCTNetworkTaskFinished;
|
|
__strong id strongToken = _requestToken;
|
|
if (strongToken && [_handler respondsToSelector:@selector(cancelRequest:)]) {
|
|
[_handler cancelRequest:strongToken];
|
|
}
|
|
[self invalidate];
|
|
}
|
|
|
|
- (BOOL)validateRequestToken:(id)requestToken
|
|
{
|
|
BOOL valid = YES;
|
|
if (_requestToken == nil) {
|
|
if (requestToken == nil) {
|
|
if (RCT_DEBUG) {
|
|
RCTLogError(@"Missing request token for request: %@", _request);
|
|
}
|
|
valid = NO;
|
|
}
|
|
_requestToken = requestToken;
|
|
} else if (![requestToken isEqual:_requestToken]) {
|
|
if (RCT_DEBUG) {
|
|
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
|
|
}
|
|
valid = NO;
|
|
}
|
|
if (!valid) {
|
|
_status = RCTNetworkTaskFinished;
|
|
if (_completionBlock) {
|
|
_completionBlock(_response, nil, [NSError errorWithDomain:RCTErrorDomain code:0
|
|
userInfo:@{NSLocalizedDescriptionKey: @"Invalid request token."}]);
|
|
}
|
|
[self invalidate];
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
- (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]) {
|
|
_status = RCTNetworkTaskFinished;
|
|
if (_completionBlock) {
|
|
_completionBlock(_response, _data, error);
|
|
}
|
|
[self invalidate];
|
|
}
|
|
}
|
|
|
|
@end
|