react-native-blob-util 0.22.0 → 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +56 -56
- package/LICENSE +21 -21
- package/Migration.md +41 -41
- package/NuGet.config +11 -11
- package/README.md +1109 -1104
- package/android/gradle.properties +1 -1
- package/android/src/main/AndroidManifest.xml +37 -37
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConfig.java +57 -57
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilConst.java +20 -20
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilFileTransformer.java +9 -9
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilImpl.java +431 -431
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilProgressConfig.java +39 -39
- package/android/src/main/java/com/ReactNativeBlobUtil/ReactNativeBlobUtilReq.java +1014 -1014
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilDefaultResp.java +98 -98
- package/android/src/main/java/com/ReactNativeBlobUtil/Response/ReactNativeBlobUtilFileResp.java +166 -166
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileDescription.java +19 -19
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/FileProvider.java +4 -4
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/MimeType.java +70 -70
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/PathResolver.java +223 -223
- package/android/src/main/java/com/ReactNativeBlobUtil/Utils/Tls12SocketFactory.java +68 -68
- package/android/src/main/res/values/strings.xml +3 -3
- package/android/src/main/res/xml/provider_paths.xml +12 -12
- package/android.js +58 -58
- package/class/ReactNativeBlobUtilCanceledFetchError.js +9 -9
- package/class/ReactNativeBlobUtilFile.js +5 -5
- package/class/ReactNativeBlobUtilReadStream.js +84 -84
- package/class/ReactNativeBlobUtilSession.js +68 -68
- package/class/ReactNativeBlobUtilWriteStream.js +50 -50
- package/class/StatefulPromise.js +7 -7
- package/codegenSpecs/NativeBlobUtils.js +86 -86
- package/components/Fetch.onPress.js +10 -10
- package/components/Fetch.when.js +15 -15
- package/fetch.js +350 -350
- package/fs.js +480 -480
- package/index.js +64 -64
- package/index.js.flow +192 -192
- package/index.web.js +1 -1
- package/ios/PrivacyInfo.xcprivacy +28 -28
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.h +55 -55
- package/ios/ReactNativeBlobUtil/ReactNativeBlobUtil.mm +992 -992
- package/ios/ReactNativeBlobUtil.xcodeproj/project.pbxproj +405 -405
- package/ios/ReactNativeBlobUtilConst.h +58 -58
- package/ios/ReactNativeBlobUtilConst.mm +47 -47
- package/ios/ReactNativeBlobUtilFS.h +108 -108
- package/ios/ReactNativeBlobUtilFS.mm +1064 -1064
- package/ios/ReactNativeBlobUtilFileTransformer.h +23 -23
- package/ios/ReactNativeBlobUtilFileTransformer.mm +20 -20
- package/ios/ReactNativeBlobUtilNetwork.h +49 -49
- package/ios/ReactNativeBlobUtilNetwork.mm +159 -159
- package/ios/ReactNativeBlobUtilProgress.h +32 -32
- package/ios/ReactNativeBlobUtilProgress.mm +57 -57
- package/ios/ReactNativeBlobUtilReqBuilder.h +37 -37
- package/ios/ReactNativeBlobUtilReqBuilder.mm +292 -292
- package/ios/ReactNativeBlobUtilRequest.h +48 -48
- package/ios/ReactNativeBlobUtilRequest.mm +584 -584
- package/ios.js +61 -61
- package/json-stream.js +42 -42
- package/lib/oboe-browser.js +2703 -2703
- package/mediacollection.js +36 -36
- package/package.json +68 -68
- package/polyfill/Blob.js +362 -362
- package/polyfill/Event.js +11 -11
- package/polyfill/EventTarget.js +78 -78
- package/polyfill/Fetch.js +219 -219
- package/polyfill/File.js +27 -27
- package/polyfill/FileReader.js +91 -91
- package/polyfill/ProgressEvent.js +32 -32
- package/polyfill/XMLHttpRequest.js +466 -466
- package/polyfill/XMLHttpRequestEventTarget.js +126 -126
- package/polyfill/index.js +11 -11
- package/react-native-blob-util.podspec +48 -48
- package/types.js +69 -69
- package/utils/log.js +40 -40
- package/utils/unicode.js +7 -7
- package/utils/uri.js +33 -33
- package/utils/uuid.js +4 -4
- package/windows/ExperimentalFeatures.props +33 -33
- package/windows/README.md +2 -2
- package/windows/ReactNativeBlobUtil/PropertySheet.props +15 -15
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.cpp +1836 -1836
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.def +3 -3
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.h +372 -372
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj +139 -139
- package/windows/ReactNativeBlobUtil/ReactNativeBlobUtil.vcxproj.filters +43 -43
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.cpp +20 -20
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.h +24 -24
- package/windows/ReactNativeBlobUtil/ReactPackageProvider.idl +9 -9
- package/windows/ReactNativeBlobUtil/codegen/.clang-format +1 -1
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsDataTypes.g.h +42 -42
- package/windows/ReactNativeBlobUtil/codegen/NativeBlobUtilsSpec.g.h +353 -353
- package/windows/ReactNativeBlobUtil/packages.config +3 -3
- package/windows/ReactNativeBlobUtil/packages.lock.json +59 -59
- package/windows/ReactNativeBlobUtil/pch.cpp +1 -1
- package/windows/ReactNativeBlobUtil/pch.h +30 -30
- package/windows/ReactNativeBlobUtil/resource.h +5 -5
- package/windows/ReactNativeBlobUtil/targetver.h +8 -8
- package/windows/ReactNativeBlobUtil.sln +43 -43
|
@@ -1,584 +1,584 @@
|
|
|
1
|
-
//
|
|
2
|
-
// ReactNativeBlobUtilRequest.m
|
|
3
|
-
// ReactNativeBlobUtil
|
|
4
|
-
//
|
|
5
|
-
// Created by Artur Chrusciel on 15.01.18.
|
|
6
|
-
// Copyright © 2018 wkh237.github.io. All rights reserved.
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
#import "ReactNativeBlobUtilRequest.h"
|
|
10
|
-
|
|
11
|
-
#import "ReactNativeBlobUtilFS.h"
|
|
12
|
-
#import "ReactNativeBlobUtilConst.h"
|
|
13
|
-
#import "ReactNativeBlobUtilFileTransformer.h"
|
|
14
|
-
#import "ReactNativeBlobUtilReqBuilder.h"
|
|
15
|
-
|
|
16
|
-
#import <CommonCrypto/CommonDigest.h>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
typedef NS_ENUM(NSUInteger, ResponseFormat) {
|
|
20
|
-
UTF8,
|
|
21
|
-
BASE64,
|
|
22
|
-
AUTO
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
@interface ReactNativeBlobUtilRequest ()
|
|
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 ReactNativeBlobUtilRequest
|
|
44
|
-
|
|
45
|
-
@synthesize taskId;
|
|
46
|
-
@synthesize expectedBytes;
|
|
47
|
-
@synthesize receivedBytes;
|
|
48
|
-
@synthesize respData;
|
|
49
|
-
@synthesize callback;
|
|
50
|
-
@synthesize baseModule;
|
|
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
|
-
baseModule:(__strong ReactNativeBlobUtil * _Nullable)baseModule
|
|
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.baseModule = baseModule;
|
|
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
|
-
if([options valueForKey:CONFIG_WIFI_ONLY] != nil && [options[CONFIG_WIFI_ONLY] boolValue]){
|
|
128
|
-
[defaultConfigObject setAllowsCellularAccess:NO];
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
|
|
132
|
-
session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
|
|
133
|
-
|
|
134
|
-
if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
|
|
135
|
-
respFile = YES;
|
|
136
|
-
|
|
137
|
-
NSString* cacheKey = taskId;
|
|
138
|
-
if (key) {
|
|
139
|
-
cacheKey = [self md5:key];
|
|
140
|
-
|
|
141
|
-
if (!cacheKey) {
|
|
142
|
-
cacheKey = taskId;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
|
146
|
-
|
|
147
|
-
if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
|
|
148
|
-
callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
|
|
149
|
-
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (path) {
|
|
155
|
-
destPath = path;
|
|
156
|
-
} else {
|
|
157
|
-
destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// We still need to initialize this as a placeholder in memory before we perform
|
|
161
|
-
// the conversion
|
|
162
|
-
if ([self ShouldTransformFile]) {
|
|
163
|
-
respData = [[NSMutableData alloc] init];
|
|
164
|
-
}
|
|
165
|
-
} else {
|
|
166
|
-
respData = [[NSMutableData alloc] init];
|
|
167
|
-
respFile = NO;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if(backgroundTask) {
|
|
171
|
-
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:req];
|
|
172
|
-
[task resume];
|
|
173
|
-
self.task = task;
|
|
174
|
-
} else {
|
|
175
|
-
NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
|
|
176
|
-
[task resume];
|
|
177
|
-
self.task = task;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// network status indicator
|
|
181
|
-
if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
|
|
182
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
183
|
-
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
////////////////////////////////////////
|
|
189
|
-
//
|
|
190
|
-
// NSURLSession delegates
|
|
191
|
-
//
|
|
192
|
-
////////////////////////////////////////
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
#pragma mark NSURLSession delegate methods
|
|
196
|
-
|
|
197
|
-
- (void)configureWriteStream {
|
|
198
|
-
if (respFile)
|
|
199
|
-
{
|
|
200
|
-
@try{
|
|
201
|
-
NSFileManager * fm = [NSFileManager defaultManager];
|
|
202
|
-
NSString * folder = [destPath stringByDeletingLastPathComponent];
|
|
203
|
-
|
|
204
|
-
if (![fm fileExistsAtPath:folder]) {
|
|
205
|
-
[fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// if not set overwrite in options, defaults to TRUE
|
|
209
|
-
BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
|
|
210
|
-
BOOL appendToExistingFile = [destPath containsString:@"?append=true"];
|
|
211
|
-
|
|
212
|
-
appendToExistingFile = !overwrite;
|
|
213
|
-
|
|
214
|
-
// For solving #141 append response data if the file already exists
|
|
215
|
-
// base on PR#139 @kejinliang
|
|
216
|
-
if (appendToExistingFile) {
|
|
217
|
-
destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
if (![fm fileExistsAtPath:destPath]) {
|
|
221
|
-
[fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
|
|
225
|
-
[writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
|
226
|
-
[writeStream open];
|
|
227
|
-
}
|
|
228
|
-
@catch(NSException * ex)
|
|
229
|
-
{
|
|
230
|
-
NSLog(@"write file error");
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
- (void)processData:(NSData *)data {
|
|
236
|
-
if (respFile && ![self ShouldTransformFile]) {
|
|
237
|
-
[writeStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
|
|
238
|
-
} else {
|
|
239
|
-
[respData appendData:data];
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
#pragma mark - Received Response
|
|
245
|
-
// set expected content length on response received
|
|
246
|
-
- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
|
|
247
|
-
{
|
|
248
|
-
expectedBytes = [response expectedContentLength];
|
|
249
|
-
|
|
250
|
-
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
|
|
251
|
-
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
|
|
252
|
-
NSString * respType = @"";
|
|
253
|
-
respStatus = statusCode;
|
|
254
|
-
|
|
255
|
-
if ([response respondsToSelector:@selector(allHeaderFields)])
|
|
256
|
-
{
|
|
257
|
-
NSDictionary *headers = [httpResponse allHeaderFields];
|
|
258
|
-
NSString * respCType = [[ReactNativeBlobUtilReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
|
|
259
|
-
|
|
260
|
-
if (self.isServerPush) {
|
|
261
|
-
if (partBuffer) {
|
|
262
|
-
[self.baseModule
|
|
263
|
-
emitEventDict:EVENT_SERVER_PUSH
|
|
264
|
-
body:@{
|
|
265
|
-
@"taskId": taskId,
|
|
266
|
-
@"chunk": [partBuffer base64EncodedStringWithOptions:0],
|
|
267
|
-
}
|
|
268
|
-
];
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
partBuffer = [[NSMutableData alloc] init];
|
|
272
|
-
completionHandler(NSURLSessionResponseAllow);
|
|
273
|
-
|
|
274
|
-
return;
|
|
275
|
-
} else {
|
|
276
|
-
self.isServerPush = [[respCType lowercaseString] containsString:@"multipart/x-mixed-replace;"];
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if(respCType)
|
|
280
|
-
{
|
|
281
|
-
NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
|
|
282
|
-
|
|
283
|
-
if ([respCType containsString:@"text/"]) {
|
|
284
|
-
respType = @"text";
|
|
285
|
-
} else if ([respCType containsString:@"application/json"]) {
|
|
286
|
-
respType = @"json";
|
|
287
|
-
} else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches
|
|
288
|
-
for (NSString * substr in extraBlobCTypes) {
|
|
289
|
-
if ([respCType containsString:[substr lowercaseString]]) {
|
|
290
|
-
respType = @"blob";
|
|
291
|
-
respFile = YES;
|
|
292
|
-
destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:nil];
|
|
293
|
-
break;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
} else {
|
|
297
|
-
respType = @"blob";
|
|
298
|
-
|
|
299
|
-
// for XMLHttpRequest, switch response data handling strategy automatically
|
|
300
|
-
if ([options valueForKey:@"auto"]) {
|
|
301
|
-
respFile = YES;
|
|
302
|
-
destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:@""];
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
} else {
|
|
306
|
-
respType = @"text";
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
#pragma mark - handling cookies
|
|
310
|
-
// # 153 get cookies
|
|
311
|
-
if (response.URL) {
|
|
312
|
-
NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
|
313
|
-
NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
|
|
314
|
-
if (cookies.count) {
|
|
315
|
-
[cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
[self.baseModule emitEventDict : EVENT_STATE_CHANGE
|
|
319
|
-
body:@{
|
|
320
|
-
@"taskId": taskId,
|
|
321
|
-
@"state": @"2",
|
|
322
|
-
@"headers": headers,
|
|
323
|
-
@"redirects": redirects,
|
|
324
|
-
@"respType" : respType,
|
|
325
|
-
@"timeout" : @NO,
|
|
326
|
-
@"status": [NSNumber numberWithInteger:statusCode]
|
|
327
|
-
}
|
|
328
|
-
];
|
|
329
|
-
} else {
|
|
330
|
-
NSLog(@"oops");
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
[self configureWriteStream];
|
|
334
|
-
|
|
335
|
-
completionHandler(NSURLSessionResponseAllow);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
// download progress handler
|
|
340
|
-
- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
|
341
|
-
{
|
|
342
|
-
// For #143 handling multipart/x-mixed-replace response
|
|
343
|
-
if (self.isServerPush)
|
|
344
|
-
{
|
|
345
|
-
[partBuffer appendData:data];
|
|
346
|
-
|
|
347
|
-
return ;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
NSNumber * received = [NSNumber numberWithLong:[data length]];
|
|
351
|
-
receivedBytes += [received longValue];
|
|
352
|
-
NSString * chunkString = @"";
|
|
353
|
-
|
|
354
|
-
if (isIncrement) {
|
|
355
|
-
chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// If we need to process the data, we defer writing into the file until the we have all the data, at which point
|
|
359
|
-
// we can perform the processing and then write into the file
|
|
360
|
-
[self processData:data];
|
|
361
|
-
|
|
362
|
-
if (expectedBytes == 0) {
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// For non-chunked download, progress is received / expected
|
|
367
|
-
// For chunked download, progress can be either 0 (started) or 1 (ended)
|
|
368
|
-
NSNumber *now;
|
|
369
|
-
if (expectedBytes != NSURLResponseUnknownLength) {
|
|
370
|
-
now = [NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
|
|
371
|
-
} else {
|
|
372
|
-
now = @0;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
if ([self.progressConfig shouldReport:now]) {
|
|
376
|
-
NSDictionary *body;
|
|
377
|
-
if (expectedBytes == NSURLResponseUnknownLength) {
|
|
378
|
-
// For chunked downloads
|
|
379
|
-
body = @{
|
|
380
|
-
@"taskId": taskId,
|
|
381
|
-
@"written": [NSString stringWithFormat:@"%d", 0],
|
|
382
|
-
@"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
|
|
383
|
-
@"chunk": chunkString,
|
|
384
|
-
};
|
|
385
|
-
} else {
|
|
386
|
-
// For non-chunked downloads
|
|
387
|
-
body = @{
|
|
388
|
-
@"taskId": taskId,
|
|
389
|
-
@"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
390
|
-
@"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
|
|
391
|
-
@"chunk": chunkString,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
[self.baseModule emitEventDict:EVENT_PROGRESS body:body];
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
|
|
399
|
-
{
|
|
400
|
-
if ([session isEqual:session]) {
|
|
401
|
-
session = nil;
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
407
|
-
{
|
|
408
|
-
|
|
409
|
-
self.error = error;
|
|
410
|
-
NSString * errMsg;
|
|
411
|
-
NSString * respStr;
|
|
412
|
-
NSString * rnfbRespType;
|
|
413
|
-
|
|
414
|
-
// only run this if we were requested to change it
|
|
415
|
-
if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
|
|
416
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
417
|
-
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
|
418
|
-
});
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (error) {
|
|
422
|
-
if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) {
|
|
423
|
-
errMsg = @"task cancelled";
|
|
424
|
-
} else {
|
|
425
|
-
errMsg = [error localizedDescription];
|
|
426
|
-
}
|
|
427
|
-
} else if ([self.progressConfig shouldReport:@1] && expectedBytes == NSURLResponseUnknownLength) {
|
|
428
|
-
// For chunked downloads
|
|
429
|
-
[self.baseModule emitEventDict:EVENT_PROGRESS body:@{
|
|
430
|
-
@"taskId": taskId,
|
|
431
|
-
@"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
432
|
-
@"total": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
433
|
-
@"chunk": @"",
|
|
434
|
-
}];
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
if (respFile) {
|
|
438
|
-
if ([self ShouldTransformFile]) {
|
|
439
|
-
// At this point, we have the data that we deferred to write into a file. We can now
|
|
440
|
-
// perform the conversion and write the converted data into the file.
|
|
441
|
-
NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
|
|
442
|
-
if (!fileTransformer) {
|
|
443
|
-
errMsg = @"Transform file specified but file transfomer not set";
|
|
444
|
-
} else {
|
|
445
|
-
@try{
|
|
446
|
-
NSData* transformedData = [fileTransformer onWriteFile:respData];
|
|
447
|
-
[writeStream write:(const uint8_t *)[transformedData bytes] maxLength:[transformedData length]];
|
|
448
|
-
} @catch(NSException * ex)
|
|
449
|
-
{
|
|
450
|
-
errMsg = [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]];
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
[writeStream close];
|
|
455
|
-
rnfbRespType = RESP_TYPE_PATH;
|
|
456
|
-
respStr = destPath;
|
|
457
|
-
} else { // base64 response
|
|
458
|
-
// #73 fix unicode data encoding issue :
|
|
459
|
-
// when response type is BASE64, we should first try to encode the response data to UTF8 format
|
|
460
|
-
// if it turns out not to be `nil` that means the response data contains valid UTF8 string,
|
|
461
|
-
// in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
|
|
462
|
-
NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
|
|
463
|
-
|
|
464
|
-
if (responseFormat == BASE64) {
|
|
465
|
-
rnfbRespType = RESP_TYPE_BASE64;
|
|
466
|
-
respStr = [respData base64EncodedStringWithOptions:0];
|
|
467
|
-
} else if (responseFormat == UTF8) {
|
|
468
|
-
rnfbRespType = RESP_TYPE_UTF8;
|
|
469
|
-
respStr = utf8;
|
|
470
|
-
} else {
|
|
471
|
-
if (utf8) {
|
|
472
|
-
rnfbRespType = RESP_TYPE_UTF8;
|
|
473
|
-
respStr = utf8;
|
|
474
|
-
} else {
|
|
475
|
-
rnfbRespType = RESP_TYPE_BASE64;
|
|
476
|
-
respStr = [respData base64EncodedStringWithOptions:0];
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
NSHTTPURLResponse *response = (NSHTTPURLResponse *) [task response];
|
|
482
|
-
|
|
483
|
-
callback(@[
|
|
484
|
-
errMsg ?: [NSNull null],
|
|
485
|
-
rnfbRespType ?: @"",
|
|
486
|
-
respStr ?: [NSNull null],
|
|
487
|
-
@{
|
|
488
|
-
@"status": [NSNumber numberWithInteger:[response statusCode]]
|
|
489
|
-
}
|
|
490
|
-
]);
|
|
491
|
-
|
|
492
|
-
respData = nil;
|
|
493
|
-
receivedBytes = 0;
|
|
494
|
-
[session finishTasksAndInvalidate];
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
- (BOOL) ShouldTransformFile{
|
|
499
|
-
return [[options valueForKey:CONFIG_TRANSFORM_FILE] boolValue];
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// upload progress handler
|
|
503
|
-
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
|
|
504
|
-
{
|
|
505
|
-
if (totalBytesExpectedToWrite == 0) {
|
|
506
|
-
return;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
|
|
510
|
-
|
|
511
|
-
if ([self.uploadProgressConfig shouldReport:now]) {
|
|
512
|
-
[self.baseModule emitEventDict:EVENT_PROGRESS_UPLOAD
|
|
513
|
-
body:@{
|
|
514
|
-
@"taskId": taskId,
|
|
515
|
-
@"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten],
|
|
516
|
-
@"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite]
|
|
517
|
-
}
|
|
518
|
-
];
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
|
|
524
|
-
{
|
|
525
|
-
if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
|
|
526
|
-
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
|
527
|
-
} else {
|
|
528
|
-
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
|
|
534
|
-
{
|
|
535
|
-
NSLog(@"sess done in background");
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
|
|
539
|
-
{
|
|
540
|
-
|
|
541
|
-
if (followRedirect) {
|
|
542
|
-
if (request.URL) {
|
|
543
|
-
[redirects addObject:[request.URL absoluteString]];
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
completionHandler(request);
|
|
547
|
-
} else {
|
|
548
|
-
completionHandler(nil);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
// NSURLSessionDownloadTask delegates
|
|
553
|
-
|
|
554
|
-
#pragma mark NSURLSessionDownloadTask delegate methods
|
|
555
|
-
|
|
556
|
-
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
|
557
|
-
|
|
558
|
-
NSFileManager *fm = [NSFileManager defaultManager];
|
|
559
|
-
NSData *data = [fm contentsAtPath:location.path];
|
|
560
|
-
|
|
561
|
-
[self configureWriteStream];
|
|
562
|
-
|
|
563
|
-
[self processData:data];
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
|
568
|
-
if (totalBytesExpectedToWrite == 0) {
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
NSNumber * now =[NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
|
|
573
|
-
if ([self.progressConfig shouldReport:now]) {
|
|
574
|
-
[self.baseModule emitEventDict:EVENT_PROGRESS
|
|
575
|
-
body:@{
|
|
576
|
-
@"taskId": taskId,
|
|
577
|
-
@"written": [NSString stringWithFormat:@"%lld", (long long) totalBytesWritten],
|
|
578
|
-
@"total": [NSString stringWithFormat:@"%lld", (long long) totalBytesExpectedToWrite]
|
|
579
|
-
}
|
|
580
|
-
];
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
@end
|
|
1
|
+
//
|
|
2
|
+
// ReactNativeBlobUtilRequest.m
|
|
3
|
+
// ReactNativeBlobUtil
|
|
4
|
+
//
|
|
5
|
+
// Created by Artur Chrusciel on 15.01.18.
|
|
6
|
+
// Copyright © 2018 wkh237.github.io. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import "ReactNativeBlobUtilRequest.h"
|
|
10
|
+
|
|
11
|
+
#import "ReactNativeBlobUtilFS.h"
|
|
12
|
+
#import "ReactNativeBlobUtilConst.h"
|
|
13
|
+
#import "ReactNativeBlobUtilFileTransformer.h"
|
|
14
|
+
#import "ReactNativeBlobUtilReqBuilder.h"
|
|
15
|
+
|
|
16
|
+
#import <CommonCrypto/CommonDigest.h>
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
typedef NS_ENUM(NSUInteger, ResponseFormat) {
|
|
20
|
+
UTF8,
|
|
21
|
+
BASE64,
|
|
22
|
+
AUTO
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
@interface ReactNativeBlobUtilRequest ()
|
|
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 ReactNativeBlobUtilRequest
|
|
44
|
+
|
|
45
|
+
@synthesize taskId;
|
|
46
|
+
@synthesize expectedBytes;
|
|
47
|
+
@synthesize receivedBytes;
|
|
48
|
+
@synthesize respData;
|
|
49
|
+
@synthesize callback;
|
|
50
|
+
@synthesize baseModule;
|
|
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
|
+
baseModule:(__strong ReactNativeBlobUtil * _Nullable)baseModule
|
|
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.baseModule = baseModule;
|
|
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
|
+
if([options valueForKey:CONFIG_WIFI_ONLY] != nil && [options[CONFIG_WIFI_ONLY] boolValue]){
|
|
128
|
+
[defaultConfigObject setAllowsCellularAccess:NO];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
defaultConfigObject.HTTPMaximumConnectionsPerHost = 10;
|
|
132
|
+
session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue];
|
|
133
|
+
|
|
134
|
+
if (path || [self.options valueForKey:CONFIG_USE_TEMP]) {
|
|
135
|
+
respFile = YES;
|
|
136
|
+
|
|
137
|
+
NSString* cacheKey = taskId;
|
|
138
|
+
if (key) {
|
|
139
|
+
cacheKey = [self md5:key];
|
|
140
|
+
|
|
141
|
+
if (!cacheKey) {
|
|
142
|
+
cacheKey = taskId;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
|
146
|
+
|
|
147
|
+
if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) {
|
|
148
|
+
callback(@[[NSNull null], RESP_TYPE_PATH, destPath]);
|
|
149
|
+
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (path) {
|
|
155
|
+
destPath = path;
|
|
156
|
+
} else {
|
|
157
|
+
destPath = [ReactNativeBlobUtilFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]];
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// We still need to initialize this as a placeholder in memory before we perform
|
|
161
|
+
// the conversion
|
|
162
|
+
if ([self ShouldTransformFile]) {
|
|
163
|
+
respData = [[NSMutableData alloc] init];
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
respData = [[NSMutableData alloc] init];
|
|
167
|
+
respFile = NO;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if(backgroundTask) {
|
|
171
|
+
NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:req];
|
|
172
|
+
[task resume];
|
|
173
|
+
self.task = task;
|
|
174
|
+
} else {
|
|
175
|
+
NSURLSessionDataTask *task = [session dataTaskWithRequest:req];
|
|
176
|
+
[task resume];
|
|
177
|
+
self.task = task;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// network status indicator
|
|
181
|
+
if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
|
|
182
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
183
|
+
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
////////////////////////////////////////
|
|
189
|
+
//
|
|
190
|
+
// NSURLSession delegates
|
|
191
|
+
//
|
|
192
|
+
////////////////////////////////////////
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
#pragma mark NSURLSession delegate methods
|
|
196
|
+
|
|
197
|
+
- (void)configureWriteStream {
|
|
198
|
+
if (respFile)
|
|
199
|
+
{
|
|
200
|
+
@try{
|
|
201
|
+
NSFileManager * fm = [NSFileManager defaultManager];
|
|
202
|
+
NSString * folder = [destPath stringByDeletingLastPathComponent];
|
|
203
|
+
|
|
204
|
+
if (![fm fileExistsAtPath:folder]) {
|
|
205
|
+
[fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// if not set overwrite in options, defaults to TRUE
|
|
209
|
+
BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue];
|
|
210
|
+
BOOL appendToExistingFile = [destPath containsString:@"?append=true"];
|
|
211
|
+
|
|
212
|
+
appendToExistingFile = !overwrite;
|
|
213
|
+
|
|
214
|
+
// For solving #141 append response data if the file already exists
|
|
215
|
+
// base on PR#139 @kejinliang
|
|
216
|
+
if (appendToExistingFile) {
|
|
217
|
+
destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (![fm fileExistsAtPath:destPath]) {
|
|
221
|
+
[fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile];
|
|
225
|
+
[writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
|
226
|
+
[writeStream open];
|
|
227
|
+
}
|
|
228
|
+
@catch(NSException * ex)
|
|
229
|
+
{
|
|
230
|
+
NSLog(@"write file error");
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
- (void)processData:(NSData *)data {
|
|
236
|
+
if (respFile && ![self ShouldTransformFile]) {
|
|
237
|
+
[writeStream write:(const uint8_t *)[data bytes] maxLength:[data length]];
|
|
238
|
+
} else {
|
|
239
|
+
[respData appendData:data];
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
#pragma mark - Received Response
|
|
245
|
+
// set expected content length on response received
|
|
246
|
+
- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
|
|
247
|
+
{
|
|
248
|
+
expectedBytes = [response expectedContentLength];
|
|
249
|
+
|
|
250
|
+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
|
|
251
|
+
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
|
|
252
|
+
NSString * respType = @"";
|
|
253
|
+
respStatus = statusCode;
|
|
254
|
+
|
|
255
|
+
if ([response respondsToSelector:@selector(allHeaderFields)])
|
|
256
|
+
{
|
|
257
|
+
NSDictionary *headers = [httpResponse allHeaderFields];
|
|
258
|
+
NSString * respCType = [[ReactNativeBlobUtilReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString];
|
|
259
|
+
|
|
260
|
+
if (self.isServerPush) {
|
|
261
|
+
if (partBuffer) {
|
|
262
|
+
[self.baseModule
|
|
263
|
+
emitEventDict:EVENT_SERVER_PUSH
|
|
264
|
+
body:@{
|
|
265
|
+
@"taskId": taskId,
|
|
266
|
+
@"chunk": [partBuffer base64EncodedStringWithOptions:0],
|
|
267
|
+
}
|
|
268
|
+
];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
partBuffer = [[NSMutableData alloc] init];
|
|
272
|
+
completionHandler(NSURLSessionResponseAllow);
|
|
273
|
+
|
|
274
|
+
return;
|
|
275
|
+
} else {
|
|
276
|
+
self.isServerPush = [[respCType lowercaseString] containsString:@"multipart/x-mixed-replace;"];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if(respCType)
|
|
280
|
+
{
|
|
281
|
+
NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE];
|
|
282
|
+
|
|
283
|
+
if ([respCType containsString:@"text/"]) {
|
|
284
|
+
respType = @"text";
|
|
285
|
+
} else if ([respCType containsString:@"application/json"]) {
|
|
286
|
+
respType = @"json";
|
|
287
|
+
} else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches
|
|
288
|
+
for (NSString * substr in extraBlobCTypes) {
|
|
289
|
+
if ([respCType containsString:[substr lowercaseString]]) {
|
|
290
|
+
respType = @"blob";
|
|
291
|
+
respFile = YES;
|
|
292
|
+
destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:nil];
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} else {
|
|
297
|
+
respType = @"blob";
|
|
298
|
+
|
|
299
|
+
// for XMLHttpRequest, switch response data handling strategy automatically
|
|
300
|
+
if ([options valueForKey:@"auto"]) {
|
|
301
|
+
respFile = YES;
|
|
302
|
+
destPath = [ReactNativeBlobUtilFS getTempPath:taskId withExtension:@""];
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
respType = @"text";
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
#pragma mark - handling cookies
|
|
310
|
+
// # 153 get cookies
|
|
311
|
+
if (response.URL) {
|
|
312
|
+
NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
|
313
|
+
NSArray<NSHTTPCookie *> * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL];
|
|
314
|
+
if (cookies.count) {
|
|
315
|
+
[cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
[self.baseModule emitEventDict : EVENT_STATE_CHANGE
|
|
319
|
+
body:@{
|
|
320
|
+
@"taskId": taskId,
|
|
321
|
+
@"state": @"2",
|
|
322
|
+
@"headers": headers,
|
|
323
|
+
@"redirects": redirects,
|
|
324
|
+
@"respType" : respType,
|
|
325
|
+
@"timeout" : @NO,
|
|
326
|
+
@"status": [NSNumber numberWithInteger:statusCode]
|
|
327
|
+
}
|
|
328
|
+
];
|
|
329
|
+
} else {
|
|
330
|
+
NSLog(@"oops");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
[self configureWriteStream];
|
|
334
|
+
|
|
335
|
+
completionHandler(NSURLSessionResponseAllow);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
// download progress handler
|
|
340
|
+
- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
|
|
341
|
+
{
|
|
342
|
+
// For #143 handling multipart/x-mixed-replace response
|
|
343
|
+
if (self.isServerPush)
|
|
344
|
+
{
|
|
345
|
+
[partBuffer appendData:data];
|
|
346
|
+
|
|
347
|
+
return ;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
NSNumber * received = [NSNumber numberWithLong:[data length]];
|
|
351
|
+
receivedBytes += [received longValue];
|
|
352
|
+
NSString * chunkString = @"";
|
|
353
|
+
|
|
354
|
+
if (isIncrement) {
|
|
355
|
+
chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// If we need to process the data, we defer writing into the file until the we have all the data, at which point
|
|
359
|
+
// we can perform the processing and then write into the file
|
|
360
|
+
[self processData:data];
|
|
361
|
+
|
|
362
|
+
if (expectedBytes == 0) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// For non-chunked download, progress is received / expected
|
|
367
|
+
// For chunked download, progress can be either 0 (started) or 1 (ended)
|
|
368
|
+
NSNumber *now;
|
|
369
|
+
if (expectedBytes != NSURLResponseUnknownLength) {
|
|
370
|
+
now = [NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)];
|
|
371
|
+
} else {
|
|
372
|
+
now = @0;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if ([self.progressConfig shouldReport:now]) {
|
|
376
|
+
NSDictionary *body;
|
|
377
|
+
if (expectedBytes == NSURLResponseUnknownLength) {
|
|
378
|
+
// For chunked downloads
|
|
379
|
+
body = @{
|
|
380
|
+
@"taskId": taskId,
|
|
381
|
+
@"written": [NSString stringWithFormat:@"%d", 0],
|
|
382
|
+
@"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
|
|
383
|
+
@"chunk": chunkString,
|
|
384
|
+
};
|
|
385
|
+
} else {
|
|
386
|
+
// For non-chunked downloads
|
|
387
|
+
body = @{
|
|
388
|
+
@"taskId": taskId,
|
|
389
|
+
@"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
390
|
+
@"total": [NSString stringWithFormat:@"%lld", (long long) expectedBytes],
|
|
391
|
+
@"chunk": chunkString,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
[self.baseModule emitEventDict:EVENT_PROGRESS body:body];
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error
|
|
399
|
+
{
|
|
400
|
+
if ([session isEqual:session]) {
|
|
401
|
+
session = nil;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
|
|
407
|
+
{
|
|
408
|
+
|
|
409
|
+
self.error = error;
|
|
410
|
+
NSString * errMsg;
|
|
411
|
+
NSString * respStr;
|
|
412
|
+
NSString * rnfbRespType;
|
|
413
|
+
|
|
414
|
+
// only run this if we were requested to change it
|
|
415
|
+
if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) {
|
|
416
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
417
|
+
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
if (error) {
|
|
422
|
+
if (error.domain == NSURLErrorDomain && error.code == NSURLErrorCancelled) {
|
|
423
|
+
errMsg = @"task cancelled";
|
|
424
|
+
} else {
|
|
425
|
+
errMsg = [error localizedDescription];
|
|
426
|
+
}
|
|
427
|
+
} else if ([self.progressConfig shouldReport:@1] && expectedBytes == NSURLResponseUnknownLength) {
|
|
428
|
+
// For chunked downloads
|
|
429
|
+
[self.baseModule emitEventDict:EVENT_PROGRESS body:@{
|
|
430
|
+
@"taskId": taskId,
|
|
431
|
+
@"written": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
432
|
+
@"total": [NSString stringWithFormat:@"%lld", (long long) receivedBytes],
|
|
433
|
+
@"chunk": @"",
|
|
434
|
+
}];
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (respFile) {
|
|
438
|
+
if ([self ShouldTransformFile]) {
|
|
439
|
+
// At this point, we have the data that we deferred to write into a file. We can now
|
|
440
|
+
// perform the conversion and write the converted data into the file.
|
|
441
|
+
NSObject<FileTransformer>* fileTransformer = [ReactNativeBlobUtilFileTransformer getFileTransformer];
|
|
442
|
+
if (!fileTransformer) {
|
|
443
|
+
errMsg = @"Transform file specified but file transfomer not set";
|
|
444
|
+
} else {
|
|
445
|
+
@try{
|
|
446
|
+
NSData* transformedData = [fileTransformer onWriteFile:respData];
|
|
447
|
+
[writeStream write:(const uint8_t *)[transformedData bytes] maxLength:[transformedData length]];
|
|
448
|
+
} @catch(NSException * ex)
|
|
449
|
+
{
|
|
450
|
+
errMsg = [NSString stringWithFormat:@"Exception on File Transformer: '%@' ", [ex description]];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
[writeStream close];
|
|
455
|
+
rnfbRespType = RESP_TYPE_PATH;
|
|
456
|
+
respStr = destPath;
|
|
457
|
+
} else { // base64 response
|
|
458
|
+
// #73 fix unicode data encoding issue :
|
|
459
|
+
// when response type is BASE64, we should first try to encode the response data to UTF8 format
|
|
460
|
+
// if it turns out not to be `nil` that means the response data contains valid UTF8 string,
|
|
461
|
+
// in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding.
|
|
462
|
+
NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding];
|
|
463
|
+
|
|
464
|
+
if (responseFormat == BASE64) {
|
|
465
|
+
rnfbRespType = RESP_TYPE_BASE64;
|
|
466
|
+
respStr = [respData base64EncodedStringWithOptions:0];
|
|
467
|
+
} else if (responseFormat == UTF8) {
|
|
468
|
+
rnfbRespType = RESP_TYPE_UTF8;
|
|
469
|
+
respStr = utf8;
|
|
470
|
+
} else {
|
|
471
|
+
if (utf8) {
|
|
472
|
+
rnfbRespType = RESP_TYPE_UTF8;
|
|
473
|
+
respStr = utf8;
|
|
474
|
+
} else {
|
|
475
|
+
rnfbRespType = RESP_TYPE_BASE64;
|
|
476
|
+
respStr = [respData base64EncodedStringWithOptions:0];
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
NSHTTPURLResponse *response = (NSHTTPURLResponse *) [task response];
|
|
482
|
+
|
|
483
|
+
callback(@[
|
|
484
|
+
errMsg ?: [NSNull null],
|
|
485
|
+
rnfbRespType ?: @"",
|
|
486
|
+
respStr ?: [NSNull null],
|
|
487
|
+
@{
|
|
488
|
+
@"status": [NSNumber numberWithInteger:[response statusCode]]
|
|
489
|
+
}
|
|
490
|
+
]);
|
|
491
|
+
|
|
492
|
+
respData = nil;
|
|
493
|
+
receivedBytes = 0;
|
|
494
|
+
[session finishTasksAndInvalidate];
|
|
495
|
+
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
- (BOOL) ShouldTransformFile{
|
|
499
|
+
return [[options valueForKey:CONFIG_TRANSFORM_FILE] boolValue];
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// upload progress handler
|
|
503
|
+
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite
|
|
504
|
+
{
|
|
505
|
+
if (totalBytesExpectedToWrite == 0) {
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
|
|
510
|
+
|
|
511
|
+
if ([self.uploadProgressConfig shouldReport:now]) {
|
|
512
|
+
[self.baseModule emitEventDict:EVENT_PROGRESS_UPLOAD
|
|
513
|
+
body:@{
|
|
514
|
+
@"taskId": taskId,
|
|
515
|
+
@"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten],
|
|
516
|
+
@"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite]
|
|
517
|
+
}
|
|
518
|
+
];
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler
|
|
524
|
+
{
|
|
525
|
+
if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) {
|
|
526
|
+
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
|
527
|
+
} else {
|
|
528
|
+
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
|
|
534
|
+
{
|
|
535
|
+
NSLog(@"sess done in background");
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler
|
|
539
|
+
{
|
|
540
|
+
|
|
541
|
+
if (followRedirect) {
|
|
542
|
+
if (request.URL) {
|
|
543
|
+
[redirects addObject:[request.URL absoluteString]];
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
completionHandler(request);
|
|
547
|
+
} else {
|
|
548
|
+
completionHandler(nil);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// NSURLSessionDownloadTask delegates
|
|
553
|
+
|
|
554
|
+
#pragma mark NSURLSessionDownloadTask delegate methods
|
|
555
|
+
|
|
556
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
|
|
557
|
+
|
|
558
|
+
NSFileManager *fm = [NSFileManager defaultManager];
|
|
559
|
+
NSData *data = [fm contentsAtPath:location.path];
|
|
560
|
+
|
|
561
|
+
[self configureWriteStream];
|
|
562
|
+
|
|
563
|
+
[self processData:data];
|
|
564
|
+
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
|
|
568
|
+
if (totalBytesExpectedToWrite == 0) {
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
NSNumber * now =[NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)];
|
|
573
|
+
if ([self.progressConfig shouldReport:now]) {
|
|
574
|
+
[self.baseModule emitEventDict:EVENT_PROGRESS
|
|
575
|
+
body:@{
|
|
576
|
+
@"taskId": taskId,
|
|
577
|
+
@"written": [NSString stringWithFormat:@"%lld", (long long) totalBytesWritten],
|
|
578
|
+
@"total": [NSString stringWithFormat:@"%lld", (long long) totalBytesExpectedToWrite]
|
|
579
|
+
}
|
|
580
|
+
];
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
@end
|