|  | @@ -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
 |