├── AFAmazonS3Manager.podspec
├── AFAmazonS3Manager.xcworkspace
└── contents.xcworkspacedata
├── AFAmazonS3Manager
├── AFAmazonS3Manager.h
├── AFAmazonS3Manager.m
├── AFAmazonS3RequestSerializer.h
├── AFAmazonS3RequestSerializer.m
├── AFAmazonS3ResponseSerializer.h
└── AFAmazonS3ResponseSerializer.m
├── LICENSE
└── README.md
/AFAmazonS3Manager.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "AFAmazonS3Manager"
3 | s.version = "3.2.1"
4 | s.summary = "AFNetworking extension for the Amazon S3 API."
5 | s.homepage = "https://github.com/AFNetworking/AFAmazonS3Manager"
6 | s.social_media_url = "https://twitter.com/AFNetworking"
7 | s.license = 'MIT'
8 | s.author = { "Mattt Thompson" => "m@mattt.me" }
9 | s.source = { :git => "https://github.com/AFNetworking/AFAmazonS3Manager.git",
10 | :tag => s.version }
11 |
12 | s.ios.deployment_target = '6.0'
13 | s.osx.deployment_target = '10.8'
14 |
15 | s.source_files = 'AFAmazonS3Manager'
16 | s.requires_arc = true
17 |
18 | s.dependency 'AFNetworking/NSURLConnection', '~>2.4'
19 | end
20 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
9 |
10 |
12 |
13 |
15 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3Manager.h:
--------------------------------------------------------------------------------
1 | // AFAmazonS3Manager.h
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFHTTPRequestOperationManager.h"
24 | #import "AFAmazonS3RequestSerializer.h"
25 |
26 | /**
27 | AFAmazonS3Manager` is an `AFHTTPRequestOperationManager` subclass for interacting with the Amazon S3 webservice API (http://aws.amazon.com/s3/).
28 | */
29 | @interface AFAmazonS3Manager : AFHTTPRequestOperationManager
30 |
31 | /**
32 | The base URL for the S3 manager.
33 |
34 | @discussion By default, the `baseURL` of `AFAmazonS3Manager` is derived from the `bucket` and `region` values. If `baseURL` is set directly, it will override the default `baseURL` and disregard values set for the `bucket`, `region`, and `useSSL` properties.
35 | */
36 | @property (readonly, nonatomic, strong) NSURL *baseURL;
37 |
38 | /**
39 | Requests created by `AFAmazonS3Manager` are serialized by a subclass of `AFAmazonS3RequestSerializer`, which is responsible for encoding credentials, and any specified region and bucket.
40 | */
41 | @property (nonatomic, strong) AFAmazonS3RequestSerializer * requestSerializer;
42 |
43 | /**
44 | Initializes and returns a newly allocated Amazon S3 client with specified credentials.
45 |
46 | This is the designated initializer.
47 |
48 | @param accessKey The AWS access key.
49 | @param secret The AWS secret.
50 | */
51 | - (id)initWithAccessKeyID:(NSString *)accessKey
52 | secret:(NSString *)secret;
53 |
54 | /**
55 | Creates and enqueues a request operation to the client's operation queue.
56 |
57 | @param method The HTTP method for the request.
58 | @param path The path to be appended to the HTTP client's base URL and used as the request URL.
59 | @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.
60 | @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.
61 |
62 | @return The operation that was enqueued on operationQueue
63 | */
64 | - (AFHTTPRequestOperation *)enqueueS3RequestOperationWithMethod:(NSString *)method
65 | path:(NSString *)path
66 | parameters:(NSDictionary *)parameters
67 | success:(void (^)(id responseObject))success
68 | failure:(void (^)(NSError *error))failure;
69 |
70 | ///-------------------------
71 | /// @name Service Operations
72 | ///-------------------------
73 |
74 | /**
75 | Returns a list of all buckets owned by the authenticated request sender.
76 |
77 | @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.
78 | @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.
79 |
80 | @return The operation that was enqueued on operationQueue
81 | */
82 | - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))success
83 | failure:(void (^)(NSError *error))failure;
84 |
85 |
86 | ///------------------------
87 | /// @name Bucket Operations
88 | ///------------------------
89 |
90 | /**
91 | Lists information about the objects in a bucket for a user that has read access to the bucket.
92 |
93 | @param bucket The S3 bucket to get. Must not be `nil`.
94 | @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.
95 | @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.
96 |
97 | @return The operation that was enqueued on operationQueue
98 | */
99 | - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket
100 | success:(void (^)(id responseObject))success
101 | failure:(void (^)(NSError *error))failure;
102 |
103 | /**
104 | Creates a new bucket belonging to the account of the authenticated request sender. Optionally, you can specify a EU (Ireland) or US-West (N. California) location constraint.
105 |
106 | @param bucket The S3 bucket to create. Must not be `nil`.
107 | @param parameters The parameters to be encoded and set in the request HTTP body.
108 | @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.
109 | @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.
110 |
111 | @return The operation that was enqueued on operationQueue
112 | */
113 | - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket
114 | parameters:(NSDictionary *)parameters
115 | success:(void (^)(id responseObject))success
116 | failure:(void (^)(NSError *error))failure;
117 |
118 | /**
119 | Deletes the specified bucket. All objects in the bucket must be deleted before the bucket itself can be deleted.
120 |
121 | @param bucket The S3 bucket to be delete. Must not be `nil`.
122 | @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.
123 | @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.
124 |
125 | @return The operation that was enqueued on operationQueue
126 | */
127 | - (AFHTTPRequestOperation *)deleteBucket:(NSString *)bucket
128 | success:(void (^)(id responseObject))success
129 | failure:(void (^)(NSError *error))failure;
130 |
131 | ///----------------------------------------------
132 | /// @name Object Operations
133 | ///----------------------------------------------
134 |
135 | /**
136 | Retrieves information about an object for a user with read access without fetching the object.
137 |
138 | @param path The object path. Must not be `nil`.
139 | @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.
140 | @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.
141 | */
142 | - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path
143 | success:(void (^)(NSHTTPURLResponse *response))success
144 | failure:(void (^)(NSError *error))failure;
145 |
146 | /**
147 | Gets an object for a user that has read access to the object.
148 |
149 | @param path The object path. Must not be `nil`.
150 | @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread.
151 | @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.
152 | @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.
153 |
154 | @return The operation that was enqueued
155 | */
156 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path
157 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress
158 | success:(void (^)(id responseObject, NSData *responseData))success
159 | failure:(void (^)(NSError *error))failure;
160 |
161 | /**
162 | Gets an object for a user that has read access to the object.
163 |
164 | @param path The object path. Must not be `nil`.
165 | @param outputStream The `NSOutputStream` object receiving data from the request.
166 | @param progress A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread.
167 | @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.
168 | @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.
169 |
170 | @return The operation that was enqueued
171 | */
172 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path
173 | outputStream:(NSOutputStream *)outputStream
174 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress
175 | success:(void (^)(id responseObject))success
176 | failure:(void (^)(NSError *error))failure;
177 |
178 | /**
179 | Adds an object to a bucket using forms.
180 |
181 | @param path The path to the local file. Must not be `nil`.
182 | @param destinationPath The destination path for the remote file. Must not be `nil`.
183 | @param parameters The parameters to be encoded and set in the request HTTP body.
184 | @param progress A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
185 | @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.
186 | @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.
187 |
188 | @discussion `destinationPath` is relative to the bucket's root, and will create any intermediary directories as necessary. For example, specifying "/a/b/c.txt" will create the "/a" and / or "/a/b" directories within the bucket, if they do not already exist, and upload the source file as "c.txt" into "/a/b".
189 | */
190 | - (AFHTTPRequestOperation *)postObjectWithFile:(NSString *)path
191 | destinationPath:(NSString *)destinationPath
192 | parameters:(NSDictionary *)parameters
193 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
194 | success:(void (^)(id responseObject))success
195 | failure:(void (^)(NSError *error))failure;
196 |
197 | /**
198 | Adds an object to a bucket for a user that has write access to the bucket. A success response indicates the object was successfully stored; if the object already exists, it will be overwritten.
199 |
200 | @param path The path to the local file. Must not be `nil`.
201 | @param destinationPath The destination path for the remote file, including its name. Must not be `nil`.
202 | @param parameters The parameters to be encoded and set in the request HTTP body.
203 | @param progress A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
204 | @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.
205 | @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.
206 |
207 | @return The operation that was enqueued on operationQueue
208 |
209 | @discussion `destinationPath` is relative to the bucket's root, and will create any intermediary directories as necessary. For example, specifying "/a/b/c.txt" will create the "/a" and / or "/a/b" directories within the bucket, if they do not already exist, and upload the source file as "c.txt" into "/a/b".
210 | */
211 | - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path
212 | destinationPath:(NSString *)destinationPath
213 | parameters:(NSDictionary *)parameters
214 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
215 | success:(void (^)(id responseObject))success
216 | failure:(void (^)(NSError *error))failure;
217 |
218 | /**
219 | Deletes the specified object. Once deleted, there is no method to restore or undelete an object.
220 |
221 | @param path The path for the remote file to be deleted. Must not be `nil`.
222 | @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.
223 | @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.
224 |
225 | @return The operation that was enqueued on operationQueue
226 | */
227 | - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path
228 | success:(void (^)(id responseObject))success
229 | failure:(void (^)(NSError *error))failure;
230 |
231 | @end
232 |
233 | ///----------------
234 | /// @name Constants
235 | ///----------------
236 |
237 | /**
238 | ## Error Domain
239 |
240 | `AFAmazonS3ManagerErrorDomain`
241 | AFAmazonS3Manager errors. Error codes for `AFAmazonS3ManagerErrorDomain` correspond to codes in `NSURLErrorDomain`.
242 | */
243 | extern NSString * const AFAmazonS3ManagerErrorDomain;
244 |
245 | @compatibility_alias AFAmazonS3Client AFAmazonS3Manager;
246 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3Manager.m:
--------------------------------------------------------------------------------
1 | // AFAmazonS3Manager.m
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFAmazonS3Manager.h"
24 | #import "AFAmazonS3ResponseSerializer.h"
25 |
26 | NSString * const AFAmazonS3ManagerErrorDomain = @"com.alamofire.networking.s3.error";
27 |
28 | static NSString * AFPathByEscapingSpacesWithPlusSigns(NSString *path) {
29 | return [path stringByReplacingOccurrencesOfString:@" " withString:@"+"];
30 | }
31 |
32 | @interface AFAmazonS3Manager ()
33 | @property (readwrite, nonatomic, strong) NSURL *baseURL;
34 | @end
35 |
36 | @implementation AFAmazonS3Manager
37 | @synthesize baseURL = _s3_baseURL;
38 | @dynamic requestSerializer;
39 |
40 | - (instancetype)initWithBaseURL:(NSURL *)url {
41 | self = [super initWithBaseURL:url];
42 | if (!self) {
43 | return nil;
44 | }
45 |
46 | self.requestSerializer = [AFAmazonS3RequestSerializer serializer];
47 | self.responseSerializer = [AFAmazonS3ResponseSerializer serializer];
48 |
49 | return self;
50 | }
51 |
52 | - (id)initWithAccessKeyID:(NSString *)accessKey
53 | secret:(NSString *)secret
54 | {
55 | self = [self initWithBaseURL:nil];
56 | if (!self) {
57 | return nil;
58 | }
59 |
60 | [self.requestSerializer setAccessKeyID:accessKey secret:secret];
61 |
62 | return self;
63 | }
64 |
65 | - (NSURL *)baseURL {
66 | if (!_s3_baseURL) {
67 | return self.requestSerializer.endpointURL;
68 | }
69 |
70 | return _s3_baseURL;
71 | }
72 |
73 | #pragma mark -
74 |
75 | - (AFHTTPRequestOperation *)enqueueS3RequestOperationWithMethod:(NSString *)method
76 | path:(NSString *)path
77 | parameters:(NSDictionary *)parameters
78 | success:(void (^)(id responseObject))success
79 | failure:(void (^)(NSError *error))failure
80 | {
81 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:parameters error:nil];
82 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) {
83 | if (success) {
84 | success(responseObject);
85 | }
86 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
87 | if (failure) {
88 | failure(error);
89 | }
90 | }];
91 |
92 | [self.operationQueue addOperation:requestOperation];
93 |
94 | return requestOperation;
95 | }
96 |
97 |
98 | #pragma mark Service Operations
99 |
100 | - (AFHTTPRequestOperation *)getServiceWithSuccess:(void (^)(id responseObject))success
101 | failure:(void (^)(NSError *error))failure
102 | {
103 | return [self enqueueS3RequestOperationWithMethod:@"GET" path:@"/" parameters:nil success:success failure:failure];
104 | }
105 |
106 | #pragma mark Bucket Operations
107 |
108 | - (AFHTTPRequestOperation *)getBucket:(NSString *)bucket
109 | success:(void (^)(id responseObject))success
110 | failure:(void (^)(NSError *error))failure
111 | {
112 | NSParameterAssert(bucket);
113 |
114 | return [self enqueueS3RequestOperationWithMethod:@"GET" path:bucket parameters:nil success:success failure:failure];
115 | }
116 |
117 | - (AFHTTPRequestOperation *)putBucket:(NSString *)bucket
118 | parameters:(NSDictionary *)parameters
119 | success:(void (^)(id responseObject))success
120 | failure:(void (^)(NSError *error))failure
121 | {
122 | NSParameterAssert(bucket);
123 |
124 | return [self enqueueS3RequestOperationWithMethod:@"PUT" path:bucket parameters:parameters success:success failure:failure];
125 | }
126 |
127 | - (AFHTTPRequestOperation *)deleteBucket:(NSString *)bucket
128 | success:(void (^)(id responseObject))success
129 | failure:(void (^)(NSError *error))failure
130 | {
131 | NSParameterAssert(bucket);
132 |
133 | return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:bucket parameters:nil success:success failure:failure];
134 | }
135 |
136 | #pragma mark Object Operations
137 |
138 | - (AFHTTPRequestOperation *)headObjectWithPath:(NSString *)path
139 | success:(void (^)(NSHTTPURLResponse *response))success
140 | failure:(void (^)(NSError *error))failure
141 | {
142 | NSParameterAssert(path);
143 |
144 | path = AFPathByEscapingSpacesWithPlusSigns(path);
145 |
146 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"HEAD" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil];
147 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, __unused id responseObject) {
148 | if (success) {
149 | success(operation.response);
150 | }
151 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
152 | if (failure) {
153 | failure(error);
154 | }
155 | }];
156 |
157 | [self.operationQueue addOperation:requestOperation];
158 |
159 | return requestOperation;
160 | }
161 |
162 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path
163 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress
164 | success:(void (^)(id responseObject, NSData *responseData))success
165 | failure:(void (^)(NSError *error))failure
166 | {
167 | NSParameterAssert(path);
168 |
169 | path = AFPathByEscapingSpacesWithPlusSigns(path);
170 |
171 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil];
172 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
173 | if (success) {
174 | success(responseObject, operation.responseData);
175 | }
176 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
177 | if (failure) {
178 | failure(error);
179 | }
180 | }];
181 |
182 | [requestOperation setDownloadProgressBlock:progress];
183 |
184 | [self.operationQueue addOperation:requestOperation];
185 |
186 | return requestOperation;
187 | }
188 |
189 | - (AFHTTPRequestOperation *)getObjectWithPath:(NSString *)path
190 | outputStream:(NSOutputStream *)outputStream
191 | progress:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))progress
192 | success:(void (^)(id responseObject))success
193 | failure:(void (^)(NSError *error))failure
194 | {
195 | NSParameterAssert(path);
196 |
197 | path = AFPathByEscapingSpacesWithPlusSigns(path);
198 |
199 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:@"GET" URLString:[[self.baseURL URLByAppendingPathComponent:path] absoluteString] parameters:nil error:nil];
200 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) {
201 | if (success) {
202 | success(responseObject);
203 | }
204 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
205 | if (failure) {
206 | failure(error);
207 | }
208 | }];
209 |
210 | requestOperation.outputStream = outputStream;
211 |
212 | [requestOperation setDownloadProgressBlock:progress];
213 |
214 | [self.operationQueue addOperation:requestOperation];
215 |
216 | return requestOperation;
217 | }
218 |
219 | - (AFHTTPRequestOperation *)postObjectWithFile:(NSString *)path
220 | destinationPath:(NSString *)destinationPath
221 | parameters:(NSDictionary *)parameters
222 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
223 | success:(void (^)(id responseObject))success
224 | failure:(void (^)(NSError *error))failure
225 | {
226 | return [self setObjectWithMethod:@"POST" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure];
227 | }
228 |
229 | - (AFHTTPRequestOperation *)putObjectWithFile:(NSString *)path
230 | destinationPath:(NSString *)destinationPath
231 | parameters:(NSDictionary *)parameters
232 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
233 | success:(void (^)(id responseObject))success
234 | failure:(void (^)(NSError *error))failure
235 | {
236 | return [self setObjectWithMethod:@"PUT" file:path destinationPath:destinationPath parameters:parameters progress:progress success:success failure:failure];
237 | }
238 |
239 | - (AFHTTPRequestOperation *)setObjectWithMethod:(NSString *)method
240 | file:(NSString *)filePath
241 | destinationPath:(NSString *)destinationPath
242 | parameters:(NSDictionary *)parameters
243 | progress:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))progress
244 | success:(void (^)(id responseObject))success
245 | failure:(void (^)(NSError *error))failure
246 | {
247 | NSParameterAssert(method);
248 | NSParameterAssert(filePath);
249 | NSParameterAssert(destinationPath);
250 |
251 | NSMutableURLRequest *fileRequest = [NSMutableURLRequest requestWithURL:[NSURL fileURLWithPath:filePath]];
252 | fileRequest.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
253 |
254 | NSURLResponse *response = nil;
255 | NSError *fileError = nil;
256 | NSData *data = [NSURLConnection sendSynchronousRequest:fileRequest returningResponse:&response error:&fileError];
257 |
258 | if (fileError || !response || !data) {
259 | if (failure) {
260 | failure(fileError);
261 | }
262 |
263 | return nil;
264 | }
265 |
266 | destinationPath = AFPathByEscapingSpacesWithPlusSigns(destinationPath);
267 |
268 | NSMutableURLRequest *request = nil;
269 | if ([method compare:@"POST" options:NSCaseInsensitiveSearch] == NSOrderedSame) {
270 | NSError *requestError = nil;
271 | request = [self.requestSerializer multipartFormRequestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:parameters constructingBodyWithBlock:^(id formData) {
272 | if (![parameters valueForKey:@"key"]) {
273 | [formData appendPartWithFormData:[[filePath lastPathComponent] dataUsingEncoding:NSUTF8StringEncoding] name:@"key"];
274 | }
275 |
276 | [formData appendPartWithFileData:data name:@"file" fileName:[filePath lastPathComponent] mimeType:[response MIMEType]];
277 | } error:&requestError];
278 |
279 | if (requestError || !request) {
280 | if (failure) {
281 | failure(requestError);
282 | }
283 |
284 | return nil;
285 | }
286 | } else {
287 | request = [self.requestSerializer requestWithMethod:method URLString:[[self.baseURL URLByAppendingPathComponent:destinationPath] absoluteString] parameters:nil error:nil];
288 |
289 | // S3 expects parameters as headers for PUT requests
290 | if (parameters != nil) {
291 | for (id key in parameters) {
292 | [request setValue:[parameters objectForKey:key] forHTTPHeaderField:key];
293 | }
294 | }
295 |
296 | request.HTTPBody = data;
297 | }
298 |
299 | AFHTTPRequestOperation *requestOperation = [self HTTPRequestOperationWithRequest:request success:^(__unused AFHTTPRequestOperation *operation, id responseObject) {
300 | if (success) {
301 | success(responseObject);
302 | }
303 | } failure:^(__unused AFHTTPRequestOperation *operation, NSError *error) {
304 | if (failure) {
305 | failure(error);
306 | }
307 | }];
308 |
309 | [requestOperation setUploadProgressBlock:progress];
310 |
311 | [self.operationQueue addOperation:requestOperation];
312 |
313 | return requestOperation;
314 | }
315 |
316 | - (AFHTTPRequestOperation *)deleteObjectWithPath:(NSString *)path
317 | success:(void (^)(id responseObject))success
318 | failure:(void (^)(NSError *error))failure
319 | {
320 | NSParameterAssert(path);
321 |
322 | path = AFPathByEscapingSpacesWithPlusSigns(path);
323 |
324 | return [self enqueueS3RequestOperationWithMethod:@"DELETE" path:path parameters:nil success:success failure:failure];
325 | }
326 |
327 | #pragma mark - NSKeyValueObserving
328 |
329 | + (NSSet *)keyPathsForValuesAffectingBaseURL {
330 | return [NSSet setWithObjects:@"baseURL", @"requestSerializer.bucket", @"requestSerializer.region", @"requestSerializer.useSSL", nil];
331 | }
332 |
333 | #pragma mark - NSCopying
334 |
335 | - (id)copyWithZone:(NSZone *)zone {
336 | AFAmazonS3Manager *manager = [[[self class] allocWithZone:zone] initWithBaseURL:_s3_baseURL];
337 |
338 | manager.requestSerializer = [self.requestSerializer copyWithZone:zone];
339 | manager.responseSerializer = [self.responseSerializer copyWithZone:zone];
340 |
341 | return manager;
342 | }
343 |
344 | @end
345 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3RequestSerializer.h:
--------------------------------------------------------------------------------
1 | // AFAmazonS3RequestSerializer.h
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFURLRequestSerialization.h"
24 |
25 | /**
26 | `AFAmazonS3RequestSerializer` is an `AFHTTPRequestSerializer` subclass with convenience methods for creating requests for the Amazon S3 webservice, including creating an authorization header and building an endpoint URL for a given bucket, region, and TLS preferences.
27 |
28 | @discussion Due to aggressive cache policies from the Foundation URL Loading System cause the unsupported `If-Modified-Since` header to be sent for certain requests, the default `cachePolicy` is `NSURLRequestReloadIgnoringCacheData`.
29 | */
30 | @interface AFAmazonS3RequestSerializer : AFHTTPRequestSerializer
31 |
32 | /**
33 | The S3 bucket for the client. `nil` by default.
34 |
35 | @see `AFAmazonS3Manager -baseURL`
36 | */
37 | @property (nonatomic, copy) NSString *bucket;
38 |
39 | /**
40 | The AWS region for the client. `AFAmazonS3USStandardRegion` by default. Must not be `nil`. See "AWS Regions" for defined constant values.
41 |
42 | @see `AFAmazonS3Manager -baseURL`
43 | */
44 | @property (nonatomic, copy) NSString *region;
45 |
46 | /**
47 | The AWS STS session token. `nil` by default.
48 | */
49 | @property (nonatomic, copy) NSString *sessionToken;
50 |
51 | /**
52 | Whether to connect over HTTPS. `YES` by default.
53 |
54 | @see `AFAmazonS3Manager -baseURL`
55 | */
56 | @property (nonatomic, assign) BOOL useSSL;
57 |
58 | /**
59 | A readonly endpoint URL created for the specified bucket, region, and TLS preference. `AFAmazonS3Manager` uses this as a `baseURL` unless one is manually specified.
60 | */
61 | @property (readonly, nonatomic, copy) NSURL *endpointURL;
62 |
63 | /**
64 | Sets the access key ID and secret, used to generate authorization headers.
65 |
66 | @param accessKey The Amazon S3 Access Key ID.
67 | @param secret The Amazon S3 Secret.
68 |
69 | @discussion These values can be found on the AWS control panel: http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key
70 | */
71 | - (void)setAccessKeyID:(NSString *)accessKey
72 | secret:(NSString *)secret;
73 |
74 | /**
75 | Returns a request with the necessary AWS authorization HTTP header fields from the specified request using the provided credentials.
76 |
77 | @param request The request.
78 | @param error The error that occured while constructing the request.
79 |
80 | @return The request with necessary `Authorization` and `Date` HTTP header fields.
81 | */
82 | - (NSURLRequest *)requestBySettingAuthorizationHeadersForRequest:(NSURLRequest *)request
83 | error:(NSError * __autoreleasing *)error;
84 |
85 | /**
86 | Returns a request with pre-signed credentials in the query string.
87 |
88 | @param request The request. `HTTPMethod` must be `GET`.
89 | @param expiration The request expiration. If `nil`, defaults to 1 hour from when method is called.
90 | @param error The error that occured while constructing the request.
91 |
92 | @return The request with credentials signed in query string.
93 | */
94 | - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request
95 | expiration:(NSDate *)expiration
96 | error:(NSError * __autoreleasing *)error;
97 |
98 | @end
99 |
100 | ///----------------
101 | /// @name Constants
102 | ///----------------
103 |
104 | /**
105 | ## AWS Regions
106 |
107 | The following AWS regions are defined:
108 |
109 | `AFAmazonS3USStandardRegion`: US Standard (s3.amazonaws.com);
110 | `AFAmazonS3USWest1Region`: US West (Oregon) Region (s3-us-west-1.amazonaws.com)
111 | `AFAmazonS3USWest2Region`: US West (Northern California) Region (s3-us-west-2.amazonaws.com)
112 | `AFAmazonS3EUWest1Region`: EU (Ireland) Region (s3-eu-west-1.amazonaws.com)
113 | `AFAmazonS3APSoutheast1Region`: Asia Pacific (Singapore) Region (s3-ap-southeast-1.amazonaws.com)
114 | `AFAmazonS3APSoutheast2Region`: Asia Pacific (Sydney) Region (s3-ap-southeast-2.amazonaws.com)
115 | `AFAmazonS3APNortheast2Region`: Asia Pacific (Tokyo) Region (s3-ap-northeast-1.amazonaws.com)
116 | `AFAmazonS3SAEast1Region`: South America (Sao Paulo) Region (s3-sa-east-1.amazonaws.com)
117 |
118 | For a full list of available regions, see http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
119 | */
120 | extern NSString * const AFAmazonS3USStandardRegion;
121 | extern NSString * const AFAmazonS3USWest1Region;
122 | extern NSString * const AFAmazonS3USWest2Region;
123 | extern NSString * const AFAmazonS3EUWest1Region;
124 | extern NSString * const AFAmazonS3EUCentral1Region;
125 | extern NSString * const AFAmazonS3APSoutheast1Region;
126 | extern NSString * const AFAmazonS3APSoutheast2Region;
127 | extern NSString * const AFAmazonS3APNortheast2Region;
128 | extern NSString * const AFAmazonS3SAEast1Region;
129 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3RequestSerializer.m:
--------------------------------------------------------------------------------
1 | // AFAmazonS3RequestSerializer.m
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFAmazonS3RequestSerializer.h"
24 | #import "AFAmazonS3Manager.h"
25 |
26 | #import
27 |
28 | NSString * const AFAmazonS3USStandardRegion = @"s3.amazonaws.com";
29 | NSString * const AFAmazonS3USWest1Region = @"s3-us-west-1.amazonaws.com";
30 | NSString * const AFAmazonS3USWest2Region = @"s3-us-west-2.amazonaws.com";
31 | NSString * const AFAmazonS3EUWest1Region = @"s3-eu-west-1.amazonaws.com";
32 | NSString * const AFAmazonS3EUCentral1Region = @"s3-eu-central-1.amazonaws.com";
33 | NSString * const AFAmazonS3APSoutheast1Region = @"s3-ap-southeast-1.amazonaws.com";
34 | NSString * const AFAmazonS3APSoutheast2Region = @"s3-ap-southeast-2.amazonaws.com";
35 | NSString * const AFAmazonS3APNortheast1Region = @"s3-ap-northeast-1.amazonaws.com";
36 | NSString * const AFAmazonS3SAEast1Region = @"s3-sa-east-1.amazonaws.com";
37 |
38 | static NSTimeInterval const AFAmazonS3DefaultExpirationTimeInterval = 60 * 60;
39 |
40 | static NSData * AFHMACSHA1EncodedDataFromStringWithKey(NSString *string, NSString *key) {
41 | NSData *data = [string dataUsingEncoding:NSASCIIStringEncoding];
42 | CCHmacContext context;
43 | const char *keyCString = [key cStringUsingEncoding:NSASCIIStringEncoding];
44 |
45 | CCHmacInit(&context, kCCHmacAlgSHA1, keyCString, strlen(keyCString));
46 | CCHmacUpdate(&context, [data bytes], [data length]);
47 |
48 | unsigned char digestRaw[CC_SHA1_DIGEST_LENGTH];
49 | NSUInteger digestLength = CC_SHA1_DIGEST_LENGTH;
50 |
51 | CCHmacFinal(&context, digestRaw);
52 |
53 | return [NSData dataWithBytes:digestRaw length:digestLength];
54 | }
55 |
56 | static NSString * AFRFC822FormatStringFromDate(NSDate *date) {
57 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
58 | [dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];
59 | [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss z"];
60 | [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]];
61 |
62 | return [dateFormatter stringFromDate:date];
63 | }
64 |
65 | static NSString * AFBase64EncodedStringFromData(NSData *data) {
66 | NSUInteger length = [data length];
67 | NSMutableData *mutableData = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
68 |
69 | uint8_t *input = (uint8_t *)[data bytes];
70 | uint8_t *output = (uint8_t *)[mutableData mutableBytes];
71 |
72 | for (NSUInteger i = 0; i < length; i += 3) {
73 | NSUInteger value = 0;
74 | for (NSUInteger j = i; j < (i + 3); j++) {
75 | value <<= 8;
76 | if (j < length) {
77 | value |= (0xFF & input[j]);
78 | }
79 | }
80 |
81 | static uint8_t const kAFBase64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
82 |
83 | NSUInteger idx = (i / 3) * 4;
84 | output[idx + 0] = kAFBase64EncodingTable[(value >> 18) & 0x3F];
85 | output[idx + 1] = kAFBase64EncodingTable[(value >> 12) & 0x3F];
86 | output[idx + 2] = (i + 1) < length ? kAFBase64EncodingTable[(value >> 6) & 0x3F] : '=';
87 | output[idx + 3] = (i + 2) < length ? kAFBase64EncodingTable[(value >> 0) & 0x3F] : '=';
88 | }
89 |
90 | return [[NSString alloc] initWithData:mutableData encoding:NSASCIIStringEncoding];
91 | }
92 |
93 | static NSString * AFAWSSignatureForRequest(NSURLRequest *request, NSString *bucket, NSString *timestamp, NSString *key) {
94 | NSMutableDictionary *mutableAMZHeaderFields = [NSMutableDictionary dictionary];
95 | [[request allHTTPHeaderFields] enumerateKeysAndObjectsUsingBlock:^(NSString *field, id value, __unused BOOL *stop) {
96 | field = [field lowercaseString];
97 | if ([field hasPrefix:@"x-amz"]) {
98 | if ([mutableAMZHeaderFields objectForKey:field]) {
99 | value = [[mutableAMZHeaderFields objectForKey:field] stringByAppendingFormat:@",%@", value];
100 | }
101 | [mutableAMZHeaderFields setObject:value forKey:field];
102 | }
103 | }];
104 |
105 | NSMutableString *mutableCanonicalizedAMZHeaderString = [NSMutableString string];
106 | for (NSString *field in [[mutableAMZHeaderFields allKeys] sortedArrayUsingSelector:@selector(compare:)]) {
107 | id value = [mutableAMZHeaderFields objectForKey:field];
108 | [mutableCanonicalizedAMZHeaderString appendFormat:@"%@:%@\n", field, value];
109 | }
110 |
111 | NSString *canonicalizedResource = bucket ? [NSString stringWithFormat:@"/%@%@", bucket, request.URL.path] : request.URL.path;
112 | NSString *method = [request HTTPMethod];
113 | NSString *contentMD5 = [request valueForHTTPHeaderField:@"Content-MD5"];
114 | NSString *contentType = [request valueForHTTPHeaderField:@"Content-Type"];
115 |
116 | NSMutableString *mutableString = [NSMutableString string];
117 | [mutableString appendFormat:@"%@\n", method ? method : @""];
118 | [mutableString appendFormat:@"%@\n", contentMD5 ? contentMD5 : @""];
119 | [mutableString appendFormat:@"%@\n", contentType ? contentType : @""];
120 | [mutableString appendFormat:@"%@\n", timestamp ? timestamp : @""];
121 | [mutableString appendFormat:@"%@", mutableCanonicalizedAMZHeaderString];
122 | [mutableString appendFormat:@"%@", canonicalizedResource];
123 |
124 | return AFBase64EncodedStringFromData(AFHMACSHA1EncodedDataFromStringWithKey(mutableString, key));
125 | }
126 |
127 | @interface AFAmazonS3RequestSerializer ()
128 | @property (readwrite, nonatomic, copy) NSString *accessKey;
129 | @property (readwrite, nonatomic, copy) NSString *secret;
130 | @end
131 |
132 | @implementation AFAmazonS3RequestSerializer
133 |
134 | - (instancetype)init {
135 | self = [super init];
136 | if (!self) {
137 | return nil;
138 | }
139 |
140 | self.cachePolicy = NSURLRequestReloadIgnoringCacheData;
141 |
142 | self.region = AFAmazonS3USStandardRegion;
143 | self.useSSL = YES;
144 |
145 | return self;
146 | }
147 |
148 | - (void)setAccessKeyID:(NSString *)accessKey
149 | secret:(NSString *)secret
150 | {
151 | NSParameterAssert(accessKey);
152 | NSParameterAssert(secret);
153 |
154 | self.accessKey = accessKey;
155 | self.secret = secret;
156 | }
157 |
158 | - (void)setRegion:(NSString *)region {
159 | NSParameterAssert(region);
160 | _region = region;
161 | }
162 |
163 | - (NSURL *)endpointURL {
164 | NSString *URLString = nil;
165 | NSString *scheme = self.useSSL ? @"https" : @"http";
166 | if (self.bucket) {
167 | URLString = [NSString stringWithFormat:@"%@://%@.%@", scheme, self.bucket, self.region];
168 | } else {
169 | URLString = [NSString stringWithFormat:@"%@://%@", scheme, self.region];
170 | }
171 |
172 | return [NSURL URLWithString:URLString];
173 | }
174 |
175 | #pragma mark -
176 |
177 | - (NSURLRequest *)requestBySettingAuthorizationHeadersForRequest:(NSURLRequest *)request
178 | error:(NSError * __autoreleasing *)error
179 | {
180 | NSMutableURLRequest *mutableRequest = [request mutableCopy];
181 |
182 | if (self.accessKey && self.secret) {
183 | NSDate *date = [NSDate date];
184 | NSString *signature = AFAWSSignatureForRequest(request, self.bucket, AFRFC822FormatStringFromDate(date), self.secret);
185 |
186 | [mutableRequest setValue:[NSString stringWithFormat:@"AWS %@:%@", self.accessKey, signature] forHTTPHeaderField:@"Authorization"];
187 | [mutableRequest setValue:date ? AFRFC822FormatStringFromDate(date) : @"" forHTTPHeaderField:@"Date"];
188 | } else {
189 | if (error) {
190 | NSDictionary *userInfo = @{
191 | NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Access Key and Secret Required", @"AFAmazonS3Manager", nil)
192 | };
193 |
194 | *error = [[NSError alloc] initWithDomain:AFAmazonS3ManagerErrorDomain code:NSURLErrorUserAuthenticationRequired userInfo:userInfo];
195 | }
196 | }
197 |
198 | return mutableRequest;
199 | }
200 |
201 | - (NSURLRequest *)preSignedRequestWithRequest:(NSURLRequest *)request
202 | expiration:(NSDate *)expiration
203 | error:(NSError * __autoreleasing *)error
204 | {
205 | NSParameterAssert(request);
206 | NSParameterAssert([request.HTTPMethod compare:@"GET" options:NSCaseInsensitiveSearch] == NSOrderedSame);
207 |
208 | if (!expiration) {
209 | expiration = [[NSDate date] dateByAddingTimeInterval:AFAmazonS3DefaultExpirationTimeInterval];
210 | }
211 |
212 | if (self.accessKey && self.secret) {
213 | NSString *expires = @((NSUInteger)[expiration timeIntervalSince1970]).stringValue;
214 | NSString *signature = AFAWSSignatureForRequest(request, self.bucket, expires, self.secret);
215 |
216 | NSDictionary *parameters = @{
217 | @"AWSAccessKeyId": self.accessKey,
218 | @"Expires": expires,
219 | @"Signature": signature
220 | };
221 |
222 | return [self requestBySerializingRequest:request withParameters:parameters error:error];
223 | } else {
224 | if (error) {
225 | NSDictionary *userInfo = @{
226 | NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"Access Key required", @"AFAmazonS3Manager", nil)
227 | };
228 |
229 | *error = [[NSError alloc] initWithDomain:AFAmazonS3ManagerErrorDomain code:NSURLErrorUserAuthenticationRequired userInfo:userInfo];
230 | }
231 |
232 | return nil;
233 | }
234 | }
235 |
236 | #pragma mark - AFHTTPRequestSerializer
237 |
238 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method
239 | URLString:(NSString *)URLString
240 | parameters:(NSDictionary *)parameters
241 | error:(NSError *__autoreleasing *)error
242 | {
243 | NSMutableURLRequest *request = [super requestWithMethod:method URLString:URLString parameters:parameters error:error];
244 |
245 | if (self.sessionToken) {
246 | [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"];
247 | }
248 |
249 | return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy];
250 | }
251 |
252 | - (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
253 | URLString:(NSString *)URLString
254 | parameters:(NSDictionary *)parameters
255 | constructingBodyWithBlock:(void (^)(id))block
256 | error:(NSError *__autoreleasing *)error
257 | {
258 | NSMutableURLRequest *request = [super multipartFormRequestWithMethod:method URLString:URLString parameters:parameters constructingBodyWithBlock:block error:error];
259 |
260 | if (self.sessionToken) {
261 | [request setValue:self.sessionToken forHTTPHeaderField:@"x-amz-security-token"];
262 | }
263 |
264 | return [[self requestBySettingAuthorizationHeadersForRequest:request error:error] mutableCopy];
265 | }
266 |
267 | @end
268 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3ResponseSerializer.h:
--------------------------------------------------------------------------------
1 | // AFAmazonS3ResponseSerializer.h
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFURLResponseSerialization.h"
24 |
25 | /**
26 | Returns an `AFAmazonS3ResponseObject` object from the AmazonS3 HTTP response
27 | */
28 | @interface AFAmazonS3ResponseSerializer : AFHTTPResponseSerializer
29 |
30 | @end
31 |
32 | #pragma mark -
33 |
34 | /**
35 | Returned as the response object for S3 requests
36 | */
37 | @interface AFAmazonS3ResponseObject : NSObject
38 |
39 | /**
40 | Creates a new from the HTTP response S3 returns
41 |
42 | @param response AFAmazonS3ResponseObject
43 |
44 | @return Returns an initialized instance of AFAmazonS3ResponseObject
45 | */
46 | + (instancetype)responseObject:(NSHTTPURLResponse *)response;
47 |
48 | ///-------------------------
49 | /// @name Reading Attributes
50 | ///-------------------------
51 |
52 | /**
53 | The URL of the file sent to or retrieved from S3
54 | */
55 | @property (readonly, nonatomic, copy) NSURL *URL;
56 |
57 | /**
58 | Contains the MD5 hash S3 computed for the file in the request
59 | */
60 | @property (readonly, nonatomic, copy) NSString *ETag;
61 |
62 | /**
63 | The original NSHTTPURLResponse object returned by S3
64 | */
65 | @property (readonly, nonatomic, strong) NSHTTPURLResponse *originalResponse;
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/AFAmazonS3Manager/AFAmazonS3ResponseSerializer.m:
--------------------------------------------------------------------------------
1 | // AFAmazonS3ResponseSerializer.m
2 | //
3 | // Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | // THE SOFTWARE.
22 |
23 | #import "AFAmazonS3ResponseSerializer.h"
24 |
25 | @implementation AFAmazonS3ResponseSerializer
26 |
27 | - (id)responseObjectForResponse:(NSURLResponse *)response
28 | data:(NSData *)data
29 | error:(NSError * __autoreleasing *)error
30 | {
31 | if ([self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
32 | return [AFAmazonS3ResponseObject responseObject:(NSHTTPURLResponse *)response];
33 | }
34 |
35 | return nil;
36 | }
37 |
38 | @end
39 |
40 | #pragma mark -
41 |
42 | @interface AFAmazonS3ResponseObject ()
43 | @property (readwrite, nonatomic, strong) NSHTTPURLResponse *originalResponse;
44 | @end
45 |
46 | #pragma mark -
47 |
48 | @implementation AFAmazonS3ResponseObject
49 |
50 | + (instancetype)responseObject:(NSHTTPURLResponse *)response {
51 | AFAmazonS3ResponseObject *responseObject = [[AFAmazonS3ResponseObject alloc] init];
52 | responseObject.originalResponse = response;
53 |
54 | return responseObject;
55 | }
56 |
57 | #pragma mark -
58 |
59 | - (NSURL *)URL {
60 | return self.originalResponse.URL;
61 | }
62 |
63 | - (NSString *)ETag {
64 | NSString *ETag = self.originalResponse.allHeaderFields[@"ETag"];
65 |
66 | if ([ETag length] == 0) {
67 | return nil;
68 | }
69 |
70 | return [ETag stringByReplacingOccurrencesOfString:@"\"" withString:@""];
71 | }
72 |
73 | #pragma mark - NSObject
74 |
75 | - (NSString *)description {
76 | return [NSString stringWithFormat:@"<%@: %p, URL: %@, ETAG: %@, originalResponse: %@>", NSStringFromClass([self class]), self, [self.URL absoluteString], self.ETag, self.originalResponse];
77 | }
78 |
79 | @end
80 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011–2015 AFNetworking (http://afnetworking.com/)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AFAmazonS3Manager
2 |
3 | `AFAmazonS3Manager` is an `AFHTTPRequestOperationManager` subclass for interacting with the [Amazon S3 API](http://aws.amazon.com/s3/).
4 |
5 | ## Example Usage
6 |
7 | ```objective-c
8 | AFAmazonS3Manager *s3Manager = [[AFAmazonS3Manager alloc] initWithAccessKeyID:@"..." secret:@"..."];
9 | s3Manager.requestSerializer.region = AFAmazonS3USWest1Region;
10 | s3Manager.requestSerializer.bucket = @"my-bucket-name";
11 |
12 | NSString *destinationPath = @"/pathOnS3/to/file.txt";
13 |
14 | [s3Manager postObjectWithFile:@"/path/to/file.txt"
15 | destinationPath:destinationPath
16 | parameters:nil
17 | progress:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
18 | NSLog(@"%f%% Uploaded", (totalBytesWritten / (totalBytesExpectedToWrite * 1.0f) * 100));
19 | }
20 | success:^(AFAmazonS3ResponseObject *responseObject) {
21 | NSLog(@"Upload Complete: %@", responseObject.URL);
22 | }
23 | failure:^(NSError *error) {
24 | NSLog(@"Error: %@", error);
25 | }];
26 | ```
27 |
28 | ## Contact
29 |
30 | Mattt Thompson
31 |
32 | - http://github.com/mattt
33 | - http://twitter.com/mattt
34 | - m@mattt.me
35 |
36 | ## License
37 |
38 | AFAmazonS3Manager is available under the MIT license. See the LICENSE file for more info.
39 |
--------------------------------------------------------------------------------