From a9a6a573aa83dc2ce04447a31ec0fe72e0ef869e Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 7 Jul 2015 16:38:51 -0400 Subject: [PATCH 1/4] Add putObjectWithData which correctly take care of x-amz-xxxx headers --- AFAmazonS3Manager/AFAmazonS3Manager.h | 9 +++++ AFAmazonS3Manager/AFAmazonS3Manager.m | 36 +++++++++++++++++++ .../AFAmazonS3RequestSerializer.h | 5 +++ .../AFAmazonS3RequestSerializer.m | 20 +++++++++++ 4 files changed, 70 insertions(+) diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.h b/AFAmazonS3Manager/AFAmazonS3Manager.h index b93084f..f3118bb 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.h +++ b/AFAmazonS3Manager/AFAmazonS3Manager.h @@ -215,6 +215,15 @@ success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure; +/* same as putObjectWithFile but with NSData */ + +- (AFHTTPRequestOperation *)putObjectWithData:(NSData *)data + destinationPath:(NSString *)destinationPath + parameters:(NSDictionary *)parameters + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(void (^)(id responseObject))success + failure:(void (^)(NSError *error))failure; + /** Deletes the specified object. Once deleted, there is no method to restore or undelete an object. diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.m b/AFAmazonS3Manager/AFAmazonS3Manager.m index 8075821..236ed42 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.m +++ b/AFAmazonS3Manager/AFAmazonS3Manager.m @@ -236,6 +236,42 @@ - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path return [self setObjectWithMethod:@"PUT" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure]; } +// there is a bug in calcalute authentication in the AFAmazonS3Manager code + +- (AFHTTPRequestOperation *)putObjectWithData:(NSData *)data + destinationPath:(NSString *)destinationPath + parameters:(NSDictionary *)parameters + progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress + success:(void (^)(id responseObject))success + failure:(void (^)(NSError *error))failure + +{ + NSParameterAssert(data); + NSParameterAssert(destinationPath); + + NSMutableURLRequest *request = nil; + request = [self.requestSerializer putRequestWithURLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:parameters error:nil]; + + request.HTTPBody = data; + + + AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { + if (success) { + success(responseObject); + } + } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) { + if (failure) { + failure(error); + } + }]; + + [requestOperation setUploadProgressBlock:progress]; + + [self.operationQueue addOperation:requestOperation]; + + return requestOperation; +} + - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method file:(NSString *)filePath destinationPath:(NSString *)destinationPath diff --git a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h index 739682b..bace233 100644 --- a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h +++ b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h @@ -95,6 +95,11 @@ expiration:(NSDate *)expiration error:(NSError * __autoreleasing *)error; +// S3 expects parameters as headers for PUT requests for calculating authentication +- (NSMutableURLRequest *)putRequestWithURLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error; + @end ///---------------- diff --git a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m index 2fad707..cef4bce 100644 --- a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m +++ b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m @@ -235,6 +235,26 @@ - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request #pragma mark - AFHTTPRequestSerializer + +// S3 expects parameters as headers for PUT requests for calculating authentication +- (NSMutableURLRequest *)putRequestWithURLString:(NSString *)URLString + parameters:(NSDictionary *)parameters + error:(NSError *__autoreleasing *)error +{ + NSMutableURLRequest *request = [super requestWithMethod:@"PUT" URLString:URLString parameters:nil error:error]; + + if (self.sessionToken) { + [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"]; + } + if (parameters != nil) { + for (id key in parameters) { + [request setValue:[parameters objectForKey:key] forHTTPHeaderField:key]; + } + } + + return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy]; +} + - (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(NSDictionary *)parameters From f8bc9e3a4d01d685526c4d24bde8eb7e1f60a581 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 7 Jul 2015 16:41:16 -0400 Subject: [PATCH 2/4] Add paramters to getBucket to list directory --- AFAmazonS3Manager/AFAmazonS3Manager.h | 2 ++ AFAmazonS3Manager/AFAmazonS3Manager.m | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.h b/AFAmazonS3Manager/AFAmazonS3Manager.h index f3118bb..2253ac3 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.h +++ b/AFAmazonS3Manager/AFAmazonS3Manager.h @@ -91,12 +91,14 @@ Lists information about the objects in a bucket for a user that has read access to the bucket. @param bucket The S3 bucket to get. Must not be `nil`. + @param parameters The parameters for list objects, you can implement something such directory listing with it. @param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes a single argument: the response object from the server. @param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes a single argument: the `NSError` object describing error that occurred. @return The operation that was enqueued on operationQueue */ - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket + parameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure; diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.m b/AFAmazonS3Manager/AFAmazonS3Manager.m index 236ed42..6bdc6bb 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.m +++ b/AFAmazonS3Manager/AFAmazonS3Manager.m @@ -106,12 +106,13 @@ - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))s #pragma mark Bucket Operations - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket + parameters:(NSDictionary *)parameters success:(void (^)(id responseObject))success failure:(void (^)(NSError *error))failure { NSParameterAssert(bucket); - return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:nil success:success failure:failure]; + return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:parameters success:success failure:failure]; } - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket From 10bd28cf5bfd9f03dfd6843e78bcf9c83cbcd53c Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 7 Jul 2015 16:43:47 -0400 Subject: [PATCH 3/4] remove AFPathByEscapingSpacesWithPlusSigns, because uri encoding is taken care by URLByAppendingPathComponent --- AFAmazonS3Manager/AFAmazonS3Manager.m | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/AFAmazonS3Manager/AFAmazonS3Manager.m b/AFAmazonS3Manager/AFAmazonS3Manager.m index 6bdc6bb..c8b07dc 100644 --- a/AFAmazonS3Manager/AFAmazonS3Manager.m +++ b/AFAmazonS3Manager/AFAmazonS3Manager.m @@ -25,10 +25,6 @@ NSString * const AFAmazonS3ManagerErrorDomain = @"com.alamofire.networking.s3.error"; -static NSString * AFPathByEscapingSpacesWithPlusSigns(NSString *path) { - return [path stringByReplacingOccurrencesOfString:@" " withString:@"+"]; -} - @interface AFAmazonS3Manager () @property (readwrite, nonatomic, strong) NSURL *baseURL; @end @@ -142,8 +138,6 @@ - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, __unused id responseObject) { if (success) { @@ -167,8 +161,6 @@ - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) { if (success) { @@ -195,8 +187,6 @@ - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil]; AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) { if (success) { @@ -300,8 +290,6 @@ - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method return nil; } - destinationPath = AFPathByEscapingSpacesWithPlusSigns(destinationPath); - NSMutableURLRequest *request = nil; if ([method compare:@"POST" options:NSCaseInsensitiveSearch] == NSOrderedSame) { NSError *requestError = nil; @@ -356,8 +344,6 @@ - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path { NSParameterAssert(path); - path = AFPathByEscapingSpacesWithPlusSigns(path); - return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:path parameters:nil success:success failure:failure]; } From 417ae1c8f0a1174e03da1cd1747663cdd8e7ee15 Mon Sep 17 00:00:00 2001 From: Dong Liu Date: Tue, 7 Jul 2015 16:45:25 -0400 Subject: [PATCH 4/4] Authentication calculation needs use percent encoded path --- AFAmazonS3Manager/AFAmazonS3RequestSerializer.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m index cef4bce..e673247 100644 --- a/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m +++ b/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m @@ -108,7 +108,9 @@ [mutableCanonicalizedAMZHeaderString appendFormat:@"%@:%@\n", field, value]; } - NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, request.URL.path] : request.URL.path; + // the authentication need use percent encoded path + NSURLComponents *urlComp = [NSURLComponents componentsWithURL:request.URL resolvingAgainstBaseURL:NO]; + NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, urlComp.percentEncodedPath] : urlComp.percentEncodedPath; NSString *method = [request HTTPMethod]; NSString *contentMD5 = [request valueForHTTPHeaderField:@"Content-MD5"]; NSString *contentType = [request valueForHTTPHeaderField:@"Content-Type"];