|
@@ -8,13 +8,10 @@
|
8
|
8
|
|
9
|
9
|
|
10
|
10
|
#import <Foundation/Foundation.h>
|
11
|
|
-#import "RNFetchBlob.h"
|
12
|
|
-#import "RNFetchBlobFS.h"
|
13
|
11
|
#import "RNFetchBlobNetwork.h"
|
|
12
|
+
|
|
13
|
+#import "RNFetchBlob.h"
|
14
|
14
|
#import "RNFetchBlobConst.h"
|
15
|
|
-#import "RNFetchBlobReqBuilder.h"
|
16
|
|
-#import "IOS7Polyfill.h"
|
17
|
|
-#import <CommonCrypto/CommonDigest.h>
|
18
|
15
|
#import "RNFetchBlobProgress.h"
|
19
|
16
|
|
20
|
17
|
#if __has_include(<React/RCTAssert.h>)
|
|
@@ -35,130 +32,43 @@
|
35
|
32
|
//
|
36
|
33
|
////////////////////////////////////////
|
37
|
34
|
|
38
|
|
-NSMapTable * taskTable;
|
39
|
35
|
NSMapTable * expirationTable;
|
40
|
|
-NSMutableDictionary * progressTable;
|
41
|
|
-NSMutableDictionary * uploadProgressTable;
|
42
|
36
|
|
43
|
37
|
__attribute__((constructor))
|
44
|
38
|
static void initialize_tables() {
|
45
|
|
- if(expirationTable == nil)
|
46
|
|
- {
|
|
39
|
+ if (expirationTable == nil) {
|
47
|
40
|
expirationTable = [[NSMapTable alloc] init];
|
48
|
41
|
}
|
49
|
|
- if(taskTable == nil)
|
50
|
|
- {
|
51
|
|
- taskTable = [[NSMapTable alloc] init];
|
52
|
|
- }
|
53
|
|
- if(progressTable == nil)
|
54
|
|
- {
|
55
|
|
- progressTable = [[NSMutableDictionary alloc] init];
|
56
|
|
- }
|
57
|
|
- if(uploadProgressTable == nil)
|
58
|
|
- {
|
59
|
|
- uploadProgressTable = [[NSMutableDictionary alloc] init];
|
60
|
|
- }
|
61
|
|
-}
|
62
|
|
-
|
63
|
|
-
|
64
|
|
-typedef NS_ENUM(NSUInteger, ResponseFormat) {
|
65
|
|
- UTF8,
|
66
|
|
- BASE64,
|
67
|
|
- AUTO
|
68
|
|
-};
|
69
|
|
-
|
70
|
|
-
|
71
|
|
-@interface RNFetchBlobNetwork ()
|
72
|
|
-{
|
73
|
|
- BOOL * respFile;
|
74
|
|
- BOOL isNewPart;
|
75
|
|
- BOOL * isIncrement;
|
76
|
|
- NSMutableData * partBuffer;
|
77
|
|
- NSString * destPath;
|
78
|
|
- NSOutputStream * writeStream;
|
79
|
|
- long bodyLength;
|
80
|
|
- NSMutableDictionary * respInfo;
|
81
|
|
- NSInteger respStatus;
|
82
|
|
- NSMutableArray * redirects;
|
83
|
|
- ResponseFormat responseFormat;
|
84
|
|
- BOOL * followRedirect;
|
85
|
|
- BOOL backgroundTask;
|
86
|
42
|
}
|
87
|
43
|
|
88
|
|
-@end
|
89
|
44
|
|
90
|
45
|
@implementation RNFetchBlobNetwork
|
91
|
46
|
|
92
|
|
-NSOperationQueue *taskQueue;
|
93
|
|
-@synthesize taskId;
|
94
|
|
-@synthesize expectedBytes;
|
95
|
|
-@synthesize receivedBytes;
|
96
|
|
-@synthesize respData;
|
97
|
|
-@synthesize callback;
|
98
|
|
-@synthesize bridge;
|
99
|
|
-@synthesize options;
|
100
|
|
-@synthesize fileTaskCompletionHandler;
|
101
|
|
-@synthesize dataTaskCompletionHandler;
|
102
|
|
-@synthesize error;
|
103
|
47
|
|
104
|
|
-
|
105
|
|
-// constructor
|
106
|
48
|
- (id)init {
|
107
|
49
|
self = [super init];
|
108
|
|
- if(taskQueue == nil) {
|
109
|
|
- @synchronized ([RNFetchBlobNetwork class]) {
|
110
|
|
- if (taskQueue == nil) {
|
111
|
|
- taskQueue = [[NSOperationQueue alloc] init];
|
112
|
|
- taskQueue.maxConcurrentOperationCount = 10;
|
113
|
|
- }
|
114
|
|
- }
|
|
50
|
+ if (self) {
|
|
51
|
+ self.requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory];
|
|
52
|
+
|
|
53
|
+ self.taskQueue = [[NSOperationQueue alloc] init];
|
|
54
|
+ self.taskQueue.qualityOfService = NSQualityOfServiceUtility;
|
|
55
|
+ self.taskQueue.maxConcurrentOperationCount = 10;
|
115
|
56
|
}
|
|
57
|
+
|
116
|
58
|
return self;
|
117
|
59
|
}
|
118
|
60
|
|
119
|
|
-+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
|
120
|
|
-{
|
121
|
|
- if(progressTable == nil)
|
122
|
|
- {
|
123
|
|
- progressTable = [[NSMutableDictionary alloc] init];
|
124
|
|
- }
|
125
|
|
- [progressTable setValue:config forKey:taskId];
|
126
|
|
-}
|
127
|
|
-
|
128
|
|
-+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config
|
129
|
|
-{
|
130
|
|
- if(uploadProgressTable == nil)
|
131
|
|
- {
|
132
|
|
- uploadProgressTable = [[NSMutableDictionary alloc] init];
|
133
|
|
- }
|
134
|
|
- [uploadProgressTable setValue:config forKey:taskId];
|
135
|
|
-}
|
136
|
|
-
|
137
|
|
-// removing case from headers
|
138
|
|
-+ (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers
|
139
|
|
-{
|
140
|
|
-
|
141
|
|
- NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
|
142
|
|
- for(NSString * key in headers) {
|
143
|
|
- [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
|
144
|
|
- }
|
145
|
|
-
|
146
|
|
- return mheaders;
|
147
|
|
-}
|
148
|
|
-
|
149
|
|
-- (NSString *)md5:(NSString *)input {
|
150
|
|
- const char* str = [input UTF8String];
|
151
|
|
- unsigned char result[CC_MD5_DIGEST_LENGTH];
|
152
|
|
- CC_MD5(str, (CC_LONG)strlen(str), result);
|
|
61
|
++ (RNFetchBlobNetwork* _Nullable)sharedInstance {
|
|
62
|
+ static id _sharedInstance = nil;
|
|
63
|
+ static dispatch_once_t onceToken;
|
153
|
64
|
|
154
|
|
- NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
|
155
|
|
- for(int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
|
156
|
|
- [ret appendFormat:@"%02x",result[i]];
|
157
|
|
- }
|
158
|
|
- return ret;
|
|
65
|
+ dispatch_once(&onceToken, ^{
|
|
66
|
+ _sharedInstance = [[self alloc] init];
|
|
67
|
+ });
|
|
68
|
+
|
|
69
|
+ return _sharedInstance;
|
159
|
70
|
}
|
160
|
71
|
|
161
|
|
-// send HTTP request
|
162
|
72
|
- (void) sendRequest:(__weak NSDictionary * _Nullable )options
|
163
|
73
|
contentLength:(long) contentLength
|
164
|
74
|
bridge:(RCTBridge * _Nullable)bridgeRef
|
|
@@ -166,473 +76,80 @@ NSOperationQueue *taskQueue;
|
166
|
76
|
withRequest:(__weak NSURLRequest * _Nullable)req
|
167
|
77
|
callback:(_Nullable RCTResponseSenderBlock) callback
|
168
|
78
|
{
|
169
|
|
- self.taskId = taskId;
|
170
|
|
- self.respData = [[NSMutableData alloc] initWithLength:0];
|
171
|
|
- self.callback = callback;
|
172
|
|
- self.bridge = bridgeRef;
|
173
|
|
- self.expectedBytes = 0;
|
174
|
|
- self.receivedBytes = 0;
|
175
|
|
- self.options = options;
|
176
|
|
-
|
177
|
|
- backgroundTask = [options valueForKey:@"IOSBackgroundTask"] == nil ? NO : [[options valueForKey:@"IOSBackgroundTask"] boolValue];
|
178
|
|
- followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
|
179
|
|
- isIncrement = [options valueForKey:@"increment"] == nil ? NO : [[options valueForKey:@"increment"] boolValue];
|
180
|
|
- redirects = [[NSMutableArray alloc] init];
|
181
|
|
- if(req.URL != nil)
|
182
|
|
- [redirects addObject:req.URL.absoluteString];
|
183
|
|
-
|
184
|
|
- // set response format
|
185
|
|
- NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
|
186
|
|
- if([[rnfbResp lowercaseString] isEqualToString:@"base64"])
|
187
|
|
- responseFormat = BASE64;
|
188
|
|
- else if([[rnfbResp lowercaseString] isEqualToString:@"utf8"])
|
189
|
|
- responseFormat = UTF8;
|
190
|
|
- else
|
191
|
|
- responseFormat = AUTO;
|
192
|
|
-
|
193
|
|
- NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
|
194
|
|
- NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT];
|
195
|
|
- NSString * key = [self.options valueForKey:CONFIG_KEY];
|
196
|
|
- __block NSURLSession * session;
|
197
|
|
-
|
198
|
|
- bodyLength = contentLength;
|
199
|
|
-
|
200
|
|
- // the session trust any SSL certification
|
201
|
|
- NSURLSessionConfiguration *defaultConfigObject;
|
202
|
|
-
|
203
|
|
- defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
|
204
|
|
-
|
205
|
|
- if(backgroundTask)
|
206
|
|
- {
|
207
|
|
- defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
|
208
|
|
- }
|
209
|
|
-
|
210
|
|
- // set request timeout
|
211
|
|
- float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
|
212
|
|
- if(timeout > 0)
|
213
|
|
- {
|
214
|
|
- defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
|
215
|
|
- }
|
216
|
|
- defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
|
217
|
|
- session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue];
|
218
|
|
- if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil)
|
219
|
|
- {
|
220
|
|
- respFile = YES;
|
221
|
|
-
|
222
|
|
- NSString* cacheKey = taskId;
|
223
|
|
- if (key != nil) {
|
224
|
|
- cacheKey = [self md5:key];
|
225
|
|
- if (cacheKey == nil) {
|
226
|
|
- cacheKey = taskId;
|
227
|
|
- }
|
228
|
|
-
|
229
|
|
- destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
230
|
|
- if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
|
231
|
|
- callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
|
232
|
|
- return;
|
233
|
|
- }
|
234
|
|
- }
|
235
|
|
-
|
236
|
|
- if(path != nil)
|
237
|
|
- destPath = path;
|
238
|
|
- else
|
239
|
|
- destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
240
|
|
- }
|
241
|
|
- else
|
242
|
|
- {
|
243
|
|
- respData = [[NSMutableData alloc] init];
|
244
|
|
- respFile = NO;
|
245
|
|
- }
|
246
|
|
-
|
247
|
|
- __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req];
|
|
79
|
+ RNFetchBlobRequest *request = [[RNFetchBlobRequest alloc] init];
|
|
80
|
+ [request sendRequest:options
|
|
81
|
+ contentLength:contentLength
|
|
82
|
+ bridge:bridgeRef
|
|
83
|
+ taskId:taskId
|
|
84
|
+ withRequest:req
|
|
85
|
+ taskOperationQueue:self.taskQueue
|
|
86
|
+ callback:callback];
|
248
|
87
|
|
249
|
|
- [taskTable setObject:@{ @"session" : task, @"isCancelled" : @NO } forKey:taskId];
|
250
|
|
- [task resume];
|
251
|
|
-
|
252
|
|
- // network status indicator
|
253
|
|
- if ([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) {
|
254
|
|
- dispatch_async(dispatch_get_main_queue(), ^{
|
255
|
|
- [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
|
256
|
|
- });
|
257
|
|
- }
|
258
|
|
- __block UIApplication * app = [UIApplication sharedApplication];
|
259
|
|
-
|
260
|
|
-}
|
261
|
|
-
|
262
|
|
-// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
|
263
|
|
-+ (void) emitExpiredTasks
|
264
|
|
-{
|
265
|
|
- NSEnumerator * emu = [expirationTable keyEnumerator];
|
266
|
|
- NSString * key;
|
267
|
|
-
|
268
|
|
- while((key = [emu nextObject]))
|
269
|
|
- {
|
270
|
|
- RCTBridge * bridge = [RNFetchBlob getRCTBridge];
|
271
|
|
- NSData * args = @{ @"taskId": key };
|
272
|
|
- [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
|
273
|
|
-
|
|
88
|
+ @synchronized([RNFetchBlobNetwork class]) {
|
|
89
|
+ [self.requestsTable setObject:request forKey:taskId];
|
274
|
90
|
}
|
275
|
|
-
|
276
|
|
- // clear expired task entries
|
277
|
|
- [expirationTable removeAllObjects];
|
278
|
|
- expirationTable = [[NSMapTable alloc] init];
|
279
|
|
-
|
280
|
91
|
}
|
281
|
92
|
|
282
|
|
-////////////////////////////////////////
|
283
|
|
-//
|
284
|
|
-// NSURLSession delegates
|
285
|
|
-//
|
286
|
|
-////////////////////////////////////////
|
287
|
|
-
|
288
|
|
-
|
289
|
|
-#pragma mark NSURLSession delegate methods
|
290
|
|
-
|
291
|
|
-
|
292
|
|
-#pragma mark - Received Response
|
293
|
|
-// set expected content length on response received
|
294
|
|
-- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
|
|
93
|
+- (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config
|
295
|
94
|
{
|
296
|
|
- expectedBytes = [response expectedContentLength];
|
297
|
|
-
|
298
|
|
- NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
|
299
|
|
- NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
|
300
|
|
- NSString * respType = @"";
|
301
|
|
- respStatus = statusCode;
|
302
|
|
- if ([response respondsToSelector:@selector(allHeaderFields)])
|
303
|
|
- {
|
304
|
|
- NSDictionary *headers = [httpResponse allHeaderFields];
|
305
|
|
- NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
|
306
|
|
- if(self.isServerPush == NO)
|
307
|
|
- {
|
308
|
|
- self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"];
|
309
|
|
- }
|
310
|
|
- if(self.isServerPush)
|
311
|
|
- {
|
312
|
|
- if(partBuffer != nil)
|
313
|
|
- {
|
314
|
|
- [self.bridge.eventDispatcher
|
315
|
|
- sendDeviceEventWithName:EVENT_SERVER_PUSH
|
316
|
|
- body:@{
|
317
|
|
- @"taskId": taskId,
|
318
|
|
- @"chunk": [partBuffer base64EncodedStringWithOptions:0],
|
319
|
|
- }
|
320
|
|
- ];
|
321
|
|
- }
|
322
|
|
- partBuffer = [[NSMutableData alloc] init];
|
323
|
|
- completionHandler(NSURLSessionResponseAllow);
|
324
|
|
- return;
|
325
|
|
- }
|
326
|
|
- if(respCType != nil)
|
327
|
|
- {
|
328
|
|
- NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
|
329
|
|
- if([respCType RNFBContainsString:@"text/"])
|
330
|
|
- {
|
331
|
|
- respType = @"text";
|
332
|
|
- }
|
333
|
|
- else if([respCType RNFBContainsString:@"application/json"])
|
334
|
|
- {
|
335
|
|
- respType = @"json";
|
336
|
|
- }
|
337
|
|
- // If extra blob content type is not empty, check if response type matches
|
338
|
|
- else if( extraBlobCTypes != nil) {
|
339
|
|
- for(NSString * substr in extraBlobCTypes)
|
340
|
|
- {
|
341
|
|
- if([respCType RNFBContainsString:[substr lowercaseString]])
|
342
|
|
- {
|
343
|
|
- respType = @"blob";
|
344
|
|
- respFile = YES;
|
345
|
|
- destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil];
|
346
|
|
- break;
|
347
|
|
- }
|
348
|
|
- }
|
349
|
|
- }
|
350
|
|
- else
|
351
|
|
- {
|
352
|
|
- respType = @"blob";
|
353
|
|
- // for XMLHttpRequest, switch response data handling strategy automatically
|
354
|
|
- if([options valueForKey:@"auto"] == YES) {
|
355
|
|
- respFile = YES;
|
356
|
|
- destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""];
|
357
|
|
- }
|
358
|
|
- }
|
359
|
|
- }
|
360
|
|
- else
|
361
|
|
- respType = @"text";
|
362
|
|
- respInfo = @{
|
363
|
|
- @"taskId": taskId,
|
364
|
|
- @"state": @"2",
|
365
|
|
- @"headers": headers,
|
366
|
|
- @"redirects": redirects,
|
367
|
|
- @"respType" : respType,
|
368
|
|
- @"timeout" : @NO,
|
369
|
|
- @"status": [NSNumber numberWithInteger:statusCode]
|
370
|
|
- };
|
371
|
|
-
|
372
|
|
-#pragma mark - handling cookies
|
373
|
|
- // # 153 get cookies
|
374
|
|
- if(response.URL != nil)
|
375
|
|
- {
|
376
|
|
- NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
377
|
|
- NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
|
378
|
|
- if(cookies != nil && [cookies count] > 0) {
|
379
|
|
- [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
|
380
|
|
- }
|
381
|
|
- }
|
382
|
|
-
|
383
|
|
- [self.bridge.eventDispatcher
|
384
|
|
- sendDeviceEventWithName: EVENT_STATE_CHANGE
|
385
|
|
- body:respInfo
|
386
|
|
- ];
|
387
|
|
- headers = nil;
|
388
|
|
- respInfo = nil;
|
389
|
|
-
|
390
|
|
- }
|
391
|
|
- else
|
392
|
|
- NSLog(@"oops");
|
393
|
|
-
|
394
|
|
- if(respFile == YES)
|
395
|
|
- {
|
396
|
|
- @try{
|
397
|
|
- NSFileManager * fm = [NSFileManager defaultManager];
|
398
|
|
- NSString * folder = [destPath stringByDeletingLastPathComponent];
|
399
|
|
- if(![fm fileExistsAtPath:folder])
|
400
|
|
- {
|
401
|
|
- [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
|
402
|
|
- }
|
403
|
|
- BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
|
404
|
|
- BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
|
405
|
|
-
|
406
|
|
- appendToExistingFile = !overwrite;
|
407
|
|
-
|
408
|
|
- // For solving #141 append response data if the file already exists
|
409
|
|
- // base on PR#139 @kejinliang
|
410
|
|
- if(appendToExistingFile)
|
411
|
|
- {
|
412
|
|
- destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
|
413
|
|
- }
|
414
|
|
- if (![fm fileExistsAtPath:destPath])
|
415
|
|
- {
|
416
|
|
- [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
|
417
|
|
- }
|
418
|
|
- writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
|
419
|
|
- [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
420
|
|
- [writeStream open];
|
421
|
|
- }
|
422
|
|
- @catch(NSException * ex)
|
423
|
|
- {
|
424
|
|
- NSLog(@"write file error");
|
|
95
|
+ if (config) {
|
|
96
|
+ @synchronized ([RNFetchBlobNetwork class]) {
|
|
97
|
+ [self.requestsTable objectForKey:taskId].progressConfig = config;
|
425
|
98
|
}
|
426
|
99
|
}
|
427
|
|
-
|
428
|
|
- completionHandler(NSURLSessionResponseAllow);
|
429
|
|
-}
|
430
|
|
-
|
431
|
|
-
|
432
|
|
-// download progress handler
|
433
|
|
-- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
434
|
|
-{
|
435
|
|
- // For #143 handling multipart/x-mixed-replace response
|
436
|
|
- if(self.isServerPush)
|
437
|
|
- {
|
438
|
|
- [partBuffer appendData:data];
|
439
|
|
- return ;
|
440
|
|
- }
|
441
|
|
-
|
442
|
|
- NSNumber * received = [NSNumber numberWithLong:[data length]];
|
443
|
|
- receivedBytes += [received longValue];
|
444
|
|
- NSString * chunkString = @"";
|
445
|
|
-
|
446
|
|
- if(isIncrement == YES)
|
447
|
|
- {
|
448
|
|
- chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
449
|
|
- }
|
450
|
|
-
|
451
|
|
- if(respFile == NO)
|
452
|
|
- {
|
453
|
|
- [respData appendData:data];
|
454
|
|
- }
|
455
|
|
- else
|
456
|
|
- {
|
457
|
|
- [writeStream write:[data bytes] maxLength:[data length]];
|
458
|
|
- }
|
459
|
|
- RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId];
|
460
|
|
- if(expectedBytes == 0)
|
461
|
|
- return;
|
462
|
|
- NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
|
463
|
|
- if(pconfig != nil && [pconfig shouldReport:now])
|
464
|
|
- {
|
465
|
|
- [self.bridge.eventDispatcher
|
466
|
|
- sendDeviceEventWithName:EVENT_PROGRESS
|
467
|
|
- body:@{
|
468
|
|
- @"taskId": taskId,
|
469
|
|
- @"written": [NSString stringWithFormat:@"%d", receivedBytes],
|
470
|
|
- @"total": [NSString stringWithFormat:@"%d", expectedBytes],
|
471
|
|
- @"chunk": chunkString
|
472
|
|
- }
|
473
|
|
- ];
|
474
|
|
- }
|
475
|
|
- received = nil;
|
476
|
|
-
|
477
|
|
-}
|
478
|
|
-
|
479
|
|
-- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
|
480
|
|
-{
|
481
|
|
- if([session isEqual:session])
|
482
|
|
- session = nil;
|
483
|
100
|
}
|
484
|
101
|
|
485
|
|
-
|
486
|
|
-- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
102
|
+- (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config
|
487
|
103
|
{
|
488
|
|
-
|
489
|
|
- self.error = error;
|
490
|
|
- NSString * errMsg = [NSNull null];
|
491
|
|
- NSString * respStr = [NSNull null];
|
492
|
|
- NSString * rnfbRespType = @"";
|
493
|
|
-
|
494
|
|
- dispatch_async(dispatch_get_main_queue(), ^{
|
495
|
|
- [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
496
|
|
- });
|
497
|
|
-
|
498
|
|
- if(respInfo == nil)
|
499
|
|
- {
|
500
|
|
- respInfo = [NSNull null];
|
501
|
|
- }
|
502
|
|
-
|
503
|
|
- if(error != nil)
|
504
|
|
- {
|
505
|
|
- errMsg = [error localizedDescription];
|
506
|
|
- }
|
507
|
|
- NSDictionary * taskSession = [taskTable objectForKey:taskId];
|
508
|
|
- BOOL isCancelled = [[taskSession valueForKey:@"isCancelled"] boolValue];
|
509
|
|
- if(isCancelled) {
|
510
|
|
- errMsg = @"task cancelled";
|
511
|
|
- }
|
512
|
|
-
|
513
|
|
- if(respFile == YES)
|
514
|
|
- {
|
515
|
|
- [writeStream close];
|
516
|
|
- rnfbRespType = RESP_TYPE_PATH;
|
517
|
|
- respStr = destPath;
|
518
|
|
- }
|
519
|
|
- // base64 response
|
520
|
|
- else {
|
521
|
|
- // #73 fix unicode data encoding issue :
|
522
|
|
- // when response type is BASE64, we should first try to encode the response data to UTF8 format
|
523
|
|
- // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
|
524
|
|
- // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
|
525
|
|
- NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
|
526
|
|
-
|
527
|
|
- if(responseFormat == BASE64)
|
528
|
|
- {
|
529
|
|
- rnfbRespType = RESP_TYPE_BASE64;
|
530
|
|
- respStr = [respData base64EncodedStringWithOptions:0];
|
531
|
|
- }
|
532
|
|
- else if (responseFormat == UTF8)
|
533
|
|
- {
|
534
|
|
- rnfbRespType = RESP_TYPE_UTF8;
|
535
|
|
- respStr = utf8;
|
536
|
|
- }
|
537
|
|
- else
|
538
|
|
- {
|
539
|
|
- if(utf8 != nil)
|
540
|
|
- {
|
541
|
|
- rnfbRespType = RESP_TYPE_UTF8;
|
542
|
|
- respStr = utf8;
|
543
|
|
- }
|
544
|
|
- else
|
545
|
|
- {
|
546
|
|
- rnfbRespType = RESP_TYPE_BASE64;
|
547
|
|
- respStr = [respData base64EncodedStringWithOptions:0];
|
548
|
|
- }
|
|
104
|
+ if (config) {
|
|
105
|
+ @synchronized ([RNFetchBlobNetwork class]) {
|
|
106
|
+ [self.requestsTable objectForKey:taskId].uploadProgressConfig = config;
|
549
|
107
|
}
|
550
|
108
|
}
|
551
|
|
-
|
552
|
|
-
|
553
|
|
- callback(@[ errMsg, rnfbRespType, respStr]);
|
554
|
|
-
|
555
|
|
- @synchronized(taskTable, uploadProgressTable, progressTable)
|
556
|
|
- {
|
557
|
|
- if([taskTable objectForKey:taskId] == nil)
|
558
|
|
- NSLog(@"object released by ARC.");
|
559
|
|
- else
|
560
|
|
- [taskTable removeObjectForKey:taskId];
|
561
|
|
- [uploadProgressTable removeObjectForKey:taskId];
|
562
|
|
- [progressTable removeObjectForKey:taskId];
|
563
|
|
- }
|
564
|
|
-
|
565
|
|
- respData = nil;
|
566
|
|
- receivedBytes = 0;
|
567
|
|
- [session finishTasksAndInvalidate];
|
568
|
|
-
|
569
|
|
-}
|
570
|
|
-
|
571
|
|
-// upload progress handler
|
572
|
|
-- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
|
573
|
|
-{
|
574
|
|
- RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId];
|
575
|
|
- if(totalBytesExpectedToWrite == 0)
|
576
|
|
- return;
|
577
|
|
- NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
|
578
|
|
- if(pconfig != nil && [pconfig shouldReport:now]) {
|
579
|
|
- [self.bridge.eventDispatcher
|
580
|
|
- sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
|
581
|
|
- body:@{
|
582
|
|
- @"taskId": taskId,
|
583
|
|
- @"written": [NSString stringWithFormat:@"%d", totalBytesWritten],
|
584
|
|
- @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite]
|
585
|
|
- }
|
586
|
|
- ];
|
587
|
|
- }
|
588
|
109
|
}
|
589
|
110
|
|
590
|
|
-+ (void) cancelRequest:(NSString *)taskId
|
|
111
|
+- (void) cancelRequest:(NSString *)taskId
|
591
|
112
|
{
|
592
|
|
- NSDictionary * task = [taskTable objectForKey:taskId];
|
|
113
|
+ NSURLSessionDataTask * task;
|
593
|
114
|
|
594
|
|
- if(task != nil) {
|
595
|
|
- NSURLSessionDataTask * session = [task objectForKey:@"session"];
|
596
|
|
- if(session.state == NSURLSessionTaskStateRunning) {
|
597
|
|
- [task setValue:@NO forKey:@"isCancelled"];
|
598
|
|
- [session cancel];
|
599
|
|
- }
|
|
115
|
+ @synchronized ([RNFetchBlobNetwork class]) {
|
|
116
|
+ task = [self.requestsTable objectForKey:taskId].task;
|
600
|
117
|
}
|
601
|
118
|
|
602
|
|
-}
|
603
|
|
-
|
604
|
|
-
|
605
|
|
-- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
|
606
|
|
-{
|
607
|
|
- BOOL trusty = [options valueForKey:CONFIG_TRUSTY];
|
608
|
|
- if(!trusty)
|
609
|
|
- {
|
610
|
|
- completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
611
|
|
- }
|
612
|
|
- else
|
613
|
|
- {
|
614
|
|
- completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
|
119
|
+ if (task && task.state == NSURLSessionTaskStateRunning) {
|
|
120
|
+ [task cancel];
|
615
|
121
|
}
|
616
|
122
|
}
|
617
|
123
|
|
618
|
|
-
|
619
|
|
-- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
|
|
124
|
+// removing case from headers
|
|
125
|
++ (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers
|
620
|
126
|
{
|
621
|
|
- NSLog(@"sess done in background");
|
|
127
|
+ NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init];
|
|
128
|
+ for (NSString * key in headers) {
|
|
129
|
+ [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]];
|
|
130
|
+ }
|
|
131
|
+
|
|
132
|
+ return mheaders;
|
622
|
133
|
}
|
623
|
134
|
|
624
|
|
-- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
|
|
135
|
+// #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled
|
|
136
|
++ (void) emitExpiredTasks
|
625
|
137
|
{
|
626
|
|
-
|
627
|
|
- if(followRedirect)
|
628
|
|
- {
|
629
|
|
- if(request.URL != nil)
|
630
|
|
- [redirects addObject:[request.URL absoluteString]];
|
631
|
|
- completionHandler(request);
|
632
|
|
- }
|
633
|
|
- else
|
634
|
|
- {
|
635
|
|
- completionHandler(nil);
|
|
138
|
+ @synchronized ([RNFetchBlobNetwork class]){
|
|
139
|
+ NSEnumerator * emu = [expirationTable keyEnumerator];
|
|
140
|
+ NSString * key;
|
|
141
|
+
|
|
142
|
+ while ((key = [emu nextObject]))
|
|
143
|
+ {
|
|
144
|
+ RCTBridge * bridge = [RNFetchBlob getRCTBridge];
|
|
145
|
+ id args = @{ @"taskId": key };
|
|
146
|
+ [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args];
|
|
147
|
+
|
|
148
|
+ }
|
|
149
|
+
|
|
150
|
+ // clear expired task entries
|
|
151
|
+ [expirationTable removeAllObjects];
|
|
152
|
+ expirationTable = [[NSMapTable alloc] init];
|
636
|
153
|
}
|
637
|
154
|
}
|
638
|
155
|
|