From 8121dbf66db16999653a590b1ee41c48add6dba9 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 27 Mar 2017 22:28:13 +0800 Subject: [PATCH 01/24] Apply possible fix to fs.readFile and fs.readStream for #287 --- .../java/com/RNFetchBlob/RNFetchBlobFS.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java index b7237a461..7457dba19 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java @@ -138,11 +138,13 @@ static public void writeFile(String path, ReadableArray data, final boolean appe * @param promise */ static public void readFile(String path, String encoding, final Promise promise ) { - path = normalizePath(path); + String resolved = normalizePath(path); + if(resolved != null) + path = resolved; try { byte[] bytes; - if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { + if(resolved != null && resolved.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { String assetName = path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, ""); long length = RNFetchBlob.RCTContext.getAssets().openFd(assetName).getLength(); bytes = new byte[(int) length]; @@ -150,6 +152,14 @@ static public void readFile(String path, String encoding, final Promise promise in.read(bytes, 0, (int) length); in.close(); } + // issue 287 + else if(resolved == null) { + InputStream in = RNFetchBlob.RCTContext.getContentResolver().openInputStream(Uri.parse(path)); + int length = (int) in.available(); + bytes = new byte[length]; + in.read(bytes); + in.close(); + } else { File f = new File(path); int length = (int) f.length(); @@ -225,7 +235,9 @@ static public String getTmpPath(ReactApplicationContext ctx, String taskId) { * @param bufferSize Buffer size of read stream, default to 4096 (4095 when encode is `base64`) */ public void readStream(String path, String encoding, int bufferSize, int tick, final String streamId) { - path = normalizePath(path); + String resolved = normalizePath(path); + if(resolved != null) + path = resolved; try { int chunkSize = encoding.equalsIgnoreCase("base64") ? 4095 : 4096; @@ -233,9 +245,13 @@ public void readStream(String path, String encoding, int bufferSize, int tick, f chunkSize = bufferSize; InputStream fs; - if(path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { + if(resolved != null && path.startsWith(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET)) { fs = RNFetchBlob.RCTContext.getAssets().open(path.replace(RNFetchBlobConst.FILE_PREFIX_BUNDLE_ASSET, "")); } + // fix issue 287 + else if(resolved == null) { + fs = RNFetchBlob.RCTContext.getContentResolver().openInputStream(Uri.parse(path)); + } else { fs = new FileInputStream(new File(path)); } From 40fefd4b2e29bdb13c3cbf1e64e0ced2dac33da7 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Sun, 16 Apr 2017 16:34:11 +0800 Subject: [PATCH 02/24] Add Android fs.readFile app provider URI support #287 --- .../java/com/RNFetchBlob/Utils/PathResolver.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java index e5742b81e..0327c8e26 100644 --- a/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java +++ b/android/src/main/java/com/RNFetchBlob/Utils/PathResolver.java @@ -59,6 +59,14 @@ else if (isMediaDocument(uri)) { return getDataColumn(context, contentUri, selection, selectionArgs); } + else if ("content".equalsIgnoreCase(uri.getScheme())) { + + // Return the remote address + if (isGooglePhotosUri(uri)) + return uri.getLastPathSegment(); + + return getDataColumn(context, uri, null, null); + } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { @@ -103,7 +111,12 @@ public static String getDataColumn(Context context, Uri uri, String selection, final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } - } finally { + } + catch (Exception ex) { + ex.printStackTrace(); + return null; + } + finally { if (cursor != null) cursor.close(); } From 08f8403dc43ad34320ee3038fa877a92b2b735c3 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Mon, 3 Jul 2017 08:48:44 +0800 Subject: [PATCH 03/24] Fixed a bug which causes XMLHttpRequest getting incorrect header when sending multiple requests in the same time --- polyfill/XMLHttpRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/XMLHttpRequest.js b/polyfill/XMLHttpRequest.js index 89171921f..42c987704 100644 --- a/polyfill/XMLHttpRequest.js +++ b/polyfill/XMLHttpRequest.js @@ -277,7 +277,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ _headerReceived = (e) => { log.debug('header received ', this._task.taskId, e) this.responseURL = this._url - if(e.state === "2") { + if(e.state === "2" && e.taskId === this._task.taskId) { this._responseHeaders = e.headers this._statusText = e.status this._status = Math.floor(e.status) From 8a75a9be2a613f6ce78dd6cd0e849437a9a8c41c Mon Sep 17 00:00:00 2001 From: Jon San Miguel Date: Mon, 3 Jul 2017 19:40:08 -0400 Subject: [PATCH 04/24] Set mime if set in addAndroidDownloads (#421) --- android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index db213c1e8..56fec29b5 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -148,6 +148,9 @@ public void run() { if(options.addAndroidDownloads.hasKey("path")) { req.setDestinationUri(Uri.parse("file://" + options.addAndroidDownloads.getString("path"))); } + if(options.addAndroidDownloads.hasKey("mime")) { + req.setMimeType(options.addAndroidDownloads.getString("mime")); + } // set headers ReadableMapKeySetIterator it = headers.keySetIterator(); while (it.hasNextKey()) { From ed2732a47ef5b4e7748b4c7632771d6f30a12c88 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 5 Jul 2017 14:14:16 +0800 Subject: [PATCH 05/24] Fix Download Manager bug when the file is not a multimedia #391 --- .../java/com/RNFetchBlob/RNFetchBlobReq.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index db213c1e8..31377650a 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -150,6 +150,14 @@ public void run() { } // set headers ReadableMapKeySetIterator it = headers.keySetIterator(); + // #391 Add MIME type to the request + if(options.addAndroidDownloads.hasKey("mime")) { + req.setMimeType(options.addAndroidDownloads.getString("mime")); + } + + if(options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.hasKey("mediaScannable") == true ) { + req.allowScanningByMediaScanner(); + } while (it.hasNextKey()) { String key = it.nextKey(); req.addRequestHeader(key, headers.getString(key)); @@ -636,16 +644,20 @@ public void onReceive(Context context, Intent intent) { return; } String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)); - if (contentUri != null) { + if ( contentUri != null && + options.addAndroidDownloads.hasKey("mime") && + options.addAndroidDownloads.getString("mime").contains("image")) { Uri uri = Uri.parse(contentUri); Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null); - // use default destination of DownloadManager + + // use default destination of DownloadManager if (cursor != null) { cursor.moveToFirst(); filePath = cursor.getString(0); } } } + // When the file is not found in media content database, check if custom path exists if (options.addAndroidDownloads.hasKey("path")) { try { From 6bb7c65161c17e4961e28dee4b8ff0d359da03ff Mon Sep 17 00:00:00 2001 From: Jeff Hellman Date: Fri, 14 Jul 2017 01:15:53 -0600 Subject: [PATCH 06/24] Add support for TLS 1.2 when running Android 4 (#430) --- .../java/com/RNFetchBlob/RNFetchBlobReq.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index 34dc2887c..8a81a832e 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -7,10 +7,12 @@ import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.util.Base64; import com.RNFetchBlob.Response.RNFetchBlobDefaultResp; import com.RNFetchBlob.Response.RNFetchBlobFileResp; +import com.facebook.common.logging.FLog; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; @@ -21,6 +23,7 @@ import com.facebook.react.bridge.WritableMap; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.modules.network.OkHttpClientProvider; +import com.facebook.react.modules.network.TLSSocketFactory; import java.io.File; import java.io.FileOutputStream; @@ -35,11 +38,14 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.util.ArrayList; +import java.util.List; import java.util.HashMap; + import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.ConnectionPool; +import okhttp3.ConnectionSpec; import okhttp3.Headers; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -48,6 +54,8 @@ import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.ResponseBody; +import okhttp3.TlsVersion; + public class RNFetchBlobReq extends BroadcastReceiver implements Runnable { @@ -366,9 +374,10 @@ public Response intercept(Chain chain) throws IOException { clientBuilder.retryOnConnectionFailure(false); clientBuilder.followRedirects(options.followRedirect); clientBuilder.followSslRedirects(options.followRedirect); + clientBuilder.retryOnConnectionFailure(true); + OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build(); - OkHttpClient client = clientBuilder.retryOnConnectionFailure(true).build(); Call call = client.newCall(req); taskTable.put(taskId, call); call.enqueue(new okhttp3.Callback() { @@ -683,5 +692,28 @@ public void onReceive(Context context, Intent intent) { } } + public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + try { + client.sslSocketFactory(new TLSSocketFactory()); + + ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .build(); + + List< ConnectionSpec > specs = new ArrayList < > (); + specs.add(cs); + specs.add(ConnectionSpec.COMPATIBLE_TLS); + specs.add(ConnectionSpec.CLEARTEXT); + + client.connectionSpecs(specs); + } catch (Exception exc) { + FLog.e("OkHttpClientProvider", "Error while enabling TLS 1.2", exc); + } + } + + return client; + } + } From b70a12455dd353517dc392a65d77f43e2f1938aa Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Fri, 14 Jul 2017 15:34:11 +0800 Subject: [PATCH 07/24] Update README.md --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b315848b9..7680fab86 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ A project committed to making file access and data transfer easier and more effi * [Android Media Scanner, and Download Manager Support](#user-content-android-media-scanner-and-download-manager-support) * [Self-Signed SSL Server](#user-content-self-signed-ssl-server) * [Transfer Encoding](#user-content-transfer-encoding) - * [RNFetchBlob as Fetch](#user-content-rnfetchblob-as-fetch) + * [Drop-in Fetch Replacement](#user-content-drop-in-fetch-replacement) * [File System](#user-content-file-system) * [File access](#user-content-file-access) * [File stream](#user-content-file-stream) * [Manage cached files](#user-content-cache-file-management) * [Web API Polyfills](#user-content-web-api-polyfills) -* [Performance Tips](#user-content-performance-tipsd) +* [Performance Tips](#user-content-performance-tips) * [API References](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API) * [Caveats](#user-content-caveats) * [Development](#user-content-development) @@ -452,11 +452,11 @@ task.cancel((err) => { ... }) ``` -### RNFetchBlob as Fetch +### Drop-in Fetch Replacement 0.9.0 -If you have existing code that uses `whatwg-fetch`(the official **fetch**), you don't have to change them after 0.9.0, just use fetch replacement. The difference between Official fetch and fetch replacement is, official fetch uses [whatwg-fetch](https://github.com/github/fetch) js library which wraps XMLHttpRequest polyfill under the hood it's a great library for web developers, however that does not play very well with RN. Our implementation is simply a wrapper of RNFetchBlob.fetch and fs APIs, so you can access all the features we provide. +If you have existing code that uses `whatwg-fetch`(the official **fetch**), it's not necessary to replace them with `RNFetchblob.fetch`, you can simply use our **Fetch Replacement**. The difference between Official them is official fetch uses [whatwg-fetch](https://github.com/github/fetch) which wraps XMLHttpRequest polyfill under the hood. It's a great library for web developers, but does not play very well with RN. Our implementation is simply a wrapper of our `fetch` and `fs` APIs, so you can access all the features we provided. [See document and examples](https://github.com/wkh237/react-native-fetch-blob/wiki/Fetch-API#fetch-replacement) @@ -613,6 +613,8 @@ In `v0.5.0` we've added `writeStream` and `readStream`, which allows your app r When calling `readStream` method, you have to `open` the stream, and start to read data. When the file is large, consider using an appropriate `bufferSize` and `interval` to reduce the native event dispatching overhead (see [Performance Tips](#user-content-performance-tips)) +> The file stream event has a default throttle(10ms) and buffer size which preventing it cause too much overhead to main thread, yo can also [tweak these values](#user-content-performance-tips). + ```js let data = '' RNFetchBlob.fs.readStream( From 712c8a3a07044fb2ed9679eb5cfa010f553ad215 Mon Sep 17 00:00:00 2001 From: JoshB Date: Mon, 17 Jul 2017 05:22:33 -0700 Subject: [PATCH 08/24] [iOS] Fix for RNFetchBlob.writeChunk failing to write base64 encoded strings (#433) --- ios/RNFetchBlobFS.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ios/RNFetchBlobFS.m b/ios/RNFetchBlobFS.m index 9d4e00b0d..5e102d184 100644 --- a/ios/RNFetchBlobFS.m +++ b/ios/RNFetchBlobFS.m @@ -568,11 +568,11 @@ - (NSString *)openWithPath:(NSString *)destPath encode:(nullable NSString *)enco // Write file chunk into an opened stream - (void)writeEncodeChunk:(NSString *) chunk { - NSMutableData * decodedData = [NSData alloc]; + NSData * decodedData = nil; if([[self.encoding lowercaseString] isEqualToString:@"base64"]) { - decodedData = [[NSData alloc] initWithBase64EncodedData:chunk options:0]; - } - if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) { + decodedData = [[NSData alloc] initWithBase64EncodedString:chunk options: NSDataBase64DecodingIgnoreUnknownCharacters]; + } + else if([[self.encoding lowercaseString] isEqualToString:@"utf8"]) { decodedData = [chunk dataUsingEncoding:NSUTF8StringEncoding]; } else if([[self.encoding lowercaseString] isEqualToString:@"ascii"]) { @@ -793,4 +793,4 @@ + (void) writeAssetToPath:(ALAssetRepresentation * )rep dest:(NSString *)dest return; } -@end +@end \ No newline at end of file From d83d8007a8e8cdddda368d78bb622a53a99b8d3b Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 25 Jul 2017 09:57:53 +0800 Subject: [PATCH 09/24] Add missing API Blob.safeClose() --- polyfill/Blob.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/polyfill/Blob.js b/polyfill/Blob.js index 384ae8fd9..a91a897b6 100644 --- a/polyfill/Blob.js +++ b/polyfill/Blob.js @@ -130,6 +130,8 @@ export default class Blob extends EventTarget { // Blob data from file path else if(typeof data === 'string' && data.startsWith('RNFetchBlob-file://')) { log.verbose('create Blob cache file from file path', data) + // set this flag so that we know this blob is a wrapper of an existing file + this._isReference = true this._ref = String(data).replace('RNFetchBlob-file://', '') let orgPath = this._ref if(defer) @@ -282,6 +284,20 @@ export default class Blob extends EventTarget { }) } + safeClose() { + if(this._closed) + return Promise.reject('Blob has been released.) + this._closed = true + if(!this._isReference) { + return fs.unlink(this._ref).catch((err) => { + console.warn(err) + }) + } + else { + return Promise.resolve() + } + } + _invokeOnCreateEvent() { log.verbose('invoke create event', this._onCreated) this._blobCreated = true From d1d07d0922468ed812de5bc11d4ed4743589074b Mon Sep 17 00:00:00 2001 From: Hizoul Date: Wed, 2 Aug 2017 02:43:47 +0200 Subject: [PATCH 10/24] Fix Compilation Error in React Native 0.47.0 (#452) createJSModules was removedin React Native 0.47.0 and results in the attached Build Error. removing @Override fixes build (didn't remove function because I don't know if it should be kept for downard compatability?) ``` node_modules/react-native-fetch-blob/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java:23: error: method does not override or implement a method from a supertype @Override ^ 1 error Incremental compilation of 1 classes completed in 0.219 secs. :react-native-fetch-blob:compileReleaseJavaWithJavac FAILED FAILURE: Build failed with an exception. ``` --- android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java index 74e0224a7..48aac7ac3 100644 --- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java +++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobPackage.java @@ -20,7 +20,6 @@ public List createNativeModules(ReactApplicationContext reactConte return modules; } - @Override public List> createJSModules() { return Collections.emptyList(); } From 6bde5167b435e23e334377aa3bb62e8cf7c7e188 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 2 Aug 2017 23:03:16 +0800 Subject: [PATCH 11/24] Bump to 0.10.7 --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a4524df60..0d88ec178 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.6", + "version": "0.10.7", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { @@ -8,7 +8,7 @@ }, "dependencies": { "base-64": "0.1.0", - "glob": "^7.0.6" + "glob": "7.0.6" }, "keywords": [ "react-native", @@ -35,4 +35,4 @@ "Ben ", "" ] -} \ No newline at end of file +} From 50c157345f13725855e6405f91a1f544575cfd4e Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Wed, 2 Aug 2017 23:13:10 +0800 Subject: [PATCH 12/24] Update PULL_REQUEST_TEMPLATE --- .github/PULL_REQUEST_TEMPLATE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index edadaa9b6..cef8ed2af 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,5 +1,5 @@ Thank you for making a pull request ! Just a gentle reminder :) 1. If the PR is offering a feature please make the request to our "Feature Branch" 0.11.0 -2. Bug fix request to "Bug Fix Branch" 0.10.7 +2. Bug fix request to "Bug Fix Branch" 0.10.8 3. Correct README.md can directly to master From 55009f148288f6471e7e084db3881bbdd1d7d98a Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:34:37 +0800 Subject: [PATCH 13/24] Correct unterminated string #455 --- polyfill/Blob.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polyfill/Blob.js b/polyfill/Blob.js index a91a897b6..53662a798 100644 --- a/polyfill/Blob.js +++ b/polyfill/Blob.js @@ -286,7 +286,7 @@ export default class Blob extends EventTarget { safeClose() { if(this._closed) - return Promise.reject('Blob has been released.) + return Promise.reject('Blob has been released.') this._closed = true if(!this._isReference) { return fs.unlink(this._ref).catch((err) => { From 9ab4ebb72e648156b2af60578de112022f856fb4 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Thu, 3 Aug 2017 09:37:08 +0800 Subject: [PATCH 14/24] bump to 0.10.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d88ec178..a93dba81d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-fetch-blob", - "version": "0.10.7", + "version": "0.10.8", "description": "A module provides upload, download, and files access API. Supports file stream read/write for process large files.", "main": "index.js", "scripts": { From 38f4d75eadb1cb7faed34ad613d190562d73c10f Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Fri, 12 Jan 2018 11:47:20 +0100 Subject: [PATCH 15/24] Synchronized dictionaries and tables operations --- ios/RNFetchBlobNetwork.m | 94 +++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 7be57fc59..621f48693 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -105,29 +105,35 @@ @implementation RNFetchBlobNetwork // constructor - (id)init { self = [super init]; - if(taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.maxConcurrentOperationCount = 10; + @synchronized ([RNFetchBlobNetwork class]) { + if (taskQueue == nil) { + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.maxConcurrentOperationCount = 10; + } } return self; } + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(progressTable == nil) + { + progressTable = [[NSMutableDictionary alloc] init]; + } + [progressTable setValue:config forKey:taskId]; } - [progressTable setValue:config forKey:taskId]; } + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; + @synchronized ([RNFetchBlobNetwork class]) { + if(uploadProgressTable == nil) + { + uploadProgressTable = [[NSMutableDictionary alloc] init]; + } + [uploadProgressTable setValue:config forKey:taskId]; } - [uploadProgressTable setValue:config forKey:taskId]; } // removing case from headers @@ -241,8 +247,10 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options } __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; - [taskTable setObject:task forKey:taskId]; - [task resume]; + @synchronized ([RNFetchBlobNetwork class]){ + [taskTable setObject:task forKey:taskId]; + [task resume]; + } // network status indicator if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) @@ -254,21 +262,22 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled + (void) emitExpiredTasks { - NSEnumerator * emu = [expirationTable keyEnumerator]; - NSString * key; - - while((key = [emu nextObject])) - { - RCTBridge * bridge = [RNFetchBlob getRCTBridge]; - NSData * args = @{ @"taskId": key }; - [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; + @synchronized ([RNFetchBlobNetwork class]){ + NSEnumerator * emu = [expirationTable keyEnumerator]; + NSString * key; - } + while((key = [emu nextObject])) + { + RCTBridge * bridge = [RNFetchBlob getRCTBridge]; + NSData * args = @{ @"taskId": key }; + [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; - // clear expired task entries - [expirationTable removeAllObjects]; - expirationTable = [[NSMapTable alloc] init]; + } + // clear expired task entries + [expirationTable removeAllObjects]; + expirationTable = [[NSMapTable alloc] init]; + } } //////////////////////////////////////// @@ -448,10 +457,18 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat { [writeStream write:[data bytes] maxLength:[data length]]; } - RNFetchBlobProgress * pconfig = [progressTable valueForKey:taskId]; + if(expectedBytes == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]){ + pconfig = [progressTable valueForKey:taskId]; + } + NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; + if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher @@ -461,11 +478,9 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"written": [NSString stringWithFormat:@"%d", receivedBytes], @"total": [NSString stringWithFormat:@"%d", expectedBytes], @"chunk": chunkString - } + } ]; } - received = nil; - } - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error @@ -537,7 +552,7 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom callback(@[ errMsg, rnfbRespType, respStr]); - @synchronized(taskTable, uploadProgressTable, progressTable) + @synchronized ([RNFetchBlobNetwork class]) { if([taskTable objectForKey:taskId] == nil) NSLog(@"object released by ARC."); @@ -556,17 +571,23 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom // upload progress handler - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite { - RNFetchBlobProgress * pconfig = [uploadProgressTable valueForKey:taskId]; if(totalBytesExpectedToWrite == 0) return; + + RNFetchBlobProgress * pconfig; + + @synchronized ([RNFetchBlobNetwork class]) { + pconfig = [uploadProgressTable valueForKey:taskId]; + } + NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; if(pconfig != nil && [pconfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD body:@{ @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%d", totalBytesWritten], - @"total": [NSString stringWithFormat:@"%d", totalBytesExpectedToWrite] + @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], + @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] } ]; } @@ -574,7 +595,12 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen + (void) cancelRequest:(NSString *)taskId { - NSURLSessionDataTask * task = [taskTable objectForKey:taskId]; + NSURLSessionDataTask * task; + + @synchronized ([RNFetchBlobNetwork class]) { + task = [taskTable objectForKey:taskId]; + } + if(task != nil && task.state == NSURLSessionTaskStateRunning) [task cancel]; } From 905ab1c2da7f138e6b37f52ac23d4a2bc77851fa Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 14:16:19 +0100 Subject: [PATCH 16/24] Mostly RNFetchBlobNetwork cleanup --- ios/RNFetchBlob/RNFetchBlob.h | 1 - ios/RNFetchBlob/RNFetchBlob.m | 4 +- ios/RNFetchBlobConst.m | 56 +++++++++++++------------- ios/RNFetchBlobFS.h | 4 +- ios/RNFetchBlobNetwork.h | 20 +++------- ios/RNFetchBlobNetwork.m | 74 +++++++++++++++-------------------- ios/RNFetchBlobReqBuilder.h | 2 +- ios/RNFetchBlobReqBuilder.m | 2 +- 8 files changed, 72 insertions(+), 91 deletions(-) diff --git a/ios/RNFetchBlob/RNFetchBlob.h b/ios/RNFetchBlob/RNFetchBlob.h index e6385114f..669a093b5 100644 --- a/ios/RNFetchBlob/RNFetchBlob.h +++ b/ios/RNFetchBlob/RNFetchBlob.h @@ -39,7 +39,6 @@ @property (retain) UIDocumentInteractionController * documentController; + (RCTBridge *)getRCTBridge; -+ (void) checkExpiredSessions; @end diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 246d6707c..40fc4c502 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -38,7 +38,7 @@ - (dispatch_queue_t) methodQueue { + (RCTBridge *)getRCTBridge { - RCTRootView * rootView = [[UIApplication sharedApplication] keyWindow].rootViewController.view; + RCTRootView * rootView = (RCTRootView*) [[UIApplication sharedApplication] keyWindow].rootViewController.view; return rootView.bridge; } @@ -128,7 +128,7 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - __block RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; + RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; } }]; diff --git a/ios/RNFetchBlobConst.m b/ios/RNFetchBlobConst.m index 6f7fef4b2..bc9b793a5 100644 --- a/ios/RNFetchBlobConst.m +++ b/ios/RNFetchBlobConst.m @@ -7,38 +7,38 @@ // #import "RNFetchBlobConst.h" -extern NSString *const FILE_PREFIX = @"RNFetchBlob-file://"; -extern NSString *const ASSET_PREFIX = @"bundle-assets://"; -extern NSString *const AL_PREFIX = @"assets-library://"; +NSString *const FILE_PREFIX = @"RNFetchBlob-file://"; +NSString *const ASSET_PREFIX = @"bundle-assets://"; +NSString *const AL_PREFIX = @"assets-library://"; // fetch configs -extern NSString *const CONFIG_USE_TEMP = @"fileCache"; -extern NSString *const CONFIG_FILE_PATH = @"path"; -extern NSString *const CONFIG_FILE_EXT = @"appendExt"; -extern NSString *const CONFIG_TRUSTY = @"trusty"; -extern NSString *const CONFIG_INDICATOR = @"indicator"; -extern NSString *const CONFIG_KEY = @"key"; -extern NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes"; +NSString *const CONFIG_USE_TEMP = @"fileCache"; +NSString *const CONFIG_FILE_PATH = @"path"; +NSString *const CONFIG_FILE_EXT = @"appendExt"; +NSString *const CONFIG_TRUSTY = @"trusty"; +NSString *const CONFIG_INDICATOR = @"indicator"; +NSString *const CONFIG_KEY = @"key"; +NSString *const CONFIG_EXTRA_BLOB_CTYPE = @"binaryContentTypes"; -extern NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState"; -extern NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush"; -extern NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress"; -extern NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload"; -extern NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire"; +NSString *const EVENT_STATE_CHANGE = @"RNFetchBlobState"; +NSString *const EVENT_SERVER_PUSH = @"RNFetchBlobServerPush"; +NSString *const EVENT_PROGRESS = @"RNFetchBlobProgress"; +NSString *const EVENT_PROGRESS_UPLOAD = @"RNFetchBlobProgress-upload"; +NSString *const EVENT_EXPIRE = @"RNFetchBlobExpire"; -extern NSString *const MSG_EVENT = @"RNFetchBlobMessage"; -extern NSString *const MSG_EVENT_LOG = @"log"; -extern NSString *const MSG_EVENT_WARN = @"warn"; -extern NSString *const MSG_EVENT_ERROR = @"error"; -extern NSString *const FS_EVENT_DATA = @"data"; -extern NSString *const FS_EVENT_END = @"end"; -extern NSString *const FS_EVENT_WARN = @"warn"; -extern NSString *const FS_EVENT_ERROR = @"error"; +NSString *const MSG_EVENT = @"RNFetchBlobMessage"; +NSString *const MSG_EVENT_LOG = @"log"; +NSString *const MSG_EVENT_WARN = @"warn"; +NSString *const MSG_EVENT_ERROR = @"error"; +NSString *const FS_EVENT_DATA = @"data"; +NSString *const FS_EVENT_END = @"end"; +NSString *const FS_EVENT_WARN = @"warn"; +NSString *const FS_EVENT_ERROR = @"error"; -extern NSString *const KEY_REPORT_PROGRESS = @"reportProgress"; -extern NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress"; +NSString *const KEY_REPORT_PROGRESS = @"reportProgress"; +NSString *const KEY_REPORT_UPLOAD_PROGRESS = @"reportUploadProgress"; // response type -extern NSString *const RESP_TYPE_BASE64 = @"base64"; -extern NSString *const RESP_TYPE_UTF8 = @"utf8"; -extern NSString *const RESP_TYPE_PATH = @"path"; +NSString *const RESP_TYPE_BASE64 = @"base64"; +NSString *const RESP_TYPE_UTF8 = @"utf8"; +NSString *const RESP_TYPE_PATH = @"path"; diff --git a/ios/RNFetchBlobFS.h b/ios/RNFetchBlobFS.h index 97386ef9d..c714fa66c 100644 --- a/ios/RNFetchBlobFS.h +++ b/ios/RNFetchBlobFS.h @@ -34,8 +34,8 @@ NSString * streamId; } -@property (nonatomic) NSOutputStream * outStream; -@property (nonatomic) NSInputStream * inStream; +@property (nonatomic) NSOutputStream * _Nullable outStream; +@property (nonatomic) NSInputStream * _Nullable inStream; @property (strong, nonatomic) RCTResponseSenderBlock callback; @property (nonatomic) RCTBridge * bridge; @property (nonatomic) NSString * encoding; diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index d3b4654a5..ef9e85ea7 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -20,39 +20,31 @@ #define RNFetchBlobNetwork_h - typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error); typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error); @interface RNFetchBlobNetwork : NSObject @property (nullable, nonatomic) NSString * taskId; -@property (nonatomic) int expectedBytes; -@property (nonatomic) int receivedBytes; +@property (nonatomic) long long expectedBytes; +@property (nonatomic) long long receivedBytes; @property (nonatomic) BOOL isServerPush; @property (nullable, nonatomic) NSMutableData * respData; -@property (strong, nonatomic) RCTResponseSenderBlock callback; +@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; @property (nullable, nonatomic) RCTBridge * bridge; @property (nullable, nonatomic) NSDictionary * options; @property (nullable, nonatomic) RNFetchBlobFS * fileStream; -@property (strong, nonatomic) CompletionHander fileTaskCompletionHandler; -@property (strong, nonatomic) DataTaskCompletionHander dataTaskCompletionHandler; @property (nullable, nonatomic) NSError * error; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; -+ (void) cancelRequest:(NSString *)taskId; -+ (void) enableProgressReport:(NSString *) taskId; -+ (void) enableUploadProgress:(NSString *) taskId; ++ (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; ++ (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; ++ (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -- (void) sendRequest; - (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; -+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config; -+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config; - - @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 621f48693..88fde5752 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -70,18 +70,17 @@ typedef NS_ENUM(NSUInteger, ResponseFormat) { @interface RNFetchBlobNetwork () { - BOOL * respFile; + BOOL respFile; BOOL isNewPart; - BOOL * isIncrement; + BOOL isIncrement; NSMutableData * partBuffer; NSString * destPath; NSOutputStream * writeStream; long bodyLength; - NSMutableDictionary * respInfo; NSInteger respStatus; NSMutableArray * redirects; ResponseFormat responseFormat; - BOOL * followRedirect; + BOOL followRedirect; BOOL backgroundTask; } @@ -97,8 +96,6 @@ @implementation RNFetchBlobNetwork @synthesize callback; @synthesize bridge; @synthesize options; -@synthesize fileTaskCompletionHandler; -@synthesize dataTaskCompletionHandler; @synthesize error; @@ -121,7 +118,7 @@ + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *) { progressTable = [[NSMutableDictionary alloc] init]; } - [progressTable setValue:config forKey:taskId]; + if (config) [progressTable setValue:config forKey:taskId]; } } @@ -132,7 +129,7 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) { uploadProgressTable = [[NSMutableDictionary alloc] init]; } - [uploadProgressTable setValue:config forKey:taskId]; + if (config) [uploadProgressTable setValue:config forKey:taskId]; } } @@ -193,9 +190,8 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options responseFormat = AUTO; NSString * path = [self.options valueForKey:CONFIG_FILE_PATH]; - NSString * ext = [self.options valueForKey:CONFIG_FILE_EXT]; NSString * key = [self.options valueForKey:CONFIG_KEY]; - __block NSURLSession * session; + NSURLSession * session; bodyLength = contentLength; @@ -246,16 +242,15 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options respFile = NO; } - __block NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; + NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; @synchronized ([RNFetchBlobNetwork class]){ [taskTable setObject:task forKey:taskId]; - [task resume]; } + [task resume]; // network status indicator if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - __block UIApplication * app = [UIApplication sharedApplication]; } @@ -269,7 +264,7 @@ + (void) emitExpiredTasks while((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; - NSData * args = @{ @"taskId": key }; + id args = @{ @"taskId": key }; [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; } @@ -352,23 +347,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat { respType = @"blob"; // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"] == YES) { + if([options valueForKey:@"auto"]) { respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; } } - } - else + } else { respType = @"text"; - respInfo = @{ - @"taskId": taskId, - @"state": @"2", - @"headers": headers, - @"redirects": redirects, - @"respType" : respType, - @"timeout" : @NO, - @"status": [NSNumber numberWithInteger:statusCode] - }; + } #pragma mark - handling cookies // # 153 get cookies @@ -383,11 +369,16 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat [self.bridge.eventDispatcher sendDeviceEventWithName: EVENT_STATE_CHANGE - body:respInfo + body:@{ + @"taskId": taskId, + @"state": @"2", + @"headers": headers, + @"redirects": redirects, + @"respType" : respType, + @"timeout" : @NO, + @"status": [NSNumber numberWithInteger:statusCode] + } ]; - headers = nil; - respInfo = nil; - } else NSLog(@"oops"); @@ -475,8 +466,8 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat sendDeviceEventWithName:EVENT_PROGRESS body:@{ @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%d", receivedBytes], - @"total": [NSString stringWithFormat:@"%d", expectedBytes], + @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], + @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], @"chunk": chunkString } ]; @@ -494,17 +485,12 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom { self.error = error; - NSString * errMsg = [NSNull null]; - NSString * respStr = [NSNull null]; - NSString * rnfbRespType = @""; + NSString * errMsg; + NSString * respStr; + NSString * rnfbRespType; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - if(respInfo == nil) - { - respInfo = [NSNull null]; - } - if(error != nil) { errMsg = [error localizedDescription]; @@ -550,7 +536,11 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom } - callback(@[ errMsg, rnfbRespType, respStr]); + callback(@[ + errMsg ?: [NSNull null], + rnfbRespType ?: @"", + respStr ?: [NSNull null] + ]); @synchronized ([RNFetchBlobNetwork class]) { @@ -608,7 +598,7 @@ + (void) cancelRequest:(NSString *)taskId - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler { - BOOL trusty = [options valueForKey:CONFIG_TRUSTY]; + BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; if(!trusty) { completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); diff --git a/ios/RNFetchBlobReqBuilder.h b/ios/RNFetchBlobReqBuilder.h index e7abeb9c7..1edc3ff50 100644 --- a/ios/RNFetchBlobReqBuilder.h +++ b/ios/RNFetchBlobReqBuilder.h @@ -29,7 +29,7 @@ body:(NSString *)body onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete; -+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableArray *) headers; ++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers; @end diff --git a/ios/RNFetchBlobReqBuilder.m b/ios/RNFetchBlobReqBuilder.m index 15465a1ae..e9eced6c4 100644 --- a/ios/RNFetchBlobReqBuilder.m +++ b/ios/RNFetchBlobReqBuilder.m @@ -277,7 +277,7 @@ void __block (^getFieldData)(id field) = ^(id field) } } -+(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableDictionary *) headers { ++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSDictionary *) headers { NSString * normalCase = [headers valueForKey:field]; NSString * ignoredCase = [headers valueForKey:[field lowercaseString]]; From 11f674c88b1c2b6d0d93141a2d5011f104e2d7e1 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 15:13:44 +0100 Subject: [PATCH 17/24] QOS level for task queue --- ios/RNFetchBlobNetwork.m | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 88fde5752..37490fdc7 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -105,6 +105,7 @@ - (id)init { @synchronized ([RNFetchBlobNetwork class]) { if (taskQueue == nil) { taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.qualityOfService = NSQualityOfServiceUtility; taskQueue.maxConcurrentOperationCount = 10; } } From f0301db36ce6b54e2a9f942173523ad1a94e33ae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 15:46:31 +0100 Subject: [PATCH 18/24] Unused completion handlers leftovers removed --- ios/RNFetchBlobNetwork.h | 3 --- ios/RNFetchBlobNetwork.m | 1 - 2 files changed, 4 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index ef9e85ea7..dcc5b99c5 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -20,9 +20,6 @@ #define RNFetchBlobNetwork_h -typedef void(^CompletionHander)(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error); -typedef void(^DataTaskCompletionHander) (NSData * _Nullable resp, NSURLResponse * _Nullable response, NSError * _Nullable error); - @interface RNFetchBlobNetwork : NSObject @property (nullable, nonatomic) NSString * taskId; diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 37490fdc7..ae22b37b8 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -137,7 +137,6 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) // removing case from headers + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { - NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init]; for(NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; From 8dd310d9f62979b77b39ddfb11ef6287fcf71bae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Mon, 15 Jan 2018 17:16:46 +0100 Subject: [PATCH 19/24] Separate requests from network logic --- ios/RNFetchBlob.xcodeproj/project.pbxproj | 8 +- ios/RNFetchBlob/RNFetchBlob.m | 16 +- ios/RNFetchBlobNetwork.h | 14 +- ios/RNFetchBlobNetwork.m | 580 ++-------------------- ios/RNFetchBlobRequest.h | 47 ++ ios/RNFetchBlobRequest.m | 495 ++++++++++++++++++ 6 files changed, 616 insertions(+), 544 deletions(-) create mode 100644 ios/RNFetchBlobRequest.h create mode 100644 ios/RNFetchBlobRequest.m diff --git a/ios/RNFetchBlob.xcodeproj/project.pbxproj b/ios/RNFetchBlob.xcodeproj/project.pbxproj index acd524413..070fcabb1 100644 --- a/ios/RNFetchBlob.xcodeproj/project.pbxproj +++ b/ios/RNFetchBlob.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */; }; A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */; }; A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */; }; A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */ = {isa = PBXBuildFile; fileRef = A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */; }; @@ -29,6 +30,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobRequest.h; sourceTree = ""; }; + 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobRequest.m; sourceTree = ""; }; A158F4261D052E49006FFD38 /* RNFetchBlobFS.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFetchBlobFS.m; sourceTree = ""; }; A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobFS.h; sourceTree = ""; }; A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNFetchBlobConst.h; sourceTree = ""; }; @@ -71,8 +74,10 @@ A1F950181D7E9134002A95A6 /* IOS7Polyfill.h */, A1AAE2981D300E4D0051D11C /* RNFetchBlobReqBuilder.m */, A1AAE2971D300E3E0051D11C /* RNFetchBlobReqBuilder.h */, - A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */, A158F42E1D0539CE006FFD38 /* RNFetchBlobNetwork.h */, + A158F42F1D0539DB006FFD38 /* RNFetchBlobNetwork.m */, + 8C4801A4200CF71700FED7ED /* RNFetchBlobRequest.h */, + 8C4801A5200CF71700FED7ED /* RNFetchBlobRequest.m */, A158F42C1D0535BB006FFD38 /* RNFetchBlobConst.m */, A158F4291D0534A9006FFD38 /* RNFetchBlobConst.h */, A158F4281D052E57006FFD38 /* RNFetchBlobFS.h */, @@ -149,6 +154,7 @@ buildActionMask = 2147483647; files = ( A166D1AA1CE0647A00273590 /* RNFetchBlob.h in Sources */, + 8C4801A6200CF71700FED7ED /* RNFetchBlobRequest.m in Sources */, A158F42D1D0535BB006FFD38 /* RNFetchBlobConst.m in Sources */, A158F4271D052E49006FFD38 /* RNFetchBlobFS.m in Sources */, A158F4301D0539DB006FFD38 /* RNFetchBlobNetwork.m in Sources */, diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 40fc4c502..6e8141f9e 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -96,8 +96,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; - [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; + [RNFetchBlobNetwork sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; @@ -128,8 +132,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - RNFetchBlobNetwork * utils = [[RNFetchBlobNetwork alloc] init]; - [utils sendRequest:options contentLength:bodyLength bridge:self.bridge taskId:taskId withRequest:req callback:callback]; + [RNFetchBlobNetwork sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; } diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index dcc5b99c5..b93541267 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -22,18 +22,8 @@ @interface RNFetchBlobNetwork : NSObject -@property (nullable, nonatomic) NSString * taskId; -@property (nonatomic) long long expectedBytes; -@property (nonatomic) long long receivedBytes; -@property (nonatomic) BOOL isServerPush; -@property (nullable, nonatomic) NSMutableData * respData; -@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; -@property (nullable, nonatomic) RCTBridge * bridge; -@property (nullable, nonatomic) NSDictionary * options; -@property (nullable, nonatomic) RNFetchBlobFS * fileStream; -@property (nullable, nonatomic) NSError * error; - ++ (_Nullable instancetype)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; + (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; @@ -41,7 +31,7 @@ + (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -- (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; ++ (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index ae22b37b8..5b3a32966 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -8,14 +8,12 @@ #import -#import "RNFetchBlob.h" -#import "RNFetchBlobFS.h" #import "RNFetchBlobNetwork.h" + +#import "RNFetchBlob.h" #import "RNFetchBlobConst.h" -#import "RNFetchBlobReqBuilder.h" -#import "IOS7Polyfill.h" -#import #import "RNFetchBlobProgress.h" +#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -35,10 +33,7 @@ // //////////////////////////////////////// -NSMapTable * taskTable; NSMapTable * expirationTable; -NSMutableDictionary * progressTable; -NSMutableDictionary * uploadProgressTable; __attribute__((constructor)) static void initialize_tables() { @@ -46,91 +41,73 @@ static void initialize_tables() { { expirationTable = [[NSMapTable alloc] init]; } - if(taskTable == nil) - { - taskTable = [[NSMapTable alloc] init]; - } - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; - } - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; - } } -typedef NS_ENUM(NSUInteger, ResponseFormat) { - UTF8, - BASE64, - AUTO -}; - - -@interface RNFetchBlobNetwork () -{ - BOOL respFile; - BOOL isNewPart; - BOOL isIncrement; - NSMutableData * partBuffer; - NSString * destPath; - NSOutputStream * writeStream; - long bodyLength; - NSInteger respStatus; - NSMutableArray * redirects; - ResponseFormat responseFormat; - BOOL followRedirect; - BOOL backgroundTask; -} - -@end - @implementation RNFetchBlobNetwork NSOperationQueue *taskQueue; -@synthesize taskId; -@synthesize expectedBytes; -@synthesize receivedBytes; -@synthesize respData; -@synthesize callback; -@synthesize bridge; -@synthesize options; -@synthesize error; +NSMapTable * requestsTable; - -// constructor - (id)init { self = [super init]; - @synchronized ([RNFetchBlobNetwork class]) { - if (taskQueue == nil) { - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.qualityOfService = NSQualityOfServiceUtility; - taskQueue.maxConcurrentOperationCount = 10; - } + if (self) { + requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; + + taskQueue = [[NSOperationQueue alloc] init]; + taskQueue.qualityOfService = NSQualityOfServiceUtility; + taskQueue.maxConcurrentOperationCount = 10; } + return self; } ++ (instancetype)sharedInstance { + static id _sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedInstance = [[self alloc] init]; + }); + + return _sharedInstance; +} + ++ (void) sendRequest:(__weak NSDictionary * _Nullable )options + contentLength:(long) contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(__weak NSURLRequest * _Nullable)req + callback:(_Nullable RCTResponseSenderBlock) callback +{ + RNFetchBlobRequest *request = [[RNFetchBlobRequest alloc] init]; + [request sendRequest:options + contentLength:contentLength + bridge:bridgeRef + taskId:taskId + withRequest:req + taskOperationQueue:taskQueue + callback:callback]; + + @synchronized([RNFetchBlobNetwork class]) { + [requestsTable setObject:request forKey:taskId]; + } +} + + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { - @synchronized ([RNFetchBlobNetwork class]) { - if(progressTable == nil) - { - progressTable = [[NSMutableDictionary alloc] init]; + if (config) { + @synchronized ([RNFetchBlobNetwork class]) { + [requestsTable objectForKey:taskId].progressConfig = config; } - if (config) [progressTable setValue:config forKey:taskId]; } } + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { - @synchronized ([RNFetchBlobNetwork class]) { - if(uploadProgressTable == nil) - { - uploadProgressTable = [[NSMutableDictionary alloc] init]; + if (config) { + @synchronized ([RNFetchBlobNetwork class]) { + [requestsTable objectForKey:taskId].uploadProgressConfig = config; } - if (config) [uploadProgressTable setValue:config forKey:taskId]; } } @@ -145,488 +122,37 @@ + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers return mheaders; } -- (NSString *)md5:(NSString *)input { - const char* str = [input UTF8String]; - unsigned char result[CC_MD5_DIGEST_LENGTH]; - CC_MD5(str, (CC_LONG)strlen(str), result); - - NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; - for(int i = 0; i 0) - { - defaultConfigObject.timeoutIntervalForRequest = timeout/1000; - } - defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; - session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue]; - if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) - { - respFile = YES; - - NSString* cacheKey = taskId; - if (key != nil) { - cacheKey = [self md5:key]; - if (cacheKey == nil) { - cacheKey = taskId; - } - - destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { - callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); - return; - } - } - - if(path != nil) - destPath = path; - else - destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - } - else - { - respData = [[NSMutableData alloc] init]; - respFile = NO; - } - - NSURLSessionDataTask * task = [session dataTaskWithRequest:req]; - @synchronized ([RNFetchBlobNetwork class]){ - [taskTable setObject:task forKey:taskId]; - } - [task resume]; - - // network status indicator - if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - -} - // #115 Invoke fetch.expire event on those expired requests so that the expired event can be handled + (void) emitExpiredTasks { @synchronized ([RNFetchBlobNetwork class]){ NSEnumerator * emu = [expirationTable keyEnumerator]; NSString * key; - + while((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; id args = @{ @"taskId": key }; [bridge.eventDispatcher sendDeviceEventWithName:EVENT_EXPIRE body:args]; - + } - + // clear expired task entries [expirationTable removeAllObjects]; expirationTable = [[NSMapTable alloc] init]; } } -//////////////////////////////////////// -// -// NSURLSession delegates -// -//////////////////////////////////////// - - -#pragma mark NSURLSession delegate methods - - -#pragma mark - Received Response -// set expected content length on response received -- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler -{ - expectedBytes = [response expectedContentLength]; - - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; - NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; - NSString * respType = @""; - respStatus = statusCode; - if ([response respondsToSelector:@selector(allHeaderFields)]) - { - NSDictionary *headers = [httpResponse allHeaderFields]; - NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; - if(self.isServerPush == NO) - { - self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; - } - if(self.isServerPush) - { - if(partBuffer != nil) - { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_SERVER_PUSH - body:@{ - @"taskId": taskId, - @"chunk": [partBuffer base64EncodedStringWithOptions:0], - } - ]; - } - partBuffer = [[NSMutableData alloc] init]; - completionHandler(NSURLSessionResponseAllow); - return; - } - if(respCType != nil) - { - NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; - if([respCType RNFBContainsString:@"text/"]) - { - respType = @"text"; - } - else if([respCType RNFBContainsString:@"application/json"]) - { - respType = @"json"; - } - // If extra blob content type is not empty, check if response type matches - else if( extraBlobCTypes != nil) { - for(NSString * substr in extraBlobCTypes) - { - if([respCType RNFBContainsString:[substr lowercaseString]]) - { - respType = @"blob"; - respFile = YES; - destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; - break; - } - } - } - else - { - respType = @"blob"; - // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"]) { - respFile = YES; - destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; - } - } - } else { - respType = @"text"; - } - -#pragma mark - handling cookies - // # 153 get cookies - if(response.URL != nil) - { - NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; - NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; - if(cookies != nil && [cookies count] > 0) { - [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; - } - } - - [self.bridge.eventDispatcher - sendDeviceEventWithName: EVENT_STATE_CHANGE - body:@{ - @"taskId": taskId, - @"state": @"2", - @"headers": headers, - @"redirects": redirects, - @"respType" : respType, - @"timeout" : @NO, - @"status": [NSNumber numberWithInteger:statusCode] - } - ]; - } - else - NSLog(@"oops"); - - if(respFile == YES) - { - @try{ - NSFileManager * fm = [NSFileManager defaultManager]; - NSString * folder = [destPath stringByDeletingLastPathComponent]; - if(![fm fileExistsAtPath:folder]) - { - [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; - } - BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; - BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; - - appendToExistingFile = !overwrite; - - // For solving #141 append response data if the file already exists - // base on PR#139 @kejinliang - if(appendToExistingFile) - { - destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; - } - if (![fm fileExistsAtPath:destPath]) - { - [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; - } - writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; - [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - [writeStream open]; - } - @catch(NSException * ex) - { - NSLog(@"write file error"); - } - } - - completionHandler(NSURLSessionResponseAllow); -} - - -// download progress handler -- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data -{ - // For #143 handling multipart/x-mixed-replace response - if(self.isServerPush) - { - [partBuffer appendData:data]; - return ; - } - - NSNumber * received = [NSNumber numberWithLong:[data length]]; - receivedBytes += [received longValue]; - NSString * chunkString = @""; - - if(isIncrement == YES) - { - chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; - } - - if(respFile == NO) - { - [respData appendData:data]; - } - else - { - [writeStream write:[data bytes] maxLength:[data length]]; - } - - if(expectedBytes == 0) - return; - - RNFetchBlobProgress * pconfig; - - @synchronized ([RNFetchBlobNetwork class]){ - pconfig = [progressTable valueForKey:taskId]; - } - - NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; - - if(pconfig != nil && [pconfig shouldReport:now]) - { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_PROGRESS - body:@{ - @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], - @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], - @"chunk": chunkString - } - ]; - } -} - -- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error -{ - if([session isEqual:session]) - session = nil; -} - - -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error -{ - - self.error = error; - NSString * errMsg; - NSString * respStr; - NSString * rnfbRespType; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - if(error != nil) - { - errMsg = [error localizedDescription]; - } - - if(respFile == YES) - { - [writeStream close]; - rnfbRespType = RESP_TYPE_PATH; - respStr = destPath; - } - // base64 response - else { - // #73 fix unicode data encoding issue : - // when response type is BASE64, we should first try to encode the response data to UTF8 format - // if it turns out not to be `nil` that means the response data contains valid UTF8 string, - // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. - NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; - - if(responseFormat == BASE64) - { - rnfbRespType = RESP_TYPE_BASE64; - respStr = [respData base64EncodedStringWithOptions:0]; - } - else if (responseFormat == UTF8) - { - rnfbRespType = RESP_TYPE_UTF8; - respStr = utf8; - } - else - { - if(utf8 != nil) - { - rnfbRespType = RESP_TYPE_UTF8; - respStr = utf8; - } - else - { - rnfbRespType = RESP_TYPE_BASE64; - respStr = [respData base64EncodedStringWithOptions:0]; - } - } - } - - - callback(@[ - errMsg ?: [NSNull null], - rnfbRespType ?: @"", - respStr ?: [NSNull null] - ]); - - @synchronized ([RNFetchBlobNetwork class]) - { - if([taskTable objectForKey:taskId] == nil) - NSLog(@"object released by ARC."); - else - [taskTable removeObjectForKey:taskId]; - [uploadProgressTable removeObjectForKey:taskId]; - [progressTable removeObjectForKey:taskId]; - } - - respData = nil; - receivedBytes = 0; - [session finishTasksAndInvalidate]; - -} - -// upload progress handler -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite -{ - if(totalBytesExpectedToWrite == 0) - return; - - RNFetchBlobProgress * pconfig; - - @synchronized ([RNFetchBlobNetwork class]) { - pconfig = [uploadProgressTable valueForKey:taskId]; - } - - NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; - if(pconfig != nil && [pconfig shouldReport:now]) { - [self.bridge.eventDispatcher - sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD - body:@{ - @"taskId": taskId, - @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], - @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] - } - ]; - } -} - + (void) cancelRequest:(NSString *)taskId { NSURLSessionDataTask * task; @synchronized ([RNFetchBlobNetwork class]) { - task = [taskTable objectForKey:taskId]; + task = [requestsTable objectForKey:taskId].task; } - if(task != nil && task.state == NSURLSessionTaskStateRunning) + if(task && task.state == NSURLSessionTaskStateRunning) { [task cancel]; -} - - -- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler -{ - BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; - if(!trusty) - { - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } - else - { - completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } -} - - -- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session -{ - NSLog(@"sess done in background"); -} - -- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler -{ - - if(followRedirect) - { - if(request.URL != nil) - [redirects addObject:[request.URL absoluteString]]; - completionHandler(request); - } - else - { - completionHandler(nil); } } diff --git a/ios/RNFetchBlobRequest.h b/ios/RNFetchBlobRequest.h new file mode 100644 index 000000000..a88ff05f6 --- /dev/null +++ b/ios/RNFetchBlobRequest.h @@ -0,0 +1,47 @@ +// +// RNFetchBlobRequest.h +// RNFetchBlob +// +// Created by Artur Chrusciel on 15.01.18. +// Copyright © 2018 wkh237.github.io. All rights reserved. +// + +#import + +#import "RNFetchBlobProgress.h" + +#if __has_include() +#import +#else +#import "RCTBridgeModule.h" +#endif + +#ifndef RNFetchBlobRequest_h +#define RNFetchBlobRequest_h + +@interface RNFetchBlobRequest : NSObject + +@property (nullable, nonatomic) NSString * taskId; +@property (nonatomic) long long expectedBytes; +@property (nonatomic) long long receivedBytes; +@property (nonatomic) BOOL isServerPush; +@property (nullable, nonatomic) NSMutableData * respData; +@property (nullable, strong, nonatomic) RCTResponseSenderBlock callback; +@property (nullable, nonatomic) RCTBridge * bridge; +@property (nullable, nonatomic) NSDictionary * options; +@property (nullable, nonatomic) NSError * error; +@property (nullable, nonatomic) RNFetchBlobProgress *progressConfig; +@property (nullable, nonatomic) RNFetchBlobProgress *uploadProgressConfig; +@property (nullable, nonatomic, weak) NSURLSessionDataTask *task; + +- (void) sendRequest:(NSDictionary * _Nullable )options + contentLength:(long)contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(NSURLRequest * _Nullable)req + taskOperationQueue:(NSOperationQueue * _Nonnull)operationQueue + callback:(_Nullable RCTResponseSenderBlock) callback; + +@end + +#endif /* RNFetchBlobRequest_h */ diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m new file mode 100644 index 000000000..73b562cc7 --- /dev/null +++ b/ios/RNFetchBlobRequest.m @@ -0,0 +1,495 @@ +// +// RNFetchBlobRequest.m +// RNFetchBlob +// +// Created by Artur Chrusciel on 15.01.18. +// Copyright © 2018 wkh237.github.io. All rights reserved. +// + +#import "RNFetchBlobRequest.h" + +#import "RNFetchBlobFS.h" +#import "RNFetchBlobConst.h" +#import "RNFetchBlobReqBuilder.h" + +#import "IOS7Polyfill.h" +#import + + +typedef NS_ENUM(NSUInteger, ResponseFormat) { + UTF8, + BASE64, + AUTO +}; + +@interface RNFetchBlobRequest () +{ + BOOL respFile; + BOOL isNewPart; + BOOL isIncrement; + NSMutableData * partBuffer; + NSString * destPath; + NSOutputStream * writeStream; + long bodyLength; + NSInteger respStatus; + NSMutableArray * redirects; + ResponseFormat responseFormat; + BOOL followRedirect; + BOOL backgroundTask; +} + +@end + +@implementation RNFetchBlobRequest + +@synthesize taskId; +@synthesize expectedBytes; +@synthesize receivedBytes; +@synthesize respData; +@synthesize callback; +@synthesize bridge; +@synthesize options; +@synthesize error; + + +- (NSString *)md5:(NSString *)input { + const char* str = [input UTF8String]; + unsigned char result[CC_MD5_DIGEST_LENGTH]; + CC_MD5(str, (CC_LONG)strlen(str), result); + + NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; + for(int i = 0; i 0) + { + defaultConfigObject.timeoutIntervalForRequest = timeout/1000; + } + defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; + session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue]; + if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) + { + respFile = YES; + + NSString* cacheKey = taskId; + if (key != nil) { + cacheKey = [self md5:key]; + if (cacheKey == nil) { + cacheKey = taskId; + } + + destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { + callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); + return; + } + } + + if(path != nil) + destPath = path; + else + destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + } + else + { + respData = [[NSMutableData alloc] init]; + respFile = NO; + } + + self.task = [session dataTaskWithRequest:req]; + [self.task resume]; + + // network status indicator + if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; + +} + +//////////////////////////////////////// +// +// NSURLSession delegates +// +//////////////////////////////////////// + + +#pragma mark NSURLSession delegate methods + + +#pragma mark - Received Response +// set expected content length on response received +- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler +{ + expectedBytes = [response expectedContentLength]; + + NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response; + NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; + NSString * respType = @""; + respStatus = statusCode; + if ([response respondsToSelector:@selector(allHeaderFields)]) + { + NSDictionary *headers = [httpResponse allHeaderFields]; + NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; + if(self.isServerPush == NO) + { + self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; + } + if(self.isServerPush) + { + if(partBuffer != nil) + { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_SERVER_PUSH + body:@{ + @"taskId": taskId, + @"chunk": [partBuffer base64EncodedStringWithOptions:0], + } + ]; + } + partBuffer = [[NSMutableData alloc] init]; + completionHandler(NSURLSessionResponseAllow); + return; + } + if(respCType != nil) + { + NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; + if([respCType RNFBContainsString:@"text/"]) + { + respType = @"text"; + } + else if([respCType RNFBContainsString:@"application/json"]) + { + respType = @"json"; + } + // If extra blob content type is not empty, check if response type matches + else if( extraBlobCTypes != nil) { + for(NSString * substr in extraBlobCTypes) + { + if([respCType RNFBContainsString:[substr lowercaseString]]) + { + respType = @"blob"; + respFile = YES; + destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; + break; + } + } + } + else + { + respType = @"blob"; + // for XMLHttpRequest, switch response data handling strategy automatically + if([options valueForKey:@"auto"]) { + respFile = YES; + destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; + } + } + } else { + respType = @"text"; + } + +#pragma mark - handling cookies + // # 153 get cookies + if(response.URL != nil) + { + NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; + if(cookies != nil && [cookies count] > 0) { + [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; + } + } + + [self.bridge.eventDispatcher + sendDeviceEventWithName: EVENT_STATE_CHANGE + body:@{ + @"taskId": taskId, + @"state": @"2", + @"headers": headers, + @"redirects": redirects, + @"respType" : respType, + @"timeout" : @NO, + @"status": [NSNumber numberWithInteger:statusCode] + } + ]; + } + else + NSLog(@"oops"); + + if(respFile == YES) + { + @try{ + NSFileManager * fm = [NSFileManager defaultManager]; + NSString * folder = [destPath stringByDeletingLastPathComponent]; + if(![fm fileExistsAtPath:folder]) + { + [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; + } + BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; + BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; + + appendToExistingFile = !overwrite; + + // For solving #141 append response data if the file already exists + // base on PR#139 @kejinliang + if(appendToExistingFile) + { + destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; + } + if (![fm fileExistsAtPath:destPath]) + { + [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; + } + writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; + [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; + [writeStream open]; + } + @catch(NSException * ex) + { + NSLog(@"write file error"); + } + } + + completionHandler(NSURLSessionResponseAllow); +} + + +// download progress handler +- (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data +{ + // For #143 handling multipart/x-mixed-replace response + if(self.isServerPush) + { + [partBuffer appendData:data]; + return ; + } + + NSNumber * received = [NSNumber numberWithLong:[data length]]; + receivedBytes += [received longValue]; + NSString * chunkString = @""; + + if(isIncrement == YES) + { + chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + } + + if(respFile == NO) + { + [respData appendData:data]; + } + else + { + [writeStream write:[data bytes] maxLength:[data length]]; + } + + if(expectedBytes == 0) + return; + + NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; + + if([self.progressConfig shouldReport:now]) + { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_PROGRESS + body:@{ + @"taskId": taskId, + @"written": [NSString stringWithFormat:@"%ld", (long) receivedBytes], + @"total": [NSString stringWithFormat:@"%ld", (long) expectedBytes], + @"chunk": chunkString + } + ]; + } +} + +- (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error +{ + if([session isEqual:session]) + session = nil; +} + + +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error +{ + + self.error = error; + NSString * errMsg; + NSString * respStr; + NSString * rnfbRespType; + + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + + if(error != nil) + { + errMsg = [error localizedDescription]; + } + + if(respFile == YES) + { + [writeStream close]; + rnfbRespType = RESP_TYPE_PATH; + respStr = destPath; + } + // base64 response + else { + // #73 fix unicode data encoding issue : + // when response type is BASE64, we should first try to encode the response data to UTF8 format + // if it turns out not to be `nil` that means the response data contains valid UTF8 string, + // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. + NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; + + if(responseFormat == BASE64) + { + rnfbRespType = RESP_TYPE_BASE64; + respStr = [respData base64EncodedStringWithOptions:0]; + } + else if (responseFormat == UTF8) + { + rnfbRespType = RESP_TYPE_UTF8; + respStr = utf8; + } + else + { + if(utf8 != nil) + { + rnfbRespType = RESP_TYPE_UTF8; + respStr = utf8; + } + else + { + rnfbRespType = RESP_TYPE_BASE64; + respStr = [respData base64EncodedStringWithOptions:0]; + } + } + } + + + callback(@[ + errMsg ?: [NSNull null], + rnfbRespType ?: @"", + respStr ?: [NSNull null] + ]); + /* + @synchronized ([RNFetchBlobNetwork class]) + { + if([taskTable objectForKey:taskId] == nil) + NSLog(@"object released by ARC."); + else + [taskTable removeObjectForKey:taskId]; + [uploadProgressTable removeObjectForKey:taskId]; + [progressTable removeObjectForKey:taskId]; + }*/ + + respData = nil; + receivedBytes = 0; + [session finishTasksAndInvalidate]; + +} + +// upload progress handler +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite +{ + if(totalBytesExpectedToWrite == 0) + return; + + NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; + + if([self.uploadProgressConfig shouldReport:now]) { + [self.bridge.eventDispatcher + sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD + body:@{ + @"taskId": taskId, + @"written": [NSString stringWithFormat:@"%ld", (long) totalBytesWritten], + @"total": [NSString stringWithFormat:@"%ld", (long) totalBytesExpectedToWrite] + } + ]; + } +} + + +- (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler +{ + BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; + if(!trusty) + { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } + else + { + completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } +} + + +- (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session +{ + NSLog(@"sess done in background"); +} + +- (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler +{ + + if(followRedirect) + { + if(request.URL != nil) + [redirects addObject:[request.URL absoluteString]]; + completionHandler(request); + } + else + { + completionHandler(nil); + } +} + + +@end From db688751468496ffb4592ec000787597f96184ae Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:22:00 +0100 Subject: [PATCH 20/24] Shared instance used --- ios/RNFetchBlobNetwork.h | 5 ++++- ios/RNFetchBlobNetwork.m | 23 ++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index b93541267..743d51e03 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -9,6 +9,7 @@ #import #import "RNFetchBlobProgress.h" #import "RNFetchBlobFS.h" +#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -22,8 +23,10 @@ @interface RNFetchBlobNetwork : NSObject +@property(nonnull, nonatomic) NSOperationQueue *taskQueue; +@property(nonnull, nonatomic) NSMapTable * requestsTable; -+ (_Nullable instancetype)sharedInstance; ++ (RNFetchBlobNetwork* _Nullable)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; + (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 5b3a32966..1b4181959 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -13,7 +13,6 @@ #import "RNFetchBlob.h" #import "RNFetchBlobConst.h" #import "RNFetchBlobProgress.h" -#import "RNFetchBlobRequest.h" #if __has_include() #import @@ -46,23 +45,21 @@ static void initialize_tables() { @implementation RNFetchBlobNetwork -NSOperationQueue *taskQueue; -NSMapTable * requestsTable; - (id)init { self = [super init]; if (self) { - requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; + self.requestsTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory valueOptions:NSMapTableWeakMemory]; - taskQueue = [[NSOperationQueue alloc] init]; - taskQueue.qualityOfService = NSQualityOfServiceUtility; - taskQueue.maxConcurrentOperationCount = 10; + self.taskQueue = [[NSOperationQueue alloc] init]; + self.taskQueue.qualityOfService = NSQualityOfServiceUtility; + self.taskQueue.maxConcurrentOperationCount = 10; } return self; } -+ (instancetype)sharedInstance { ++ (RNFetchBlobNetwork* _Nullable)sharedInstance { static id _sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -85,11 +82,11 @@ + (void) sendRequest:(__weak NSDictionary * _Nullable )options bridge:bridgeRef taskId:taskId withRequest:req - taskOperationQueue:taskQueue + taskOperationQueue:[self sharedInstance].taskQueue callback:callback]; @synchronized([RNFetchBlobNetwork class]) { - [requestsTable setObject:request forKey:taskId]; + [[self sharedInstance].requestsTable setObject:request forKey:taskId]; } } @@ -97,7 +94,7 @@ + (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *) { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [requestsTable objectForKey:taskId].progressConfig = config; + [[self sharedInstance].requestsTable objectForKey:taskId].progressConfig = config; } } } @@ -106,7 +103,7 @@ + (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *) { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [requestsTable objectForKey:taskId].uploadProgressConfig = config; + [[self sharedInstance].requestsTable objectForKey:taskId].uploadProgressConfig = config; } } } @@ -148,7 +145,7 @@ + (void) cancelRequest:(NSString *)taskId NSURLSessionDataTask * task; @synchronized ([RNFetchBlobNetwork class]) { - task = [requestsTable objectForKey:taskId].task; + task = [[self sharedInstance].requestsTable objectForKey:taskId].task; } if(task && task.state == NSURLSessionTaskStateRunning) { From ad657634283173a7ae3ce19e3fceb5dfa0b85910 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:31:49 +0100 Subject: [PATCH 21/24] Proper usage of shared instance --- ios/RNFetchBlob/RNFetchBlob.m | 30 ++++++++++++------------- ios/RNFetchBlobNetwork.h | 14 ++++++++---- ios/RNFetchBlobNetwork.m | 42 +++++++++++++++++------------------ 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/ios/RNFetchBlob/RNFetchBlob.m b/ios/RNFetchBlob/RNFetchBlob.m index 6e8141f9e..8b92431e9 100644 --- a/ios/RNFetchBlob/RNFetchBlob.m +++ b/ios/RNFetchBlob/RNFetchBlob.m @@ -96,12 +96,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - [RNFetchBlobNetwork sendRequest:options - contentLength:bodyLength - bridge:self.bridge - taskId:taskId - withRequest:req - callback:callback]; + [[RNFetchBlobNetwork sharedInstance] sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; @@ -132,12 +132,12 @@ - (NSDictionary *)constantsToExport // send HTTP request else { - [RNFetchBlobNetwork sendRequest:options - contentLength:bodyLength - bridge:self.bridge - taskId:taskId - withRequest:req - callback:callback]; + [[RNFetchBlobNetwork sharedInstance] sendRequest:options + contentLength:bodyLength + bridge:self.bridge + taskId:taskId + withRequest:req + callback:callback]; } }]; } @@ -496,7 +496,7 @@ - (NSDictionary *)constantsToExport #pragma mark - net.cancelRequest RCT_EXPORT_METHOD(cancelRequest:(NSString *)taskId callback:(RCTResponseSenderBlock)callback) { - [RNFetchBlobNetwork cancelRequest:taskId]; + [[RNFetchBlobNetwork sharedInstance] cancelRequest:taskId]; callback(@[[NSNull null], taskId]); } @@ -506,14 +506,14 @@ - (NSDictionary *)constantsToExport { RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Download interval:interval count:count]; - [RNFetchBlobNetwork enableProgressReport:taskId config:cfg]; + [[RNFetchBlobNetwork sharedInstance] enableProgressReport:taskId config:cfg]; } #pragma mark - net.enableUploadProgressReport RCT_EXPORT_METHOD(enableUploadProgressReport:(NSString *)taskId interval:(nonnull NSNumber*)interval count:(nonnull NSNumber*)count) { RNFetchBlobProgress * cfg = [[RNFetchBlobProgress alloc] initWithType:Upload interval:interval count:count]; - [RNFetchBlobNetwork enableUploadProgress:taskId config:cfg]; + [[RNFetchBlobNetwork sharedInstance] enableUploadProgress:taskId config:cfg]; } #pragma mark - fs.slice diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index 743d51e03..ecee93761 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -28,13 +28,19 @@ + (RNFetchBlobNetwork* _Nullable)sharedInstance; + (NSMutableDictionary * _Nullable ) normalizeHeaders:(NSDictionary * _Nullable)headers; -+ (void) cancelRequest:(NSString * _Nonnull)taskId; + (void) emitExpiredTasks; -+ (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; -+ (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; - (nullable id) init; -+ (void) sendRequest:(NSDictionary * _Nullable )options contentLength:(long)contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId withRequest:(NSURLRequest * _Nullable)req callback:(_Nullable RCTResponseSenderBlock) callback; +- (void) sendRequest:(NSDictionary * _Nullable )options + contentLength:(long)contentLength + bridge:(RCTBridge * _Nullable)bridgeRef + taskId:(NSString * _Nullable)taskId + withRequest:(NSURLRequest * _Nullable)req + callback:(_Nullable RCTResponseSenderBlock) callback; +- (void) cancelRequest:(NSString * _Nonnull)taskId; +- (void) enableProgressReport:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; +- (void) enableUploadProgress:(NSString * _Nonnull) taskId config:(RNFetchBlobProgress * _Nullable)config; + @end diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 1b4181959..1cd9e93b3 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -69,7 +69,7 @@ + (RNFetchBlobNetwork* _Nullable)sharedInstance { return _sharedInstance; } -+ (void) sendRequest:(__weak NSDictionary * _Nullable )options +- (void) sendRequest:(__weak NSDictionary * _Nullable )options contentLength:(long) contentLength bridge:(RCTBridge * _Nullable)bridgeRef taskId:(NSString * _Nullable)taskId @@ -82,32 +82,45 @@ + (void) sendRequest:(__weak NSDictionary * _Nullable )options bridge:bridgeRef taskId:taskId withRequest:req - taskOperationQueue:[self sharedInstance].taskQueue + taskOperationQueue:self.taskQueue callback:callback]; @synchronized([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable setObject:request forKey:taskId]; + [self.requestsTable setObject:request forKey:taskId]; } } -+ (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config +- (void) enableProgressReport:(NSString *) taskId config:(RNFetchBlobProgress *)config { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable objectForKey:taskId].progressConfig = config; + [self.requestsTable objectForKey:taskId].progressConfig = config; } } } -+ (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config +- (void) enableUploadProgress:(NSString *) taskId config:(RNFetchBlobProgress *)config { if (config) { @synchronized ([RNFetchBlobNetwork class]) { - [[self sharedInstance].requestsTable objectForKey:taskId].uploadProgressConfig = config; + [self.requestsTable objectForKey:taskId].uploadProgressConfig = config; } } } +- (void) cancelRequest:(NSString *)taskId +{ + NSURLSessionDataTask * task; + + @synchronized ([RNFetchBlobNetwork class]) { + task = [self.requestsTable objectForKey:taskId].task; + } + + if(task && task.state == NSURLSessionTaskStateRunning) { + [task cancel]; + } +} + // removing case from headers + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { @@ -115,7 +128,7 @@ + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers for(NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; } - + return mheaders; } @@ -140,17 +153,4 @@ + (void) emitExpiredTasks } } -+ (void) cancelRequest:(NSString *)taskId -{ - NSURLSessionDataTask * task; - - @synchronized ([RNFetchBlobNetwork class]) { - task = [[self sharedInstance].requestsTable objectForKey:taskId].task; - } - - if(task && task.state == NSURLSessionTaskStateRunning) { - [task cancel]; - } -} - @end From 1d19f984ea4464298334587e4b57eb499489f509 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 08:33:47 +0100 Subject: [PATCH 22/24] Commented code removed --- ios/RNFetchBlobRequest.m | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 73b562cc7..8ebdd8d19 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -419,16 +419,6 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom rnfbRespType ?: @"", respStr ?: [NSNull null] ]); - /* - @synchronized ([RNFetchBlobNetwork class]) - { - if([taskTable objectForKey:taskId] == nil) - NSLog(@"object released by ARC."); - else - [taskTable removeObjectForKey:taskId]; - [uploadProgressTable removeObjectForKey:taskId]; - [progressTable removeObjectForKey:taskId]; - }*/ respData = nil; receivedBytes = 0; From d4b465a366603a90b50a3a46323183e7e6795e7d Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 09:06:57 +0100 Subject: [PATCH 23/24] Fixed header import defines --- ios/RNFetchBlobNetwork.h | 6 +++--- ios/RNFetchBlobRequest.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ios/RNFetchBlobNetwork.h b/ios/RNFetchBlobNetwork.h index ecee93761..1512712af 100644 --- a/ios/RNFetchBlobNetwork.h +++ b/ios/RNFetchBlobNetwork.h @@ -6,6 +6,9 @@ // Copyright © 2016 wkh237. All rights reserved. // +#ifndef RNFetchBlobNetwork_h +#define RNFetchBlobNetwork_h + #import #import "RNFetchBlobProgress.h" #import "RNFetchBlobFS.h" @@ -17,9 +20,6 @@ #import "RCTBridgeModule.h" #endif -#ifndef RNFetchBlobNetwork_h -#define RNFetchBlobNetwork_h - @interface RNFetchBlobNetwork : NSObject diff --git a/ios/RNFetchBlobRequest.h b/ios/RNFetchBlobRequest.h index a88ff05f6..b550ac22e 100644 --- a/ios/RNFetchBlobRequest.h +++ b/ios/RNFetchBlobRequest.h @@ -6,6 +6,9 @@ // Copyright © 2018 wkh237.github.io. All rights reserved. // +#ifndef RNFetchBlobRequest_h +#define RNFetchBlobRequest_h + #import #import "RNFetchBlobProgress.h" @@ -16,9 +19,6 @@ #import "RCTBridgeModule.h" #endif -#ifndef RNFetchBlobRequest_h -#define RNFetchBlobRequest_h - @interface RNFetchBlobRequest : NSObject @property (nullable, nonatomic) NSString * taskId; From 57102f4d1e9b2e3c59739acadb7b870066eaa903 Mon Sep 17 00:00:00 2001 From: Artur Chrusciel Date: Tue, 16 Jan 2018 12:50:18 +0100 Subject: [PATCH 24/24] Some code guidelines for network classes --- ios/RNFetchBlobNetwork.m | 10 +- ios/RNFetchBlobRequest.m | 200 ++++++++++++++++++--------------------- 2 files changed, 97 insertions(+), 113 deletions(-) diff --git a/ios/RNFetchBlobNetwork.m b/ios/RNFetchBlobNetwork.m index 1cd9e93b3..4531bf35e 100644 --- a/ios/RNFetchBlobNetwork.m +++ b/ios/RNFetchBlobNetwork.m @@ -36,8 +36,7 @@ __attribute__((constructor)) static void initialize_tables() { - if(expirationTable == nil) - { + if (expirationTable == nil) { expirationTable = [[NSMapTable alloc] init]; } } @@ -62,6 +61,7 @@ - (id)init { + (RNFetchBlobNetwork* _Nullable)sharedInstance { static id _sharedInstance = nil; static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ _sharedInstance = [[self alloc] init]; }); @@ -116,7 +116,7 @@ - (void) cancelRequest:(NSString *)taskId task = [self.requestsTable objectForKey:taskId].task; } - if(task && task.state == NSURLSessionTaskStateRunning) { + if (task && task.state == NSURLSessionTaskStateRunning) { [task cancel]; } } @@ -125,7 +125,7 @@ - (void) cancelRequest:(NSString *)taskId + (NSMutableDictionary *) normalizeHeaders:(NSDictionary *)headers { NSMutableDictionary * mheaders = [[NSMutableDictionary alloc]init]; - for(NSString * key in headers) { + for (NSString * key in headers) { [mheaders setValue:[headers valueForKey:key] forKey:[key lowercaseString]]; } @@ -139,7 +139,7 @@ + (void) emitExpiredTasks NSEnumerator * emu = [expirationTable keyEnumerator]; NSString * key; - while((key = [emu nextObject])) + while ((key = [emu nextObject])) { RCTBridge * bridge = [RNFetchBlob getRCTBridge]; id args = @{ @"taskId": key }; diff --git a/ios/RNFetchBlobRequest.m b/ios/RNFetchBlobRequest.m index 8ebdd8d19..10b5e660f 100644 --- a/ios/RNFetchBlobRequest.m +++ b/ios/RNFetchBlobRequest.m @@ -58,7 +58,7 @@ - (NSString *)md5:(NSString *)input { CC_MD5(str, (CC_LONG)strlen(str), result); NSMutableString *ret = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH*2]; - for(int i = 0; i 0) - { + + if (timeout > 0) { defaultConfigObject.timeoutIntervalForRequest = timeout/1000; } + defaultConfigObject.HTTPMaximumConnectionsPerHost = 10; session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:operationQueue]; - if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) - { + + if (path || [self.options valueForKey:CONFIG_USE_TEMP]) { respFile = YES; NSString* cacheKey = taskId; - if (key != nil) { + if (key) { cacheKey = [self md5:key]; - if (cacheKey == nil) { + + if (!cacheKey) { cacheKey = taskId; } destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; + if ([[NSFileManager defaultManager] fileExistsAtPath:destPath]) { callback(@[[NSNull null], RESP_TYPE_PATH, destPath]); + return; } } - if(path != nil) + if (path) { destPath = path; - else + } else { destPath = [RNFetchBlobFS getTempPath:cacheKey withExtension:[self.options valueForKey:CONFIG_FILE_EXT]]; - } - else - { + } + } else { respData = [[NSMutableData alloc] init]; respFile = NO; } @@ -154,9 +161,9 @@ - (void) sendRequest:(__weak NSDictionary * _Nullable )options [self.task resume]; // network status indicator - if([[options objectForKey:CONFIG_INDICATOR] boolValue] == YES) + if ([[options objectForKey:CONFIG_INDICATOR] boolValue]) { [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - + } } //////////////////////////////////////// @@ -179,18 +186,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode]; NSString * respType = @""; respStatus = statusCode; + if ([response respondsToSelector:@selector(allHeaderFields)]) { NSDictionary *headers = [httpResponse allHeaderFields]; NSString * respCType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"Content-Type" fromHeaders:headers] lowercaseString]; - if(self.isServerPush == NO) - { - self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; - } - if(self.isServerPush) - { - if(partBuffer != nil) - { + + if (self.isServerPush) { + if (partBuffer) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_SERVER_PUSH body:@{ @@ -199,39 +202,37 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat } ]; } + partBuffer = [[NSMutableData alloc] init]; completionHandler(NSURLSessionResponseAllow); + return; + } else { + self.isServerPush = [[respCType lowercaseString] RNFBContainsString:@"multipart/x-mixed-replace;"]; } - if(respCType != nil) + + if(respCType) { NSArray * extraBlobCTypes = [options objectForKey:CONFIG_EXTRA_BLOB_CTYPE]; - if([respCType RNFBContainsString:@"text/"]) - { + + if ([respCType RNFBContainsString:@"text/"]) { respType = @"text"; - } - else if([respCType RNFBContainsString:@"application/json"]) - { + } else if ([respCType RNFBContainsString:@"application/json"]) { respType = @"json"; - } - // If extra blob content type is not empty, check if response type matches - else if( extraBlobCTypes != nil) { - for(NSString * substr in extraBlobCTypes) - { - if([respCType RNFBContainsString:[substr lowercaseString]]) - { + } else if(extraBlobCTypes) { // If extra blob content type is not empty, check if response type matches + for (NSString * substr in extraBlobCTypes) { + if ([respCType RNFBContainsString:[substr lowercaseString]]) { respType = @"blob"; respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:nil]; break; } } - } - else - { + } else { respType = @"blob"; + // for XMLHttpRequest, switch response data handling strategy automatically - if([options valueForKey:@"auto"]) { + if ([options valueForKey:@"auto"]) { respFile = YES; destPath = [RNFetchBlobFS getTempPath:taskId withExtension:@""]; } @@ -242,11 +243,10 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat #pragma mark - handling cookies // # 153 get cookies - if(response.URL != nil) - { + if (response.URL) { NSHTTPCookieStorage * cookieStore = [NSHTTPCookieStorage sharedHTTPCookieStorage]; NSArray * cookies = [NSHTTPCookie cookiesWithResponseHeaderFields: headers forURL:response.URL]; - if(cookies != nil && [cookies count] > 0) { + if (cookies.count) { [cookieStore setCookies:cookies forURL:response.URL mainDocumentURL:nil]; } } @@ -263,19 +263,21 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"status": [NSNumber numberWithInteger:statusCode] } ]; - } - else + } else { NSLog(@"oops"); + } - if(respFile == YES) + if (respFile) { @try{ NSFileManager * fm = [NSFileManager defaultManager]; NSString * folder = [destPath stringByDeletingLastPathComponent]; - if(![fm fileExistsAtPath:folder]) - { + + if (![fm fileExistsAtPath:folder]) { [fm createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:NULL error:nil]; } + + // if not set overwrite in options, defaults to TRUE BOOL overwrite = [options valueForKey:@"overwrite"] == nil ? YES : [[options valueForKey:@"overwrite"] boolValue]; BOOL appendToExistingFile = [destPath RNFBContainsString:@"?append=true"]; @@ -283,14 +285,14 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat // For solving #141 append response data if the file already exists // base on PR#139 @kejinliang - if(appendToExistingFile) - { + if (appendToExistingFile) { destPath = [destPath stringByReplacingOccurrencesOfString:@"?append=true" withString:@""]; } - if (![fm fileExistsAtPath:destPath]) - { + + if (![fm fileExistsAtPath:destPath]) { [fm createFileAtPath:destPath contents:[[NSData alloc] init] attributes:nil]; } + writeStream = [[NSOutputStream alloc] initToFileAtPath:destPath append:appendToExistingFile]; [writeStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; [writeStream open]; @@ -309,9 +311,10 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { // For #143 handling multipart/x-mixed-replace response - if(self.isServerPush) + if (self.isServerPush) { [partBuffer appendData:data]; + return ; } @@ -319,27 +322,23 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat receivedBytes += [received longValue]; NSString * chunkString = @""; - if(isIncrement == YES) - { + if (isIncrement) { chunkString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } - if(respFile == NO) - { - [respData appendData:data]; - } - else - { + if (respFile) { [writeStream write:[data bytes] maxLength:[data length]]; + } else { + [respData appendData:data]; } - if(expectedBytes == 0) + if (expectedBytes == 0) { return; + } NSNumber * now =[NSNumber numberWithFloat:((float)receivedBytes/(float)expectedBytes)]; - if([self.progressConfig shouldReport:now]) - { + if ([self.progressConfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS body:@{ @@ -354,8 +353,9 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat - (void) URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error { - if([session isEqual:session]) + if ([session isEqual:session]) { session = nil; + } } @@ -369,44 +369,32 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - if(error != nil) - { + if (error) { errMsg = [error localizedDescription]; } - if(respFile == YES) - { + if (respFile) { [writeStream close]; rnfbRespType = RESP_TYPE_PATH; respStr = destPath; - } - // base64 response - else { + } else { // base64 response // #73 fix unicode data encoding issue : // when response type is BASE64, we should first try to encode the response data to UTF8 format // if it turns out not to be `nil` that means the response data contains valid UTF8 string, // in order to properly encode the UTF8 string, use URL encoding before BASE64 encoding. NSString * utf8 = [[NSString alloc] initWithData:respData encoding:NSUTF8StringEncoding]; - if(responseFormat == BASE64) - { + if (responseFormat == BASE64) { rnfbRespType = RESP_TYPE_BASE64; respStr = [respData base64EncodedStringWithOptions:0]; - } - else if (responseFormat == UTF8) - { + } else if (responseFormat == UTF8) { rnfbRespType = RESP_TYPE_UTF8; respStr = utf8; - } - else - { - if(utf8 != nil) - { + } else { + if (utf8) { rnfbRespType = RESP_TYPE_UTF8; respStr = utf8; - } - else - { + } else { rnfbRespType = RESP_TYPE_BASE64; respStr = [respData base64EncodedStringWithOptions:0]; } @@ -429,12 +417,13 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCom // upload progress handler - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesWritten totalBytesExpectedToSend:(int64_t)totalBytesExpectedToWrite { - if(totalBytesExpectedToWrite == 0) + if (totalBytesExpectedToWrite == 0) { return; + } NSNumber * now = [NSNumber numberWithFloat:((float)totalBytesWritten/(float)totalBytesExpectedToWrite)]; - if([self.uploadProgressConfig shouldReport:now]) { + if ([self.uploadProgressConfig shouldReport:now]) { [self.bridge.eventDispatcher sendDeviceEventWithName:EVENT_PROGRESS_UPLOAD body:@{ @@ -449,14 +438,10 @@ - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didSen - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable credantial))completionHandler { - BOOL trusty = [[options valueForKey:CONFIG_TRUSTY] boolValue]; - if(!trusty) - { - completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); - } - else - { + if ([[options valueForKey:CONFIG_TRUSTY] boolValue]) { completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); + } else { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } } @@ -469,14 +454,13 @@ - (void) URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)sessio - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { - if(followRedirect) - { - if(request.URL != nil) + if (followRedirect) { + if (request.URL) { [redirects addObject:[request.URL absoluteString]]; + } + completionHandler(request); - } - else - { + } else { completionHandler(nil); } }