You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2012/07/28 02:46:12 UTC

ios commit: Enhances iOS FileTransfer's support for upload headers

Updated Branches:
  refs/heads/master ad836cbb7 -> fa8cbdaba


Enhances iOS FileTransfer's support for upload headers

Instead of looking for the headers map on the params dict, it looks for
them in the args. This aligns with pull request:
https://github.com/apache/incubator-cordova-js/pull/20

Adds support for setting multiple header values by allowing
header values to be an array of values.


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/fa8cbdab
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/fa8cbdab
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/fa8cbdab

Branch: refs/heads/master
Commit: fa8cbdaba44b9131a5181368efbd451e0761c686
Parents: ad836cb
Author: Andrew Grieve <ag...@chromium.org>
Authored: Fri Jul 13 10:54:31 2012 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Fri Jul 27 20:40:25 2012 -0400

----------------------------------------------------------------------
 CordovaLib/Classes/CDVFileTransfer.m              |   90 +++++++++-------
 CordovaLib/CordovaLibTests/CDVFileTransferTests.m |   27 ++++--
 2 files changed, 69 insertions(+), 48 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/fa8cbdab/CordovaLib/Classes/CDVFileTransfer.m
----------------------------------------------------------------------
diff --git a/CordovaLib/Classes/CDVFileTransfer.m b/CordovaLib/Classes/CDVFileTransfer.m
index e831537..d020eda 100644
--- a/CordovaLib/Classes/CDVFileTransfer.m
+++ b/CordovaLib/Classes/CDVFileTransfer.m
@@ -22,6 +22,8 @@
 #include <CFNetwork/CFNetwork.h>
 
 @interface CDVFileTransfer ()
+// Sets the requests headers for the request.
+- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req;
 // Creates a delegate to handle an upload.
 - (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command;
 // Creates an NSData* for the file for the given upload arguments.
@@ -32,6 +34,10 @@
 static const NSUInteger kStreamBufferSize = 32768;
 // Magic value within the options dict used to set a cookie.
 NSString* const kOptionsKeyCookie = @"__cookie";
+// Form boundary for multi-part requests.
+NSString* const kFormBoundary = @"*****org.apache.cordova.formBoundary";
+  
+
 
 // Writes the given data to the stream in a blocking way.
 // If successful, returns bytesToWrite.
@@ -74,6 +80,39 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
     return [schemeAndHost stringByAppendingString:pathComponent];
 }
 
+- (void) applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req {
+    [req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
+
+    NSString* userAgent = [[self.webView request] valueForHTTPHeaderField:@"User-Agent"];    
+    if (userAgent) {
+        [req setValue: userAgent forHTTPHeaderField:@"User-Agent"];
+    }
+    
+    for (NSString* headerName in headers) {
+        id value = [headers objectForKey:headerName];
+        if (!value || value == [NSNull null]) {
+            value = @"null";
+        }
+
+        // First, remove an existing header if one exists.
+        [req setValue:nil forHTTPHeaderField:headerName];
+
+        if (![value isKindOfClass:[NSArray class]]) {
+            value = [NSArray arrayWithObject:value];
+        }
+        // Then, append all header values.
+        for (id subValue in value) {
+            // Convert from an NSNumber -> NSString.
+            if ([subValue respondsToSelector:@selector(stringValue)]) {
+                subValue = [subValue stringValue];
+            }
+            if ([subValue isKindOfClass:[NSString class]]) {
+                [req addValue:subValue forHTTPHeaderField:headerName];
+            }
+        }
+    }
+}
+
 - (NSURLRequest*) requestForUploadCommand:(CDVInvokedUrlCommand *)command fileData:(NSData *)fileData {
     // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]
     // however, params is a JavaScript object and during marshalling is put into the options dict, 
@@ -87,6 +126,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
     NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil];
 //  NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs
     BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
+    NSDictionary* headers = [arguments objectAtIndex:8 withDefault:nil];
 
     // CFStreamCreateBoundPair crashes on iOS < 5.
     if (!IsAtLeastiOSVersion(@"5")) {
@@ -124,47 +164,16 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
 		[req setValue:[options objectForKey:kOptionsKeyCookie] forHTTPHeaderField:@"Cookie"];
 		[req setHTTPShouldHandleCookies:NO];
 	}
-	
-	NSString *boundary = @"*****org.apache.cordova.formBoundary";
     
-	NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
+	NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary];
 	[req setValue:contentType forHTTPHeaderField:@"Content-Type"];
-    //Content-Type: multipart/form-data; boundary=*****org.apache.cordova.formBoundary
-	[req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
-	NSString* userAgent = [[self.webView request] valueForHTTPHeaderField:@"User-agent"];
-	
-    if(userAgent) {
-		[req setValue: userAgent forHTTPHeaderField:@"User-Agent"];
-	}
-	
-    NSDictionary* headers = [options objectForKey:@"headers"];
-    NSEnumerator *enumerator = [headers keyEnumerator];
-	id val;
-   	NSString *nkey;
-    
-	while (nkey = [enumerator nextObject]) {
-		val = [headers objectForKey:nkey];
-		if(!val || val == [NSNull null]) {
-			continue;	
-		}
-		// if it responds to stringValue selector (eg NSNumber) get the NSString
-		if ([val respondsToSelector:@selector(stringValue)]) {
-			val = [val stringValue];
-		}
-		// finally, check whether it is a NSString (for dataUsingEncoding selector below)
-		if (![val isKindOfClass:[NSString class]]) {
-			continue;
-		}
-        
-        [req setValue:val forHTTPHeaderField:nkey];	
-    }
+    [self applyRequestHeaders:headers toRequest:req];
     
+    NSData* formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
 	NSMutableData *postBodyBeforeFile = [NSMutableData data];
-	enumerator = [options keyEnumerator];
-	
-	id key;
-	while ((key = [enumerator nextObject])) {
-		val = [options objectForKey:key];
+
+	for (NSString* key in options) {
+		id val = [options objectForKey:key];
 		if(!val || val == [NSNull null] || [key isEqualToString:kOptionsKeyCookie]) {
 			continue;	
 		}
@@ -177,13 +186,13 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
 			continue;
 		}
 		
-		[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+		[postBodyBeforeFile appendData:formBoundaryData];
 		[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
 		[postBodyBeforeFile appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
 		[postBodyBeforeFile appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
 	}
     
-	[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
+	[postBodyBeforeFile appendData:formBoundaryData];
 	[postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
     if (mimeType != nil) {
         [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
@@ -191,7 +200,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
     [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Length: %d\r\n\r\n", [fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
 
     DLog(@"fileData length: %d", [fileData length]);
- 	NSData *postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding];
+ 	NSData *postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
 
     NSUInteger totalPayloadLength = [postBodyBeforeFile length] + [fileData length] + [postBodyAfterFile length];
     [req setValue:[[NSNumber numberWithInteger:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
@@ -295,6 +304,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream) {
     }
     
     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
+    [self applyRequestHeaders:nil toRequest:req];
 
     CDVFileTransferDelegate* delegate = [[[CDVFileTransferDelegate alloc] init] autorelease];
 	delegate.command = self;

http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/fa8cbdab/CordovaLib/CordovaLibTests/CDVFileTransferTests.m
----------------------------------------------------------------------
diff --git a/CordovaLib/CordovaLibTests/CDVFileTransferTests.m b/CordovaLib/CordovaLibTests/CDVFileTransferTests.m
index 6c2b929..22c609a 100644
--- a/CordovaLib/CordovaLibTests/CDVFileTransferTests.m
+++ b/CordovaLib/CordovaLibTests/CDVFileTransferTests.m
@@ -63,7 +63,7 @@ static NSData* readStream(NSInputStream* stream) {
     
     _arguments = [[NSMutableArray alloc] initWithObjects:
         kDummyArgTarget, kDummyArgServer, kDummyArgFileKey, [NSNull null],
-        [NSNull null], [NSNull null], [NSNull null], [NSNull null], nil];
+        [NSNull null], [NSNull null], [NSNull null], [NSNull null], [NSNull null], nil];
     _dummyFileData = [[kDummyFileContents dataUsingEncoding:NSUTF8StringEncoding] retain];
     _fileTransfer = [[CDVFileTransfer alloc] init];
 }
@@ -95,6 +95,10 @@ static NSData* readStream(NSInputStream* stream) {
     [_arguments replaceObjectAtIndex:5 withObject:params];
 }
 
+- (void)setHeaders:(NSDictionary*)headers {
+    [_arguments replaceObjectAtIndex:8 withObject:headers];
+}
+
 - (NSURLRequest*)requestForUpload {
     CDVInvokedUrlCommand* command = [[[CDVInvokedUrlCommand alloc] initWithArguments:_arguments
                                                                           callbackId:kDummyArgCallbackId
@@ -115,6 +119,7 @@ static NSData* readStream(NSInputStream* stream) {
         STAssertNil([request HTTPBodyStream], nil);
         payloadData = [request HTTPBody];
     }
+    STAssertNotNil([request valueForHTTPHeaderField:@"X-Requested-With"], nil);
     NSUInteger contentLength = [[request valueForHTTPHeaderField:@"Content-Length"] intValue];
     STAssertEquals([payloadData length], contentLength, nil);
 }
@@ -170,19 +175,14 @@ static NSData* readStream(NSInputStream* stream) {
     [self checkUploadRequest:request chunked:YES];
 }
 
-- (void)testUpload_withOptions
+- (void)testUpload_withParams
 {
     [self setChunkedModeArg:NO];
-    NSDictionary* headers = [NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1",
-        @"val2", @"key2", nil];
     NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:@"cookieval", kOptionsKeyCookie,
-        headers, @"headers", @"val3", @"key3", nil];
+        @"val3", @"key3", nil];
     [self setParams:params];
     NSURLRequest* request = [self requestForUpload];
     NSString* payload = [[[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding] autorelease];
-    // Check that headers are properly set.
-    STAssertTrue([@"val1" isEqualToString:[request valueForHTTPHeaderField:@"key1"]], nil);
-    STAssertTrue([@"val2" isEqualToString:[request valueForHTTPHeaderField:@"key2"]], nil);
     // Check that the cookie is set, and that it is not in the payload like others in the options dict.
     STAssertTrue([@"cookieval" isEqualToString:[request valueForHTTPHeaderField:@"Cookie"]], nil);
     STAssertEquals([payload rangeOfString:@"cookieval"].length, 0U, nil);
@@ -191,4 +191,15 @@ static NSData* readStream(NSInputStream* stream) {
     STAssertTrue([payload rangeOfString:@"val3"].length > 0, nil);
 }
 
+- (void)testUpload_withHeaders
+{
+    [self setChunkedModeArg:NO];
+    [self setHeaders:[NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1",
+        [NSArray arrayWithObjects:@"val2a", @"val2b", nil], @"key2", [NSNull null], @"X-Requested-With", nil]];
+    NSURLRequest* request = [self requestForUpload];
+    STAssertTrue([@"val1" isEqualToString:[request valueForHTTPHeaderField:@"key1"]], nil);
+    STAssertTrue([@"val2a,val2b" isEqualToString:[request valueForHTTPHeaderField:@"key2"]], nil);
+    STAssertTrue([@"null" isEqualToString:[request valueForHTTPHeaderField:@"X-Requested-With"]], nil);
+}
+
 @end