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,7 +207,6 @@ NSMutableDictionary *fileStreams = nil;
207 207
 
208 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 210
     @try {
212 211
         NSFileManager * fm = [NSFileManager defaultManager];
213 212
         NSError * err = nil;

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

@@ -23,10 +23,11 @@
23 23
 //
24 24
 ////////////////////////////////////////
25 25
 
26
-NSMutableDictionary * taskTable;
26
+NSMapTable * taskTable;
27 27
 NSMutableDictionary * progressTable;
28 28
 NSMutableDictionary * uploadProgressTable;
29 29
 
30
+
30 31
 @interface RNFetchBlobNetwork ()
31 32
 {
32 33
     BOOL * respFile;
@@ -34,6 +35,7 @@ NSMutableDictionary * uploadProgressTable;
34 35
     NSOutputStream * writeStream;
35 36
     long bodyLength;
36 37
     NSMutableDictionary * respInfo;
38
+    NSInteger respStatus;
37 39
 }
38 40
 
39 41
 @end
@@ -61,7 +63,7 @@ NSOperationQueue *taskQueue;
61 63
         taskQueue.maxConcurrentOperationCount = 10;
62 64
     }
63 65
     if(taskTable == nil) {
64
-        taskTable = [[NSMutableDictionary alloc] init];
66
+        taskTable = [[NSMapTable alloc] init];
65 67
     }
66 68
     if(progressTable == nil)
67 69
     {
@@ -139,6 +141,7 @@ NSOperationQueue *taskQueue;
139 141
     {
140 142
         defaultConfigObject.timeoutIntervalForRequest = [[options valueForKey:@"timeout"] floatValue]/1000;
141 143
     }
144
+    defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
142 145
     session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
143 146
     if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
144 147
     {
@@ -193,6 +196,7 @@ NSOperationQueue *taskQueue;
193 196
  
194 197
     NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
195 198
     NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
199
+    respStatus = statusCode;
196 200
     if ([response respondsToSelector:@selector(allHeaderFields)])
197 201
     {
198 202
         NSDictionary *headers = [httpResponse allHeaderFields];
@@ -242,7 +246,7 @@ NSOperationQueue *taskQueue;
242 246
                      @"respType" : respType,
243 247
                      @"timeout" : @NO,
244 248
                      @"status": [NSString stringWithFormat:@"%d", statusCode ]
245
-                     };
249
+                    };
246 250
         
247 251
         [self.bridge.eventDispatcher
248 252
          sendDeviceEventWithName: EVENT_STATE_CHANGE
@@ -270,6 +274,7 @@ NSOperationQueue *taskQueue;
270 274
             NSLog(@"write file error");
271 275
         }
272 276
     }
277
+    
273 278
     completionHandler(NSURLSessionResponseAllow);
274 279
 }
275 280
 
@@ -312,42 +317,66 @@ NSOperationQueue *taskQueue;
312 317
 {
313 318
     
314 319
     self.error = error;
315
-    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
316
-    
320
+    NSString * errMsg = [NSNull null];
321
+    NSString * respStr = [NSNull null];
317 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 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 376
     respData = nil;
349 377
     receivedBytes = 0;
350 378
     [session finishTasksAndInvalidate];
379
+
351 380
 }
352 381
 
353 382
 // upload progress handler