Skip to content

Commit

Permalink
Added support for S3 list requests
Browse files Browse the repository at this point in the history
Added more tests, bug fixes
  • Loading branch information
pokeb committed Jul 14, 2009
1 parent 3ab29ef commit 6941435
Show file tree
Hide file tree
Showing 11 changed files with 666 additions and 90 deletions.
52 changes: 52 additions & 0 deletions Classes/ASIS3BucketObject.h
@@ -0,0 +1,52 @@
//
// ASIS3BucketObject.h
// Mac
//
// Created by Ben Copsey on 13/07/2009.
// Copyright 2009 All-Seeing Interactive. All rights reserved.
//

#import <Foundation/Foundation.h>
@class ASIS3Request;

@interface ASIS3BucketObject : NSObject {

// The bucket this object belongs to
NSString *bucket;

// The key (path) of this object in the bucket
NSString *key;

// When this object was last modified
NSDate *lastModified;

// The ETag for this object's content
NSString *ETag;

// The size in bytes of this object
unsigned long long size;

// Info about the owner
NSString *ownerID;
NSString *ownerName;
}

+ (id)objectWithBucket:(NSString *)bucket;

// Returns a request that will fetch this object when run
- (ASIS3Request *)GETRequest;

// Returns a request that will replace this object with the contents of the file at filePath when run
- (ASIS3Request *)PUTRequestWithFile:(NSString *)filePath;

// Returns a request that will delete this object when run
- (ASIS3Request *)DELETERequest;

@property (retain) NSString *bucket;
@property (retain) NSString *key;
@property (retain) NSDate *lastModified;
@property (retain) NSString *ETag;
@property (assign) unsigned long long size;
@property (retain) NSString *ownerID;
@property (retain) NSString *ownerName;
@end
61 changes: 61 additions & 0 deletions Classes/ASIS3BucketObject.m
@@ -0,0 +1,61 @@
//
// ASIS3BucketObject.m
// Mac
//
// Created by Ben Copsey on 13/07/2009.
// Copyright 2009 All-Seeing Interactive. All rights reserved.
//

#import "ASIS3BucketObject.h"
#import "ASIS3Request.h"

@implementation ASIS3BucketObject

+ (id)objectWithBucket:(NSString *)bucket
{
ASIS3BucketObject *object = [[[ASIS3BucketObject alloc] init] autorelease];
[object setBucket:bucket];
return object;
}

- (void)dealloc
{
[key release];
[lastModified release];
[ETag release];
[ownerID release];
[ownerName release];
[super dealloc];
}

- (ASIS3Request *)GETRequest
{
return [ASIS3Request requestWithBucket:[self bucket] path:[NSString stringWithFormat:@"/%@",[self key]]];
}

- (ASIS3Request *)PUTRequestWithFile:(NSString *)filePath
{
return [ASIS3Request PUTRequestForFile:filePath withBucket:[self bucket] path:[NSString stringWithFormat:@"/%@",[self key]]];
}

- (ASIS3Request *)DELETERequest
{
ASIS3Request *request = [ASIS3Request requestWithBucket:[self bucket] path:[NSString stringWithFormat:@"/%@",[self key]]];
[request setRequestMethod:@"DELETE"];
return request;
}


- (NSString *)description
{
return [NSString stringWithFormat:@"Key: %@ lastModified: %@ ETag: %@ size: %llu ownerID: %@ ownerName: %@",[self key],[self lastModified],[self ETag],[self size],[self ownerID],[self ownerName]];
}

@synthesize bucket;
@synthesize key;
@synthesize lastModified;
@synthesize ETag;
@synthesize size;
@synthesize ownerID;
@synthesize ownerName;
@end
42 changes: 42 additions & 0 deletions Classes/ASIS3ListRequest.h
@@ -0,0 +1,42 @@
//
// ASIS3ListRequest.h
// Mac
//
// Created by Ben Copsey on 13/07/2009.
// Copyright 2009 All-Seeing Interactive. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "ASIS3Request.h"
@class ASIS3BucketObject;

@interface ASIS3ListRequest : ASIS3Request {

NSString *prefix;
NSString *marker;
int maxResultCount;
NSString *delimiter;

// Internally used while parsing the response
NSString *currentContent;
NSString *currentElement;
ASIS3BucketObject *currentObject;
NSMutableArray *objects;


}
// Create a list request
+ (id)listRequestWithBucket:(NSString *)bucket;


// Returns an array of ASIS3BucketObjects created from the XML response
- (NSArray *)bucketObjects;

//Builds a query string out of the list parameters we supplied
- (void)createQueryString;

@property (retain) NSString *prefix;
@property (retain) NSString *marker;
@property (assign) int maxResultCount;
@property (retain) NSString *delimiter;
@end
137 changes: 137 additions & 0 deletions Classes/ASIS3ListRequest.m
@@ -0,0 +1,137 @@
//
// ASIS3ListRequest.m
// Mac
//
// Created by Ben Copsey on 13/07/2009.
// Copyright 2009 All-Seeing Interactive. All rights reserved.
//
#import "ASIS3ListRequest.h"
#import "ASIS3BucketObject.h"


static NSDateFormatter *dateFormatter = nil;

// Private stuff
@interface ASIS3ListRequest ()
@property (retain,setter=setURL:) NSURL *url;
@property (retain, nonatomic) NSString *currentContent;
@property (retain, nonatomic) NSString *currentElement;
@property (retain, nonatomic) ASIS3BucketObject *currentObject;
@property (retain, nonatomic) NSMutableArray *objects;
@end

@implementation ASIS3ListRequest

+ (void)initialize
{
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'.000Z'"];
}

+ (id)listRequestWithBucket:(NSString *)bucket
{
ASIS3ListRequest *request = [[[ASIS3ListRequest alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://%@.s3.amazonaws.com",bucket]]] autorelease];
[request setBucket:bucket];
return request;
}

- (void)dealloc
{
[currentElement release];
[currentObject release];
[objects release];
[super dealloc];
}

- (void)createQueryString
{
NSMutableArray *queryParts = [[[NSMutableArray alloc] init] autorelease];
if ([self prefix]) {
[queryParts addObject:[NSString stringWithFormat:@"prefix=%@",[[self prefix] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
}
if ([self marker]) {
[queryParts addObject:[NSString stringWithFormat:@"marker=%@",[[self marker] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
}
if ([self delimiter]) {
[queryParts addObject:[NSString stringWithFormat:@"delimiter=%@",[[self delimiter] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
}
if ([self maxResultCount] > 0) {
[queryParts addObject:[NSString stringWithFormat:@"delimiter=%hi",[self maxResultCount]]];
}
if ([queryParts count]) {
[self setURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?%@",[[self url] absoluteString],[queryParts componentsJoinedByString:@"&"]]]];
}
}

- (void)main
{
[self createQueryString];
[super main];
}

- (NSArray *)bucketObjects
{
if ([self objects]) {
return [self objects];
}
[self setObjects:[[[NSMutableArray alloc] init] autorelease]];
NSXMLParser *parser = [[[NSXMLParser alloc] initWithData:[self responseData]] autorelease];
[parser setDelegate:self];
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
return [self objects];
}

- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
[self setCurrentElement:elementName];

if ([elementName isEqualToString:@"Contents"]) {
[self setCurrentObject:[ASIS3BucketObject objectWithBucket:[self bucket]]];
}
[self setCurrentContent:@""];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
{
if ([elementName isEqualToString:@"Contents"]) {
[objects addObject:currentObject];
[self setCurrentObject:nil];
} else if ([elementName isEqualToString:@"Key"]) {
[[self currentObject] setKey:[self currentContent]];
} else if ([elementName isEqualToString:@"LastModified"]) {
[[self currentObject] setLastModified:[dateFormatter dateFromString:[self currentContent]]];
} else if ([elementName isEqualToString:@"ETag"]) {
[[self currentObject] setETag:[self currentContent]];
} else if ([elementName isEqualToString:@"Size"]) {
[[self currentObject] setSize:(unsigned long long)[[self currentContent] longLongValue]];
} else if ([elementName isEqualToString:@"ID"]) {
[[self currentObject] setOwnerID:[self currentContent]];
} else if ([elementName isEqualToString:@"DisplayName"]) {
[[self currentObject] setOwnerName:[self currentContent]];
}
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
{
[self setCurrentContent:[[self currentContent] stringByAppendingString:string]];
}

- (void)parserDidEndDocument:(NSXMLParser *)parser
{
}


@synthesize currentContent;
@synthesize currentElement;
@synthesize currentObject;
@synthesize objects;
@synthesize prefix;
@synthesize marker;
@synthesize maxResultCount;
@synthesize delimiter;
@synthesize url;
@end
29 changes: 23 additions & 6 deletions Classes/ASIS3Request.h
Expand Up @@ -4,8 +4,7 @@
// Created by Ben Copsey on 30/06/2009.
// Copyright 2009 All-Seeing Interactive. All rights reserved.
//
// A (basic) class for accessing data stored on Amazon's Simple Storage Service (http://aws.amazon.com/s3/)
// It uses the REST API, with canned access policies rather than full support for ACLs (though if you build/parse them yourself you can still use ACLs)
// A (basic) class for accessing data stored on Amazon's Simple Storage Service (http://aws.amazon.com/s3/) using the REST API

#import <Foundation/Foundation.h>
#import "ASIHTTPRequest.h"
Expand All @@ -16,6 +15,12 @@ extern NSString *const ASIS3AccessPolicyPublicRead;
extern NSString *const ASIS3AccessPolicyPublicReadWrote;
extern NSString *const ASIS3AccessPolicyAuthenticatedRead;

typedef enum _ASIS3ErrorType {
ASIS3ResponseParsingFailedType = 1,
ASIS3ResponseErrorType = 2

} ASIS3ErrorType;

@interface ASIS3Request : ASIHTTPRequest {

// Your S3 access key. Set it on the request, or set it globally using [ASIS3Request setSharedAccessKey:]
Expand All @@ -38,7 +43,19 @@ extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
// Will be set to 'application/octet-stream' otherwise in iPhone apps, or autodetected on Mac OS X
NSString *mimeType;

// The access policy to use when PUTting a file (see the string constants at the top of this header)
NSString *accessPolicy;

// Options for filtering list requests
// See http://docs.amazonwebservices.com/AmazonS3/2006-03-01/index.html?RESTBucketGET.html
NSString *listPrefix;
NSString *listMarker;
int listMaxResults;
NSString *listDelimiter;

// Internally used while parsing errors
NSString *currentErrorString;

}

#pragma mark Constructors
Expand All @@ -49,16 +66,15 @@ extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
// Create a PUT request using the file at filePath as the body
+ (id)PUTRequestForFile:(NSString *)filePath withBucket:(NSString *)bucket path:(NSString *)path;

// Create a list request
+ (id)listRequestWithBucket:(NSString *)bucket prefix:(NSString *)prefix maxResults:(int)maxResults marker:(NSString *)marker;

// Generates the request headers S3 needs
// Automatically called before the request begins in startRequest
- (void)generateS3Headers;

// Uses the supplied date to create a Date header string
- (void)setDate:(NSDate *)date;

#pragma mark Helper functions

// Only works on Mac OS, will always return 'application/octet-stream' on iPhone
+ (NSString *)mimeTypeForFileAtPath:(NSString *)path;

Expand All @@ -77,5 +93,6 @@ extern NSString *const ASIS3AccessPolicyAuthenticatedRead;
@property (retain) NSString *mimeType;
@property (retain) NSString *accessKey;
@property (retain) NSString *secretAccessKey;
@property (assign) NSString *accessPolicy;
@property (retain) NSString *accessPolicy;

@end

0 comments on commit 6941435

Please sign in to comment.