Explorar el Código

Add IOS background HTTP expiration handling #115

Ben Hsieh hace 7 años
padre
commit
e1cb0e22f3

+ 18
- 1
src/index.js Ver fichero

48
 
48
 
49
 // register message channel event handler.
49
 // register message channel event handler.
50
 emitter.addListener("RNFetchBlobMessage", (e) => {
50
 emitter.addListener("RNFetchBlobMessage", (e) => {
51
+
51
   if(e.event === 'warn') {
52
   if(e.event === 'warn') {
52
     console.warn(e.detail)
53
     console.warn(e.detail)
53
   }
54
   }
140
       promise = fs.stat(url)
141
       promise = fs.stat(url)
141
       .then((stat) => {
142
       .then((stat) => {
142
         total = stat.size
143
         total = stat.size
143
-        return fs.readStream(url, headers.encoding || 'utf8', Math.floor(headers.bufferSize) || 4096)
144
+        return fs.readStream(url,
145
+          headers.encoding || 'utf8',
146
+          Math.floor(headers.bufferSize) || 409600,
147
+          Math.floor(headers.interval) || 100
148
+        )
144
       })
149
       })
145
       .then((stream) => new Promise((resolve, reject) => {
150
       .then((stream) => new Promise((resolve, reject) => {
146
         stream.open()
151
         stream.open()
232
       }
237
       }
233
     })
238
     })
234
 
239
 
240
+    subscription = emitter.addListener('RNFetchBlobExpire', (e) => {
241
+      console.log(e , 'EXPIRED!!')
242
+      if(e.taskId === taskId && promise.onExpire) {
243
+        promise.onExpire(e)
244
+      }
245
+    })
246
+
235
     // When the request body comes from Blob polyfill, we should use special its ref
247
     // When the request body comes from Blob polyfill, we should use special its ref
236
     // as the request body
248
     // as the request body
237
     if( body instanceof Blob && body.isRNFetchBlobPolyfill) {
249
     if( body instanceof Blob && body.isRNFetchBlobPolyfill) {
261
       delete promise['uploadProgress']
273
       delete promise['uploadProgress']
262
       delete promise['stateChange']
274
       delete promise['stateChange']
263
       delete promise['cancel']
275
       delete promise['cancel']
276
+      // delete promise['expire']
264
       promise.cancel = () => {}
277
       promise.cancel = () => {}
265
 
278
 
266
       if(err)
279
       if(err)
296
     promise.onStateChange = fn
309
     promise.onStateChange = fn
297
     return promise
310
     return promise
298
   }
311
   }
312
+  promise.expire = (fn) => {
313
+    promise.onExpire = fn
314
+    return promise
315
+  }
299
   promise.cancel = (fn) => {
316
   promise.cancel = (fn) => {
300
     fn = fn || function(){}
317
     fn = fn || function(){}
301
     subscription.remove()
318
     subscription.remove()

+ 1
- 0
src/ios/RNFetchBlob/RNFetchBlob.h Ver fichero

17
 }
17
 }
18
 
18
 
19
 @property (nonatomic) NSString * filePathPrefix;
19
 @property (nonatomic) NSString * filePathPrefix;
20
+@property (nonatomic) UIDocumentInteractionController * documentController;
20
 
21
 
21
 + (RCTBridge *)getRCTBridge;
22
 + (RCTBridge *)getRCTBridge;
22
 
23
 

+ 5
- 2
src/ios/RNFetchBlob/RNFetchBlob.m Ver fichero

6
 
6
 
7
 #import "RNFetchBlob.h"
7
 #import "RNFetchBlob.h"
8
 #import "RCTLog.h"
8
 #import "RCTLog.h"
9
+#import "RCTRootView.h"
9
 #import "RCTBridge.h"
10
 #import "RCTBridge.h"
10
 #import "RCTEventDispatcher.h"
11
 #import "RCTEventDispatcher.h"
11
 #import "RNFetchBlobFS.h"
12
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobReqBuilder.h"
15
 #import "RNFetchBlobReqBuilder.h"
15
 
16
 
16
 
17
 
17
-RCTBridge * bridgeRef;
18
+__strong RCTBridge * bridgeRef;
18
 dispatch_queue_t commonTaskQueue;
19
 dispatch_queue_t commonTaskQueue;
19
 dispatch_queue_t fsQueue;
20
 dispatch_queue_t fsQueue;
20
 
21
 
40
 
41
 
41
 + (RCTBridge *)getRCTBridge
42
 + (RCTBridge *)getRCTBridge
42
 {
43
 {
43
-    return bridgeRef;
44
+    RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view;
45
+    return rootView.bridge;
44
 }
46
 }
45
 
47
 
46
 RCT_EXPORT_MODULE();
48
 RCT_EXPORT_MODULE();
58
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
60
         [[NSFileManager defaultManager] createDirectoryAtPath:[RNFetchBlobFS getTempPath] withIntermediateDirectories:YES attributes:nil error:NULL];
59
     }
61
     }
60
     bridgeRef = _bridge;
62
     bridgeRef = _bridge;
63
+    [RNFetchBlobNetwork getExpiredTasks];
61
     return self;
64
     return self;
62
 }
65
 }
63
 
66
 

+ 1
- 0
src/ios/RNFetchBlobConst.h Ver fichero

17
 extern NSString *const MSG_EVENT_WARN;
17
 extern NSString *const MSG_EVENT_WARN;
18
 extern NSString *const MSG_EVENT_ERROR;
18
 extern NSString *const MSG_EVENT_ERROR;
19
 
19
 
20
+extern NSString *const EVENT_EXPIRE;
20
 extern NSString *const EVENT_PROGRESS;
21
 extern NSString *const EVENT_PROGRESS;
21
 extern NSString *const EVENT_PROGRESS_UPLOAD;
22
 extern NSString *const EVENT_PROGRESS_UPLOAD;
22
 extern NSString *const EVENT_STATE_CHANGE;
23
 extern NSString *const EVENT_STATE_CHANGE;

+ 1
- 2
src/ios/RNFetchBlobConst.m Ver fichero

11
 extern NSString *const ASSET_PREFIX = @"bundle-assets://";
11
 extern NSString *const ASSET_PREFIX = @"bundle-assets://";
12
 extern NSString *const AL_PREFIX = @"assets-library://";
12
 extern NSString *const AL_PREFIX = @"assets-library://";
13
 
13
 
14
-
15
-
16
 // fetch configs
14
 // fetch configs
17
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";
15
 extern NSString *const CONFIG_USE_TEMP = @"fileCache";
18
 extern NSString *const CONFIG_FILE_PATH = @"path";
16
 extern NSString *const CONFIG_FILE_PATH = @"path";
25
 extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
23
 extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
26
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
24
 extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
27
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
25
 extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
26
+extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
28
 
27
 
29
 extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
28
 extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
30
 extern NSString *const MSG_EVENT_LOG = @"log";
29
 extern NSString *const MSG_EVENT_LOG = @"log";

+ 5
- 3
src/ios/RNFetchBlobNetwork.h Ver fichero

31
 @property (nullable, nonatomic) NSError * error;
31
 @property (nullable, nonatomic) NSError * error;
32
 
32
 
33
 
33
 
34
-- (nullable id) init;
35
-- (void) sendRequest;
36
-
37
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
34
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
38
 + (void) cancelRequest:(NSString *)taskId;
35
 + (void) cancelRequest:(NSString *)taskId;
39
 + (void) enableProgressReport:(NSString *) taskId;
36
 + (void) enableProgressReport:(NSString *) taskId;
40
 + (void) enableUploadProgress:(NSString *) taskId;
37
 + (void) enableUploadProgress:(NSString *) taskId;
38
++ (void) getExpiredTasks;
39
+
40
+- (nullable id) init;
41
+- (void) sendRequest;
41
 - (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
42
 - (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
42
 
43
 
43
 
44
 
45
+
44
 @end
46
 @end
45
 
47
 
46
 
48
 

+ 47
- 4
src/ios/RNFetchBlobNetwork.m Ver fichero

9
 #import "RCTLog.h"
9
 #import "RCTLog.h"
10
 #import <Foundation/Foundation.h>
10
 #import <Foundation/Foundation.h>
11
 #import "RCTBridge.h"
11
 #import "RCTBridge.h"
12
+#import "RNFetchBlob.h"
12
 #import "RCTEventDispatcher.h"
13
 #import "RCTEventDispatcher.h"
13
 #import "RNFetchBlobFS.h"
14
 #import "RNFetchBlobFS.h"
15
+#import "RCTRootView.h"
14
 #import "RNFetchBlobNetwork.h"
16
 #import "RNFetchBlobNetwork.h"
15
 #import "RNFetchBlobConst.h"
17
 #import "RNFetchBlobConst.h"
16
 #import "RNFetchBlobReqBuilder.h"
18
 #import "RNFetchBlobReqBuilder.h"
24
 ////////////////////////////////////////
26
 ////////////////////////////////////////
25
 
27
 
26
 NSMapTable * taskTable;
28
 NSMapTable * taskTable;
29
+NSMapTable * expirationTable;
27
 NSMutableDictionary * progressTable;
30
 NSMutableDictionary * progressTable;
28
 NSMutableDictionary * uploadProgressTable;
31
 NSMutableDictionary * uploadProgressTable;
29
 
32
 
37
 @interface RNFetchBlobNetwork ()
40
 @interface RNFetchBlobNetwork ()
38
 {
41
 {
39
     BOOL * respFile;
42
     BOOL * respFile;
43
+    BOOL * isIncrement;
40
     NSString * destPath;
44
     NSString * destPath;
41
     NSOutputStream * writeStream;
45
     NSOutputStream * writeStream;
42
     long bodyLength;
46
     long bodyLength;
70
         taskQueue = [[NSOperationQueue alloc] init];
74
         taskQueue = [[NSOperationQueue alloc] init];
71
         taskQueue.maxConcurrentOperationCount = 10;
75
         taskQueue.maxConcurrentOperationCount = 10;
72
     }
76
     }
73
-    if(taskTable == nil) {
77
+    if(expirationTable == nil)
78
+    {
79
+        expirationTable = [[NSMapTable alloc] init];
80
+    }
81
+    if(taskTable == nil)
82
+    {
74
         taskTable = [[NSMapTable alloc] init];
83
         taskTable = [[NSMapTable alloc] init];
75
     }
84
     }
76
     if(progressTable == nil)
85
     if(progressTable == nil)
133
     self.expectedBytes = 0;
142
     self.expectedBytes = 0;
134
     self.receivedBytes = 0;
143
     self.receivedBytes = 0;
135
     self.options = options;
144
     self.options = options;
145
+    isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
136
     redirects = [[NSMutableArray alloc] init];
146
     redirects = [[NSMutableArray alloc] init];
137
     [redirects addObject:req.URL.absoluteString];
147
     [redirects addObject:req.URL.absoluteString];
138
     
148
     
153
     bodyLength = contentLength;
163
     bodyLength = contentLength;
154
 
164
 
155
     // the session trust any SSL certification
165
     // the session trust any SSL certification
156
-    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
166
+//    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
167
+    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
168
+    
157
     // set request timeout
169
     // set request timeout
158
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
170
     float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
159
     if(timeout > 0)
171
     if(timeout > 0)
190
         respData = [[NSMutableData alloc] init];
202
         respData = [[NSMutableData alloc] init];
191
         respFile = NO;
203
         respFile = NO;
192
     }
204
     }
193
-    NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
205
+    
206
+    __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
194
     [taskTable setObject:task forKey:taskId];
207
     [taskTable setObject:task forKey:taskId];
195
     [task resume];
208
     [task resume];
196
 
209
 
197
     // network status indicator
210
     // network status indicator
198
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
211
     if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES)
199
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
212
         [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
213
+    __block UIApplication * app = [UIApplication sharedApplication];
214
+    
215
+    // #115 handling task expired when application entering backgound for a long time
216
+    [app beginBackgroundTaskWithName:taskId expirationHandler:^{
217
+        NSLog([NSString stringWithFormat:@"session %@ expired event emit", taskId ]);
218
+        [expirationTable setObject:task forKey:taskId];
219
+        [app endBackgroundTask:task];
220
+        
221
+    }];
222
+    
223
+    
224
+}
225
+
226
+// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
227
++ (void) getExpiredTasks
228
+{
229
+    NSEnumerator * emu =  [expirationTable keyEnumerator];
230
+    NSString * key;
231
+
232
+    while((key = [emu nextObject]))
233
+    {
234
+        RCTBridge * bridge = [RNFetchBlob getRCTBridge];
235
+        NSData * args = @{ @"taskId": key };
236
+        [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
237
+    }
200
 }
238
 }
201
 
239
 
202
 ////////////////////////////////////////
240
 ////////////////////////////////////////
306
     receivedBytes += [received longValue];
344
     receivedBytes += [received longValue];
307
     NSString * chunkString = @"";
345
     NSString * chunkString = @"";
308
 
346
 
309
-    if(isInrement == YES)
347
+    if(isIncrement == YES)
310
     {
348
     {
311
         chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
349
         chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
312
     }
350
     }
480
     }
518
     }
481
 }
519
 }
482
 
520
 
521
+- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
522
+{
523
+    NSLog(@"sess done in background");
524
+}
525
+
483
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
526
 - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
484
 {
527
 {
485
     [redirects addObject:[request.URL absoluteString]];
528
     [redirects addObject:[request.URL absoluteString]];

+ 21
- 18
test/test-init.js Ver fichero

18
 // test environment variables
18
 // test environment variables
19
 
19
 
20
 prop('FILENAME', `${Platform.OS}-0.8.0-${Date.now()}.png`)
20
 prop('FILENAME', `${Platform.OS}-0.8.0-${Date.now()}.png`)
21
-prop('TEST_SERVER_URL', 'http://192.168.16.70:8123')
22
-prop('TEST_SERVER_URL_SSL', 'https://192.168.16.70:8124')
21
+prop('TEST_SERVER_URL', 'http://localhost:8123')
22
+prop('TEST_SERVER_URL_SSL', 'https://localhost:8124')
23
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
23
 prop('DROPBOX_TOKEN', 'fsXcpmKPrHgAAAAAAAAAoXZhcXYWdgLpQMan6Tb_bzJ237DXhgQSev12hA-gUXt4')
24
 prop('styles', {
24
 prop('styles', {
25
   image : {
25
   image : {
59
 })
59
 })
60
 
60
 
61
 
61
 
62
-require('./test-0.1.x-0.4.x')
63
-require('./test-0.5.1')
64
-require('./test-0.5.2')
65
-require('./test-0.6.0')
66
-require('./test-0.6.2')
67
-require('./test-0.7.0')
68
-require('./test-0.8.0')
69
-require('./test-0.9.0')
70
-require('./test-0.9.2')
71
-require('./test-0.9.4')
72
-require('./test-fetch')
73
-require('./test-fs')
74
-require('./test-xmlhttp')
75
-require('./test-blob')
76
-require('./test-firebase')
77
-require('./test-android')
62
+// require('./test-0.1.x-0.4.x')
63
+// require('./test-0.5.1')
64
+// require('./test-0.5.2')
65
+// require('./test-0.6.0')
66
+// require('./test-0.6.2')
67
+// require('./test-0.7.0')
68
+// require('./test-0.8.0')
69
+// require('./test-0.9.0')
70
+// require('./test-0.9.2')
71
+// require('./test-0.9.4')
72
+// require('./test-0.9.5')
73
+// require('./test-0.10.0')
74
+// require('./test-fetch')
75
+// require('./test-fs')
76
+// require('./test-xmlhttp')
77
+// require('./test-blob')
78
+// require('./test-firebase')
79
+require('./test-background')
80
+// require('./test-android')
78
 // require('./test-stress')
81
 // require('./test-stress')
79
 // require('./benchmark')
82
 // require('./benchmark')