No Description

RNFetchBlobReq.java 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. package com.RNFetchBlob;
  2. import android.app.DownloadManager;
  3. import android.content.BroadcastReceiver;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.database.Cursor;
  8. import android.net.Uri;
  9. import android.os.Build;
  10. import android.util.Base64;
  11. import com.RNFetchBlob.Response.RNFetchBlobDefaultResp;
  12. import com.RNFetchBlob.Response.RNFetchBlobFileResp;
  13. import com.facebook.common.logging.FLog;
  14. import com.facebook.react.bridge.Arguments;
  15. import com.facebook.react.bridge.Callback;
  16. import com.facebook.react.bridge.ReactApplicationContext;
  17. import com.facebook.react.bridge.ReadableArray;
  18. import com.facebook.react.bridge.ReadableMap;
  19. import com.facebook.react.bridge.ReadableMapKeySetIterator;
  20. import com.facebook.react.bridge.WritableArray;
  21. import com.facebook.react.bridge.WritableMap;
  22. import com.facebook.react.modules.core.DeviceEventManagerModule;
  23. import com.facebook.react.modules.network.OkHttpClientProvider;
  24. import com.facebook.react.modules.network.TLSSocketFactory;
  25. import java.io.File;
  26. import java.io.FileOutputStream;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.net.MalformedURLException;
  30. import java.net.SocketException;
  31. import java.net.SocketTimeoutException;
  32. import java.net.URL;
  33. import java.nio.ByteBuffer;
  34. import java.nio.charset.CharacterCodingException;
  35. import java.nio.charset.Charset;
  36. import java.nio.charset.CharsetEncoder;
  37. import java.util.ArrayList;
  38. import java.util.List;
  39. import java.util.HashMap;
  40. import java.util.concurrent.TimeUnit;
  41. import okhttp3.Call;
  42. import okhttp3.ConnectionPool;
  43. import okhttp3.ConnectionSpec;
  44. import okhttp3.Headers;
  45. import okhttp3.Interceptor;
  46. import okhttp3.MediaType;
  47. import okhttp3.OkHttpClient;
  48. import okhttp3.Request;
  49. import okhttp3.RequestBody;
  50. import okhttp3.Response;
  51. import okhttp3.ResponseBody;
  52. import okhttp3.TlsVersion;
  53. public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
  54. enum RequestType {
  55. Form,
  56. SingleFile,
  57. AsIs,
  58. WithoutBody,
  59. Others
  60. }
  61. enum ResponseType {
  62. KeepInMemory,
  63. FileStorage
  64. }
  65. enum ResponseFormat {
  66. Auto,
  67. UTF8,
  68. BASE64
  69. }
  70. public static HashMap<String, Call> taskTable = new HashMap<>();
  71. static HashMap<String, RNFetchBlobProgressConfig> progressReport = new HashMap<>();
  72. static HashMap<String, RNFetchBlobProgressConfig> uploadProgressReport = new HashMap<>();
  73. static ConnectionPool pool = new ConnectionPool();
  74. ReactApplicationContext ctx;
  75. RNFetchBlobConfig options;
  76. String taskId;
  77. String method;
  78. String url;
  79. String rawRequestBody;
  80. String destPath;
  81. ReadableArray rawRequestBodyArray;
  82. ReadableMap headers;
  83. Callback callback;
  84. long contentLength;
  85. long downloadManagerId;
  86. RNFetchBlobBody requestBody;
  87. RequestType requestType;
  88. ResponseType responseType;
  89. ResponseFormat responseFormat = ResponseFormat.Auto;
  90. WritableMap respInfo;
  91. boolean timeout = false;
  92. ArrayList<String> redirects = new ArrayList<>();
  93. OkHttpClient client;
  94. public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, OkHttpClient client, final Callback callback) {
  95. this.method = method.toUpperCase();
  96. this.options = new RNFetchBlobConfig(options);
  97. this.taskId = taskId;
  98. this.url = url;
  99. this.headers = headers;
  100. this.callback = callback;
  101. this.rawRequestBody = body;
  102. this.rawRequestBodyArray = arrayBody;
  103. this.client = client;
  104. if(this.options.fileCache || this.options.path != null)
  105. responseType = ResponseType.FileStorage;
  106. else
  107. responseType = ResponseType.KeepInMemory;
  108. if (body != null)
  109. requestType = RequestType.SingleFile;
  110. else if (arrayBody != null)
  111. requestType = RequestType.Form;
  112. else
  113. requestType = RequestType.WithoutBody;
  114. }
  115. public static void cancelTask(String taskId) {
  116. if(taskTable.containsKey(taskId)) {
  117. Call call = taskTable.get(taskId);
  118. call.cancel();
  119. taskTable.remove(taskId);
  120. }
  121. }
  122. @Override
  123. public void run() {
  124. // use download manager instead of default HTTP implementation
  125. if (options.addAndroidDownloads != null && options.addAndroidDownloads.hasKey("useDownloadManager")) {
  126. if (options.addAndroidDownloads.getBoolean("useDownloadManager")) {
  127. Uri uri = Uri.parse(url);
  128. DownloadManager.Request req = new DownloadManager.Request(uri);
  129. req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
  130. if(options.addAndroidDownloads.hasKey("title")) {
  131. req.setTitle(options.addAndroidDownloads.getString("title"));
  132. }
  133. if(options.addAndroidDownloads.hasKey("description")) {
  134. req.setDescription(options.addAndroidDownloads.getString("description"));
  135. }
  136. if(options.addAndroidDownloads.hasKey("path")) {
  137. req.setDestinationUri(Uri.parse("file://" + options.addAndroidDownloads.getString("path")));
  138. }
  139. // #391 Add MIME type to the request
  140. if(options.addAndroidDownloads.hasKey("mime")) {
  141. req.setMimeType(options.addAndroidDownloads.getString("mime"));
  142. }
  143. // set headers
  144. ReadableMapKeySetIterator it = headers.keySetIterator();
  145. if(options.addAndroidDownloads.hasKey("mediaScannable") && options.addAndroidDownloads.hasKey("mediaScannable") == true ) {
  146. req.allowScanningByMediaScanner();
  147. }
  148. while (it.hasNextKey()) {
  149. String key = it.nextKey();
  150. req.addRequestHeader(key, headers.getString(key));
  151. }
  152. Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
  153. DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
  154. downloadManagerId = dm.enqueue(req);
  155. appCtx.registerReceiver(this, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
  156. return;
  157. }
  158. }
  159. // find cached result if `key` property exists
  160. String cacheKey = this.taskId;
  161. String ext = this.options.appendExt.isEmpty() ? "" : "." + this.options.appendExt;
  162. if (this.options.key != null) {
  163. cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
  164. if (cacheKey == null) {
  165. cacheKey = this.taskId;
  166. }
  167. File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
  168. if (file.exists()) {
  169. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
  170. return;
  171. }
  172. }
  173. if(this.options.path != null)
  174. this.destPath = this.options.path;
  175. else if(this.options.fileCache)
  176. this.destPath = RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext;
  177. OkHttpClient.Builder clientBuilder;
  178. try {
  179. // use trusty SSL socket
  180. if (this.options.trusty) {
  181. clientBuilder = RNFetchBlobUtils.getUnsafeOkHttpClient(client);
  182. } else {
  183. clientBuilder = client.newBuilder();
  184. }
  185. final Request.Builder builder = new Request.Builder();
  186. try {
  187. builder.url(new URL(url));
  188. } catch (MalformedURLException e) {
  189. e.printStackTrace();
  190. }
  191. HashMap<String, String> mheaders = new HashMap<>();
  192. // set headers
  193. if (headers != null) {
  194. ReadableMapKeySetIterator it = headers.keySetIterator();
  195. while (it.hasNextKey()) {
  196. String key = it.nextKey();
  197. String value = headers.getString(key);
  198. if(key.equalsIgnoreCase("RNFB-Response")) {
  199. if(value.equalsIgnoreCase("base64"))
  200. responseFormat = ResponseFormat.BASE64;
  201. else if (value.equalsIgnoreCase("utf8"))
  202. responseFormat = ResponseFormat.UTF8;
  203. }
  204. else {
  205. builder.header(key.toLowerCase(), value);
  206. mheaders.put(key.toLowerCase(), value);
  207. }
  208. }
  209. }
  210. if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch")) {
  211. String cType = getHeaderIgnoreCases(mheaders, "Content-Type").toLowerCase();
  212. if(rawRequestBodyArray != null) {
  213. requestType = RequestType.Form;
  214. }
  215. else if(cType.isEmpty()) {
  216. builder.header("Content-Type", "application/octet-stream");
  217. requestType = RequestType.SingleFile;
  218. }
  219. if(rawRequestBody != null) {
  220. if(rawRequestBody.startsWith(RNFetchBlobConst.FILE_PREFIX)) {
  221. requestType = RequestType.SingleFile;
  222. }
  223. else if (cType.toLowerCase().contains(";base64") || cType.toLowerCase().startsWith("application/octet")) {
  224. cType = cType.replace(";base64","").replace(";BASE64","");
  225. if(mheaders.containsKey("content-type"))
  226. mheaders.put("content-type", cType);
  227. if(mheaders.containsKey("Content-Type"))
  228. mheaders.put("Content-Type", cType);
  229. requestType = RequestType.SingleFile;
  230. } else {
  231. requestType = RequestType.AsIs;
  232. }
  233. }
  234. }
  235. else {
  236. requestType = RequestType.WithoutBody;
  237. }
  238. boolean isChunkedRequest = getHeaderIgnoreCases(mheaders, "Transfer-Encoding").equalsIgnoreCase("chunked");
  239. // set request body
  240. switch (requestType) {
  241. case SingleFile:
  242. requestBody = new RNFetchBlobBody(taskId)
  243. .chunkedEncoding(isChunkedRequest)
  244. .setRequestType(requestType)
  245. .setBody(rawRequestBody)
  246. .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
  247. builder.method(method, requestBody);
  248. break;
  249. case AsIs:
  250. requestBody = new RNFetchBlobBody(taskId)
  251. .chunkedEncoding(isChunkedRequest)
  252. .setRequestType(requestType)
  253. .setBody(rawRequestBody)
  254. .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
  255. builder.method(method, requestBody);
  256. break;
  257. case Form:
  258. String boundary = "RNFetchBlob-" + taskId;
  259. requestBody = new RNFetchBlobBody(taskId)
  260. .chunkedEncoding(isChunkedRequest)
  261. .setRequestType(requestType)
  262. .setBody(rawRequestBodyArray)
  263. .setMIME(MediaType.parse("multipart/form-data; boundary="+ boundary));
  264. builder.method(method, requestBody);
  265. break;
  266. case WithoutBody:
  267. if(method.equalsIgnoreCase("post") || method.equalsIgnoreCase("put") || method.equalsIgnoreCase("patch"))
  268. {
  269. builder.method(method, RequestBody.create(null, new byte[0]));
  270. }
  271. else
  272. builder.method(method, null);
  273. break;
  274. }
  275. // #156 fix cookie issue
  276. final Request req = builder.build();
  277. clientBuilder.addNetworkInterceptor(new Interceptor() {
  278. @Override
  279. public Response intercept(Chain chain) throws IOException {
  280. redirects.add(chain.request().url().toString());
  281. return chain.proceed(chain.request());
  282. }
  283. });
  284. // Add request interceptor for upload progress event
  285. clientBuilder.addInterceptor(new Interceptor() {
  286. @Override
  287. public Response intercept(Chain chain) throws IOException {
  288. try {
  289. Response originalResponse = chain.proceed(req);
  290. ResponseBody extended;
  291. switch (responseType) {
  292. case KeepInMemory:
  293. extended = new RNFetchBlobDefaultResp(
  294. RNFetchBlob.RCTContext,
  295. taskId,
  296. originalResponse.body(),
  297. options.increment);
  298. break;
  299. case FileStorage:
  300. extended = new RNFetchBlobFileResp(
  301. RNFetchBlob.RCTContext,
  302. taskId,
  303. originalResponse.body(),
  304. destPath,
  305. options.overwrite);
  306. break;
  307. default:
  308. extended = new RNFetchBlobDefaultResp(
  309. RNFetchBlob.RCTContext,
  310. taskId,
  311. originalResponse.body(),
  312. options.increment);
  313. break;
  314. }
  315. return originalResponse.newBuilder().body(extended).build();
  316. }
  317. catch(SocketException e) {
  318. timeout = true;
  319. }
  320. catch (SocketTimeoutException e ){
  321. timeout = true;
  322. RNFetchBlobUtils.emitWarningEvent("RNFetchBlob error when sending request : " + e.getLocalizedMessage());
  323. } catch(Exception ex) {
  324. }
  325. return chain.proceed(chain.request());
  326. }
  327. });
  328. if(options.timeout >= 0) {
  329. clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
  330. clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
  331. }
  332. clientBuilder.connectionPool(pool);
  333. clientBuilder.retryOnConnectionFailure(false);
  334. clientBuilder.followRedirects(options.followRedirect);
  335. clientBuilder.followSslRedirects(options.followRedirect);
  336. clientBuilder.retryOnConnectionFailure(true);
  337. OkHttpClient client = enableTls12OnPreLollipop(clientBuilder).build();
  338. Call call = client.newCall(req);
  339. taskTable.put(taskId, call);
  340. call.enqueue(new okhttp3.Callback() {
  341. @Override
  342. public void onFailure(Call call, IOException e) {
  343. cancelTask(taskId);
  344. if(respInfo == null) {
  345. respInfo = Arguments.createMap();
  346. }
  347. // check if this error caused by socket timeout
  348. if(e.getClass().equals(SocketTimeoutException.class)) {
  349. respInfo.putBoolean("timeout", true);
  350. callback.invoke("request timed out.", null, null);
  351. }
  352. else
  353. callback.invoke(e.getLocalizedMessage(), null, null);
  354. releaseTaskResource();
  355. }
  356. @Override
  357. public void onResponse(Call call, Response response) throws IOException {
  358. ReadableMap notifyConfig = options.addAndroidDownloads;
  359. // Download manager settings
  360. if(notifyConfig != null ) {
  361. String title = "", desc = "", mime = "text/plain";
  362. boolean scannable = false, notification = false;
  363. if(notifyConfig.hasKey("title"))
  364. title = options.addAndroidDownloads.getString("title");
  365. if(notifyConfig.hasKey("description"))
  366. desc = notifyConfig.getString("description");
  367. if(notifyConfig.hasKey("mime"))
  368. mime = notifyConfig.getString("mime");
  369. if(notifyConfig.hasKey("mediaScannable"))
  370. scannable = notifyConfig.getBoolean("mediaScannable");
  371. if(notifyConfig.hasKey("notification"))
  372. notification = notifyConfig.getBoolean("notification");
  373. DownloadManager dm = (DownloadManager)RNFetchBlob.RCTContext.getSystemService(RNFetchBlob.RCTContext.DOWNLOAD_SERVICE);
  374. dm.addCompletedDownload(title, desc, scannable, mime, destPath, contentLength, notification);
  375. }
  376. done(response);
  377. }
  378. });
  379. } catch (Exception error) {
  380. error.printStackTrace();
  381. releaseTaskResource();
  382. callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause());
  383. }
  384. }
  385. /**
  386. * Remove cached information of the HTTP task
  387. */
  388. private void releaseTaskResource() {
  389. if(taskTable.containsKey(taskId))
  390. taskTable.remove(taskId);
  391. if(uploadProgressReport.containsKey(taskId))
  392. uploadProgressReport.remove(taskId);
  393. if(progressReport.containsKey(taskId))
  394. progressReport.remove(taskId);
  395. if(requestBody != null)
  396. requestBody.clearRequestBody();
  397. }
  398. /**
  399. * Send response data back to javascript context.
  400. * @param resp OkHttp response object
  401. */
  402. private void done(Response resp) {
  403. boolean isBlobResp = isBlobResponse(resp);
  404. emitStateEvent(getResponseInfo(resp, isBlobResp));
  405. switch (responseType) {
  406. case KeepInMemory:
  407. try {
  408. // For XMLHttpRequest, automatic response data storing strategy, when response
  409. // data is considered as binary data, write it to file system
  410. if(isBlobResp && options.auto) {
  411. String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
  412. InputStream ins = resp.body().byteStream();
  413. FileOutputStream os = new FileOutputStream(new File(dest));
  414. int read;
  415. byte [] buffer = new byte[10240];
  416. while ((read = ins.read(buffer)) != -1) {
  417. os.write(buffer, 0, read);
  418. }
  419. ins.close();
  420. os.flush();
  421. os.close();
  422. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, dest);
  423. }
  424. // response data directly pass to JS context as string.
  425. else {
  426. // #73 Check if the response data contains valid UTF8 string, since BASE64
  427. // encoding will somehow break the UTF8 string format, to encode UTF8
  428. // string correctly, we should do URL encoding before BASE64.
  429. byte[] b = resp.body().bytes();
  430. CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
  431. if(responseFormat == ResponseFormat.BASE64) {
  432. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
  433. return;
  434. }
  435. try {
  436. encoder.encode(ByteBuffer.wrap(b).asCharBuffer());
  437. // if the data contains invalid characters the following lines will be
  438. // skipped.
  439. String utf8 = new String(b);
  440. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_UTF8, utf8);
  441. }
  442. // This usually mean the data is contains invalid unicode characters, it's
  443. // binary data
  444. catch(CharacterCodingException ignored) {
  445. if(responseFormat == ResponseFormat.UTF8) {
  446. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_UTF8, "");
  447. }
  448. else {
  449. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_BASE64, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
  450. }
  451. }
  452. }
  453. } catch (IOException e) {
  454. callback.invoke("RNFetchBlob failed to encode response data to BASE64 string.", null);
  455. }
  456. break;
  457. case FileStorage:
  458. try {
  459. // In order to write response data to `destPath` we have to invoke this method.
  460. // It uses customized response body which is able to report download progress
  461. // and write response data to destination path.
  462. resp.body().bytes();
  463. } catch (Exception ignored) {
  464. // ignored.printStackTrace();
  465. }
  466. this.destPath = this.destPath.replace("?append=true", "");
  467. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
  468. break;
  469. default:
  470. try {
  471. callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_UTF8, new String(resp.body().bytes(), "UTF-8"));
  472. } catch (IOException e) {
  473. callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
  474. }
  475. break;
  476. }
  477. // if(!resp.isSuccessful())
  478. resp.body().close();
  479. releaseTaskResource();
  480. }
  481. /**
  482. * Invoke this method to enable download progress reporting.
  483. * @param taskId Task ID of the HTTP task.
  484. * @return Task ID of the target task
  485. */
  486. public static RNFetchBlobProgressConfig getReportProgress(String taskId) {
  487. if(!progressReport.containsKey(taskId)) return null;
  488. return progressReport.get(taskId);
  489. }
  490. /**
  491. * Invoke this method to enable download progress reporting.
  492. * @param taskId Task ID of the HTTP task.
  493. * @return Task ID of the target task
  494. */
  495. public static RNFetchBlobProgressConfig getReportUploadProgress(String taskId) {
  496. if(!uploadProgressReport.containsKey(taskId)) return null;
  497. return uploadProgressReport.get(taskId);
  498. }
  499. /**
  500. * Create response information object, contains status code, headers, etc.
  501. * @param resp Response object
  502. * @param isBlobResp If the response is binary data
  503. * @return Get RCT bridge object contains response information.
  504. */
  505. private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
  506. WritableMap info = Arguments.createMap();
  507. info.putInt("status", resp.code());
  508. info.putString("state", "2");
  509. info.putString("taskId", this.taskId);
  510. info.putBoolean("timeout", timeout);
  511. WritableMap headers = Arguments.createMap();
  512. for(int i =0;i< resp.headers().size();i++) {
  513. headers.putString(resp.headers().name(i), resp.headers().value(i));
  514. }
  515. WritableArray redirectList = Arguments.createArray();
  516. for(String r : redirects) {
  517. redirectList.pushString(r);
  518. }
  519. info.putArray("redirects", redirectList);
  520. info.putMap("headers", headers);
  521. Headers h = resp.headers();
  522. if(isBlobResp) {
  523. info.putString("respType", "blob");
  524. }
  525. else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
  526. info.putString("respType", "text");
  527. }
  528. else if(getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
  529. info.putString("respType", "json");
  530. }
  531. else {
  532. info.putString("respType", "");
  533. }
  534. return info;
  535. }
  536. /**
  537. * Check if response data is binary data.
  538. * @param resp OkHttp response.
  539. * @return If the response data contains binary bytes
  540. */
  541. private boolean isBlobResponse(Response resp) {
  542. Headers h = resp.headers();
  543. String ctype = getHeaderIgnoreCases(h, "Content-Type");
  544. boolean isText = !ctype.equalsIgnoreCase("text/");
  545. boolean isJSON = !ctype.equalsIgnoreCase("application/json");
  546. boolean isCustomBinary = false;
  547. if(options.binaryContentTypes != null) {
  548. for(int i = 0; i< options.binaryContentTypes.size();i++) {
  549. if(ctype.toLowerCase().contains(options.binaryContentTypes.getString(i).toLowerCase())) {
  550. isCustomBinary = true;
  551. break;
  552. }
  553. }
  554. }
  555. return (!(isJSON || isText)) || isCustomBinary;
  556. }
  557. private String getHeaderIgnoreCases(Headers headers, String field) {
  558. String val = headers.get(field);
  559. if(val != null) return val;
  560. return headers.get(field.toLowerCase()) == null ? "" : headers.get(field.toLowerCase());
  561. }
  562. private String getHeaderIgnoreCases(HashMap<String,String> headers, String field) {
  563. String val = headers.get(field);
  564. if(val != null) return val;
  565. String lowerCasedValue = headers.get(field.toLowerCase());
  566. return lowerCasedValue == null ? "" : lowerCasedValue;
  567. }
  568. private void emitStateEvent(WritableMap args) {
  569. RNFetchBlob.RCTContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
  570. .emit(RNFetchBlobConst.EVENT_HTTP_STATE, args);
  571. }
  572. @Override
  573. public void onReceive(Context context, Intent intent) {
  574. String action = intent.getAction();
  575. if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
  576. Context appCtx = RNFetchBlob.RCTContext.getApplicationContext();
  577. long id = intent.getExtras().getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
  578. if (id == this.downloadManagerId) {
  579. DownloadManager.Query query = new DownloadManager.Query();
  580. query.setFilterById(downloadManagerId);
  581. DownloadManager dm = (DownloadManager) appCtx.getSystemService(Context.DOWNLOAD_SERVICE);
  582. dm.query(query);
  583. Cursor c = dm.query(query);
  584. String filePath = null;
  585. // the file exists in media content database
  586. if (c.moveToFirst()) {
  587. // #297 handle failed request
  588. int statusCode = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
  589. if(statusCode == DownloadManager.STATUS_FAILED) {
  590. this.callback.invoke("Download manager failed to download from " + this.url + ". Statu Code = " + statusCode, null, null);
  591. return;
  592. }
  593. String contentUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
  594. if ( contentUri != null &&
  595. options.addAndroidDownloads.hasKey("mime") &&
  596. options.addAndroidDownloads.getString("mime").contains("image")) {
  597. Uri uri = Uri.parse(contentUri);
  598. Cursor cursor = appCtx.getContentResolver().query(uri, new String[]{android.provider.MediaStore.Images.ImageColumns.DATA}, null, null, null);
  599. // use default destination of DownloadManager
  600. if (cursor != null) {
  601. cursor.moveToFirst();
  602. filePath = cursor.getString(0);
  603. }
  604. }
  605. }
  606. // When the file is not found in media content database, check if custom path exists
  607. if (options.addAndroidDownloads.hasKey("path")) {
  608. try {
  609. String customDest = options.addAndroidDownloads.getString("path");
  610. boolean exists = new File(customDest).exists();
  611. if(!exists)
  612. throw new Exception("Download manager download failed, the file does not downloaded to destination.");
  613. else
  614. this.callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, customDest);
  615. } catch(Exception ex) {
  616. ex.printStackTrace();
  617. this.callback.invoke(ex.getLocalizedMessage(), null);
  618. }
  619. }
  620. else {
  621. if(filePath == null)
  622. this.callback.invoke("Download manager could not resolve downloaded file path.", RNFetchBlobConst.RNFB_RESPONSE_PATH, null);
  623. else
  624. this.callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, filePath);
  625. }
  626. }
  627. }
  628. }
  629. public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
  630. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
  631. try {
  632. client.sslSocketFactory(new TLSSocketFactory());
  633. ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
  634. .tlsVersions(TlsVersion.TLS_1_2)
  635. .build();
  636. List< ConnectionSpec > specs = new ArrayList < > ();
  637. specs.add(cs);
  638. specs.add(ConnectionSpec.COMPATIBLE_TLS);
  639. specs.add(ConnectionSpec.CLEARTEXT);
  640. client.connectionSpecs(specs);
  641. } catch (Exception exc) {
  642. FLog.e("OkHttpClientProvider", "Error while enabling TLS 1.2", exc);
  643. }
  644. }
  645. return client;
  646. }
  647. }