Permalink
Browse files

Reorganise code to build URLS for S3 stuff - we now delay building an…

… S3 url until the last minute

Add support for S3 over HTTPS
Added a couple of new constants for access policies
  • Loading branch information...
pokeb committed Sep 19, 2010
1 parent 0220174 commit 4508fdd7f67614ba6d93199b1cf6b84dee71e7f5
@@ -60,15 +60,12 @@
// Use for deleting buckets - they must be empty for this to succeed
+ (id)DELETERequestWithBucket:(NSString *)bucket;
-//Builds a query string out of the list parameters we supplied
-- (void)createQueryString;
-
-@property (retain) NSString *bucket;
-@property (retain) NSString *subResource;
-@property (retain) NSString *prefix;
-@property (retain) NSString *marker;
-@property (assign) int maxResultCount;
-@property (retain) NSString *delimiter;
+@property (retain, nonatomic) NSString *bucket;
+@property (retain, nonatomic) NSString *subResource;
+@property (retain, nonatomic) NSString *prefix;
+@property (retain, nonatomic) NSString *marker;
+@property (assign, nonatomic) int maxResultCount;
+@property (retain, nonatomic) NSString *delimiter;
@property (retain, readonly) NSMutableArray *objects;
@property (retain, readonly) NSMutableArray *commonPrefixes;
@property (assign, readonly) BOOL isTruncated;
@@ -30,20 +30,19 @@ - (id)initWithURL:(NSURL *)newURL
+ (id)requestWithBucket:(NSString *)bucket
{
- ASIS3BucketRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com",bucket]]] autorelease];
+ ASIS3BucketRequest *request = [[[self alloc] initWithURL:nil] autorelease];
[request setBucket:bucket];
return request;
}
+ (id)requestWithBucket:(NSString *)bucket subResource:(NSString *)subResource
{
- ASIS3BucketRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?%@",bucket,subResource]]] autorelease];
+ ASIS3BucketRequest *request = [[[self alloc] initWithURL:nil] autorelease];
[request setBucket:bucket];
[request setSubResource:subResource];
return request;
}
-
+ (id)PUTRequestWithBucket:(NSString *)bucket
{
ASIS3BucketRequest *request = [self requestWithBucket:bucket];
@@ -80,8 +79,14 @@ - (NSString *)canonicalizedResource
return [NSString stringWithFormat:@"/%@/",[self bucket]];
}
-- (void)createQueryString
+- (void)buildURL
{
+ NSString *baseURL;
+ if ([self subResource]) {
+ baseURL = [NSString stringWithFormat:@"%@://%@.%@/?%@",[self requestScheme],[self bucket],[[self class] S3Host],[self subResource]];
+ } else {
+ baseURL = [NSString stringWithFormat:@"%@://%@.%@",[self requestScheme],[self bucket],[[self class] S3Host]];
+ }
NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease];
if ([self prefix]) {
[queryParts addObject:[NSString stringWithFormat:@"prefix=%@",[[self prefix] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
@@ -95,20 +100,16 @@ - (void)createQueryString
if ([self maxResultCount] > 0) {
[queryParts addObject:[NSString stringWithFormat:@"max-keys=%hi",[self maxResultCount]]];
}
- if ([queryParts count])
- {
+ if ([queryParts count]) {
NSString* template = @"%@?%@";
if ([[self subResource] length] > 0) {
template = @"%@&%@";
}
- [self setURL:[NSURL URLWithString:[NSString stringWithFormat:template,[[self url] absoluteString],[queryParts componentsJoinedByString:@"&"]]]];
- }
-}
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:template,baseURL,[queryParts componentsJoinedByString:@"&"]]]];
+ } else {
+ [self setURL:[NSURL URLWithString:baseURL]];
-- (void)main
-{
- [self createQueryString];
- [super main];
+ }
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
@@ -146,7 +147,6 @@ - (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName names
}
}
-
#pragma mark NSCopying
- (id)copyWithZone:(NSZone *)zone
@@ -161,9 +161,6 @@ - (id)copyWithZone:(NSZone *)zone
return newRequest;
}
-
-
-
@synthesize bucket;
@synthesize subResource;
@synthesize currentObject;
@@ -60,11 +60,11 @@
// Creates a HEAD request for the object at path
+ (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key;
-@property (retain) NSString *bucket;
-@property (retain) NSString *key;
-@property (retain) NSString *sourceBucket;
-@property (retain) NSString *sourceKey;
+@property (retain, nonatomic) NSString *bucket;
+@property (retain, nonatomic) NSString *key;
+@property (retain, nonatomic) NSString *sourceBucket;
+@property (retain, nonatomic) NSString *sourceKey;
@property (retain, nonatomic) NSString *mimeType;
-@property (retain) NSString *subResource;
+@property (retain, nonatomic) NSString *subResource;
@end
@@ -21,17 +21,15 @@ - (ASIHTTPRequest *)HEADRequest
+ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key
{
- NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key];
- ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@",bucket,path]]] autorelease];
+ ASIS3ObjectRequest *request = [[[self alloc] initWithURL:nil] autorelease];
[request setBucket:bucket];
[request setKey:key];
return request;
}
+ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key subResource:(NSString *)subResource
{
- NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key];
- ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@?%@",bucket,path,subResource]]] autorelease];
+ ASIS3ObjectRequest *request = [[[self alloc] initWithURL:nil] autorelease];
[request setSubResource:subResource];
[request setBucket:bucket];
[request setKey:key];
@@ -79,8 +77,6 @@ + (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key
return request;
}
-
-
- (id)copyWithZone:(NSZone *)zone
{
ASIS3ObjectRequest *newRequest = [super copyWithZone:zone];
@@ -103,6 +99,15 @@ - (void)dealloc
[super dealloc];
}
+- (void)buildURL
+{
+ if ([self subResource]) {
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@.%@%@?%@",[self requestScheme],[[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]]]]];
+ }
+}
+
- (NSString *)mimeType
{
if (mimeType) {
View
@@ -18,15 +18,18 @@
// See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTAccessPolicy.html for what these mean
extern NSString *const ASIS3AccessPolicyPrivate; // This is the default in S3 when no access policy header is provided
extern NSString *const ASIS3AccessPolicyPublicRead;
-extern NSString *const ASIS3AccessPolicyPublicReadWrote;
+extern NSString *const ASIS3AccessPolicyPublicReadWrite;
extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
+extern NSString *const ASIS3AccessPolicyBucketOwnerRead;
+extern NSString *const ASIS3AccessPolicyBucketOwnerFullControl;
typedef enum _ASIS3ErrorType {
ASIS3ResponseParsingFailedType = 1,
ASIS3ResponseErrorType = 2
-
} ASIS3ErrorType;
+extern NSString *const ASIS3RequestSchemeHTTP;
+extern NSString *const ASIS3RequestSchemeHTTPS;
@interface ASIS3Request : ASIHTTPRequest <NSCopying, NSXMLParserDelegate> {
@@ -36,6 +39,9 @@ typedef enum _ASIS3ErrorType {
// Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:]
NSString *secretAccessKey;
+ // Set to ASIS3RequestSchemeHTTPS to send your requests via HTTPS (default is ASIS3RequestSchemeHTTP)
+ NSString *requestScheme;
+
// 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
NSString *dateString;
@@ -68,7 +74,7 @@ typedef enum _ASIS3ErrorType {
+ (void)setSharedAccessKey:(NSString *)newAccessKey;
+ (NSString *)sharedSecretAccessKey;
+ (void)setSharedSecretAccessKey:(NSString *)newAccessKey;
-
+
# pragma mark helpers
// Returns a date formatter than can be used to parse a date from S3
@@ -82,12 +88,17 @@ typedef enum _ASIS3ErrorType {
// You shouldn't normally need to use this yourself
+ (NSString *)stringByURLEncodingForS3Path:(NSString *)key;
+// Returns a string for the hostname used for S3 requests. You shouldn't ever need to change this.
++ (NSString *)S3Host;
+// This is called automatically before the request starts to build the request URL (if one has not been manually set already)
+- (void)buildURL;
@property (retain) NSString *dateString;
@property (retain) NSString *accessKey;
@property (retain) NSString *secretAccessKey;
@property (retain) NSString *accessPolicy;
@property (retain) NSString *currentXMLElementContent;
@property (retain) NSMutableArray *currentXMLElementStack;
+@property (retain) NSString *requestScheme;
@end
View
@@ -9,10 +9,16 @@
#import "ASIS3Request.h"
#import <CommonCrypto/CommonHMAC.h>
-NSString* const ASIS3AccessPolicyPrivate = @"private";
-NSString* const ASIS3AccessPolicyPublicRead = @"public-read";
-NSString* const ASIS3AccessPolicyPublicReadWrote = @"public-read-write";
-NSString* const ASIS3AccessPolicyAuthenticatedRead = @"authenticated-read";
+NSString *const ASIS3AccessPolicyPrivate = @"private";
+NSString *const ASIS3AccessPolicyPublicRead = @"public-read";
+NSString *const ASIS3AccessPolicyPublicReadWrite = @"public-read-write";
+NSString *const ASIS3AccessPolicyAuthenticatedRead = @"authenticated-read";
+NSString *const ASIS3AccessPolicyBucketOwnerRead = @"bucket-owner-read";
+NSString *const ASIS3AccessPolicyBucketOwnerFullControl = @"bucket-owner-full-control";
+
+NSString *const ASIS3RequestSchemeHTTP = @"http";
+NSString *const ASIS3RequestSchemeHTTPS = @"https";
+
static NSString *sharedAccessKey = nil;
static NSString *sharedSecretAccessKey = nil;
@@ -29,6 +35,7 @@ - (id)initWithURL:(NSURL *)newURL
self = [super initWithURL:newURL];
// After a bit of experimentation/guesswork, this number seems to reduce the chance of a 'RequestTimeout' error
[self setPersistentConnectionTimeoutSeconds:20];
+ [self setRequestScheme:ASIS3RequestSchemeHTTP];
return self;
}
@@ -41,6 +48,7 @@ - (void)dealloc
[accessKey release];
[secretAccessKey release];
[accessPolicy release];
+ [requestScheme release];
[super dealloc];
}
@@ -67,6 +75,14 @@ - (NSMutableDictionary *)S3Headers
return headers;
}
+- (void)main
+{
+ if (![self url]) {
+ [self buildURL];
+ }
+ [super main];
+}
+
- (NSString *)canonicalizedResource
{
return @"/";
@@ -79,6 +95,9 @@ - (NSString *)stringToSignForHeaders:(NSString *)canonicalizedAmzHeaders resourc
- (void)buildRequestHeaders
{
+ if (![self url]) {
+ [self buildURL];
+ }
[super buildRequestHeaders];
// If an access key / secret access key haven't been set for this request, let's use the shared keys
@@ -274,11 +293,20 @@ + (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string
return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
}
++ (NSString *)S3Host
+{
+ return @"s3.amazonaws.com";
+}
+
+- (void)buildURL
+{
+}
@synthesize dateString;
@synthesize accessKey;
@synthesize secretAccessKey;
@synthesize currentXMLElementContent;
@synthesize currentXMLElementStack;
@synthesize accessPolicy;
+@synthesize requestScheme;
@end
@@ -21,18 +21,17 @@ @implementation ASIS3ServiceRequest
+ (id)serviceRequest
{
- return [[[self alloc] initWithURL:[NSURL URLWithString:@"http://s3.amazonaws.com"]] autorelease];
+ ASIS3ServiceRequest *request = [[[self alloc] initWithURL:nil] autorelease];
+ return request;
}
-
- (id)initWithURL:(NSURL *)newURL
{
self = [super initWithURL:newURL];
[self setBuckets:[[[NSMutableArray alloc] init] autorelease]];
return self;
}
-
- (void)dealloc
{
[buckets release];
@@ -42,6 +41,11 @@ - (void)dealloc
[super dealloc];
}
+- (void)buildURL
+{
+ [self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@://%@",[self requestScheme],[[self class] S3Host]]]];
+}
+
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
if ([elementName isEqualToString:@"Bucket"]) {
@@ -452,7 +452,7 @@ - (void)testListRequest
[listRequest setPrefix:@"foo"];
[listRequest setMarker:@"bar"];
[listRequest setMaxResultCount:5];
- [listRequest createQueryString];
+ [listRequest buildURL];
NSString *expectedURL = [NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?acl&prefix=foo&marker=bar&delimiter=/&max-keys=5",bucket];
success = ([[[listRequest url] absoluteString] isEqualToString:expectedURL]);
GHAssertTrue(success,@"Generated the wrong url when requesting a subresource");
@@ -430,7 +430,14 @@
isa = PBXProject;
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Mac" */;
compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
mainGroup = 29B97314FDCFA39411CA2CEA /* Mac */;
projectDirPath = "";
projectReferences = (

0 comments on commit 4508fdd

Please sign in to comment.