Ben Copsey

Start work on major S3 refactor, adding support for bucket and service requests

@@ -21,7 +21,7 @@ @@ -21,7 +21,7 @@
21 #import "ASIInputStream.h" 21 #import "ASIInputStream.h"
22 22
23 // Automatically set on build 23 // Automatically set on build
24 -NSString *ASIHTTPRequestVersion = @"v1.6-4 2010-03-16"; 24 +NSString *ASIHTTPRequestVersion = @"v1.6-5 2010-03-16";
25 25
26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; 26 NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
27 27
1 // 1 //
2 -// ASIS3ListRequest.h 2 +// ASIS3BucketRequest.h
3 // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest 3 // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
4 // 4 //
5 -// Created by Ben Copsey on 13/07/2009. 5 +// Created by Ben Copsey on 16/03/2010.
6 -// Copyright 2009 All-Seeing Interactive. All rights reserved. 6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
7 // 7 //
  8 +// Use this class to create buckets, fetch a list of their contents, and delete buckets
8 9
9 #import <Foundation/Foundation.h> 10 #import <Foundation/Foundation.h>
10 #import "ASIS3Request.h" 11 #import "ASIS3Request.h"
  12 +
11 @class ASIS3BucketObject; 13 @class ASIS3BucketObject;
12 14
13 -@interface ASIS3ListRequest : ASIS3Request <NSCopying> { 15 +@interface ASIS3BucketRequest : ASIS3Request {
  16 + // Name of the bucket to talk to
  17 + NSString *bucket;
  18 + NSString *subResource;
14 19
15 - // Options for filtering list requests 20 + // Options for filtering GET requests
16 // See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html 21 // See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html
17 NSString *prefix; 22 NSString *prefix;
18 NSString *marker; 23 NSString *marker;
19 int maxResultCount; 24 int maxResultCount;
20 - NSString *delimiter; 25 + NSString *delimiter;
21 26
22 // Internally used while parsing the response 27 // Internally used while parsing the response
23 NSString *currentContent; 28 NSString *currentContent;
@@ -25,9 +30,25 @@ @@ -25,9 +30,25 @@
25 ASIS3BucketObject *currentObject; 30 ASIS3BucketObject *currentObject;
26 NSMutableArray *objects; 31 NSMutableArray *objects;
27 } 32 }
28 -// Create a list request  
29 -+ (id)listRequestWithBucket:(NSString *)bucket;  
30 33
  34 +// Fetch a bucket
  35 ++ (id)requestWithBucket:(NSString *)bucket;
  36 +
  37 +// Create a bucket request, passing a parameter in the query string
  38 +// You'll need to parse the response XML yourself
  39 +// Examples:
  40 +// Fetch ACL:
  41 +// ASIS3BucketRequest *request = [ASIS3BucketRequest requestWithBucket:@"mybucket" parameter:@"acl"];
  42 +// Fetch Location:
  43 +// ASIS3BucketRequest *request = [ASIS3BucketRequest requestWithBucket:@"mybucket" parameter:@"location"];
  44 +// See the S3 REST API docs for more information about the parameters you can pass
  45 ++ (id)requestWithBucket:(NSString *)bucket subResource:(NSString *)subResource;
  46 +
  47 +// Use for creating new buckets
  48 ++ (id)PUTRequestWithBucket:(NSString *)bucket;
  49 +
  50 +// Use for deleting buckets (CAREFULL!)
  51 ++ (id)DELETERequestWithBucket:(NSString *)bucket;
31 52
32 // Returns an array of ASIS3BucketObjects created from the XML response 53 // Returns an array of ASIS3BucketObjects created from the XML response
33 - (NSArray *)bucketObjects; 54 - (NSArray *)bucketObjects;
@@ -35,6 +56,13 @@ @@ -35,6 +56,13 @@
35 //Builds a query string out of the list parameters we supplied 56 //Builds a query string out of the list parameters we supplied
36 - (void)createQueryString; 57 - (void)createQueryString;
37 58
  59 +
  60 +// Returns a date formatter than can be used to parse a date from S3
  61 ++ (NSDateFormatter *)dateFormatter;
  62 +
  63 +
  64 +@property (retain) NSString *bucket;
  65 +@property (retain) NSString *subResource;
38 @property (retain) NSString *prefix; 66 @property (retain) NSString *prefix;
39 @property (retain) NSString *marker; 67 @property (retain) NSString *marker;
40 @property (assign) int maxResultCount; 68 @property (assign) int maxResultCount;
1 // 1 //
2 -// ASIS3ListRequest.m 2 +// ASIS3BucketRequest.m
3 // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest 3 // Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
4 // 4 //
5 -// Created by Ben Copsey on 13/07/2009. 5 +// Created by Ben Copsey on 16/03/2010.
6 -// Copyright 2009 All-Seeing Interactive. All rights reserved. 6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
7 // 7 //
8 -#import "ASIS3ListRequest.h"  
9 -#import "ASIS3BucketObject.h"  
10 8
  9 +#import "ASIS3BucketRequest.h"
  10 +#import "ASIS3BucketObject.h"
11 11
12 static NSDateFormatter *dateFormatter = nil; 12 static NSDateFormatter *dateFormatter = nil;
13 13
  14 +
14 // Private stuff 15 // Private stuff
15 -@interface ASIS3ListRequest () 16 +@interface ASIS3BucketRequest ()
16 - @property (retain, nonatomic) NSString *currentContent; 17 +@property (retain, nonatomic) NSString *currentContent;
17 - @property (retain, nonatomic) NSString *currentElement; 18 +@property (retain, nonatomic) NSString *currentElement;
18 - @property (retain, nonatomic) ASIS3BucketObject *currentObject; 19 +@property (retain, nonatomic) ASIS3BucketObject *currentObject;
19 - @property (retain, nonatomic) NSMutableArray *objects; 20 +@property (retain, nonatomic) NSMutableArray *objects;
20 @end 21 @end
21 22
22 -@implementation ASIS3ListRequest 23 +@implementation ASIS3BucketRequest
  24 +
  25 ++ (NSDateFormatter *)dateFormatter
  26 +{
  27 + if (!dateFormatter) {
  28 + dateFormatter = [[NSDateFormatter alloc] init];
  29 + [dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]];
  30 + [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
  31 + [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'.000Z'"];
  32 + }
  33 + return dateFormatter;
  34 +}
  35 +
23 36
24 -+ (void)initialize 37 ++ (id)requestWithBucket:(NSString *)bucket
25 { 38 {
26 - dateFormatter = [[NSDateFormatter alloc] init]; 39 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com",bucket]]] autorelease];
27 - [dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"] autorelease]]; 40 + [request setBucket:bucket];
28 - [dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]]; 41 + return request;
29 - [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'.000Z'"];  
30 } 42 }
31 43
32 -+ (id)listRequestWithBucket:(NSString *)bucket 44 ++ (id)requestWithBucket:(NSString *)bucket subResource:(NSString *)subResource
33 { 45 {
34 - ASIS3ListRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com",bucket]]] autorelease]; 46 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com/?%@",bucket,subResource]]] autorelease];
35 [request setBucket:bucket]; 47 [request setBucket:bucket];
  48 + [request setSubResource:subResource];
  49 + return request;
  50 +}
  51 +
  52 +
  53 ++ (id)PUTRequestWithBucket:(NSString *)bucket
  54 +{
  55 + ASIS3BucketRequest *request = [self requestWithBucket:bucket];
  56 + [request setRequestMethod:@"PUT"];
  57 + return request;
  58 +}
  59 +
  60 +
  61 ++ (id)DELETERequestWithBucket:(NSString *)bucket
  62 +{
  63 + ASIS3BucketRequest *request = [self requestWithBucket:bucket];
  64 + [request setRequestMethod:@"DELETE"];
36 return request; 65 return request;
37 } 66 }
38 67
@@ -45,9 +74,22 @@ static NSDateFormatter *dateFormatter = nil; @@ -45,9 +74,22 @@ static NSDateFormatter *dateFormatter = nil;
45 [prefix release]; 74 [prefix release];
46 [marker release]; 75 [marker release];
47 [delimiter release]; 76 [delimiter release];
  77 + [subResource release];
  78 + [bucket release];
48 [super dealloc]; 79 [super dealloc];
49 } 80 }
50 81
  82 +- (NSString *)canonicalizedResource
  83 +{
  84 + if ([self subResource]) {
  85 + return [NSString stringWithFormat:@"/%@/?%@",[self bucket],[self subResource]];
  86 + }
  87 + return [NSString stringWithFormat:@"/%@/",[self bucket]];
  88 +}
  89 +
  90 +
  91 +
  92 +
51 - (void)createQueryString 93 - (void)createQueryString
52 { 94 {
53 NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease]; 95 NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease];
@@ -128,7 +170,9 @@ static NSDateFormatter *dateFormatter = nil; @@ -128,7 +170,9 @@ static NSDateFormatter *dateFormatter = nil;
128 170
129 - (id)copyWithZone:(NSZone *)zone 171 - (id)copyWithZone:(NSZone *)zone
130 { 172 {
131 - ASIS3ListRequest *newRequest = [super copyWithZone:zone]; 173 + ASIS3BucketRequest *newRequest = [super copyWithZone:zone];
  174 + [newRequest setBucket:[self bucket]];
  175 + [newRequest setSubResource:[self subResource]];
132 [newRequest setPrefix:[self prefix]]; 176 [newRequest setPrefix:[self prefix]];
133 [newRequest setMarker:[self marker]]; 177 [newRequest setMarker:[self marker]];
134 [newRequest setMaxResultCount:[self maxResultCount]]; 178 [newRequest setMaxResultCount:[self maxResultCount]];
@@ -136,7 +180,8 @@ static NSDateFormatter *dateFormatter = nil; @@ -136,7 +180,8 @@ static NSDateFormatter *dateFormatter = nil;
136 return newRequest; 180 return newRequest;
137 } 181 }
138 182
139 - 183 +@synthesize bucket;
  184 +@synthesize subResource;
140 @synthesize currentContent; 185 @synthesize currentContent;
141 @synthesize currentElement; 186 @synthesize currentElement;
142 @synthesize currentObject; 187 @synthesize currentObject;
  1 +//
  2 +// ASIS3ObjectRequest.h
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 16/03/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +// Use this class to fetch, upload, copy and delete objects on Amazon S3
  9 +
  10 +#import <Foundation/Foundation.h>
  11 +#import "ASIS3Request.h"
  12 +
  13 +
  14 +@interface ASIS3ObjectRequest : ASIS3Request {
  15 +
  16 + // Name of the bucket to talk to
  17 + NSString *bucket;
  18 +
  19 + // Key of the resource you want to access on S3
  20 + NSString *key;
  21 +
  22 + // The bucket + path of the object to be copied (used with COPYRequestFromBucket:path:toBucket:path:)
  23 + NSString *sourceBucket;
  24 + NSString *sourceKey;
  25 +
  26 + // The mime type of the content for PUT requests
  27 + // Set this if having the correct mime type returned to you when you GET the data is important (eg it will be served by a web-server)
  28 + // Will be set to 'application/octet-stream' otherwise in iPhone apps, or autodetected on Mac OS X
  29 + NSString *mimeType;
  30 +}
  31 +
  32 +// Create a request, building an appropriate url
  33 ++ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key;
  34 +
  35 +// Create a request for an object, passing a parameter in the query string
  36 +// You'll need to parse the response XML yourself
  37 +// Examples:
  38 +// Fetch ACL:
  39 +// ASIS3ObjectRequest *request = [ASIS3ObjectRequest requestWithBucket:@"mybucket" key:@"my-key" parameter:@"acl"];
  40 +// Get object torret:
  41 +// ASIS3ObjectRequest *request = [ASIS3ObjectRequest requestWithBucket:@"mybucket" key:@"my-key" parameter:@"torrent"];
  42 +// See the S3 REST API docs for more information about the parameters you can pass
  43 ++ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key subResource:(NSString *)subResource;
  44 +
  45 +
  46 +// Create a PUT request using the file at filePath as the body
  47 ++ (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket key:(NSString *)key;
  48 +
  49 +// Create a PUT request using the supplied NSData as the body (set the mime-type manually with setMimeType: if necessary)
  50 ++ (id)PUTRequestForData:(NSData *)data withBucket:(NSString *)bucket key:(NSString *)key;
  51 +
  52 +// Create a DELETE request for the object at path
  53 ++ (id)DELETERequestWithBucket:(NSString *)bucket key:(NSString *)key;
  54 +
  55 +// Create a PUT request to copy an object from one location to another
  56 +// Clang will complain because it thinks this method should return an object with +1 retain :(
  57 ++ (id)COPYRequestFromBucket:(NSString *)sourceBucket key:(NSString *)sourceKey toBucket:(NSString *)bucket key:(NSString *)key;
  58 +
  59 +// Creates a HEAD request for the object at path
  60 ++ (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key;
  61 +
  62 +@property (retain) NSString *bucket;
  63 +@property (retain) NSString *key;
  64 +@property (retain) NSString *sourceBucket;
  65 +@property (retain) NSString *sourceKey;
  66 +@property (retain) NSString *mimeType;
  67 +
  68 +@end
  1 +//
  2 +// ASIS3ObjectRequest.m
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 16/03/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +#import "ASIS3ObjectRequest.h"
  10 +
  11 +
  12 +@implementation ASIS3ObjectRequest
  13 +
  14 +- (ASIHTTPRequest *)HEADRequest
  15 +{
  16 + ASIS3ObjectRequest *headRequest = (ASIS3ObjectRequest *)[super HEADRequest];
  17 + [headRequest setKey:[self key]];
  18 + [headRequest setBucket:[self bucket]];
  19 + return headRequest;
  20 +}
  21 +
  22 ++ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key
  23 +{
  24 + NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key];
  25 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@",bucket,path]]] autorelease];
  26 + [request setBucket:bucket];
  27 + [request setKey:key];
  28 + return request;
  29 +}
  30 +
  31 ++ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key subResource:(NSString *)subResource
  32 +{
  33 + NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key];
  34 + ASIS3ObjectRequest *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@?",bucket,path,subResource]]] autorelease];
  35 + [request setBucket:bucket];
  36 + [request setKey:key];
  37 + return request;
  38 +}
  39 +
  40 ++ (id)PUTRequestForData:(NSData *)data withBucket:(NSString *)bucket key:(NSString *)key
  41 +{
  42 + ASIS3ObjectRequest *request = [self requestWithBucket:bucket key:key];
  43 + [request appendPostData:data];
  44 + [request setRequestMethod:@"PUT"];
  45 + return request;
  46 +}
  47 +
  48 ++ (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket key:(NSString *)key
  49 +{
  50 + ASIS3ObjectRequest *request = [self requestWithBucket:bucket key:key];
  51 + [request setPostBodyFilePath:filePath];
  52 + [request setShouldStreamPostDataFromDisk:YES];
  53 + [request setRequestMethod:@"PUT"];
  54 + [request setMimeType:[ASIHTTPRequest mimeTypeForFileAtPath:filePath]];
  55 + return request;
  56 +}
  57 +
  58 ++ (id)DELETERequestWithBucket:(NSString *)bucket key:(NSString *)key
  59 +{
  60 + ASIS3ObjectRequest *request = [self requestWithBucket:bucket key:key];
  61 + [request setRequestMethod:@"DELETE"];
  62 + return request;
  63 +}
  64 +
  65 ++ (id)COPYRequestFromBucket:(NSString *)sourceBucket key:(NSString *)sourceKey toBucket:(NSString *)bucket key:(NSString *)key
  66 +{
  67 + ASIS3ObjectRequest *request = [self requestWithBucket:bucket key:key];
  68 + [request setRequestMethod:@"PUT"];
  69 + [request setSourceBucket:sourceBucket];
  70 + [request setSourceKey:sourceKey];
  71 + return request;
  72 +}
  73 +
  74 ++ (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key
  75 +{
  76 + ASIS3ObjectRequest *request = [self requestWithBucket:bucket key:key];
  77 + [request setRequestMethod:@"HEAD"];
  78 + return request;
  79 +}
  80 +
  81 +
  82 +
  83 +- (id)copyWithZone:(NSZone *)zone
  84 +{
  85 + ASIS3ObjectRequest *newRequest = [super copyWithZone:zone];
  86 + [newRequest setBucket:[self bucket]];
  87 + [newRequest setKey:[self key]];
  88 + [newRequest setMimeType:[self mimeType]];
  89 + [newRequest setSourceBucket:[self sourceBucket]];
  90 + [newRequest setSourceKey:[self sourceKey]];
  91 + [newRequest setAccessPolicy:[self accessPolicy]];
  92 + return newRequest;
  93 +}
  94 +
  95 +- (void)dealloc
  96 +{
  97 + [bucket release];
  98 + [key release];
  99 + [mimeType release];
  100 + [sourceKey release];
  101 + [sourceBucket release];
  102 + [accessPolicy release];
  103 + [super dealloc];
  104 +}
  105 +
  106 +- (void)requestFinished
  107 +{
  108 + // COPY requests return a 200 whether they succeed or fail, so we need to look at the XML to see if we were successful.
  109 + if ([self responseStatusCode] == 200 && [self sourceKey] && [self sourceBucket]) {
  110 + [self parseResponseXML];
  111 + return;
  112 + }
  113 + [super requestFinished];
  114 +}
  115 +
  116 +- (NSString *)canonicalizedResource
  117 +{
  118 + return [NSString stringWithFormat:@"/%@%@",[self bucket],[ASIS3Request stringByURLEncodingForS3Path:[self key]]];
  119 +}
  120 +
  121 +- (NSMutableDictionary *)S3Headers
  122 +{
  123 + NSMutableDictionary *headers = [NSMutableDictionary dictionary];
  124 + if ([self accessPolicy]) {
  125 + [headers setObject:[self accessPolicy] forKey:@"x-amz-acl"];
  126 + }
  127 + if ([self sourceKey]) {
  128 + NSString *path = [ASIS3Request stringByURLEncodingForS3Path:[self sourceKey]];
  129 + [headers setObject:[[self sourceBucket] stringByAppendingString:path] forKey:@"x-amz-copy-source"];
  130 + }
  131 + return headers;
  132 +}
  133 +
  134 +- (NSString *)stringToSignForHeaders:(NSString *)canonicalizedAmzHeaders resource:(NSString *)canonicalizedResource
  135 +{
  136 + if ([[self requestMethod] isEqualToString:@"PUT"] && ![self sourceKey]) {
  137 + [self addRequestHeader:@"Content-Type" value:[self mimeType]];
  138 + return [NSString stringWithFormat:@"PUT\n\n%@\n%@\n%@%@",[self mimeType],dateString,canonicalizedAmzHeaders,canonicalizedResource];
  139 + }
  140 + return [super stringToSignForHeaders:canonicalizedAmzHeaders resource:canonicalizedResource];
  141 +}
  142 +
  143 +
  144 +@synthesize bucket;
  145 +@synthesize key;
  146 +@synthesize sourceBucket;
  147 +@synthesize sourceKey;
  148 +@synthesize mimeType;
  149 +@synthesize accessPolicy;
  150 +@end
  1 +//
  2 +// ASIS3ServiceRequest.h
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 16/03/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +// Create an ASIS3ServiceRequest to obtain a list of your buckets
  9 +
  10 +#import <Foundation/Foundation.h>
  11 +#import "ASIS3Request.h"
  12 +
  13 +@interface ASIS3ServiceRequest : ASIS3Request {
  14 +
  15 +}
  16 +
  17 ++ (id)serviceRequest;
  18 +
  19 +@end
  1 +//
  2 +// ASIS3ServiceRequest.m
  3 +// Part of ASIHTTPRequest -> http://allseeing-i.com/ASIHTTPRequest
  4 +//
  5 +// Created by Ben Copsey on 16/03/2010.
  6 +// Copyright 2010 All-Seeing Interactive. All rights reserved.
  7 +//
  8 +
  9 +#import "ASIS3ServiceRequest.h"
  10 +
  11 +
  12 +@implementation ASIS3ServiceRequest
  13 +
  14 ++ (id)serviceRequest
  15 +{
  16 + return [[[self alloc] initWithURL:[NSURL URLWithString:@"http://s3.amazonaws.com"]] autorelease];
  17 +}
  18 +
  19 +
  20 +@end
@@ -5,9 +5,11 @@ @@ -5,9 +5,11 @@
5 // Created by Ben Copsey on 13/07/2009. 5 // Created by Ben Copsey on 13/07/2009.
6 // Copyright 2009 All-Seeing Interactive. All rights reserved. 6 // Copyright 2009 All-Seeing Interactive. All rights reserved.
7 // 7 //
  8 +// Instances of this class represent objects stored in a bucket on S3
  9 +// ASIS3BucketRequests return an array of ASIS3BucketObject when you perform a list query
8 10
9 #import <Foundation/Foundation.h> 11 #import <Foundation/Foundation.h>
10 -@class ASIS3Request; 12 +@class ASIS3ObjectRequest;
11 13
12 @interface ASIS3BucketObject : NSObject <NSCopying> { 14 @interface ASIS3BucketObject : NSObject <NSCopying> {
13 15
@@ -34,13 +36,13 @@ @@ -34,13 +36,13 @@
34 + (id)objectWithBucket:(NSString *)bucket; 36 + (id)objectWithBucket:(NSString *)bucket;
35 37
36 // Returns a request that will fetch this object when run 38 // Returns a request that will fetch this object when run
37 -- (ASIS3Request *)GETRequest; 39 +- (ASIS3ObjectRequest *)GETRequest;
38 40
39 // Returns a request that will replace this object with the contents of the file at filePath when run 41 // Returns a request that will replace this object with the contents of the file at filePath when run
40 -- (ASIS3Request *)PUTRequestWithFile:(NSString *)filePath; 42 +- (ASIS3ObjectRequest *)PUTRequestWithFile:(NSString *)filePath;
41 43
42 // Returns a request that will delete this object when run 44 // Returns a request that will delete this object when run
43 -- (ASIS3Request *)DELETERequest; 45 +- (ASIS3ObjectRequest *)DELETERequest;
44 46
45 @property (retain) NSString *bucket; 47 @property (retain) NSString *bucket;
46 @property (retain) NSString *key; 48 @property (retain) NSString *key;
@@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
7 // 7 //
8 8
9 #import "ASIS3BucketObject.h" 9 #import "ASIS3BucketObject.h"
10 -#import "ASIS3Request.h" 10 +#import "ASIS3ObjectRequest.h"
11 11
12 @implementation ASIS3BucketObject 12 @implementation ASIS3BucketObject
13 13
@@ -28,19 +28,19 @@ @@ -28,19 +28,19 @@
28 [super dealloc]; 28 [super dealloc];
29 } 29 }
30 30
31 -- (ASIS3Request *)GETRequest 31 +- (ASIS3ObjectRequest *)GETRequest
32 { 32 {
33 - return [ASIS3Request requestWithBucket:[self bucket] key:[self key]]; 33 + return [ASIS3ObjectRequest requestWithBucket:[self bucket] key:[self key]];
34 } 34 }
35 35
36 -- (ASIS3Request *)PUTRequestWithFile:(NSString *)filePath 36 +- (ASIS3ObjectRequest *)PUTRequestWithFile:(NSString *)filePath
37 { 37 {
38 - return [ASIS3Request PUTRequestForFile:filePath withBucket:[self bucket] key:[self key]]; 38 + return [ASIS3ObjectRequest PUTRequestForFile:filePath withBucket:[self bucket] key:[self key]];
39 } 39 }
40 40
41 -- (ASIS3Request *)DELETERequest 41 +- (ASIS3ObjectRequest *)DELETERequest
42 { 42 {
43 - ASIS3Request *request = [ASIS3Request requestWithBucket:[self bucket] key:[self key]]; 43 + ASIS3ObjectRequest *request = [ASIS3ObjectRequest requestWithBucket:[self bucket] key:[self key]];
44 [request setRequestMethod:@"DELETE"]; 44 [request setRequestMethod:@"DELETE"];
45 return request; 45 return request;
46 } 46 }
@@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
5 // Created by Ben Copsey on 30/06/2009. 5 // Created by Ben Copsey on 30/06/2009.
6 // Copyright 2009 All-Seeing Interactive. All rights reserved. 6 // Copyright 2009 All-Seeing Interactive. All rights reserved.
7 // 7 //
8 -// A (basic) class for accessing data stored on Amazon's Simple Storage Service (http://aws.amazon.com/s3/) using the REST API 8 +// A class for accessing data stored on Amazon's Simple Storage Service (http://aws.amazon.com/s3/) using the REST API
  9 +// While you can use this class directly, the included subclasses make typical operations easier
9 10
10 #import <Foundation/Foundation.h> 11 #import <Foundation/Foundation.h>
11 #import "ASIHTTPRequest.h" 12 #import "ASIHTTPRequest.h"
@@ -34,27 +35,12 @@ typedef enum _ASIS3ErrorType { @@ -34,27 +35,12 @@ typedef enum _ASIS3ErrorType {
34 // Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:] 35 // Your S3 secret access key. Set it on the request, or set it globally using [ASIS3Request setSharedSecretAccessKey:]
35 NSString *secretAccessKey; 36 NSString *secretAccessKey;
36 37
37 - // Name of the bucket to talk to  
38 - NSString *bucket;  
39 -  
40 - // Key of the resource you want to access on S3. Leave empty for the bucket root  
41 - NSString *key;  
42 -  
43 // 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 38 // 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
44 NSString *dateString; 39 NSString *dateString;
45 - 40 +
46 - // The mime type of the content for PUT requests 41 + // The access policy to use when PUTting a file (see the string constants at the top ASIS3Request.h for details on what the possible options are)
47 - // Set this if having the correct mime type returned to you when you GET the data is important (eg it will be served by a web-server)  
48 - // Will be set to 'application/octet-stream' otherwise in iPhone apps, or autodetected on Mac OS X  
49 - NSString *mimeType;  
50 -  
51 - // The access policy to use when PUTting a file (see the string constants at the top of this header)  
52 NSString *accessPolicy; 42 NSString *accessPolicy;
53 - 43 +
54 - // The bucket + path of the object to be copied (used with COPYRequestFromBucket:path:toBucket:path:)  
55 - NSString *sourceBucket;  
56 - NSString *sourceKey;  
57 -  
58 // Internally used while parsing errors 44 // Internally used while parsing errors
59 NSString *currentErrorString; 45 NSString *currentErrorString;
60 46
@@ -62,29 +48,16 @@ typedef enum _ASIS3ErrorType { @@ -62,29 +48,16 @@ typedef enum _ASIS3ErrorType {
62 48
63 #pragma mark Constructors 49 #pragma mark Constructors
64 50
65 -// Create a request, building an appropriate url  
66 -+ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key;  
67 -  
68 -// Create a PUT request using the file at filePath as the body  
69 -+ (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket key:(NSString *)key;  
70 -  
71 -// Create a PUT request using the supplied NSData as the body (set the mime-type manually with setMimeType: if necessary)  
72 -+ (id)PUTRequestForData:(NSData *)data withBucket:(NSString *)bucket key:(NSString *)key;  
73 -  
74 -// Create a DELETE request for the object at path  
75 -+ (id)DELETERequestWithBucket:(NSString *)bucket key:(NSString *)key;  
76 -  
77 -// Create a PUT request to copy an object from one location to another  
78 -// Clang will complain because it thinks this method should return an object with +1 retain :(  
79 -+ (id)COPYRequestFromBucket:(NSString *)sourceBucket key:(NSString *)sourceKey toBucket:(NSString *)bucket key:(NSString *)key;  
80 -  
81 -// Creates a HEAD request for the object at path  
82 -+ (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key;  
83 -  
84 51
85 // Uses the supplied date to create a Date header string 52 // Uses the supplied date to create a Date header string
86 - (void)setDate:(NSDate *)date; 53 - (void)setDate:(NSDate *)date;
87 54
  55 +- (NSMutableDictionary *)S3Headers;
  56 +- (NSString *)stringToSignForHeaders:(NSString *)canonicalizedAmzHeaders resource:(NSString *)canonicalizedResource;
  57 +
  58 +
  59 +# pragma mark encoding S3 key
  60 +
88 + (NSString *)stringByURLEncodingForS3Path:(NSString *)key; 61 + (NSString *)stringByURLEncodingForS3Path:(NSString *)key;
89 62
90 #pragma mark Shared access keys 63 #pragma mark Shared access keys
@@ -94,15 +67,10 @@ typedef enum _ASIS3ErrorType { @@ -94,15 +67,10 @@ typedef enum _ASIS3ErrorType {
94 + (void)setSharedAccessKey:(NSString *)newAccessKey; 67 + (void)setSharedAccessKey:(NSString *)newAccessKey;
95 + (NSString *)sharedSecretAccessKey; 68 + (NSString *)sharedSecretAccessKey;
96 + (void)setSharedSecretAccessKey:(NSString *)newAccessKey; 69 + (void)setSharedSecretAccessKey:(NSString *)newAccessKey;
97 - 70 +- (void)parseResponseXML;
98 - 71 +
99 -@property (retain) NSString *bucket;  
100 -@property (retain) NSString *key;  
101 @property (retain) NSString *dateString; 72 @property (retain) NSString *dateString;
102 -@property (retain) NSString *mimeType;  
103 @property (retain) NSString *accessKey; 73 @property (retain) NSString *accessKey;
104 @property (retain) NSString *secretAccessKey; 74 @property (retain) NSString *secretAccessKey;
105 @property (retain) NSString *accessPolicy; 75 @property (retain) NSString *accessPolicy;
106 -@property (retain) NSString *sourceBucket;  
107 -@property (retain) NSString *sourceKey;  
108 @end 76 @end
@@ -19,9 +19,7 @@ static NSString *sharedSecretAccessKey = nil; @@ -19,9 +19,7 @@ static NSString *sharedSecretAccessKey = nil;
19 19
20 // Private stuff 20 // Private stuff
21 @interface ASIS3Request () 21 @interface ASIS3Request ()
22 - - (void)parseError;  
23 + (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string; 22 + (NSData *)HMACSHA1withKey:(NSString *)key forString:(NSString *)string;
24 -  
25 @property (retain, nonatomic) NSString *currentErrorString; 23 @property (retain, nonatomic) NSString *currentErrorString;
26 @end 24 @end
27 25
@@ -41,66 +39,11 @@ static NSString *sharedSecretAccessKey = nil; @@ -41,66 +39,11 @@ static NSString *sharedSecretAccessKey = nil;
41 return path; 39 return path;
42 } 40 }
43 41
44 -+ (id)requestWithBucket:(NSString *)bucket key:(NSString *)key  
45 -{  
46 - NSString *path = [ASIS3Request stringByURLEncodingForS3Path:key];  
47 - ASIS3Request *request = [[[self alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com%@",bucket,path]]] autorelease];  
48 - [request setBucket:bucket];  
49 - [request setKey:key];  
50 - return request;  
51 -}  
52 -  
53 -+ (id)PUTRequestForData:(NSData *)data withBucket:(NSString *)bucket key:(NSString *)key  
54 -{  
55 - ASIS3Request *request = [self requestWithBucket:bucket key:key];  
56 - [request appendPostData:data];  
57 - [request setRequestMethod:@"PUT"];  
58 - return request;  
59 -}  
60 -  
61 -+ (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket key:(NSString *)key  
62 -{  
63 - ASIS3Request *request = [self requestWithBucket:bucket key:key];  
64 - [request setPostBodyFilePath:filePath];  
65 - [request setShouldStreamPostDataFromDisk:YES];  
66 - [request setRequestMethod:@"PUT"];  
67 - [request setMimeType:[ASIHTTPRequest mimeTypeForFileAtPath:filePath]];  
68 - return request;  
69 -}  
70 -  
71 -+ (id)DELETERequestWithBucket:(NSString *)bucket key:(NSString *)key  
72 -{  
73 - ASIS3Request *request = [self requestWithBucket:bucket key:key];  
74 - [request setRequestMethod:@"DELETE"];  
75 - return request;  
76 -}  
77 -  
78 -+ (id)COPYRequestFromBucket:(NSString *)sourceBucket key:(NSString *)sourceKey toBucket:(NSString *)bucket key:(NSString *)key  
79 -{  
80 - ASIS3Request *request = [self requestWithBucket:bucket key:key];  
81 - [request setRequestMethod:@"PUT"];  
82 - [request setSourceBucket:sourceBucket];  
83 - [request setSourceKey:sourceKey];  
84 - return request;  
85 -}  
86 -  
87 -+ (id)HEADRequestWithBucket:(NSString *)bucket key:(NSString *)key  
88 -{  
89 - ASIS3Request *request = [self requestWithBucket:bucket key:key];  
90 - [request setRequestMethod:@"HEAD"];  
91 - return request;  
92 -}  
93 -  
94 - (void)dealloc 42 - (void)dealloc
95 { 43 {
96 - [bucket release];  
97 - [key release];  
98 [dateString release]; 44 [dateString release];
99 - [mimeType release];  
100 [accessKey release]; 45 [accessKey release];
101 [secretAccessKey release]; 46 [secretAccessKey release];
102 - [sourceKey release];  
103 - [sourceBucket release];  
104 [super dealloc]; 47 [super dealloc];
105 } 48 }
106 49
@@ -119,11 +62,23 @@ static NSString *sharedSecretAccessKey = nil; @@ -119,11 +62,23 @@ static NSString *sharedSecretAccessKey = nil;
119 ASIS3Request *headRequest = (ASIS3Request *)[super HEADRequest]; 62 ASIS3Request *headRequest = (ASIS3Request *)[super HEADRequest];
120 [headRequest setAccessKey:[self accessKey]]; 63 [headRequest setAccessKey:[self accessKey]];
121 [headRequest setSecretAccessKey:[self secretAccessKey]]; 64 [headRequest setSecretAccessKey:[self secretAccessKey]];
122 - [headRequest setKey:[self key]];  
123 - [headRequest setBucket:[self bucket]];  
124 return headRequest; 65 return headRequest;
125 } 66 }
126 67
  68 +- (NSMutableDictionary *)S3Headers
  69 +{
  70 + return [NSMutableDictionary dictionary];
  71 +}
  72 +
  73 +- (NSString *)canonicalizedResource
  74 +{
  75 + return @"/";
  76 +}
  77 +
  78 +- (NSString *)stringToSignForHeaders:(NSString *)canonicalizedAmzHeaders resource:(NSString *)canonicalizedResource
  79 +{
  80 + return [NSString stringWithFormat:@"%@\n\n\n%@\n%@%@",[self requestMethod],[self dateString],canonicalizedAmzHeaders,canonicalizedResource];
  81 +}
127 82
128 - (void)buildRequestHeaders 83 - (void)buildRequestHeaders
129 { 84 {
@@ -143,32 +98,19 @@ static NSString *sharedSecretAccessKey = nil; @@ -143,32 +98,19 @@ static NSString *sharedSecretAccessKey = nil;
143 [self addRequestHeader:@"Date" value:[self dateString]]; 98 [self addRequestHeader:@"Date" value:[self dateString]];
144 99
145 // Ensure our formatted string doesn't use '(null)' for the empty path 100 // Ensure our formatted string doesn't use '(null)' for the empty path
146 - NSString *canonicalizedResource = [NSString stringWithFormat:@"/%@%@",[self bucket],[ASIS3Request stringByURLEncodingForS3Path:[self key]]]; 101 + NSString *canonicalizedResource = [self canonicalizedResource];
147 102
148 // Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private) 103 // Add a header for the access policy if one was set, otherwise we won't add one (and S3 will default to private)
149 - NSMutableDictionary *amzHeaders = [[[NSMutableDictionary alloc] init] autorelease]; 104 + NSMutableDictionary *amzHeaders = [self S3Headers];
150 NSString *canonicalizedAmzHeaders = @""; 105 NSString *canonicalizedAmzHeaders = @"";
151 - if ([self accessPolicy]) {  
152 - [amzHeaders setObject:[self accessPolicy] forKey:@"x-amz-acl"];  
153 - }  
154 - if ([self sourceKey]) {  
155 - NSString *path = [ASIS3Request stringByURLEncodingForS3Path:[self sourceKey]];  
156 - [amzHeaders setObject:[[self sourceBucket] stringByAppendingString:path] forKey:@"x-amz-copy-source"];  
157 - }  
158 for (NSString *header in [amzHeaders keyEnumerator]) { 106 for (NSString *header in [amzHeaders keyEnumerator]) {
159 canonicalizedAmzHeaders = [NSString stringWithFormat:@"%@%@:%@\n",canonicalizedAmzHeaders,[header lowercaseString],[amzHeaders objectForKey:header]]; 107 canonicalizedAmzHeaders = [NSString stringWithFormat:@"%@%@:%@\n",canonicalizedAmzHeaders,[header lowercaseString],[amzHeaders objectForKey:header]];
160 - [self addRequestHeader:key value:[amzHeaders objectForKey:key]]; 108 + [self addRequestHeader:header value:[amzHeaders objectForKey:header]];
161 } 109 }
162 110
163 111
164 // Jump through hoops while eating hot food 112 // Jump through hoops while eating hot food
165 - NSString *stringToSign; 113 + NSString *stringToSign = [self stringToSignForHeaders:canonicalizedAmzHeaders resource:canonicalizedResource];
166 - if ([[self requestMethod] isEqualToString:@"PUT"] && ![self sourceKey]) {  
167 - [self addRequestHeader:@"Content-Type" value:[self mimeType]];  
168 - stringToSign = [NSString stringWithFormat:@"PUT\n\n%@\n%@\n%@%@",[self mimeType],dateString,canonicalizedAmzHeaders,canonicalizedResource];  
169 - } else {  
170 - stringToSign = [NSString stringWithFormat:@"%@\n\n\n%@\n%@%@",[self requestMethod],dateString,canonicalizedAmzHeaders,canonicalizedResource];  
171 - }  
172 NSString *signature = [ASIHTTPRequest base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]]; 114 NSString *signature = [ASIHTTPRequest base64forData:[ASIS3Request HMACSHA1withKey:[self secretAccessKey] forString:stringToSign]];
173 NSString *authorizationString = [NSString stringWithFormat:@"AWS %@:%@",[self accessKey],signature]; 115 NSString *authorizationString = [NSString stringWithFormat:@"AWS %@:%@",[self accessKey],signature];
174 [self addRequestHeader:@"Authorization" value:authorizationString]; 116 [self addRequestHeader:@"Authorization" value:authorizationString];
@@ -179,21 +121,16 @@ static NSString *sharedSecretAccessKey = nil; @@ -179,21 +121,16 @@ static NSString *sharedSecretAccessKey = nil;
179 121
180 - (void)requestFinished 122 - (void)requestFinished
181 { 123 {
182 - // COPY requests return a 200 whether they succeed or fail, so we need to look at the XML to see if we were successful.  
183 - if ([self responseStatusCode] == 200 && [self sourceKey] && [self sourceBucket]) {  
184 - [self parseError];  
185 - return;  
186 - }  
187 if ([self responseStatusCode] < 207) { 124 if ([self responseStatusCode] < 207) {
188 [super requestFinished]; 125 [super requestFinished];
189 return; 126 return;
190 } 127 }
191 - [self parseError]; 128 + [self parseResponseXML];
192 } 129 }
193 130
194 #pragma mark Error XML parsing 131 #pragma mark Error XML parsing
195 132
196 -- (void)parseError 133 +- (void)parseResponseXML
197 { 134 {
198 NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease]; 135 NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease];
199 [parser setDelegate:self]; 136 [parser setDelegate:self];
@@ -231,12 +168,6 @@ static NSString *sharedSecretAccessKey = nil; @@ -231,12 +168,6 @@ static NSString *sharedSecretAccessKey = nil;
231 ASIS3Request *newRequest = [super copyWithZone:zone]; 168 ASIS3Request *newRequest = [super copyWithZone:zone];
232 [newRequest setAccessKey:[self accessKey]]; 169 [newRequest setAccessKey:[self accessKey]];
233 [newRequest setSecretAccessKey:[self secretAccessKey]]; 170 [newRequest setSecretAccessKey:[self secretAccessKey]];
234 - [newRequest setBucket:[self bucket]];  
235 - [newRequest setKey:[self key]];  
236 - [newRequest setMimeType:[self mimeType]];  
237 - [newRequest setAccessPolicy:[self accessPolicy]];  
238 - [newRequest setSourceBucket:[self sourceBucket]];  
239 - [newRequest setSourceKey:[self sourceKey]];  
240 return newRequest; 171 return newRequest;
241 } 172 }
242 173
@@ -266,7 +197,6 @@ static NSString *sharedSecretAccessKey = nil; @@ -266,7 +197,6 @@ static NSString *sharedSecretAccessKey = nil;
266 } 197 }
267 198
268 199
269 -  
270 #pragma mark S3 Authentication helpers 200 #pragma mark S3 Authentication helpers
271 201
272 // From: http://stackoverflow.com/questions/476455/is-there-a-library-for-iphone-to-work-with-hmac-sha-1-encoding 202 // From: http://stackoverflow.com/questions/476455/is-there-a-library-for-iphone-to-work-with-hmac-sha-1-encoding
@@ -286,14 +216,10 @@ static NSString *sharedSecretAccessKey = nil; @@ -286,14 +216,10 @@ static NSString *sharedSecretAccessKey = nil;
286 return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH]; 216 return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
287 } 217 }
288 218
289 -@synthesize bucket; 219 +
290 -@synthesize key;  
291 @synthesize dateString; 220 @synthesize dateString;
292 -@synthesize mimeType;  
293 @synthesize accessKey; 221 @synthesize accessKey;
294 @synthesize secretAccessKey; 222 @synthesize secretAccessKey;
295 -@synthesize accessPolicy;  
296 @synthesize currentErrorString; 223 @synthesize currentErrorString;
297 -@synthesize sourceBucket; 224 +@synthesize accessPolicy;
298 -@synthesize sourceKey;  
299 @end 225 @end
This diff is collapsed. Click to expand it.
This diff was suppressed by a .gitattributes entry.