Skip to content
This repository has been archived by the owner on Mar 16, 2019. It is now read-only.

Commit

Permalink
Add IOS background HTTP expiration handling #115
Browse files Browse the repository at this point in the history
  • Loading branch information
wkh237 committed Sep 17, 2016
1 parent 301c8cf commit e1cb0e2
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 30 deletions.
19 changes: 18 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const RNFetchBlob:RNFetchBlobNative = NativeModules.RNFetchBlob

// register message channel event handler.
emitter.addListener("RNFetchBlobMessage", (e) => {

if(e.event === 'warn') {
console.warn(e.detail)
}
Expand Down Expand Up @@ -140,7 +141,11 @@ function fetchFile(options = {}, method, url, headers = {}, body):Promise {
promise = fs.stat(url)
.then((stat) => {
total = stat.size
return fs.readStream(url, headers.encoding || 'utf8', Math.floor(headers.bufferSize) || 4096)
return fs.readStream(url,
headers.encoding || 'utf8',
Math.floor(headers.bufferSize) || 409600,
Math.floor(headers.interval) || 100
)
})
.then((stream) => new Promise((resolve, reject) => {
stream.open()
Expand Down Expand Up @@ -232,6 +237,13 @@ function fetch(...args:any):Promise {
}
})

subscription = emitter.addListener('RNFetchBlobExpire', (e) => {
console.log(e , 'EXPIRED!!')
if(e.taskId === taskId && promise.onExpire) {
promise.onExpire(e)
}
})

// When the request body comes from Blob polyfill, we should use special its ref
// as the request body
if( body instanceof Blob && body.isRNFetchBlobPolyfill) {
Expand Down Expand Up @@ -261,6 +273,7 @@ function fetch(...args:any):Promise {
delete promise['uploadProgress']
delete promise['stateChange']
delete promise['cancel']
// delete promise['expire']
promise.cancel = () => {}

if(err)
Expand Down Expand Up @@ -296,6 +309,10 @@ function fetch(...args:any):Promise {
promise.onStateChange = fn
return promise
}
promise.expire = (fn) => {
promise.onExpire = fn
return promise
}
promise.cancel = (fn) => {
fn = fn || function(){}
subscription.remove()
Expand Down
1 change: 1 addition & 0 deletions src/ios/RNFetchBlob/RNFetchBlob.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
}

@property (nonatomic) NSString * filePathPrefix;
@property (nonatomic) UIDocumentInteractionController * documentController;

+ (RCTBridge *)getRCTBridge;

Expand Down
7 changes: 5 additions & 2 deletions src/ios/RNFetchBlob/RNFetchBlob.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#import "RNFetchBlob.h"
#import "RCTLog.h"
#import "RCTRootView.h"
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
#import "RNFetchBlobFS.h"
Expand All @@ -14,7 +15,7 @@
#import "RNFetchBlobReqBuilder.h"


RCTBridge * bridgeRef;
__strong RCTBridge * bridgeRef;
dispatch_queue_t commonTaskQueue;
dispatch_queue_t fsQueue;

Expand All @@ -40,7 +41,8 @@ - (dispatch_queue_t) methodQueue {

+ (RCTBridge *)getRCTBridge
{
return bridgeRef;
RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view;
return rootView.bridge;
}

RCT_EXPORT_MODULE();
Expand All @@ -58,6 +60,7 @@ - (id) init {
[[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
}
bridgeRef = _bridge;
[RNFetchBlobNetwork getExpiredTasks];
return self;
}

Expand Down
1 change: 1 addition & 0 deletions src/ios/RNFetchBlobConst.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ extern NSString *const MSG_EVENT_LOG;
extern NSString *const MSG_EVENT_WARN;
extern NSString *const MSG_EVENT_ERROR;

extern NSString *const EVENT_EXPIRE;
extern NSString *const EVENT_PROGRESS;
extern NSString *const EVENT_PROGRESS_UPLOAD;
extern NSString *const EVENT_STATE_CHANGE;
Expand Down
3 changes: 1 addition & 2 deletions src/ios/RNFetchBlobConst.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
extern NSString *const ASSET_PREFIX = @"bundle-assets://";
extern NSString *const AL_PREFIX = @"assets-library://";



// fetch configs
extern NSString *const CONFIG_USE_TEMP = @"fileCache";
extern NSString *const CONFIG_FILE_PATH = @"path";
Expand All @@ -25,6 +23,7 @@
extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";

extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
extern NSString *const MSG_EVENT_LOG = @"log";
Expand Down
8 changes: 5 additions & 3 deletions src/ios/RNFetchBlobNetwork.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,18 @@ typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse
@property (nullable, nonatomic) NSError * error;


- (nullable id) init;
- (void) sendRequest;

+ (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
+ (void) cancelRequest:(NSString *)taskId;
+ (void) enableProgressReport:(NSString *) taskId;
+ (void) enableUploadProgress:(NSString *) taskId;
+ (void) getExpiredTasks;

- (nullable id) init;
- (void) sendRequest;
- (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;



@end


Expand Down
51 changes: 47 additions & 4 deletions src/ios/RNFetchBlobNetwork.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
#import "RCTLog.h"
#import <Foundation/Foundation.h>
#import "RCTBridge.h"
#import "RNFetchBlob.h"
#import "RCTEventDispatcher.h"
#import "RNFetchBlobFS.h"
#import "RCTRootView.h"
#import "RNFetchBlobNetwork.h"
#import "RNFetchBlobConst.h"
#import "RNFetchBlobReqBuilder.h"
Expand All @@ -24,6 +26,7 @@
////////////////////////////////////////

NSMapTable * taskTable;
NSMapTable * expirationTable;
NSMutableDictionary * progressTable;
NSMutableDictionary * uploadProgressTable;

Expand All @@ -37,6 +40,7 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) {
@interface RNFetchBlobNetwork ()
{
BOOL * respFile;
BOOL * isIncrement;
NSString * destPath;
NSOutputStream * writeStream;
long bodyLength;
Expand Down Expand Up @@ -70,7 +74,12 @@ - (id)init {
taskQueue = [[NSOperationQueue alloc] init];
taskQueue.maxConcurrentOperationCount = 10;
}
if(taskTable == nil) {
if(expirationTable == nil)
{
expirationTable = [[NSMapTable alloc] init];
}
if(taskTable == nil)
{
taskTable = [[NSMapTable alloc] init];
}
if(progressTable == nil)
Expand Down Expand Up @@ -133,6 +142,7 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
self.expectedBytes = 0;
self.receivedBytes = 0;
self.options = options;
isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
redirects = [[NSMutableArray alloc] init];
[redirects addObject:req.URL.absoluteString];

Expand All @@ -153,7 +163,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
bodyLength = contentLength;

// the session trust any SSL certification
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
// NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];

// set request timeout
float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
if(timeout > 0)
Expand Down Expand Up @@ -190,13 +202,39 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options
respData = [[NSMutableData alloc] init];
respFile = NO;
}
NSURLSessionDataTask * task = [session dataTaskWithRequest:req];

__block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
[taskTable setObject:task forKey:taskId];
[task resume];

// network status indicator
if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
__block UIApplication * app = [UIApplication sharedApplication];

// #115 handling task expired when application entering backgound for a long time
[app beginBackgroundTaskWithName:taskId expirationHandler:^{
NSLog([NSString stringWithFormat:@"session %@ expired event emit", taskId ]);
[expirationTable setObject:task forKey:taskId];
[app endBackgroundTask:task];

}];


}

// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
+ (void) getExpiredTasks
{
NSEnumerator * emu = [expirationTable keyEnumerator];
NSString * key;

while((key = [emu nextObject]))
{
RCTBridge * bridge = [RNFetchBlob getRCTBridge];
NSData * args = @{ @"taskId": key };
[bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
}
}

////////////////////////////////////////
Expand Down Expand Up @@ -306,7 +344,7 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat
receivedBytes += [received longValue];
NSString * chunkString = @"";

if(isInrement == YES)
if(isIncrement == YES)
{
chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
Expand Down Expand Up @@ -480,6 +518,11 @@ - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthentica
}
}

- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
NSLog(@"sess done in background");
}

- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
{
[redirects addObject:[request.URL absoluteString]];
Expand Down
39 changes: 21 additions & 18 deletions test/test-init.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ const { Assert, Comparer, Info, prop } = RNTest
// test environment variables

prop('FILENAME', `${Platform.OS}-0.8.0-${Date.now()}.png`)
prop('TEST_SERVER_URL', 'http://192.168.16.70:8123')
prop('TEST_SERVER_URL_SSL', 'https://192.168.16.70:8124')
prop('TEST_SERVER_URL', 'http://localhost:8123')
prop('TEST_SERVER_URL_SSL', 'https://localhost:8124')
prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
prop('styles', {
image : {
Expand Down Expand Up @@ -59,21 +59,24 @@ describe('GET image from server', (report, done) => {
})


require('./test-0.1.x-0.4.x')
require('./test-0.5.1')
require('./test-0.5.2')
require('./test-0.6.0')
require('./test-0.6.2')
require('./test-0.7.0')
require('./test-0.8.0')
require('./test-0.9.0')
require('./test-0.9.2')
require('./test-0.9.4')
require('./test-fetch')
require('./test-fs')
require('./test-xmlhttp')
require('./test-blob')
require('./test-firebase')
require('./test-android')
// require('./test-0.1.x-0.4.x')
// require('./test-0.5.1')
// require('./test-0.5.2')
// require('./test-0.6.0')
// require('./test-0.6.2')
// require('./test-0.7.0')
// require('./test-0.8.0')
// require('./test-0.9.0')
// require('./test-0.9.2')
// require('./test-0.9.4')
// require('./test-0.9.5')
// require('./test-0.10.0')
// require('./test-fetch')
// require('./test-fs')
// require('./test-xmlhttp')
// require('./test-blob')
// require('./test-firebase')
require('./test-background')
// require('./test-android')
// require('./test-stress')
// require('./benchmark')

0 comments on commit e1cb0e2

Please sign in to comment.