Skip to content
This repository was archived by the owner on Mar 16, 2019. It is now read-only.

Commit 15d4068

Browse files
committed
Fix XMLHttpRequest auto strategy on Android
1 parent fb25768 commit 15d4068

File tree

4 files changed

+64
-31
lines changed

4 files changed

+64
-31
lines changed

src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.RNFetchBlob;
22

3+
import com.facebook.react.bridge.ReadableArray;
34
import com.facebook.react.bridge.ReadableMap;
45

56
import java.util.HashMap;
@@ -17,7 +18,8 @@ public class RNFetchBlobConfig {
1718
public String key;
1819
public String mime;
1920
public Boolean auto;
20-
public long timeout = 30000;
21+
public long timeout = -1;
22+
public ReadableArray binaryContentTypes = null;
2123

2224
RNFetchBlobConfig(ReadableMap options) {
2325
if(options == null)
@@ -29,6 +31,8 @@ public class RNFetchBlobConfig {
2931
if(options.hasKey("addAndroidDownloads")) {
3032
this.addAndroidDownloads = options.getMap("addAndroidDownloads");
3133
}
34+
if(options.hasKey("binaryContentTypes"))
35+
this.binaryContentTypes = options.getArray("binaryContentTypes");
3236
this.key = options.hasKey("key") ? options.getString("key") : null;
3337
this.mime = options.hasKey("contentType") ? options.getString("contentType") : null;
3438
this.auto = options.hasKey("auto") ? options.getBoolean("auto") : false;

src/android/src/main/java/com/RNFetchBlob/RNFetchBlobFS.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ static public Map<String, Object> getSystemfolders(ReactApplicationContext ctx)
237237
* @return
238238
*/
239239
static public String getTmpPath(ReactApplicationContext ctx, String taskId) {
240-
return ctx.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
240+
return RNFetchBlob.RCTContext.getFilesDir() + "/RNFetchBlobTmp_" + taskId;
241241
}
242242

243243
/**

src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ else if(this.options.fileCache == true)
275275

276276
final Request req = builder.build();
277277

278-
// create response handler
278+
// Create response body depends on the responseType
279279
clientBuilder.addInterceptor(new Interceptor() {
280280
@Override
281281
public Response intercept(Chain chain) throws IOException {
@@ -304,35 +304,36 @@ public Response intercept(Chain chain) throws IOException {
304304
break;
305305
}
306306
return originalResponse.newBuilder().body(extended).build();
307-
} catch(SocketTimeoutException ex) {
307+
} catch(Exception ex) {
308308
timeout = true;
309309
}
310310
return chain.proceed(chain.request());
311311
}
312312
});
313313

314+
314315
if(options.timeout > 0) {
315316
clientBuilder.connectTimeout(options.timeout, TimeUnit.MILLISECONDS);
316317
clientBuilder.readTimeout(options.timeout, TimeUnit.MILLISECONDS);
317318
}
318-
else {
319-
clientBuilder.connectTimeout(-1, TimeUnit.MILLISECONDS);
320-
clientBuilder.readTimeout(-1, TimeUnit.MILLISECONDS);
321-
}
319+
322320
clientBuilder.connectionPool(pool);
323321
clientBuilder.retryOnConnectionFailure(false);
322+
clientBuilder.followRedirects(true);
323+
324324
OkHttpClient client = clientBuilder.build();
325325
Call call = client.newCall(req);
326326
taskTable.put(taskId, call);
327327
call.enqueue(new okhttp3.Callback() {
328328

329329
@Override
330330
public void onFailure(Call call, IOException e) {
331+
cancelTask(taskId);
331332
if(respInfo == null) {
332333
respInfo = Arguments.createMap();
333334
}
334335

335-
// check if this error caused by timeout
336+
// check if this error caused by socket timeout
336337
if(e.getClass().equals(SocketTimeoutException.class)) {
337338
respInfo.putBoolean("timeout", true);
338339
callback.invoke("request timed out.", respInfo, null);
@@ -345,7 +346,6 @@ public void onFailure(Call call, IOException e) {
345346
@Override
346347
public void onResponse(Call call, Response response) throws IOException {
347348
ReadableMap notifyConfig = options.addAndroidDownloads;
348-
respInfo = getResponseInfo(response);
349349
// Download manager settings
350350
if(notifyConfig != null ) {
351351
String title = "", desc = "", mime = "text/plain";
@@ -371,7 +371,7 @@ public void onResponse(Call call, Response response) throws IOException {
371371
} catch (Exception error) {
372372
error.printStackTrace();
373373
taskTable.remove(taskId);
374-
callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause(), this.respInfo);
374+
callback.invoke("RNFetchBlob request error: " + error.getMessage() + error.getCause());
375375
}
376376
}
377377

@@ -392,14 +392,15 @@ private void removeTaskInfo() {
392392
* @param resp OkHttp response object
393393
*/
394394
private void done(Response resp) {
395-
emitStateEvent(getResponseInfo(resp));
395+
boolean isBlobResp = isBlobResponse(resp);
396+
emitStateEvent(getResponseInfo(resp, isBlobResp));
396397
switch (responseType) {
397398
case KeepInMemory:
398399
try {
399400
// For XMLHttpRequest, automatic response data storing strategy, when response
400401
// header is not `application/json` or `text/plain`, write response data to
401402
// file system.
402-
if(isBlobResponse(resp) && options.auto == true) {
403+
if(isBlobResp && options.auto == true) {
403404
String dest = RNFetchBlobFS.getTmpPath(ctx, taskId);
404405
InputStream ins = resp.body().byteStream();
405406
FileOutputStream os = new FileOutputStream(new File(dest));
@@ -412,27 +413,26 @@ private void done(Response resp) {
412413
}
413414
ins.close();
414415
os.close();
415-
WritableMap info = getResponseInfo(resp);
416-
callback.invoke(null, info, dest);
416+
callback.invoke(null, null, dest);
417417
}
418418
else {
419-
// we should check if the response data is a UTF8 string, because BASE64
420-
// encoding will somehow break the UTF8 string format. In order to encode
421-
// UTF8 string correctly, we should do URL encoding before BASE64.
419+
// #73 Check if the response data contains valid UTF8 string, since BASE64
420+
// encoding will somehow break the UTF8 string format, to encode UTF8
421+
// string correctly, we should do URL encoding before BASE64.
422422
String utf8Str;
423423
byte[] b = resp.body().bytes();
424424
CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
425425
try {
426426
encoder.encode(ByteBuffer.wrap(b).asCharBuffer());
427427
// if the data can be encoded to UTF8 append URL encode
428-
b = URLEncoder.encode(new String(b), "UTF-8").getBytes();
428+
b = URLEncoder.encode(new String(b), "UTF-8").replace("+", "%20").getBytes();
429429
}
430430
// This usually mean the data is binary data
431431
catch(CharacterCodingException e) {
432432

433433
}
434434
finally {
435-
callback.invoke(null, getResponseInfo(resp), android.util.Base64.encodeToString(b, Base64.NO_WRAP));
435+
callback.invoke(null, null, android.util.Base64.encodeToString(b, Base64.NO_WRAP));
436436
}
437437
}
438438
} catch (IOException e) {
@@ -441,15 +441,18 @@ private void done(Response resp) {
441441
break;
442442
case FileStorage:
443443
try{
444+
// In order to write response data to `destPath` we have to invoke this method.
445+
// It uses customized response body which is able to report download progress
446+
// and write response data to destination path.
444447
resp.body().bytes();
445448
} catch (Exception ignored) {
446449

447450
}
448-
callback.invoke(null, getResponseInfo(resp), this.destPath);
451+
callback.invoke(null, null, this.destPath);
449452
break;
450453
default:
451454
try {
452-
callback.invoke(null, getResponseInfo(resp), new String(resp.body().bytes(), "UTF-8"));
455+
callback.invoke(null, null, new String(resp.body().bytes(), "UTF-8"));
453456
} catch (IOException e) {
454457
callback.invoke("RNFetchBlob failed to encode response data to UTF8 string.", null);
455458
}
@@ -458,17 +461,27 @@ private void done(Response resp) {
458461
removeTaskInfo();
459462
}
460463

464+
/**
465+
* Invoke this method to enable download progress reporting.
466+
* @param taskId Task ID of the HTTP task.
467+
* @return Task ID of the target task
468+
*/
461469
public static boolean isReportProgress(String taskId) {
462470
if(!progressReport.containsKey(taskId)) return false;
463471
return progressReport.get(taskId);
464472
}
465473

474+
/**
475+
* Invoke this method to enable download progress reporting.
476+
* @param taskId Task ID of the HTTP task.
477+
* @return Task ID of the target task
478+
*/
466479
public static boolean isReportUploadProgress(String taskId) {
467480
if(!uploadProgressReport.containsKey(taskId)) return false;
468481
return uploadProgressReport.get(taskId);
469482
}
470483

471-
private WritableMap getResponseInfo(Response resp) {
484+
private WritableMap getResponseInfo(Response resp, boolean isBlobResp) {
472485
WritableMap info = Arguments.createMap();
473486
info.putInt("status", resp.code());
474487
info.putString("state", "2");
@@ -480,26 +493,36 @@ private WritableMap getResponseInfo(Response resp) {
480493
}
481494
info.putMap("headers", headers);
482495
Headers h = resp.headers();
483-
if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
496+
if(isBlobResp) {
497+
info.putString("respType", "blob");
498+
}
499+
else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/")) {
484500
info.putString("respType", "text");
485501
}
486502
else if(getHeaderIgnoreCases(h, "content-type").contains("application/json")) {
487503
info.putString("respType", "json");
488504
}
489-
else if(getHeaderIgnoreCases(h, "content-type").length() < 1) {
490-
info.putString("respType", "blob");
491-
}
492505
else {
493-
info.putString("respType", "text");
506+
info.putString("respType", "");
494507
}
495508
return info;
496509
}
497510

498511
private boolean isBlobResponse(Response resp) {
499512
Headers h = resp.headers();
500-
boolean isText = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/");
501-
boolean isJSON = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json");
502-
return !(isJSON || isText);
513+
String ctype = getHeaderIgnoreCases(h, "Content-Type");
514+
boolean isText = !ctype.equalsIgnoreCase("text/");
515+
boolean isJSON = !ctype.equalsIgnoreCase("application/json");
516+
boolean isCustomBinary = false;
517+
if(options.binaryContentTypes != null) {
518+
for(int i = 0; i< options.binaryContentTypes.size();i++) {
519+
if(ctype.toLowerCase().contains(options.binaryContentTypes.getString(i).toLowerCase())) {
520+
isCustomBinary = true;
521+
break;
522+
}
523+
}
524+
}
525+
return (!(isJSON || isText)) || isCustomBinary;
503526
}
504527

505528
private String getHeaderIgnoreCases(Headers headers, String field) {

src/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,12 @@ class FetchBlobResponse {
247247
reject(err)
248248
}
249249
}
250+
else {
251+
polyfill.Blob.build(wrap(this.data))
252+
.then((b) => {
253+
resolve(b)
254+
})
255+
}
250256
})
251257
}
252258
/**

0 commit comments

Comments
 (0)