Просмотр исходного кода

Merge branch 'synchronized' into exception_fixes

Artur Chrusciel 6 лет назад
Родитель
Сommit
f6fae9b237

+ 7
- 1
ios/RNFetchBlob.xcodeproj/project.pbxproj Просмотреть файл

@@ -7,6 +7,7 @@
7 7
 	objects = {
8 8
 
9 9
 /* Begin PBXBuildFile section */
10
+		8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */; };
10 11
 		A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */; };
11 12
 		A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */; };
12 13
 		A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; };
@@ -29,6 +30,8 @@
29 30
 /* End PBXCopyFilesBuildPhase section */
30 31
 
31 32
 /* Begin PBXFileReference section */
33
+		8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobRequest.h; sourceTree = "<group>"; };
34
+		8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobRequest.m; sourceTree = "<group>"; };
32 35
 		A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobFS.m; sourceTree = "<group>"; };
33 36
 		A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobFS.h; sourceTree = "<group>"; };
34 37
 		A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobConst.h; sourceTree = "<group>"; };
@@ -71,8 +74,10 @@
71 74
 				A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */,
72 75
 				A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */,
73 76
 				A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */,
74
-				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
75 77
 				A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */,
78
+				A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */,
79
+				8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */,
80
+				8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */,
76 81
 				A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */,
77 82
 				A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */,
78 83
 				A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */,
@@ -149,6 +154,7 @@
149 154
 			buildActionMask = 2147483647;
150 155
 			files = (
151 156
 				A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */,
157
+				8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */,
152 158
 				A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */,
153 159
 				A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */,
154 160
 				A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */,

+ 0
- 1
ios/RNFetchBlob/RNFetchBlob.h Просмотреть файл

@@ -39,7 +39,6 @@
39 39
 @property (retain) UIDocumentInteractionController * documentController;
40 40
 
41 41
 + (RCTBridge *)getRCTBridge;
42
-+ (void) checkExpiredSessions;
43 42
 
44 43
 @end
45 44
 

+ 16
- 8
ios/RNFetchBlob/RNFetchBlob.m Просмотреть файл

@@ -38,7 +38,7 @@ dispatch_queue_t fsQueue;
38 38
 
39 39
 + (RCTBridge *)getRCTBridge
40 40
 {
41
-    RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view;
41
+    RCTRootView * rootView = (RCTRootView*) [[UIApplication sharedApplication] keyWindow].rootViewController.view;
42 42
     return rootView.bridge;
43 43
 }
44 44
 
@@ -96,8 +96,12 @@ RCT_EXPORT_METHOD(fetchBlobForm:(NSDictionary *)options
96 96
         // send HTTP request
97 97
         else
98 98
         {
99
-            RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
100
-            [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback];
99
+            [[RNFetchBlobNetwork sharedInstance] sendRequest:options
100
+                                               contentLength:bodyLength
101
+                                                      bridge:self.bridge
102
+                                                      taskId:taskId
103
+                                                 withRequest:req
104
+                                                    callback:callback];
101 105
         }
102 106
     }];
103 107
 
@@ -128,8 +132,12 @@ RCT_EXPORT_METHOD(fetchBlob:(NSDictionary *)options
128 132
         // send HTTP request
129 133
         else
130 134
         {
131
-            __block RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init];
132
-            [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback];
135
+            [[RNFetchBlobNetwork sharedInstance] sendRequest:options
136
+                                               contentLength:bodyLength
137
+                                                      bridge:self.bridge
138
+                                                      taskId:taskId
139
+                                                 withRequest:req
140
+                                                    callback:callback];
133 141
         }
134 142
     }];
135 143
 }
@@ -488,7 +496,7 @@ RCT_EXPORT_METHOD(getEnvironmentDirs:(RCTResponseSenderBlock) callback)
488 496
 
489 497
 #pragma mark - net.cancelRequest
490 498
 RCT_EXPORT_METHOD(cancelRequest:(NSString *)taskId callback:(RCTResponseSenderBlock)callback) {
491
-    [RNFetchBlobNetwork cancelRequest:taskId];
499
+    [[RNFetchBlobNetwork sharedInstance] cancelRequest:taskId];
492 500
     callback(@[[NSNull null], taskId]);
493 501
 
494 502
 }
@@ -498,14 +506,14 @@ RCT_EXPORT_METHOD(enableProgressReport:(NSString *)taskId interval:(nonnull NSNu
498 506
 {
499 507
 
500 508
     RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Download interval:interval count:count];
501
-    [RNFetchBlobNetwork enableProgressReport:taskId config:cfg];
509
+    [[RNFetchBlobNetwork sharedInstance] enableProgressReport:taskId config:cfg];
502 510
 }
503 511
 
504 512
 #pragma mark - net.enableUploadProgressReport
505 513
 RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId interval:(nonnull NSNumber*)interval count:(nonnull NSNumber*)count)
506 514
 {
507 515
     RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Upload interval:interval count:count];
508
-    [RNFetchBlobNetwork enableUploadProgress:taskId config:cfg];
516
+    [[RNFetchBlobNetwork sharedInstance] enableUploadProgress:taskId config:cfg];
509 517
 }
510 518
 
511 519
 #pragma mark - fs.slice

+ 28
- 28
ios/RNFetchBlobConst.m Просмотреть файл

@@ -7,38 +7,38 @@
7 7
 //
8 8
 #import "RNFetchBlobConst.h"
9 9
 
10
-extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
11
-extern NSString *const ASSET_PREFIX = @"bundle-assets://";
12
-extern NSString *const AL_PREFIX = @"assets-library://";
10
+NSString *const FILE_PREFIX = @"RNFetchBlob-file://";
11
+NSString *const ASSET_PREFIX = @"bundle-assets://";
12
+NSString *const AL_PREFIX = @"assets-library://";
13 13
 
14 14
 // fetch configs
15
-extern NSString *const CONFIG_USE_TEMP = @"fileCache";
16
-extern NSString *const CONFIG_FILE_PATH = @"path";
17
-extern NSString *const CONFIG_FILE_EXT = @"appendExt";
18
-extern NSString *const CONFIG_TRUSTY = @"trusty";
19
-extern NSString *const CONFIG_INDICATOR = @"indicator";
20
-extern NSString *const CONFIG_KEY = @"key";
21
-extern NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes";
15
+NSString *const CONFIG_USE_TEMP = @"fileCache";
16
+NSString *const CONFIG_FILE_PATH = @"path";
17
+NSString *const CONFIG_FILE_EXT = @"appendExt";
18
+NSString *const CONFIG_TRUSTY = @"trusty";
19
+NSString *const CONFIG_INDICATOR = @"indicator";
20
+NSString *const CONFIG_KEY = @"key";
21
+NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes";
22 22
 
23
-extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
24
-extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush";
25
-extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
26
-extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
27
-extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
23
+NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState";
24
+NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush";
25
+NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress";
26
+NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload";
27
+NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire";
28 28
 
29
-extern NSString *const MSG_EVENT = @"RNFetchBlobMessage";
30
-extern NSString *const MSG_EVENT_LOG = @"log";
31
-extern NSString *const MSG_EVENT_WARN = @"warn";
32
-extern NSString *const MSG_EVENT_ERROR = @"error";
33
-extern NSString *const FS_EVENT_DATA = @"data";
34
-extern NSString *const FS_EVENT_END = @"end";
35
-extern NSString *const FS_EVENT_WARN = @"warn";
36
-extern NSString *const FS_EVENT_ERROR = @"error";
29
+NSString *const MSG_EVENT = @"RNFetchBlobMessage";
30
+NSString *const MSG_EVENT_LOG = @"log";
31
+NSString *const MSG_EVENT_WARN = @"warn";
32
+NSString *const MSG_EVENT_ERROR = @"error";
33
+NSString *const FS_EVENT_DATA = @"data";
34
+NSString *const FS_EVENT_END = @"end";
35
+NSString *const FS_EVENT_WARN = @"warn";
36
+NSString *const FS_EVENT_ERROR = @"error";
37 37
 
38
-extern NSString *const KEY_REPORT_PROGRESS = @"reportProgress";
39
-extern NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress";
38
+NSString *const KEY_REPORT_PROGRESS = @"reportProgress";
39
+NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress";
40 40
 
41 41
 // response type
42
-extern NSString *const RESP_TYPE_BASE64 = @"base64";
43
-extern NSString *const RESP_TYPE_UTF8 = @"utf8";
44
-extern NSString *const RESP_TYPE_PATH = @"path";
42
+NSString *const RESP_TYPE_BASE64 = @"base64";
43
+NSString *const RESP_TYPE_UTF8 = @"utf8";
44
+NSString *const RESP_TYPE_PATH = @"path";

+ 2
- 2
ios/RNFetchBlobFS.h Просмотреть файл

@@ -34,8 +34,8 @@
34 34
     NSString * streamId;
35 35
 }
36 36
 
37
-@property (nonatomic) NSOutputStream * outStream;
38
-@property (nonatomic) NSInputStream * inStream;
37
+@property (nonatomic) NSOutputStream * _Nullable outStream;
38
+@property (nonatomic) NSInputStream * _Nullable inStream;
39 39
 @property (strong, nonatomic) RCTResponseSenderBlock callback;
40 40
 @property (nonatomic) RCTBridge * bridge;
41 41
 @property (nonatomic) NSString * encoding;

+ 16
- 28
ios/RNFetchBlobNetwork.h Просмотреть файл

@@ -6,9 +6,13 @@
6 6
 //  Copyright © 2016 wkh237. All rights reserved.
7 7
 //
8 8
 
9
+#ifndef RNFetchBlobNetwork_h
10
+#define RNFetchBlobNetwork_h
11
+
9 12
 #import <Foundation/Foundation.h>
10 13
 #import "RNFetchBlobProgress.h"
11 14
 #import "RNFetchBlobFS.h"
15
+#import "RNFetchBlobRequest.h"
12 16
 
13 17
 #if __has_include(<React/RCTAssert.h>)
14 18
 #import <React/RCTBridgeModule.h>
@@ -16,42 +20,26 @@
16 20
 #import "RCTBridgeModule.h"
17 21
 #endif
18 22
 
19
-#ifndef RNFetchBlobNetwork_h
20
-#define RNFetchBlobNetwork_h
21
-
22
-
23
-
24
-typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error);
25
-typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error);
26 23
 
27 24
 @interface RNFetchBlobNetwork : NSObject  <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
28 25
 
29
-@property (nullable, nonatomic) NSString * taskId;
30
-@property (nonatomic) int expectedBytes;
31
-@property (nonatomic) int receivedBytes;
32
-@property (nonatomic) BOOL isServerPush;
33
-@property (nullable, nonatomic) NSMutableData * respData;
34
-@property (strong, nonatomic) RCTResponseSenderBlock callback;
35
-@property (nullable, nonatomic) RCTBridge * bridge;
36
-@property (nullable, nonatomic) NSDictionary * options;
37
-@property (nullable, nonatomic) RNFetchBlobFS * fileStream;
38
-@property (strong, nonatomic) CompletionHander fileTaskCompletionHandler;
39
-@property (strong, nonatomic) DataTaskCompletionHander dataTaskCompletionHandler;
40
-@property (nullable, nonatomic) NSError * error;
41
-
26
+@property(nonnull, nonatomic) NSOperationQueue *taskQueue;
27
+@property(nonnull, nonatomic) NSMapTable<NSString*, RNFetchBlobRequest*> * requestsTable;
42 28
 
29
++ (RNFetchBlobNetwork* _Nullable)sharedInstance;
43 30
 + (NSMutableDictionary  * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers;
44
-+ (void) cancelRequest:(NSString *)taskId;
45
-+ (void) enableProgressReport:(NSString *) taskId;
46
-+ (void) enableUploadProgress:(NSString *) taskId;
47 31
 + (void) emitExpiredTasks;
48 32
 
49 33
 - (nullable id) init;
50
-- (void) sendRequest;
51
-- (void) sendRequest:(NSDictionary  * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback;
52
-+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config;
53
-+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config;
54
-
34
+- (void) sendRequest:(NSDictionary  * _Nullable )options
35
+       contentLength:(long)contentLength
36
+              bridge:(RCTBridge * _Nullable)bridgeRef
37
+              taskId:(NSString * _Nullable)taskId
38
+         withRequest:(NSURLRequest * _Nullable)req
39
+            callback:(_Nullable RCTResponseSenderBlock) callback;
40
+- (void) cancelRequest:(NSString * _Nonnull)taskId;
41
+- (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config;
42
+- (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config;
55 43
 
56 44
 
57 45
 @end

+ 64
- 551
ios/RNFetchBlobNetwork.m Просмотреть файл

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

+ 1
- 1
ios/RNFetchBlobReqBuilder.h Просмотреть файл

@@ -29,7 +29,7 @@
29 29
                      body:(NSString *)body
30 30
                onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete;
31 31
 
32
-+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableArray *) headers;
32
++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers;
33 33
 
34 34
 
35 35
 @end

+ 1
- 1
ios/RNFetchBlobReqBuilder.m Просмотреть файл

@@ -277,7 +277,7 @@
277 277
     }
278 278
 }
279 279
 
280
-+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableDictionary *) headers {
280
++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers {
281 281
 
282 282
     NSString * normalCase = [headers valueForKey:field];
283 283
     NSString * ignoredCase = [headers valueForKey:[field lowercaseString]];

+ 47
- 0
ios/RNFetchBlobRequest.h Просмотреть файл

@@ -0,0 +1,47 @@
1
+//
2
+//  RNFetchBlobRequest.h
3
+//  RNFetchBlob
4
+//
5
+//  Created by Artur Chrusciel on 15.01.18.
6
+//  Copyright © 2018 wkh237.github.io. All rights reserved.
7
+//
8
+
9
+#ifndef RNFetchBlobRequest_h
10
+#define RNFetchBlobRequest_h
11
+
12
+#import <Foundation/Foundation.h>
13
+
14
+#import "RNFetchBlobProgress.h"
15
+
16
+#if __has_include(<React/RCTAssert.h>)
17
+#import <React/RCTBridgeModule.h>
18
+#else
19
+#import "RCTBridgeModule.h"
20
+#endif
21
+
22
+@interface RNFetchBlobRequest : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
23
+
24
+@property (nullable, nonatomic) NSString * taskId;
25
+@property (nonatomic) long long expectedBytes;
26
+@property (nonatomic) long long receivedBytes;
27
+@property (nonatomic) BOOL isServerPush;
28
+@property (nullable, nonatomic) NSMutableData * respData;
29
+@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback;
30
+@property (nullable, nonatomic) RCTBridge * bridge;
31
+@property (nullable, nonatomic) NSDictionary * options;
32
+@property (nullable, nonatomic) NSError * error;
33
+@property (nullable, nonatomic) RNFetchBlobProgress *progressConfig;
34
+@property (nullable, nonatomic) RNFetchBlobProgress *uploadProgressConfig;
35
+@property (nullable, nonatomic, weak) NSURLSessionDataTask *task;
36
+
37
+- (void) sendRequest:(NSDictionary  * _Nullable )options
38
+       contentLength:(long)contentLength
39
+              bridge:(RCTBridge * _Nullable)bridgeRef
40
+              taskId:(NSString * _Nullable)taskId
41
+         withRequest:(NSURLRequest * _Nullable)req
42
+  taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue
43
+            callback:(_Nullable RCTResponseSenderBlock) callback;
44
+
45
+@end
46
+
47
+#endif /* RNFetchBlobRequest_h */

+ 469
- 0
ios/RNFetchBlobRequest.m Просмотреть файл

@@ -0,0 +1,469 @@
1
+//
2
+//  RNFetchBlobRequest.m
3
+//  RNFetchBlob
4
+//
5
+//  Created by Artur Chrusciel on 15.01.18.
6
+//  Copyright © 2018 wkh237.github.io. All rights reserved.
7
+//
8
+
9
+#import "RNFetchBlobRequest.h"
10
+
11
+#import "RNFetchBlobFS.h"
12
+#import "RNFetchBlobConst.h"
13
+#import "RNFetchBlobReqBuilder.h"
14
+
15
+#import "IOS7Polyfill.h"
16
+#import <CommonCrypto/CommonDigest.h>
17
+
18
+
19
+typedef NS_ENUM(NSUInteger, ResponseFormat) {
20
+    UTF8,
21
+    BASE64,
22
+    AUTO
23
+};
24
+
25
+@interface RNFetchBlobRequest ()
26
+{
27
+    BOOL respFile;
28
+    BOOL isNewPart;
29
+    BOOL isIncrement;
30
+    NSMutableData * partBuffer;
31
+    NSString * destPath;
32
+    NSOutputStream * writeStream;
33
+    long bodyLength;
34
+    NSInteger respStatus;
35
+    NSMutableArray * redirects;
36
+    ResponseFormat responseFormat;
37
+    BOOL followRedirect;
38
+    BOOL backgroundTask;
39
+}
40
+
41
+@end
42
+
43
+@implementation RNFetchBlobRequest
44
+
45
+@synthesize taskId;
46
+@synthesize expectedBytes;
47
+@synthesize receivedBytes;
48
+@synthesize respData;
49
+@synthesize callback;
50
+@synthesize bridge;
51
+@synthesize options;
52
+@synthesize error;
53
+
54
+
55
+- (NSString *)md5:(NSString *)input {
56
+    const char* str = [input UTF8String];
57
+    unsigned char result[CC_MD5_DIGEST_LENGTH];
58
+    CC_MD5(str, (CC_LONG)strlen(str), result);
59
+    
60
+    NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2];
61
+    for (int i = 0; i<CC_MD5_DIGEST_LENGTH; i++) {
62
+        [ret appendFormat:@"%02x",result[i]];
63
+    }
64
+    return ret;
65
+}
66
+
67
+// send HTTP request
68
+- (void) sendRequest:(__weak NSDictionary  * _Nullable )options
69
+       contentLength:(long) contentLength
70
+              bridge:(RCTBridge * _Nullable)bridgeRef
71
+              taskId:(NSString * _Nullable)taskId
72
+         withRequest:(__weak NSURLRequest * _Nullable)req
73
+  taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue
74
+            callback:(_Nullable RCTResponseSenderBlock) callback
75
+{
76
+    self.taskId = taskId;
77
+    self.respData = [[NSMutableData alloc] initWithLength:0];
78
+    self.callback = callback;
79
+    self.bridge = bridgeRef;
80
+    self.expectedBytes = 0;
81
+    self.receivedBytes = 0;
82
+    self.options = options;
83
+    
84
+    backgroundTask = [[options valueForKey:@"IOSBackgroundTask"] boolValue];
85
+    // when followRedirect not set in options, defaults to TRUE
86
+    followRedirect = [options valueForKey:@"followRedirect"] == nil ? YES : [[options valueForKey:@"followRedirect"] boolValue];
87
+    isIncrement = [[options valueForKey:@"increment"] boolValue];
88
+    redirects = [[NSMutableArray alloc] init];
89
+    
90
+    if (req.URL) {
91
+        [redirects addObject:req.URL.absoluteString];
92
+    }
93
+    
94
+    // set response format
95
+    NSString * rnfbResp = [req.allHTTPHeaderFields valueForKey:@"RNFB-Response"];
96
+    
97
+    if ([[rnfbResp lowercaseString] isEqualToString:@"base64"]) {
98
+        responseFormat = BASE64;
99
+    } else if ([[rnfbResp lowercaseString] isEqualToString:@"utf8"]) {
100
+        responseFormat = UTF8;
101
+    } else {
102
+        responseFormat = AUTO;
103
+    }
104
+    
105
+    NSString * path = [self.options valueForKey:CONFIG_FILE_PATH];
106
+    NSString * key = [self.options valueForKey:CONFIG_KEY];
107
+    NSURLSession * session;
108
+    
109
+    bodyLength = contentLength;
110
+    
111
+    // the session trust any SSL certification
112
+    NSURLSessionConfiguration *defaultConfigObject;
113
+    
114
+    defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
115
+    
116
+    if (backgroundTask) {
117
+        defaultConfigObject = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:taskId];
118
+    }
119
+    
120
+    // request timeout, -1 if not set in options
121
+    float timeout = [options valueForKey:@"timeout"] == nil ? -1 : [[options valueForKey:@"timeout"] floatValue];
122
+    
123
+    if (timeout > 0) {
124
+        defaultConfigObject.timeoutIntervalForRequest = timeout/1000;
125
+    }
126
+    
127
+    defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
128
+    session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
129
+    
130
+    if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
131
+        respFile = YES;
132
+        
133
+        NSString* cacheKey = taskId;
134
+        if (key) {
135
+            cacheKey = [self md5:key];
136
+            
137
+            if (!cacheKey) {
138
+                cacheKey = taskId;
139
+            }
140
+            
141
+            destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
142
+            
143
+            if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
144
+                callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
145
+                
146
+                return;
147
+            }
148
+        }
149
+        
150
+        if (path) {
151
+            destPath = path;
152
+        } else {
153
+            destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
154
+        }
155
+    } else {
156
+        respData = [[NSMutableData alloc] init];
157
+        respFile = NO;
158
+    }
159
+    
160
+    self.task = [session dataTaskWithRequest:req];
161
+    [self.task resume];
162
+    
163
+    // network status indicator
164
+    if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
165
+        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
166
+    }
167
+}
168
+
169
+////////////////////////////////////////
170
+//
171
+//  NSURLSession delegates
172
+//
173
+////////////////////////////////////////
174
+
175
+
176
+#pragma mark NSURLSession delegate methods
177
+
178
+
179
+#pragma mark - Received Response
180
+// set expected content length on response received
181
+- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
182
+{
183
+    expectedBytes = [response expectedContentLength];
184
+    
185
+    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
186
+    NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
187
+    NSString * respType = @"";
188
+    respStatus = statusCode;
189
+    
190
+    if ([response respondsToSelector:@selector(allHeaderFields)])
191
+    {
192
+        NSDictionary *headers = [httpResponse allHeaderFields];
193
+        NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
194
+        
195
+        if (self.isServerPush) {
196
+            if (partBuffer) {
197
+                [self.bridge.eventDispatcher
198
+                 sendDeviceEventWithName:EVENT_SERVER_PUSH
199
+                 body:@{
200
+                        @"taskId": taskId,
201
+                        @"chunk": [partBuffer base64EncodedStringWithOptions:0],
202
+                        }
203
+                 ];
204
+            }
205
+            
206
+            partBuffer = [[NSMutableData alloc] init];
207
+            completionHandler(NSURLSessionResponseAllow);
208
+
209
+            return;
210
+        } else {
211
+            self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"];
212
+        }
213
+        
214
+        if(respCType)
215
+        {
216
+            NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
217
+            
218
+            if ([respCType RNFBContainsString:@"text/"]) {
219
+                respType = @"text";
220
+            } else if ([respCType RNFBContainsString:@"application/json"]) {
221
+                respType = @"json";
222
+            } else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches
223
+                for (NSString * substr in extraBlobCTypes) {
224
+                    if ([respCType RNFBContainsString:[substr lowercaseString]]) {
225
+                        respType = @"blob";
226
+                        respFile = YES;
227
+                        destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil];
228
+                        break;
229
+                    }
230
+                }
231
+            } else {
232
+                respType = @"blob";
233
+                
234
+                // for XMLHttpRequest, switch response data handling strategy automatically
235
+                if ([options valueForKey:@"auto"]) {
236
+                    respFile = YES;
237
+                    destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""];
238
+                }
239
+            }
240
+        } else {
241
+            respType = @"text";
242
+        }
243
+        
244
+#pragma mark - handling cookies
245
+        // # 153 get cookies
246
+        if (response.URL) {
247
+            NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
248
+            NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
249
+            if (cookies.count) {
250
+                [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
251
+            }
252
+        }
253
+        
254
+        [self.bridge.eventDispatcher
255
+         sendDeviceEventWithName: EVENT_STATE_CHANGE
256
+         body:@{
257
+                @"taskId": taskId,
258
+                @"state": @"2",
259
+                @"headers": headers,
260
+                @"redirects": redirects,
261
+                @"respType" : respType,
262
+                @"timeout" : @NO,
263
+                @"status": [NSNumber numberWithInteger:statusCode]
264
+                }
265
+         ];
266
+    } else {
267
+        NSLog(@"oops");
268
+    }
269
+    
270
+    if (respFile)
271
+    {
272
+        @try{
273
+            NSFileManager * fm = [NSFileManager defaultManager];
274
+            NSString * folder = [destPath stringByDeletingLastPathComponent];
275
+            
276
+            if (![fm fileExistsAtPath:folder]) {
277
+                [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
278
+            }
279
+            
280
+            // if not set overwrite in options, defaults to TRUE
281
+            BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
282
+            BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"];
283
+            
284
+            appendToExistingFile = !overwrite;
285
+            
286
+            // For solving #141 append response data if the file already exists
287
+            // base on PR#139 @kejinliang
288
+            if (appendToExistingFile) {
289
+                destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
290
+            }
291
+            
292
+            if (![fm fileExistsAtPath:destPath]) {
293
+                [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
294
+            }
295
+            
296
+            writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
297
+            [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
298
+            [writeStream open];
299
+        }
300
+        @catch(NSException * ex)
301
+        {
302
+            NSLog(@"write file error");
303
+        }
304
+    }
305
+    
306
+    completionHandler(NSURLSessionResponseAllow);
307
+}
308
+
309
+
310
+// download progress handler
311
+- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
312
+{
313
+    // For #143 handling multipart/x-mixed-replace response
314
+    if (self.isServerPush)
315
+    {
316
+        [partBuffer appendData:data];
317
+        
318
+        return ;
319
+    }
320
+    
321
+    NSNumber * received = [NSNumber numberWithLong:[data length]];
322
+    receivedBytes += [received longValue];
323
+    NSString * chunkString = @"";
324
+    
325
+    if (isIncrement) {
326
+        chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
327
+    }
328
+    
329
+    if (respFile) {
330
+        [writeStream write:[data bytes] maxLength:[data length]];
331
+    } else {
332
+        [respData appendData:data];
333
+    }
334
+    
335
+    if (expectedBytes == 0) {
336
+        return;
337
+    }
338
+    
339
+    NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
340
+    
341
+    if ([self.progressConfig shouldReport:now]) {
342
+        [self.bridge.eventDispatcher
343
+         sendDeviceEventWithName:EVENT_PROGRESS
344
+         body:@{
345
+                @"taskId": taskId,
346
+                @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes],
347
+                @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes],
348
+                @"chunk": chunkString
349
+                }
350
+         ];
351
+    }
352
+}
353
+
354
+- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
355
+{
356
+    if ([session isEqual:session]) {
357
+        session = nil;
358
+    }
359
+}
360
+
361
+
362
+- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
363
+{
364
+    
365
+    self.error = error;
366
+    NSString * errMsg;
367
+    NSString * respStr;
368
+    NSString * rnfbRespType;
369
+    
370
+    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
371
+    
372
+    if (error) {
373
+        errMsg = [error localizedDescription];
374
+    }
375
+    
376
+    if (respFile) {
377
+        [writeStream close];
378
+        rnfbRespType = RESP_TYPE_PATH;
379
+        respStr = destPath;
380
+    } else { // base64 response
381
+        // #73 fix unicode data encoding issue :
382
+        // when response type is BASE64, we should first try to encode the response data to UTF8 format
383
+        // if it turns out not to be `nil` that means the response data contains valid UTF8 string,
384
+        // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
385
+        NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
386
+        
387
+        if (responseFormat == BASE64) {
388
+            rnfbRespType = RESP_TYPE_BASE64;
389
+            respStr = [respData base64EncodedStringWithOptions:0];
390
+        } else if (responseFormat == UTF8) {
391
+            rnfbRespType = RESP_TYPE_UTF8;
392
+            respStr = utf8;
393
+        } else {
394
+            if (utf8) {
395
+                rnfbRespType = RESP_TYPE_UTF8;
396
+                respStr = utf8;
397
+            } else {
398
+                rnfbRespType = RESP_TYPE_BASE64;
399
+                respStr = [respData base64EncodedStringWithOptions:0];
400
+            }
401
+        }
402
+    }
403
+    
404
+    
405
+    callback(@[
406
+               errMsg ?: [NSNull null],
407
+               rnfbRespType ?: @"",
408
+               respStr ?: [NSNull null]
409
+               ]);
410
+    
411
+    respData = nil;
412
+    receivedBytes = 0;
413
+    [session finishTasksAndInvalidate];
414
+    
415
+}
416
+
417
+// upload progress handler
418
+- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
419
+{
420
+    if (totalBytesExpectedToWrite == 0) {
421
+        return;
422
+    }
423
+    
424
+    NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
425
+
426
+    if ([self.uploadProgressConfig shouldReport:now]) {
427
+        [self.bridge.eventDispatcher
428
+         sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD
429
+         body:@{
430
+                @"taskId": taskId,
431
+                @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten],
432
+                @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite]
433
+                }
434
+         ];
435
+    }
436
+}
437
+
438
+
439
+- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
440
+{
441
+    if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
442
+        completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
443
+    } else {
444
+        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
445
+    }
446
+}
447
+
448
+
449
+- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
450
+{
451
+    NSLog(@"sess done in background");
452
+}
453
+
454
+- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
455
+{
456
+    
457
+    if (followRedirect) {
458
+        if (request.URL) {
459
+            [redirects addObject:[request.URL absoluteString]];
460
+        }
461
+        
462
+        completionHandler(request);
463
+    } else {
464
+        completionHandler(nil);
465
+    }
466
+}
467
+
468
+
469
+@end