Browse Source

Add support of switch chunked transfer encoding #117

Ben Hsieh 8 years ago
parent
commit
467788b8c4

+ 42
- 31
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobBody.java View File

34
     RNFetchBlobReq.RequestType requestType;
34
     RNFetchBlobReq.RequestType requestType;
35
     MediaType mime;
35
     MediaType mime;
36
     File bodyCache;
36
     File bodyCache;
37
+    Boolean chunkedEncoding = false;
37
 
38
 
38
 
39
 
39
-    /**
40
-     * Single file or raw content request constructor
41
-     * @param taskId
42
-     * @param type
43
-     * @param form
44
-     * @param contentType
45
-     */
46
-    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, ReadableArray form, MediaType contentType) {
40
+    public RNFetchBlobBody(String taskId) {
47
         this.mTaskId = taskId;
41
         this.mTaskId = taskId;
48
-        this.form = form;
49
-        requestType = type;
50
-        mime = contentType;
51
-        try {
52
-            bodyCache = createMultipartBodyCache();
53
-            requestStream = new FileInputStream(bodyCache);
54
-            contentLength = bodyCache.length();
55
-        } catch(Exception ex) {
56
-            ex.printStackTrace();
57
-            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
58
-        }
42
+    }
43
+
44
+    RNFetchBlobBody chunkedEncoding(boolean val) {
45
+        this.chunkedEncoding = val;
46
+        return this;
47
+    }
48
+
49
+    RNFetchBlobBody setMIME(MediaType mime) {
50
+        this.mime = mime;
51
+        return this;
52
+    }
53
+
54
+    RNFetchBlobBody setRequestType( RNFetchBlobReq.RequestType type) {
55
+        this.requestType = type;
56
+        return this;
59
     }
57
     }
60
 
58
 
61
     /**
59
     /**
62
-     * Multipart request constructor
63
-     * @param taskId
64
-     * @param type
65
-     * @param rawBody
66
-     * @param contentType
60
+     * Set request body
61
+     * @param body A string represents the request body
62
+     * @return object itself
67
      */
63
      */
68
-    public RNFetchBlobBody(String taskId, RNFetchBlobReq.RequestType type, String rawBody, MediaType contentType) {
69
-        this.mTaskId = taskId;
70
-        requestType = type;
71
-        this.rawBody = rawBody;
72
-        mime = contentType;
64
+    RNFetchBlobBody setBody(String body) {
65
+        this.rawBody = body;
73
         if(rawBody == null) {
66
         if(rawBody == null) {
74
             this.rawBody = "";
67
             this.rawBody = "";
75
             requestType = RNFetchBlobReq.RequestType.AsIs;
68
             requestType = RNFetchBlobReq.RequestType.AsIs;
90
             ex.printStackTrace();
83
             ex.printStackTrace();
91
             RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
84
             RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create single content request body :" + ex.getLocalizedMessage() + "\r\n");
92
         }
85
         }
86
+        return this;
87
+    }
93
 
88
 
89
+    /**
90
+     * Set request body (Array)
91
+     * @param body A Readable array contains form data
92
+     * @return object itself
93
+     */
94
+    RNFetchBlobBody setBody(ReadableArray body) {
95
+        this.form = body;
96
+        try {
97
+            bodyCache = createMultipartBodyCache();
98
+            requestStream = new FileInputStream(bodyCache);
99
+            contentLength = bodyCache.length();
100
+        } catch(Exception ex) {
101
+            ex.printStackTrace();
102
+            RNFetchBlobUtils.emitWarningEvent("RNFetchBlob failed to create request multipart body :" + ex.getLocalizedMessage());
103
+        }
104
+        return this;
94
     }
105
     }
95
 
106
 
96
     @Override
107
     @Override
97
     public long contentLength() {
108
     public long contentLength() {
98
-        return contentLength;
109
+        return chunkedEncoding ? -1 : contentLength;
99
     }
110
     }
100
 
111
 
101
     @Override
112
     @Override
102
     public MediaType contentType() {
113
     public MediaType contentType() {
103
         return mime;
114
         return mime;
104
     }
115
     }
105
-    
116
+
106
     @Override
117
     @Override
107
     public void writeTo(BufferedSink sink) {
118
     public void writeTo(BufferedSink sink) {
108
         try {
119
         try {

+ 32
- 49
src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java View File

17
 import com.facebook.react.bridge.ReadableArray;
17
 import com.facebook.react.bridge.ReadableArray;
18
 import com.facebook.react.bridge.ReadableMap;
18
 import com.facebook.react.bridge.ReadableMap;
19
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
19
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
20
-import com.facebook.react.bridge.WritableArray;
21
 import com.facebook.react.bridge.WritableMap;
20
 import com.facebook.react.bridge.WritableMap;
22
 import com.facebook.react.modules.core.DeviceEventManagerModule;
21
 import com.facebook.react.modules.core.DeviceEventManagerModule;
23
 
22
 
33
 import java.nio.charset.CharacterCodingException;
32
 import java.nio.charset.CharacterCodingException;
34
 import java.nio.charset.Charset;
33
 import java.nio.charset.Charset;
35
 import java.nio.charset.CharsetEncoder;
34
 import java.nio.charset.CharsetEncoder;
36
-import java.util.ArrayList;
37
 import java.util.HashMap;
35
 import java.util.HashMap;
38
 import java.util.concurrent.TimeUnit;
36
 import java.util.concurrent.TimeUnit;
39
 
37
 
74
 
72
 
75
     ReactApplicationContext ctx;
73
     ReactApplicationContext ctx;
76
     RNFetchBlobConfig options;
74
     RNFetchBlobConfig options;
77
-    ArrayList<String> redirects = new ArrayList<>();
78
     String taskId;
75
     String taskId;
79
     String method;
76
     String method;
80
     String url;
77
     String url;
107
             responseType = ResponseType.KeepInMemory;
104
             responseType = ResponseType.KeepInMemory;
108
 
105
 
109
 
106
 
110
-		if (body != null)
107
+        if (body != null)
111
             requestType = RequestType.SingleFile;
108
             requestType = RequestType.SingleFile;
112
         else if (arrayBody != null)
109
         else if (arrayBody != null)
113
             requestType = RequestType.Form;
110
             requestType = RequestType.Form;
159
 
156
 
160
         // find cached result if `key` property exists
157
         // find cached result if `key` property exists
161
         String cacheKey = this.taskId;
158
         String cacheKey = this.taskId;
162
-		String ext = this.options.appendExt.isEmpty() ? "." + this.options.appendExt : "";
159
+        String ext = this.options.appendExt.isEmpty() ? "." + this.options.appendExt : "";
163
 
160
 
164
-       	if (this.options.key != null) {
165
-           cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
166
-           if (cacheKey == null) {
167
-               cacheKey = this.taskId;
168
-           }
161
+        if (this.options.key != null) {
162
+            cacheKey = RNFetchBlobUtils.getMD5(this.options.key);
163
+            if (cacheKey == null) {
164
+                cacheKey = this.taskId;
165
+            }
169
 
166
 
170
-           File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
167
+            File file = new File(RNFetchBlobFS.getTmpPath(RNFetchBlob.RCTContext, cacheKey) + ext);
171
 
168
 
172
-           if (file.exists()) {
173
-               callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
174
-               return;
175
-           }
176
-       }
169
+            if (file.exists()) {
170
+                callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, file.getAbsolutePath());
171
+                return;
172
+            }
173
+        }
177
 
174
 
178
         if(this.options.path != null)
175
         if(this.options.path != null)
179
             this.destPath = this.options.path;
176
             this.destPath = this.options.path;
239
                 requestType = RequestType.WithoutBody;
236
                 requestType = RequestType.WithoutBody;
240
             }
237
             }
241
 
238
 
239
+            boolean isChunkedRequest = getHeaderIgnoreCases(mheaders, "Transfer-Encoding").equalsIgnoreCase("chunked");
242
 
240
 
243
             // set request body
241
             // set request body
244
             switch (requestType) {
242
             switch (requestType) {
245
                 case SingleFile:
243
                 case SingleFile:
246
-                    requestBody = new RNFetchBlobBody(
247
-                            taskId,
248
-                            requestType,
249
-                            rawRequestBody,
250
-                            MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
251
-                    );
244
+                    requestBody = new RNFetchBlobBody(taskId)
245
+                            .chunkedEncoding(isChunkedRequest)
246
+                            .setRequestType(requestType)
247
+                            .setBody(rawRequestBody)
248
+                            .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
252
                     builder.method(method, requestBody);
249
                     builder.method(method, requestBody);
253
                     break;
250
                     break;
254
                 case AsIs:
251
                 case AsIs:
255
-                    requestBody = new RNFetchBlobBody(
256
-                            taskId,
257
-                            requestType,
258
-                            rawRequestBody,
259
-                            MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type"))
260
-                    );
252
+                    requestBody = new RNFetchBlobBody(taskId)
253
+                            .chunkedEncoding(isChunkedRequest)
254
+                            .setRequestType(requestType)
255
+                            .setBody(rawRequestBody)
256
+                            .setMIME(MediaType.parse(getHeaderIgnoreCases(mheaders, "content-type")));
261
                     builder.method(method, requestBody);
257
                     builder.method(method, requestBody);
262
                     break;
258
                     break;
263
                 case Form:
259
                 case Form:
264
                     String boundary = "RNFetchBlob-" + taskId;
260
                     String boundary = "RNFetchBlob-" + taskId;
265
-                    requestBody = new RNFetchBlobBody(
266
-                            taskId,
267
-                            requestType,
268
-                            rawRequestBodyArray,
269
-                            MediaType.parse("multipart/form-data; boundary="+ boundary)
270
-                    );
261
+                    requestBody = new RNFetchBlobBody(taskId)
262
+                            .chunkedEncoding(isChunkedRequest)
263
+                            .setRequestType(requestType)
264
+                            .setBody(rawRequestBodyArray)
265
+                            .setMIME(MediaType.parse("multipart/form-data; boundary="+ boundary));
271
                     builder.method(method, requestBody);
266
                     builder.method(method, requestBody);
272
                     break;
267
                     break;
273
 
268
 
283
 
278
 
284
             final Request req = builder.build();
279
             final Request req = builder.build();
285
 
280
 
286
-            // intercept network redirections
287
-            clientBuilder.addNetworkInterceptor(new Interceptor() {
288
-                @Override
289
-                public Response intercept(Chain chain) throws IOException {
290
-                    redirects.add(chain.request().url().toString());
291
-                    return chain.proceed(chain.request());
292
-                }
293
-            });
294
-
295
             // Add request interceptor for upload progress event
281
             // Add request interceptor for upload progress event
296
             clientBuilder.addInterceptor(new Interceptor() {
282
             clientBuilder.addInterceptor(new Interceptor() {
297
                 @Override
283
                 @Override
468
                     // It uses customized response body which is able to report download progress
454
                     // It uses customized response body which is able to report download progress
469
                     // and write response data to destination path.
455
                     // and write response data to destination path.
470
                     resp.body().bytes();
456
                     resp.body().bytes();
471
-                } catch (Exception ignored) {  }
457
+                } catch (Exception ignored) {
458
+                    ignored.printStackTrace();
459
+                }
472
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
460
                 callback.invoke(null, RNFetchBlobConst.RNFB_RESPONSE_PATH, this.destPath);
473
                 break;
461
                 break;
474
             default:
462
             default:
521
             headers.putString(resp.headers().name(i), resp.headers().value(i));
509
             headers.putString(resp.headers().name(i), resp.headers().value(i));
522
         }
510
         }
523
         info.putMap("headers", headers);
511
         info.putMap("headers", headers);
524
-        WritableArray redirectList = Arguments.createArray();
525
-        for(String r : redirects) {
526
-            redirectList.pushString(r);
527
-        }
528
-        info.putArray("redirects", redirectList);
529
         Headers h = resp.headers();
512
         Headers h = resp.headers();
530
         if(isBlobResp) {
513
         if(isBlobResp) {
531
             info.putString("respType", "blob");
514
             info.putString("respType", "blob");

+ 9
- 1
src/ios/RNFetchBlobReqBuilder.m View File

90
             // generate octet-stream body
90
             // generate octet-stream body
91
             if(body != nil) {
91
             if(body != nil) {
92
                 __block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
92
                 __block NSString * cType = [[self class] getHeaderIgnoreCases:@"content-type" fromHeaders:mheaders];
93
+                __block NSString * transferEncoding = [[self class] getHeaderIgnoreCases:@"transfer-encoding" fromHeaders:mheaders];
93
                 // when headers does not contain a key named "content-type" (case ignored), use default content type
94
                 // when headers does not contain a key named "content-type" (case ignored), use default content type
94
                 if(cType == nil)
95
                 if(cType == nil)
95
                 {
96
                 {
111
                         return;
112
                         return;
112
                     }
113
                     }
113
                     size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
114
                     size = [[[NSFileManager defaultManager] attributesOfItemAtPath:orgPath error:nil] fileSize];
114
-                    [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
115
+                    if(transferEncoding != nil && [[transferEncoding lowercaseString] isEqualToString:@"chunked"])
116
+                    {
117
+                        [request setHTTPBodyStream: [NSInputStream inputStreamWithFileAtPath:orgPath ]];
118
+                    }
119
+                    else
120
+                    {
121
+                        [request setHTTPBody:[NSData dataWithContentsOfFile:orgPath ]];
122
+                    }
115
                 }
123
                 }
116
                 // otherwise convert it as BASE64 data string
124
                 // otherwise convert it as BASE64 data string
117
                 else {
125
                 else {