You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by da...@apache.org on 2016/05/17 09:45:01 UTC

cordova-plugin-file-transfer git commit: CB-10974 Cordova file transfer Content-Length header problem

Repository: cordova-plugin-file-transfer
Updated Branches:
  refs/heads/master 6ad92c92a -> 9347606dd


CB-10974 Cordova file transfer Content-Length header problem

Fixed iOS logic to not to include Content-Length when chunkedMode=true
Fixed Android logic preventing chunkedMode to be enabled for http uploads
Added tests for chunkedMode
Documented chunkedMode not supported on Windows
Documented Uploading of an empty file is not supported for chunkedMode=true and multipart=false for iOS, pended the corresponding test


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/commit/9347606d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/tree/9347606d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/diff/9347606d

Branch: refs/heads/master
Commit: 9347606dd33fe07ea36799b4dd28804019c68835
Parents: 6ad92c9
Author: daserge <v-...@microsoft.com>
Authored: Wed Apr 13 17:24:57 2016 +0300
Committer: daserge <v-...@microsoft.com>
Committed: Tue May 17 12:24:27 2016 +0300

----------------------------------------------------------------------
 README.md                     |  4 ++
 src/android/FileTransfer.java |  2 +-
 src/ios/CDVFileTransfer.h     |  1 +
 src/ios/CDVFileTransfer.m     | 13 ++++--
 tests/tests.js                | 87 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 102 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/9347606d/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index f57a808..ab224fa 100644
--- a/README.md
+++ b/README.md
@@ -190,6 +190,8 @@ A `FileUploadResult` object is passed to the success callback of the
 
 - Does not support `responseCode` or `bytesSent`.
 
+- Does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`.
+
 ### Browser Quirks
 
 - __withCredentials__: _boolean_ that tells the browser to set the withCredentials flag on the XMLHttpRequest
@@ -198,6 +200,8 @@ A `FileUploadResult` object is passed to the success callback of the
 
 - An option parameter with empty/null value is excluded in the upload operation due to the Windows API design.
 
+- __chunkedMode__ is not supported and all uploads are set to non-chunked mode.
+
 ## download
 
 __Parameters__:

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/9347606d/src/android/FileTransfer.java
----------------------------------------------------------------------
diff --git a/src/android/FileTransfer.java b/src/android/FileTransfer.java
index b9b99dc..86176a2 100644
--- a/src/android/FileTransfer.java
+++ b/src/android/FileTransfer.java
@@ -426,7 +426,7 @@ public class FileTransfer extends CordovaPlugin {
                     // setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
                     // http://code.google.com/p/android/issues/detail?id=3164
                     // It also causes OOM if HTTPS is used, even on newer devices.
-                    boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
+                    boolean useChunkedMode = chunkedMode || (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
                     useChunkedMode = useChunkedMode || (fixedLength == -1);
 
                     if (useChunkedMode) {

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/9347606d/src/ios/CDVFileTransfer.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVFileTransfer.h b/src/ios/CDVFileTransfer.h
index bb5fa13..b7301ef 100644
--- a/src/ios/CDVFileTransfer.h
+++ b/src/ios/CDVFileTransfer.h
@@ -84,5 +84,6 @@ extern NSString* const kOptionsKeyCookie;
 @property (strong) NSFileHandle* targetFileHandle;
 @property (nonatomic, strong) CDVFileTransferEntityLengthRequest* entityLengthRequest;
 @property (nonatomic, strong) CDVFile *filePlugin;
+@property (nonatomic, assign) BOOL chunkedMode;
 
 @end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/9347606d/src/ios/CDVFileTransfer.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVFileTransfer.m b/src/ios/CDVFileTransfer.m
index 4775a6d..6024901 100644
--- a/src/ios/CDVFileTransfer.m
+++ b/src/ios/CDVFileTransfer.m
@@ -229,8 +229,6 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
         totalPayloadLength += [postBodyBeforeFile length] + [postBodyAfterFile length];
     }
 
-    [req setValue:[[NSNumber numberWithLongLong:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
-
     if (chunkedMode) {
         CFReadStreamRef readStream = NULL;
         CFWriteStreamRef writeStream = NULL;
@@ -255,7 +253,11 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
                         }
                     }
                 } else {
-                    WriteDataToStream(fileData, writeStream);
+                    if (totalPayloadLength > 0) {
+                        WriteDataToStream(fileData, writeStream);
+                    } else {
+                        NSLog(@"Uploading of an empty file is not supported for chunkedMode=true and multipart=false");
+                    }
                 }
             } else {
                 NSLog(@"FileTransfer: Failed to open writeStream");
@@ -264,6 +266,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
             CFRelease(writeStream);
         }];
     } else {
+        [req setValue:[[NSNumber numberWithLongLong:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
         if (multipartFormUpload) {
             [postBodyBeforeFile appendData:fileData];
             [postBodyBeforeFile appendData:postBodyAfterFile];
@@ -281,6 +284,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
     NSString* server = [command argumentAtIndex:1];
     BOOL trustAllHosts = [[command argumentAtIndex:6 withDefault:[NSNumber numberWithBool:NO]] boolValue]; // allow self-signed certs
     NSString* objectId = [command argumentAtIndex:9];
+    BOOL chunkedMode = [[command argumentAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
 
     CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
 
@@ -292,6 +296,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
     delegate.target = server;
     delegate.trustAllHosts = trustAllHosts;
     delegate.filePlugin = [self.commandDelegate getCommandInstance:@"File"];
+    delegate.chunkedMode = chunkedMode;
 
     return delegate;
 }
@@ -809,7 +814,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
     if (self.direction == CDV_TRANSFER_UPLOAD) {
         NSMutableDictionary* uploadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
 
-        [uploadProgress setObject:[NSNumber numberWithBool:true] forKey:@"lengthComputable"];
+        [uploadProgress setObject:[NSNumber numberWithBool:(!self.chunkedMode)] forKey:@"lengthComputable"];
         [uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesWritten] forKey:@"loaded"];
         [uploadProgress setObject:[NSNumber numberWithLongLong:totalBytesExpectedToWrite] forKey:@"total"];
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadProgress];

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer/blob/9347606d/tests/tests.js
----------------------------------------------------------------------
diff --git a/tests/tests.js b/tests/tests.js
index 42fc680..d3dc4c7 100644
--- a/tests/tests.js
+++ b/tests/tests.js
@@ -1193,6 +1193,9 @@ exports.defineAutoTests = function () {
                         "CustomHeader2": ["CustomValue2", "CustomValue3"],
                     };
 
+                    // http://whatheaders.com does not support Transfer-Encoding: chunked
+                    this.uploadOptions.chunkedMode = false;
+
                     // NOTE: removing uploadOptions cause Android to timeout
                     this.transfer.upload(this.localFilePath, fileURL, uploadWin, uploadFail, this.uploadOptions);
                 }, UPLOAD_TIMEOUT);
@@ -1462,6 +1465,11 @@ exports.defineAutoTests = function () {
 
                 it("filetransfer.spec.41 should not fail to upload a file using data: source uri when the data is empty (non-multipart)", function (done) {
 
+                    if (isIos) {
+                        // iOS does not support uploads of an empty file with __chunkedMode=true__ and `multipartMode=false`:
+                        // request body will be empty in this case instead of 0\n\n.
+                        pending();
+                    }
                     var fileURL = SERVER + "/upload";
 
                     // Content-Type header disables multipart
@@ -1482,6 +1490,85 @@ exports.defineAutoTests = function () {
                     // NOTE: removing uploadOptions cause Android to timeout
                     this.transfer.upload(dataUri, fileURL, done, uploadFail, this.uploadOptions);
                 }, UPLOAD_TIMEOUT);
+
+                describe("chunkedMode handling", function() {
+                    var testChunkedModeWin = function (uploadResult, specContext) {
+                        var multipartModeEnabled = !(specContext.uploadOptions.headers && specContext.uploadOptions.headers["Content-Type"]);
+                        var obj = null;
+                        try {
+                            obj = JSON.parse(uploadResult.response);
+
+                            if (specContext.uploadOptions.chunkedMode) {
+                                expect(obj["content-length"]).not.toBeDefined("Expected Content-Length not to be defined");
+                                expect(obj["transfer-encoding"].toLowerCase()).toEqual("chunked");
+                            } else {
+                                expect(obj["content-length"]).toBeDefined("Expected Content-Length to be defined");
+                                expect(obj["transfer-encoding"].toLowerCase()).not.toEqual("chunked");
+                            }
+
+                            if (multipartModeEnabled) {
+                                expect(obj["content-type"].indexOf("multipart/form-data")).not.toBe(-1);
+                            } else {
+                                expect(obj["content-type"].indexOf("multipart/form-data")).toBe(-1);
+                            }
+                        } catch (e) {
+                            expect(obj).not.toBeNull("returned data from server should be valid json");
+                        }
+                    };
+
+                    var testChunkedModeBase = function(chunkedMode, multipart, done) {
+                        var fileURL = SERVER + "/upload_echo_headers";
+                        var specContext = this;
+
+                        specContext.uploadOptions.chunkedMode = chunkedMode;
+                        if (!multipart) {
+                            // Content-Type header disables multipart
+                            specContext.uploadOptions.headers = {
+                                "Content-Type": "text/plain"
+                            };
+                        }
+
+                        var uploadFail = function() {
+                            unexpectedCallbacks.httpFail();
+                            done();
+                        };
+
+                        // turn off the onprogress handler
+                        this.transfer.onprogress = function () {};
+
+                        // NOTE: removing uploadOptions cause Android to timeout
+                        specContext.transfer.upload(specContext.localFilePath, fileURL, function (uploadResult) {
+                            testChunkedModeWin(uploadResult, specContext);
+                            done();
+                        }, uploadFail, specContext.uploadOptions);
+                    };
+
+                    it("filetransfer.spec.42 chunkedMode=false, multipart=false", function (done) {
+
+                        testChunkedModeBase.call(this, false, false, done);
+                    }, UPLOAD_TIMEOUT);
+
+                    it("filetransfer.spec.43 chunkedMode=true, multipart=false", function (done) {
+
+                        if (isWindows) {
+                            pending();
+                        }
+                        testChunkedModeBase.call(this, true, false, done);
+                    }, UPLOAD_TIMEOUT);
+
+                    it("filetransfer.spec.44 chunkedMode=false, multipart=true", function (done) {
+
+                        testChunkedModeBase.call(this, false, true, done);
+                    }, UPLOAD_TIMEOUT);
+
+                    it("filetransfer.spec.45 chunkedMode=true, multipart=true", function (done) {
+
+                        if (isWindows) {
+                            pending();
+                        }
+                        testChunkedModeBase.call(this, true, true, done);
+                    }, UPLOAD_TIMEOUT);
+                });
             });
         });
     });


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org