Browse Source

Fix IOS task queue race condition issue when sending massive requests

Fix #72 status code except 2xx will now causing a promise rejection
Ben Hsieh 8 years ago
parent
commit
a07b93aa89
2 changed files with 57 additions and 29 deletions
  1. 0
    1
      src/ios/RNFetchBlobFS.m
  2. 57
    28
      src/ios/RNFetchBlobNetwork.m

+ 0
- 1
src/ios/RNFetchBlobFS.m View File

207
 
207
 
208
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
208
 + (void) writeFile:(NSString *)path encoding:(NSString *)encoding data:(NSString *)data append:(BOOL)append resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject
209
 {
209
 {
210
-    NSLog(encoding);
211
     @try {
210
     @try {
212
         NSFileManager * fm = [NSFileManager defaultManager];
211
         NSFileManager * fm = [NSFileManager defaultManager];
213
         NSError * err = nil;
212
         NSError * err = nil;

+ 57
- 28
src/ios/RNFetchBlobNetwork.m View File

23
 //
23
 //
24
 ////////////////////////////////////////
24
 ////////////////////////////////////////
25
 
25
 
26
-NSMutableDictionary * taskTable;
26
+NSMapTable * taskTable;
27
 NSMutableDictionary * progressTable;
27
 NSMutableDictionary * progressTable;
28
 NSMutableDictionary * uploadProgressTable;
28
 NSMutableDictionary * uploadProgressTable;
29
 
29
 
30
+
30
 @interface RNFetchBlobNetwork ()
31
 @interface RNFetchBlobNetwork ()
31
 {
32
 {
32
     BOOL * respFile;
33
     BOOL * respFile;
34
     NSOutputStream * writeStream;
35
     NSOutputStream * writeStream;
35
     long bodyLength;
36
     long bodyLength;
36
     NSMutableDictionary * respInfo;
37
     NSMutableDictionary * respInfo;
38
+    NSInteger respStatus;
37
 }
39
 }
38
 
40
 
39
 @end
41
 @end
61
         taskQueue.maxConcurrentOperationCount = 10;
63
         taskQueue.maxConcurrentOperationCount = 10;
62
     }
64
     }
63
     if(taskTable == nil) {
65
     if(taskTable == nil) {
64
-        taskTable = [[NSMutableDictionary alloc] init];
66
+        taskTable = [[NSMapTable alloc] init];
65
     }
67
     }
66
     if(progressTable == nil)
68
     if(progressTable == nil)
67
     {
69
     {
139
     {
141
     {
140
         defaultConfigObject.timeoutIntervalForRequest = [[options valueForKey:@"timeout"] floatValue]/1000;
142
         defaultConfigObject.timeoutIntervalForRequest = [[options valueForKey:@"timeout"] floatValue]/1000;
141
     }
143
     }
144
+    defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
142
     session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
145
     session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
143
     if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
146
     if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
144
     {
147
     {
193
  
196
  
194
     NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
197
     NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
195
     NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
198
     NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
199
+    respStatus = statusCode;
196
     if ([response respondsToSelector:@selector(allHeaderFields)])
200
     if ([response respondsToSelector:@selector(allHeaderFields)])
197
     {
201
     {
198
         NSDictionary *headers = [httpResponse allHeaderFields];
202
         NSDictionary *headers = [httpResponse allHeaderFields];
242
                      @"respType" : respType,
246
                      @"respType" : respType,
243
                      @"timeout" : @NO,
247
                      @"timeout" : @NO,
244
                      @"status": [NSString stringWithFormat:@"%d", statusCode ]
248
                      @"status": [NSString stringWithFormat:@"%d", statusCode ]
245
-                     };
249
+                    };
246
         
250
         
247
         [self.bridge.eventDispatcher
251
         [self.bridge.eventDispatcher
248
          sendDeviceEventWithName: EVENT_STATE_CHANGE
252
          sendDeviceEventWithName: EVENT_STATE_CHANGE
270
             NSLog(@"write file error");
274
             NSLog(@"write file error");
271
         }
275
         }
272
     }
276
     }
277
+    
273
     completionHandler(NSURLSessionResponseAllow);
278
     completionHandler(NSURLSessionResponseAllow);
274
 }
279
 }
275
 
280
 
312
 {
317
 {
313
     
318
     
314
     self.error = error;
319
     self.error = error;
315
-    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
316
-    
320
+    NSString * errMsg = [NSNull null];
321
+    NSString * respStr = [NSNull null];
317
     NSString * respType = [respInfo valueForKey:@"respType"];
322
     NSString * respType = [respInfo valueForKey:@"respType"];
318
-    if(error != nil) {
319
-        NSLog([error localizedDescription]);
323
+    
324
+    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
325
+    if(error != nil)
326
+    {
327
+        errMsg = [error localizedDescription];
328
+    }
329
+    if(respInfo == nil)
330
+    {
331
+        respInfo = [NSNull null];
320
     }
332
     }
321
     
333
     
322
-    if(respFile == YES)
334
+    // Fix #72 response with status code 200 ~ 299 considered as success
335
+    if(respStatus> 299 || respStatus < 200)
336
+    {
337
+        errMsg = [NSString stringWithFormat:@"Request failed, status %d", respStatus];
338
+    }
339
+    else
323
     {
340
     {
324
-        [writeStream close];
325
-        callback(@[error == nil ? [NSNull null] : [error localizedDescription],
326
-                   respInfo == nil ? [NSNull null] : respInfo,
327
-                   destPath
328
-                ]);
341
+        if(respFile == YES)
342
+        {
343
+            [writeStream close];
344
+            respStr = destPath;
345
+        }
346
+        // base64 response
347
+        else {
348
+            // #73 fix unicode data encoding issue :
349
+            // when response type is BASE64, we should first try to encode the response data to UTF8 format
350
+            // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
351
+            // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
352
+            NSString * urlEncoded = [[[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]
353
+                                     stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
354
+            NSString * base64 = @"";
355
+            if(urlEncoded != nil)
356
+                base64 = [[urlEncoded dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
357
+            else
358
+                base64 = [respData base64EncodedStringWithOptions:0];
359
+            respStr = base64;
360
+            
361
+        }
329
     }
362
     }
330
-    // base64 response
331
-    else {
332
-        NSString * utf8 = [[[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding] stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
333
-        NSString * base64 = @"";
334
-        if(utf8 != nil)
335
-            base64 = [[utf8 dataUsingEncoding:NSUTF8StringEncoding] base64EncodedStringWithOptions:0];
363
+    
364
+    callback(@[ errMsg, respInfo, respStr ]);
365
+    
366
+    @synchronized(taskTable, uploadProgressTable, progressTable)
367
+    {
368
+        if([taskTable objectForKey:taskId] == nil)
369
+            NSLog(@"object released.");
336
         else
370
         else
337
-            base64 = [respData base64EncodedStringWithOptions:0];
338
-        callback(@[error == nil ? [NSNull null] : [error localizedDescription],
339
-                   respInfo == nil ? [NSNull null] : respInfo,
340
-                   base64
341
-                ]);
342
-        
371
+            [taskTable removeObjectForKey:taskId];
372
+        [uploadProgressTable removeObjectForKey:taskId];
373
+        [progressTable removeObjectForKey:taskId];
343
     }
374
     }
344
     
375
     
345
-    [taskTable removeObjectForKey:taskId];
346
-    [uploadProgressTable removeObjectForKey:taskId];
347
-    [progressTable removeObjectForKey:taskId];
348
     respData = nil;
376
     respData = nil;
349
     receivedBytes = 0;
377
     receivedBytes = 0;
350
     [session finishTasksAndInvalidate];
378
     [session finishTasksAndInvalidate];
379
+
351
 }
380
 }
352
 
381
 
353
 // upload progress handler
382
 // upload progress handler