Ben Copsey

Merge branch 'new-s3-changes' into v1.8-merge

Conflicts:
	Mac.xcodeproj/project.pbxproj
@@ -60,15 +60,12 @@ @@ -60,15 +60,12 @@
60 // Use for deleting buckets - they must be empty for this to succeed 60 // Use for deleting buckets - they must be empty for this to succeed
61 + (id)DELETERequestWithBucket:(NSString *)bucket; 61 + (id)DELETERequestWithBucket:(NSString *)bucket;
62 62
63 -//Builds a query string out of the list parameters we supplied 63 +@property (retain, nonatomic) NSString *bucket;
64 -- (void)createQueryString; 64 +@property (retain, nonatomic) NSString *subResource;
65 - 65 +@property (retain, nonatomic) NSString *prefix;
66 -@property (retain) NSString *bucket; 66 +@property (retain, nonatomic) NSString *marker;
67 -@property (retain) NSString *subResource; 67 +@property (assign, nonatomic) int maxResultCount;
68 -@property (retain) NSString *prefix; 68 +@property (retain, nonatomic) NSString *delimiter;
69 -@property (retain) NSString *marker;  
70 -@property (assign) int maxResultCount;  
71 -@property (retain) NSString *delimiter;  
72 @property (retain, readonly) NSMutableArray *objects; 69 @property (retain, readonly) NSMutableArray *objects;
73 @property (retain, readonly) NSMutableArray *commonPrefixes; 70 @property (retain, readonly) NSMutableArray *commonPrefixes;
74 @property (assign, readonly) BOOL isTruncated; 71 @property (assign, readonly) BOOL isTruncated;
@@ -30,20 +30,19 @@ @@ -30,20 +30,19 @@
30 30
31 + (id)requestWithBucket:(NSString *)bucket 31 + (id)requestWithBucket:(NSString *)bucket
32 { 32 {
33 - ASIS3BucketRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com",bucket]]] autorelease]; 33 + ASIS3BucketRequest *request = [[[self alloc] initWithURL:nil] autorelease];
34 [request setBucket:bucket]; 34 [request setBucket:bucket];
35 return request; 35 return request;
36 } 36 }
37 37
38 + (id)requestWithBucket:(NSString *)bucket subResource:(NSString *)subResource 38 + (id)requestWithBucket:(NSString *)bucket subResource:(NSString *)subResource
39 { 39 {
40 - ASIS3BucketRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?%@",bucket,subResource]]] autorelease]; 40 + ASIS3BucketRequest *request = [[[self alloc] initWithURL:nil] autorelease];
41 [request setBucket:bucket]; 41 [request setBucket:bucket];
42 [request setSubResource:subResource]; 42 [request setSubResource:subResource];
43 return request; 43 return request;
44 } 44 }
45 45
46 -  
47 + (id)PUTRequestWithBucket:(NSString *)bucket 46 + (id)PUTRequestWithBucket:(NSString *)bucket
48 { 47 {
49 ASIS3BucketRequest *request = [self requestWithBucket:bucket]; 48 ASIS3BucketRequest *request = [self requestWithBucket:bucket];
@@ -80,8 +79,14 @@ @@ -80,8 +79,14 @@
80 return [NSString stringWithFormat:@"/%@/",[self bucket]]; 79 return [NSString stringWithFormat:@"/%@/",[self bucket]];
81 } 80 }
82 81
83 -- (void)createQueryString 82 +- (void)buildURL
84 { 83 {
  84 + NSString *baseURL;
  85 + if ([self subResource]) {
  86 + baseURL = [NSString stringWithFormat:@"%@://%@.%@/?%@",[self requestScheme],[self bucket],[[self class] S3Host],[self subResource]];
  87 + } else {
  88 + baseURL = [NSString stringWithFormat:@"%@://%@.%@",[self requestScheme],[self bucket],[[self class] S3Host]];
  89 + }
85 NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease]; 90 NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease];
86 if ([self prefix]) { 91 if ([self prefix]) {
87 [queryParts addObject:[NSString stringWithFormat:@"prefix=%@",[[self prefix] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]; 92 [queryParts addObject:[NSString stringWithFormat:@"prefix=%@",[[self prefix] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
@@ -95,20 +100,16 @@ @@ -95,20 +100,16 @@
95 if ([self maxResultCount] > 0) { 100 if ([self maxResultCount] > 0) {
96 [queryParts addObject:[NSString stringWithFormat:@"max-keys=%hi",[self maxResultCount]]]; 101 [queryParts addObject:[NSString stringWithFormat:@"max-keys=%hi",[self maxResultCount]]];
97 } 102 }
98 - if ([queryParts count]) 103 + if ([queryParts count]) {
99 - {  
100 NSString* template = @"%@?%@"; 104 NSString* template = @"%@?%@";
101 if ([[self subResource] length] > 0) { 105 if ([[self subResource] length] > 0) {
102 template = @"%@&%@"; 106 template = @"%@&%@";
103 } 107 }
104 - [self setURL:[NSURL URLWithString:[NSString stringWithFormat:template,[[self url] absoluteString],[queryParts componentsJoinedByString:@"&"]]]]; 108 + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:template,baseURL,[queryParts componentsJoinedByString:@"&"]]]];
105 - } 109 + } else {
106 -} 110 + [self setURL:[NSURL URLWithString:baseURL]];
107 111
108 -- (void)main 112 + }
109 -{  
110 - [self createQueryString];  
111 - [super main];  
112 } 113 }
113 114
114 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict 115 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
@@ -146,7 +147,6 @@ @@ -146,7 +147,6 @@
146 } 147 }
147 } 148 }
148 149
149 -  
150 #pragma mark NSCopying 150 #pragma mark NSCopying
151 151
152 - (id)copyWithZone:(NSZone *)zone 152 - (id)copyWithZone:(NSZone *)zone
@@ -161,9 +161,6 @@ @@ -161,9 +161,6 @@
161 return newRequest; 161 return newRequest;
162 } 162 }
163 163
164 -  
165 -  
166 -  
167 @synthesize bucket; 164 @synthesize bucket;
168 @synthesize subResource; 165 @synthesize subResource;
169 @synthesize currentObject; 166 @synthesize currentObject;
@@ -10,6 +10,9 @@ @@ -10,6 +10,9 @@
10 #import <Foundation/Foundation.h> 10 #import <Foundation/Foundation.h>
11 #import "ASIS3Request.h" 11 #import "ASIS3Request.h"
12 12
  13 +// Constants for storage class
  14 +extern NSString *const ASIS3StorageClassStandard;
  15 +extern NSString *const ASIS3StorageClassReducedRedundancy;
13 16
14 @interface ASIS3ObjectRequest : ASIS3Request { 17 @interface ASIS3ObjectRequest : ASIS3Request {
15 18
@@ -28,7 +31,14 @@ @@ -28,7 +31,14 @@
28 // Can be autodetected when PUTing a file from disk, will default to 'application/octet-stream' when PUTing data 31 // Can be autodetected when PUTing a file from disk, will default to 'application/octet-stream' when PUTing data
29 NSString *mimeType; 32 NSString *mimeType;
30 33
  34 + // Set this to specify you want to work with a particular subresource (eg an acl for that resource)
  35 + // See requestWithBucket:key:subResource:, below.
31 NSString* subResource; 36 NSString* subResource;
  37 +
  38 + // The storage class to be used for PUT requests
  39 + // Set this to ASIS3StorageClassReducedRedundancy to save money on storage, at (presumably) a slightly higher risk you will lose the data
  40 + // If this is not set, no x-amz-storage-class header will be sent to S3, and their default will be used
  41 + NSString *storageClass;
32 } 42 }
33 43
34 // Create a request, building an appropriate url 44 // Create a request, building an appropriate url
@@ -60,11 +70,11 @@ @@ -60,11 +70,11 @@
60 // Creates a HEAD request for the object at path 70 // Creates a HEAD request for the object at path
61 + (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key; 71 + (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key;
62 72
63 -@property (retain) NSString *bucket; 73 +@property (retain, nonatomic) NSString *bucket;
64 -@property (retain) NSString *key; 74 +@property (retain, nonatomic) NSString *key;
65 -@property (retain) NSString *sourceBucket; 75 +@property (retain, nonatomic) NSString *sourceBucket;
66 -@property (retain) NSString *sourceKey; 76 +@property (retain, nonatomic) NSString *sourceKey;
67 @property (retain, nonatomic) NSString *mimeType; 77 @property (retain, nonatomic) NSString *mimeType;
68 -@property (retain) NSString *subResource; 78 +@property (retain, nonatomic) NSString *subResource;
69 - 79 +@property (retain, nonatomic) NSString *storageClass;
70 @end 80 @end
@@ -8,6 +8,8 @@ @@ -8,6 +8,8 @@
8 8
9 #import "ASIS3ObjectRequest.h" 9 #import "ASIS3ObjectRequest.h"
10 10
  11 +NSString *const ASIS3StorageClassStandard = @"STANDARD";
  12 +NSString *const ASIS3StorageClassReducedRedundancy = @"REDUCED_REDUNDANCY";
11 13
12 @implementation ASIS3ObjectRequest 14 @implementation ASIS3ObjectRequest
13 15
@@ -21,8 +23,7 @@ @@ -21,8 +23,7 @@
21 23
22 + (id)requestWithBucket:(NSString *)bucket key:(NSString *)key 24 + (id)requestWithBucket:(NSString *)bucket key:(NSString *)key
23 { 25 {
24 - NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key]; 26 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:nil] autorelease];
25 - ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@",bucket,path]]] autorelease];  
26 [request setBucket:bucket]; 27 [request setBucket:bucket];
27 [request setKey:key]; 28 [request setKey:key];
28 return request; 29 return request;
@@ -30,8 +31,7 @@ @@ -30,8 +31,7 @@
30 31
31 + (id)requestWithBucket:(NSString *)bucket key:(NSString *)key subResource:(NSString *)subResource 32 + (id)requestWithBucket:(NSString *)bucket key:(NSString *)key subResource:(NSString *)subResource
32 { 33 {
33 - NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key]; 34 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:nil] autorelease];
34 - ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@?%@",bucket,path,subResource]]] autorelease];  
35 [request setSubResource:subResource]; 35 [request setSubResource:subResource];
36 [request setBucket:bucket]; 36 [request setBucket:bucket];
37 [request setKey:key]; 37 [request setKey:key];
@@ -79,8 +79,6 @@ @@ -79,8 +79,6 @@
79 return request; 79 return request;
80 } 80 }
81 81
82 -  
83 -  
84 - (id)copyWithZone:(NSZone *)zone 82 - (id)copyWithZone:(NSZone *)zone
85 { 83 {
86 ASIS3ObjectRequest *newRequest = [super copyWithZone:zone]; 84 ASIS3ObjectRequest *newRequest = [super copyWithZone:zone];
@@ -100,9 +98,19 @@ @@ -100,9 +98,19 @@
100 [sourceKey release]; 98 [sourceKey release];
101 [sourceBucket release]; 99 [sourceBucket release];
102 [subResource release]; 100 [subResource release];
  101 + [storageClass release];
103 [super dealloc]; 102 [super dealloc];
104 } 103 }
105 104
  105 +- (void)buildURL
  106 +{
  107 + if ([self subResource]) {
  108 + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@?%@",[self requestScheme],[self bucket],[[self class] S3Host],[ASIS3Request stringByURLEncodingForS3Path:[self key]],[self subResource]]]];
  109 + } else {
  110 + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@",[self requestScheme],[self bucket],[[self class] S3Host],[ASIS3Request stringByURLEncodingForS3Path:[self key]]]]];
  111 + }
  112 +}
  113 +
106 - (NSString *)mimeType 114 - (NSString *)mimeType
107 { 115 {
108 if (mimeType) { 116 if (mimeType) {
@@ -129,6 +137,9 @@ @@ -129,6 +137,9 @@
129 NSString *path = [ASIS3Request stringByURLEncodingForS3Path:[self sourceKey]]; 137 NSString *path = [ASIS3Request stringByURLEncodingForS3Path:[self sourceKey]];
130 [headers setObject:[[self sourceBucket] stringByAppendingString:path] forKey:@"x-amz-copy-source"]; 138 [headers setObject:[[self sourceBucket] stringByAppendingString:path] forKey:@"x-amz-copy-source"];
131 } 139 }
  140 + if ([self storageClass]) {
  141 + [headers setObject:[self storageClass] forKey:@"x-amz-storage-class"];
  142 + }
132 return headers; 143 return headers;
133 } 144 }
134 145
@@ -141,12 +152,11 @@ @@ -141,12 +152,11 @@
141 return [super stringToSignForHeaders:canonicalizedAmzHeaders resource:canonicalizedResource]; 152 return [super stringToSignForHeaders:canonicalizedAmzHeaders resource:canonicalizedResource];
142 } 153 }
143 154
144 -  
145 @synthesize bucket; 155 @synthesize bucket;
146 @synthesize key; 156 @synthesize key;
147 @synthesize sourceBucket; 157 @synthesize sourceBucket;
148 @synthesize sourceKey; 158 @synthesize sourceKey;
149 @synthesize mimeType; 159 @synthesize mimeType;
150 @synthesize subResource; 160 @synthesize subResource;
151 - 161 +@synthesize storageClass;
152 @end 162 @end
@@ -18,16 +18,24 @@ @@ -18,16 +18,24 @@
18 // See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAccessPolicy.html for what these mean 18 // See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAccessPolicy.html for what these mean
19 extern NSString *const ASIS3AccessPolicyPrivate; // This is the default in S3 when no access policy header is provided 19 extern NSString *const ASIS3AccessPolicyPrivate; // This is the default in S3 when no access policy header is provided
20 extern NSString *const ASIS3AccessPolicyPublicRead; 20 extern NSString *const ASIS3AccessPolicyPublicRead;
21 -extern NSString *const ASIS3AccessPolicyPublicReadWrote; 21 +extern NSString *const ASIS3AccessPolicyPublicReadWrite;
22 extern NSString *const ASIS3AccessPolicyAuthenticatedRead; 22 extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
  23 +extern NSString *const ASIS3AccessPolicyBucketOwnerRead;
  24 +extern NSString *const ASIS3AccessPolicyBucketOwnerFullControl;
  25 +
  26 +// Constants for requestScheme - defaults is ASIS3RequestSchemeHTTP
  27 +extern NSString *const ASIS3RequestSchemeHTTP;
  28 +extern NSString *const ASIS3RequestSchemeHTTPS;
  29 +
  30 +
23 31
24 typedef enum _ASIS3ErrorType { 32 typedef enum _ASIS3ErrorType {
25 ASIS3ResponseParsingFailedType = 1, 33 ASIS3ResponseParsingFailedType = 1,
26 ASIS3ResponseErrorType = 2 34 ASIS3ResponseErrorType = 2
27 -  
28 } ASIS3ErrorType; 35 } ASIS3ErrorType;
29 36
30 37
  38 +
31 @interface ASIS3Request : ASIHTTPRequest <NSCopying, NSXMLParserDelegate> { 39 @interface ASIS3Request : ASIHTTPRequest <NSCopying, NSXMLParserDelegate> {
32 40
33 // Your S3 access key. Set it on the request, or set it globally using [ASIS3Request setSharedAccessKey:] 41 // Your S3 access key. Set it on the request, or set it globally using [ASIS3Request setSharedAccessKey:]
@@ -36,6 +44,9 @@ typedef enum _ASIS3ErrorType { @@ -36,6 +44,9 @@ typedef enum _ASIS3ErrorType {
36 // Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:] 44 // Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:]
37 NSString *secretAccessKey; 45 NSString *secretAccessKey;
38 46
  47 + // Set to ASIS3RequestSchemeHTTPS to send your requests via HTTPS (default is ASIS3RequestSchemeHTTP)
  48 + NSString *requestScheme;
  49 +
39 // The string that will be used in the HTTP date header. Generally you'll want to ignore this and let the class add the current date for you, but the accessor is used by the tests 50 // The string that will be used in the HTTP date header. Generally you'll want to ignore this and let the class add the current date for you, but the accessor is used by the tests
40 NSString *dateString; 51 NSString *dateString;
41 52
@@ -68,7 +79,7 @@ typedef enum _ASIS3ErrorType { @@ -68,7 +79,7 @@ typedef enum _ASIS3ErrorType {
68 + (void)setSharedAccessKey:(NSString *)newAccessKey; 79 + (void)setSharedAccessKey:(NSString *)newAccessKey;
69 + (NSString *)sharedSecretAccessKey; 80 + (NSString *)sharedSecretAccessKey;
70 + (void)setSharedSecretAccessKey:(NSString *)newAccessKey; 81 + (void)setSharedSecretAccessKey:(NSString *)newAccessKey;
71 - 82 +
72 # pragma mark helpers 83 # pragma mark helpers
73 84
74 // Returns a date formatter than can be used to parse a date from S3 85 // Returns a date formatter than can be used to parse a date from S3
@@ -82,7 +93,11 @@ typedef enum _ASIS3ErrorType { @@ -82,7 +93,11 @@ typedef enum _ASIS3ErrorType {
82 // You shouldn't normally need to use this yourself 93 // You shouldn't normally need to use this yourself
83 + (NSString *)stringByURLEncodingForS3Path:(NSString *)key; 94 + (NSString *)stringByURLEncodingForS3Path:(NSString *)key;
84 95
  96 +// Returns a string for the hostname used for S3 requests. You shouldn't ever need to change this.
  97 ++ (NSString *)S3Host;
85 98
  99 +// This is called automatically before the request starts to build the request URL (if one has not been manually set already)
  100 +- (void)buildURL;
86 101
87 @property (retain) NSString *dateString; 102 @property (retain) NSString *dateString;
88 @property (retain) NSString *accessKey; 103 @property (retain) NSString *accessKey;
@@ -90,4 +105,5 @@ typedef enum _ASIS3ErrorType { @@ -90,4 +105,5 @@ typedef enum _ASIS3ErrorType {
90 @property (retain) NSString *accessPolicy; 105 @property (retain) NSString *accessPolicy;
91 @property (retain) NSString *currentXMLElementContent; 106 @property (retain) NSString *currentXMLElementContent;
92 @property (retain) NSMutableArray *currentXMLElementStack; 107 @property (retain) NSMutableArray *currentXMLElementStack;
  108 +@property (retain) NSString *requestScheme;
93 @end 109 @end
@@ -9,10 +9,15 @@ @@ -9,10 +9,15 @@
9 #import "ASIS3Request.h" 9 #import "ASIS3Request.h"
10 #import <CommonCrypto/CommonHMAC.h> 10 #import <CommonCrypto/CommonHMAC.h>
11 11
12 -NSString* const ASIS3AccessPolicyPrivate = @"private"; 12 +NSString *const ASIS3AccessPolicyPrivate = @"private";
13 -NSString* const ASIS3AccessPolicyPublicRead = @"public-read"; 13 +NSString *const ASIS3AccessPolicyPublicRead = @"public-read";
14 -NSString* const ASIS3AccessPolicyPublicReadWrote = @"public-read-write"; 14 +NSString *const ASIS3AccessPolicyPublicReadWrite = @"public-read-write";
15 -NSString* const ASIS3AccessPolicyAuthenticatedRead = @"authenticated-read"; 15 +NSString *const ASIS3AccessPolicyAuthenticatedRead = @"authenticated-read";
  16 +NSString *const ASIS3AccessPolicyBucketOwnerRead = @"bucket-owner-read";
  17 +NSString *const ASIS3AccessPolicyBucketOwnerFullControl = @"bucket-owner-full-control";
  18 +
  19 +NSString *const ASIS3RequestSchemeHTTP = @"http";
  20 +NSString *const ASIS3RequestSchemeHTTPS = @"https";
16 21
17 static NSString *sharedAccessKey = nil; 22 static NSString *sharedAccessKey = nil;
18 static NSString *sharedSecretAccessKey = nil; 23 static NSString *sharedSecretAccessKey = nil;
@@ -29,6 +34,7 @@ static NSString *sharedSecretAccessKey = nil; @@ -29,6 +34,7 @@ static NSString *sharedSecretAccessKey = nil;
29 self = [super initWithURL:newURL]; 34 self = [super initWithURL:newURL];
30 // After a bit of experimentation/guesswork, this number seems to reduce the chance of a 'RequestTimeout' error 35 // After a bit of experimentation/guesswork, this number seems to reduce the chance of a 'RequestTimeout' error
31 [self setPersistentConnectionTimeoutSeconds:20]; 36 [self setPersistentConnectionTimeoutSeconds:20];
  37 + [self setRequestScheme:ASIS3RequestSchemeHTTP];
32 return self; 38 return self;
33 } 39 }
34 40
@@ -41,6 +47,7 @@ static NSString *sharedSecretAccessKey = nil; @@ -41,6 +47,7 @@ static NSString *sharedSecretAccessKey = nil;
41 [accessKey release]; 47 [accessKey release];
42 [secretAccessKey release]; 48 [secretAccessKey release];
43 [accessPolicy release]; 49 [accessPolicy release];
  50 + [requestScheme release];
44 [super dealloc]; 51 [super dealloc];
45 } 52 }
46 53
@@ -67,6 +74,14 @@ static NSString *sharedSecretAccessKey = nil; @@ -67,6 +74,14 @@ static NSString *sharedSecretAccessKey = nil;
67 return headers; 74 return headers;
68 } 75 }
69 76
  77 +- (void)main
  78 +{
  79 + if (![self url]) {
  80 + [self buildURL];
  81 + }
  82 + [super main];
  83 +}
  84 +
70 - (NSString *)canonicalizedResource 85 - (NSString *)canonicalizedResource
71 { 86 {
72 return @"/"; 87 return @"/";
@@ -79,6 +94,9 @@ static NSString *sharedSecretAccessKey = nil; @@ -79,6 +94,9 @@ static NSString *sharedSecretAccessKey = nil;
79 94
80 - (void)buildRequestHeaders 95 - (void)buildRequestHeaders
81 { 96 {
  97 + if (![self url]) {
  98 + [self buildURL];
  99 + }
82 [super buildRequestHeaders]; 100 [super buildRequestHeaders];
83 101
84 // If an access key / secret access key haven't been set for this request, let's use the shared keys 102 // If an access key / secret access key haven't been set for this request, let's use the shared keys
@@ -100,7 +118,7 @@ static NSString *sharedSecretAccessKey = nil; @@ -100,7 +118,7 @@ static NSString *sharedSecretAccessKey = nil;
100 // Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private) 118 // Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private)
101 NSMutableDictionary *amzHeaders = [self S3Headers]; 119 NSMutableDictionary *amzHeaders = [self S3Headers];
102 NSString *canonicalizedAmzHeaders = @""; 120 NSString *canonicalizedAmzHeaders = @"";
103 - for (NSString *header in [amzHeaders keyEnumerator]) { 121 + for (NSString *header in [amzHeaders keysSortedByValueUsingSelector:@selector(compare:)]) {
104 canonicalizedAmzHeaders = [NSString stringWithFormat:@"%@%@:%@\n",canonicalizedAmzHeaders,[header lowercaseString],[amzHeaders objectForKey:header]]; 122 canonicalizedAmzHeaders = [NSString stringWithFormat:@"%@%@:%@\n",canonicalizedAmzHeaders,[header lowercaseString],[amzHeaders objectForKey:header]];
105 [self addRequestHeader:header value:[amzHeaders objectForKey:header]]; 123 [self addRequestHeader:header value:[amzHeaders objectForKey:header]];
106 } 124 }
@@ -274,6 +292,14 @@ static NSString *sharedSecretAccessKey = nil; @@ -274,6 +292,14 @@ static NSString *sharedSecretAccessKey = nil;
274 return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; 292 return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
275 } 293 }
276 294
  295 ++ (NSString *)S3Host
  296 +{
  297 + return @"s3.amazonaws.com";
  298 +}
  299 +
  300 +- (void)buildURL
  301 +{
  302 +}
277 303
278 @synthesize dateString; 304 @synthesize dateString;
279 @synthesize accessKey; 305 @synthesize accessKey;
@@ -281,4 +307,5 @@ static NSString *sharedSecretAccessKey = nil; @@ -281,4 +307,5 @@ static NSString *sharedSecretAccessKey = nil;
281 @synthesize currentXMLElementContent; 307 @synthesize currentXMLElementContent;
282 @synthesize currentXMLElementStack; 308 @synthesize currentXMLElementStack;
283 @synthesize accessPolicy; 309 @synthesize accessPolicy;
  310 +@synthesize requestScheme;
284 @end 311 @end
@@ -21,10 +21,10 @@ @@ -21,10 +21,10 @@
21 21
22 + (id)serviceRequest 22 + (id)serviceRequest
23 { 23 {
24 - return [[[self alloc] initWithURL:[NSURL URLWithString:@"http://s3.amazonaws.com"]] autorelease]; 24 + ASIS3ServiceRequest *request = [[[self alloc] initWithURL:nil] autorelease];
  25 + return request;
25 } 26 }
26 27
27 -  
28 - (id)initWithURL:(NSURL *)newURL 28 - (id)initWithURL:(NSURL *)newURL
29 { 29 {
30 self = [super initWithURL:newURL]; 30 self = [super initWithURL:newURL];
@@ -32,7 +32,6 @@ @@ -32,7 +32,6 @@
32 return self; 32 return self;
33 } 33 }
34 34
35 -  
36 - (void)dealloc 35 - (void)dealloc
37 { 36 {
38 [buckets release]; 37 [buckets release];
@@ -42,6 +41,11 @@ @@ -42,6 +41,11 @@
42 [super dealloc]; 41 [super dealloc];
43 } 42 }
44 43
  44 +- (void)buildURL
  45 +{
  46 + [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@",[self requestScheme],[[self class] S3Host]]]];
  47 +}
  48 +
45 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict 49 - (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
46 { 50 {
47 if ([elementName isEqualToString:@"Bucket"]) { 51 if ([elementName isEqualToString:@"Bucket"]) {
@@ -21,6 +21,8 @@ @@ -21,6 +21,8 @@
21 - (void)testListRequest; 21 - (void)testListRequest;
22 - (void)testSubclasses; 22 - (void)testSubclasses;
23 - (void)createTestBucket; 23 - (void)createTestBucket;
  24 +- (void)testCopy;
  25 +- (void)testHTTPS;
24 26
25 @property (retain,nonatomic) ASINetworkQueue *networkQueue; 27 @property (retain,nonatomic) ASINetworkQueue *networkQueue;
26 @end 28 @end
@@ -20,8 +20,6 @@ static NSString *accessKey = @""; @@ -20,8 +20,6 @@ static NSString *accessKey = @"";
20 // You should run these tests on a bucket that does not yet exist 20 // You should run these tests on a bucket that does not yet exist
21 static NSString *bucket = @""; 21 static NSString *bucket = @"";
22 22
23 -  
24 -  
25 // Used for subclass test 23 // Used for subclass test
26 @interface ASIS3ObjectRequestSubclass : ASIS3ObjectRequest {} 24 @interface ASIS3ObjectRequestSubclass : ASIS3ObjectRequest {}
27 @end 25 @end
@@ -220,6 +218,7 @@ static NSString *bucket = @""; @@ -220,6 +218,7 @@ static NSString *bucket = @"";
220 ASIS3ObjectRequest *request = [ASIS3ObjectRequest PUTRequestForFile:filePath withBucket:bucket key:key]; 218 ASIS3ObjectRequest *request = [ASIS3ObjectRequest PUTRequestForFile:filePath withBucket:bucket key:key];
221 [request setSecretAccessKey:secretAccessKey]; 219 [request setSecretAccessKey:secretAccessKey];
222 [request setAccessKey:accessKey]; 220 [request setAccessKey:accessKey];
  221 + [request setStorageClass:ASIS3StorageClassReducedRedundancy];
223 [request startSynchronous]; 222 [request startSynchronous];
224 success = [[request responseString] isEqualToString:@""]; 223 success = [[request responseString] isEqualToString:@""];
225 GHAssertTrue(success,@"Failed to PUT a file to S3"); 224 GHAssertTrue(success,@"Failed to PUT a file to S3");
@@ -452,7 +451,7 @@ static NSString *bucket = @""; @@ -452,7 +451,7 @@ static NSString *bucket = @"";
452 [listRequest setPrefix:@"foo"]; 451 [listRequest setPrefix:@"foo"];
453 [listRequest setMarker:@"bar"]; 452 [listRequest setMarker:@"bar"];
454 [listRequest setMaxResultCount:5]; 453 [listRequest setMaxResultCount:5];
455 - [listRequest createQueryString]; 454 + [listRequest buildURL];
456 NSString *expectedURL = [NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?acl&prefix=foo&marker=bar&delimiter=/&max-keys=5",bucket]; 455 NSString *expectedURL = [NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?acl&prefix=foo&marker=bar&delimiter=/&max-keys=5",bucket];
457 success = ([[[listRequest url] absoluteString] isEqualToString:expectedURL]); 456 success = ([[[listRequest url] absoluteString] isEqualToString:expectedURL]);
458 GHAssertTrue(success,@"Generated the wrong url when requesting a subresource"); 457 GHAssertTrue(success,@"Generated the wrong url when requesting a subresource");
@@ -757,6 +756,94 @@ static NSString *bucket = @""; @@ -757,6 +756,94 @@ static NSString *bucket = @"";
757 } 756 }
758 757
759 758
  759 +- (void)testHTTPS
  760 +{
  761 + [ASIS3Request setSharedAccessKey:accessKey];
  762 + [ASIS3Request setSharedSecretAccessKey:secretAccessKey];
  763 +
  764 + // Create a bucket
  765 + ASIS3Request *request = [ASIS3BucketRequest PUTRequestWithBucket:bucket];
  766 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  767 + [request startSynchronous];
  768 + GHAssertNil([request error],@"Failed to create a bucket");
  769 +
  770 + // PUT something in it
  771 + NSString *key = @"king";
  772 + request = [ASIS3ObjectRequest PUTRequestForData:[@"fink" dataUsingEncoding:NSUTF8StringEncoding] withBucket:bucket key:key];
  773 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  774 + [request startSynchronous];
  775 + BOOL success = [[request responseString] isEqualToString:@""];
  776 + GHAssertTrue(success,@"Failed to PUT some data into S3");
  777 +
  778 + // GET it
  779 + request = [ASIS3ObjectRequest requestWithBucket:bucket key:key];
  780 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  781 + [request startSynchronous];
  782 + success = [[request responseString] isEqualToString:@"fink"];
  783 + GHAssertTrue(success,@"Failed to GET the correct data from S3");
  784 +
  785 + // DELETE it
  786 + request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"];
  787 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  788 + [request startSynchronous];
  789 + success = [[request responseString] isEqualToString:@""];
  790 + GHAssertTrue(success,@"Failed to DELETE the object from S3");
  791 +
  792 + // Delete the bucket
  793 + request = [ASIS3BucketRequest DELETERequestWithBucket:bucket];
  794 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  795 + [request startSynchronous];
  796 + GHAssertNil([request error],@"Failed to delete a bucket");
  797 +
  798 + [ASIS3Request setSharedAccessKey:nil];
  799 + [ASIS3Request setSharedSecretAccessKey:nil];
  800 +}
  801 +
  802 +// 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
  803 +- (void)testCannedACLs
  804 +{
  805 + [ASIS3Request setSharedAccessKey:accessKey];
  806 + [ASIS3Request setSharedSecretAccessKey:secretAccessKey];
  807 +
  808 + // Create a bucket
  809 + ASIS3Request *request = [ASIS3BucketRequest PUTRequestWithBucket:bucket];
  810 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  811 + [request startSynchronous];
  812 + GHAssertNil([request error],@"Failed to create a bucket");
  813 +
  814 + NSArray *ACLs = [NSArray arrayWithObjects:ASIS3AccessPolicyPrivate,ASIS3AccessPolicyPublicRead,ASIS3AccessPolicyPublicReadWrite,ASIS3AccessPolicyAuthenticatedRead,ASIS3AccessPolicyBucketOwnerRead,ASIS3AccessPolicyBucketOwnerFullControl,nil];
  815 +
  816 + for (NSString *cannedACL in ACLs) {
  817 + // PUT object
  818 + NSString *key = @"king";
  819 + request = [ASIS3ObjectRequest PUTRequestForData:[@"fink" dataUsingEncoding:NSUTF8StringEncoding] withBucket:bucket key:key];
  820 + [request setAccessPolicy:cannedACL];
  821 + [request startSynchronous];
  822 + GHAssertNil([request error],@"Failed to PUT some data into S3");
  823 +
  824 + // GET object ACL
  825 + request = [ASIS3ObjectRequest requestWithBucket:bucket key:key subResource:@"acl"];
  826 + [request startSynchronous];
  827 + GHAssertNil([request error],@"Failed to fetch the object");
  828 + }
  829 +
  830 + // DELETE it
  831 + request = [ASIS3ObjectRequest DELETERequestWithBucket:bucket key:@"king"];
  832 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  833 + [request startSynchronous];
  834 + BOOL success = [[request responseString] isEqualToString:@""];
  835 + GHAssertTrue(success,@"Failed to DELETE the object from S3");
  836 +
  837 + // Delete the bucket
  838 + request = [ASIS3BucketRequest DELETERequestWithBucket:bucket];
  839 + [request setRequestScheme:ASIS3RequestSchemeHTTPS];
  840 + [request startSynchronous];
  841 + GHAssertNil([request error],@"Failed to delete a bucket");
  842 +
  843 + [ASIS3Request setSharedAccessKey:nil];
  844 + [ASIS3Request setSharedSecretAccessKey:nil];
  845 +}
  846 +
760 847
761 @synthesize networkQueue; 848 @synthesize networkQueue;
762 849