Permalink
Browse files

Added support for S3 storage class (eg Reduced Redundancy)

Added tests for canned ACLs
  • Loading branch information...
1 parent 484c597 commit cd552d7892d7f3c4451a45ce90b7107cde600263 @pokeb committed Sep 19, 2010
@@ -10,6 +10,9 @@
#import <Foundation/Foundation.h>
#import "ASIS3Request.h"
+// Constants for storage class
+extern NSString *const ASIS3StorageClassStandard;
+extern NSString *const ASIS3StorageClassReducedRedundancy;
@interface ASIS3ObjectRequest : ASIS3Request {
@@ -28,7 +31,14 @@
// Can be autodetected when PUTing a file from disk, will default to 'application/octet-stream' when PUTing data
NSString *mimeType;
+ // Set this to specify you want to work with a particular subresource (eg an acl for that resource)
+ // See requestWithBucket:key:subResource:, below.
NSString* subResource;
+
+ // The storage class to be used for PUT requests
+ // Set this to ASIS3StorageClassReducedRedundancy to save money on storage, at (presumably) a slightly higher risk you will lose the data
+ // If this is not set, no x-amz-storage-class header will be sent to S3, and their default will be used
+ NSString *storageClass;
}
// Create a request, building an appropriate url
@@ -66,5 +76,5 @@
@property (retain, nonatomic) NSString *sourceKey;
@property (retain, nonatomic) NSString *mimeType;
@property (retain, nonatomic) NSString *subResource;
-
+@property (retain, nonatomic) NSString *storageClass;
@end
@@ -8,6 +8,8 @@
#import "ASIS3ObjectRequest.h"
+NSString *const ASIS3StorageClassStandard = @"STANDARD";
+NSString *const ASIS3StorageClassReducedRedundancy = @"REDUCED_REDUNDANCY";
@implementation ASIS3ObjectRequest
@@ -96,13 +98,14 @@ - (void)dealloc
[sourceKey release];
[sourceBucket release];
[subResource release];
+ [storageClass release];
[super dealloc];
}
- (void)buildURL
{
if ([self subResource]) {
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@?%@",[self requestScheme],[[self class] S3Host],[ASIS3Request stringByURLEncodingForS3Path:[self key]],[self subResource]]]];
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@?%@",[self requestScheme],[self bucket],[[self class] S3Host],[ASIS3Request stringByURLEncodingForS3Path:[self key]],[self subResource]]]];
} else {
[self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@",[self requestScheme],[self bucket],[[self class] S3Host],[ASIS3Request stringByURLEncodingForS3Path:[self key]]]]];
}
@@ -134,6 +137,9 @@ - (NSMutableDictionary *)S3Headers
NSString *path = [ASIS3Request stringByURLEncodingForS3Path:[self sourceKey]];
[headers setObject:[[self sourceBucket] stringByAppendingString:path] forKey:@"x-amz-copy-source"];
}
+ if ([self storageClass]) {
+ [headers setObject:[self storageClass] forKey:@"x-amz-storage-class"];
+ }
return headers;
}
@@ -146,12 +152,11 @@ - (NSString *)stringToSignForHeaders:(NSString *)canonicalizedAmzHeaders resourc
return [super stringToSignForHeaders:canonicalizedAmzHeaders resource:canonicalizedResource];
}
-
@synthesize bucket;
@synthesize key;
@synthesize sourceBucket;
@synthesize sourceKey;
@synthesize mimeType;
@synthesize subResource;
-
+@synthesize storageClass;
@end
@@ -23,13 +23,18 @@ extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
extern NSString *const ASIS3AccessPolicyBucketOwnerRead;
extern NSString *const ASIS3AccessPolicyBucketOwnerFullControl;
+// Constants for requestScheme - defaults is ASIS3RequestSchemeHTTP
+extern NSString *const ASIS3RequestSchemeHTTP;
+extern NSString *const ASIS3RequestSchemeHTTPS;
+
+
+
typedef enum _ASIS3ErrorType {
ASIS3ResponseParsingFailedType = 1,
ASIS3ResponseErrorType = 2
} ASIS3ErrorType;
-extern NSString *const ASIS3RequestSchemeHTTP;
-extern NSString *const ASIS3RequestSchemeHTTPS;
+
@interface ASIS3Request : ASIHTTPRequest <NSCopying, NSXMLParserDelegate> {
@@ -19,7 +19,6 @@
NSString *const ASIS3RequestSchemeHTTP = @"http";
NSString *const ASIS3RequestSchemeHTTPS = @"https";
-
static NSString *sharedAccessKey = nil;
static NSString *sharedSecretAccessKey = nil;
@@ -119,7 +118,7 @@ - (void)buildRequestHeaders
// Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private)
NSMutableDictionary *amzHeaders = [self S3Headers];
NSString *canonicalizedAmzHeaders = @"";
- for (NSString *header in [amzHeaders keyEnumerator]) {
+ for (NSString *header in [amzHeaders keysSortedByValueUsingSelector:@selector(compare:)]) {
canonicalizedAmzHeaders = [NSString stringWithFormat:@"%@%@:%@\n",canonicalizedAmzHeaders,[header lowercaseString],[amzHeaders objectForKey:header]];
[self addRequestHeader:header value:[amzHeaders objectForKey:header]];
}
@@ -20,8 +20,6 @@
// You should run these tests on a bucket that does not yet exist
static NSString *bucket = @"";
-
-
// Used for subclass test
@interface ASIS3ObjectRequestSubclass : ASIS3ObjectRequest {}
@end
@@ -220,6 +218,7 @@ - (void)testREST
ASIS3ObjectRequest *request = [ASIS3ObjectRequest PUTRequestForFile:filePath withBucket:bucket key:key];
[request setSecretAccessKey:secretAccessKey];
[request setAccessKey:accessKey];
+ [request setStorageClass:ASIS3StorageClassReducedRedundancy];
[request startSynchronous];
success = [[request responseString] isEqualToString:@""];
GHAssertTrue(success,@"Failed to PUT a file to S3");
@@ -785,9 +784,55 @@ - (void)testHTTPS
// DELETE it
request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"];
+ [request setRequestScheme:ASIS3RequestSchemeHTTPS];
[request startSynchronous];
success = [[request responseString] isEqualToString:@""];
- GHAssertTrue(success,@"Failed to DELETE the copy from S3");
+ GHAssertTrue(success,@"Failed to DELETE the object from S3");
+
+ // Delete the bucket
+ request = [ASIS3BucketRequest DELETERequestWithBucket:bucket];
+ [request setRequestScheme:ASIS3RequestSchemeHTTPS];
+ [request startSynchronous];
+ GHAssertNil([request error],@"Failed to delete a bucket");
+
+ [ASIS3Request setSharedAccessKey:nil];
+ [ASIS3Request setSharedSecretAccessKey:nil];
+}
+
+// Ideally this test would actually parse the ACL XML and check it, but for now it just makes sure S3 doesn't return an error
+- (void)testCannedACLs
+{
+ [ASIS3Request setSharedAccessKey:accessKey];
+ [ASIS3Request setSharedSecretAccessKey:secretAccessKey];
+
+ // Create a bucket
+ ASIS3Request *request = [ASIS3BucketRequest PUTRequestWithBucket:bucket];
+ [request setRequestScheme:ASIS3RequestSchemeHTTPS];
+ [request startSynchronous];
+ GHAssertNil([request error],@"Failed to create a bucket");
+
+ NSArray *ACLs = [NSArray arrayWithObjects:ASIS3AccessPolicyPrivate,ASIS3AccessPolicyPublicRead,ASIS3AccessPolicyPublicReadWrite,ASIS3AccessPolicyAuthenticatedRead,ASIS3AccessPolicyBucketOwnerRead,ASIS3AccessPolicyBucketOwnerFullControl,nil];
+
+ for (NSString *cannedACL in ACLs) {
+ // PUT object
+ NSString *key = @"king";
+ request = [ASIS3ObjectRequest PUTRequestForData:[@"fink" dataUsingEncoding:NSUTF8StringEncoding] withBucket:bucket key:key];
+ [request setAccessPolicy:cannedACL];
+ [request startSynchronous];
+ GHAssertNil([request error],@"Failed to PUT some data into S3");
+
+ // GET object ACL
+ request = [ASIS3ObjectRequest requestWithBucket:bucket key:key subResource:@"acl"];
+ [request startSynchronous];
+ GHAssertNil([request error],@"Failed to fetch the object");
+ }
+
+ // DELETE it
+ request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"];
+ [request setRequestScheme:ASIS3RequestSchemeHTTPS];
+ [request startSynchronous];
+ BOOL success = [[request responseString] isEqualToString:@""];
+ GHAssertTrue(success,@"Failed to DELETE the object from S3");
// Delete the bucket
request = [ASIS3BucketRequest DELETERequestWithBucket:bucket];

0 comments on commit cd552d7

Please sign in to comment.