Statistics
| Branch: | Tag: | Revision:

root / Classes / OpenStackRequest.m @ c91b5b28

History | View | Annotate | Download (17.5 kB)

1
//
2
//  OpenStackRequest.m
3
//  OpenStack
4
//
5
//  Created by Mike Mayo on 10/8/10.
6
//  The OpenStack project is provided under the Apache 2.0 license.
7
//
8

    
9
#import "OpenStackRequest.h"
10
#import "Provider.h"
11
#import "OpenStackAccount.h"
12
#import "JSON.h"
13
#import "Container.h"
14
#import "StorageObject.h"
15
#import "Folder.h"
16
#import "AccountManager.h"
17
#import "APICallback.h"
18
#import "APILogEntry.h"
19
#import "NSString+Conveniences.h"
20

    
21
@implementation OpenStackRequest
22

    
23
@synthesize account, callback, errorAlerter;
24

    
25
#pragma mark - Constructors
26
#pragma mark Generic
27

    
28
+ (id)request:(OpenStackAccount *)account method:(NSString *)method url:(NSURL *)url {
29
	OpenStackRequest *request = [[[self alloc] initWithURL:url] autorelease];
30
    request.account = account;
31
	request.requestMethod = method;
32
	[request addRequestHeader:@"X-Auth-Token" value:account.authToken];
33
    [request addRequestHeader:@"Content-Type" value:@"application/json"];
34
    request.timeOutSeconds = 60;
35
    request.numberOfTimesToRetryOnTimeout = 5;
36
	return request;
37
}
38

    
39
+ (id)filesRequest:(OpenStackAccount *)account method:(NSString *)method path:(NSString *)path {
40
    NSString *urlString = [account.filesURL description];
41
    if (account.sharingAccount) {
42
        urlString = [NSString stringWithFormat:@"%@%@",
43
                     [urlString substringToIndex:[urlString rangeOfString:account.username].location],
44
                     account.sharingAccount];
45
    }
46
	NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@?format=json%@",
47
                                       urlString,
48
                                       path,
49
                                       (account.shared ? @"&shared=" : @"")]];
50
    return [self request:account method:method url:url];
51
}
52

    
53
#pragma mark User Catalog
54
+ (id)userCatalogRequest:(OpenStackAccount *)account displaynames:(NSArray *)displaynames UUIDs:(NSArray *)UUIDs {
55
    OpenStackRequest *request = [self request:account method:@"POST" url:account.provider.userCatalogURL];
56
    NSMutableString *dataString = [NSMutableString stringWithString:@"{\"displaynames\":["];
57
    if (displaynames) {
58
        for (NSUInteger index = 0 ; index < displaynames.count ; index++) {
59
            [dataString appendFormat:@"\"%@\"%@", [displaynames objectAtIndex:index], ((index == displaynames.count - 1) ? @"" : @",")];
60
        }
61
    }
62
    [dataString appendFormat:@"],\"uuids\":["];
63
    if (UUIDs) {
64
        for (NSUInteger index = 0 ; index < UUIDs.count ; index++) {
65
            [dataString appendFormat:@"\"%@\"%@", [UUIDs objectAtIndex:index], ((index == UUIDs.count - 1) ? @"" : @",")];
66
        }
67
    }
68
    [dataString appendFormat:@"]}"];
69
    [request appendPostData:[dataString dataUsingEncoding:NSUTF8StringEncoding]];
70
    return request;
71
}
72

    
73
- (NSDictionary *)catalogs {
74
    SBJSON *parser = [[[SBJSON alloc] init] autorelease];
75
    NSDictionary *catalogs = [parser objectWithString:[self responseString]];
76
    return catalogs;
77
}
78

    
79
- (NSDictionary *)displaynameCatalog {
80
    return [[self catalogs] objectForKey:@"displayname_catalog"];
81
}
82

    
83
- (NSDictionary *)UUIDCatalog {
84
    return [[self catalogs] objectForKey:@"uuid_catalog"];
85
}
86

    
87
#pragma mark Top
88

    
89
+ (id)authenticationRequest:(OpenStackAccount *)account {
90
	OpenStackRequest *request = [[[self alloc] initWithURL:account.provider.authEndpointURL] autorelease];
91
    request.account = account;
92
    request.requestMethod = @"GET";
93
    [request addRequestHeader:@"X-Auth-User" value:account.username];
94
    [request addRequestHeader:@"X-Auth-Token" value:(account.authToken ? account.authToken : @"")];
95
    request.timeOutSeconds = 60;
96
    request.numberOfTimesToRetryOnTimeout = 5;
97
	return request;
98
}
99

    
100
+ (id)getSharingAccountsRequest:(OpenStackAccount *)account {
101
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"%@?format=json", account.provider.authEndpointURL]];
102
    return [self request:account method:@"GET" url:url];
103
}
104

    
105
- (NSArray *)sharingAccounts {
106
    SBJSON *parser = [[[SBJSON alloc] init] autorelease];
107
    NSArray *sharingAccounts = [parser objectWithString:[self responseString]];
108
    return sharingAccounts;
109
}
110

    
111
#pragma mark Account
112

    
113
+ (id)getStorageAccountInfoRequest:(OpenStackAccount *)account {
114
    return [self filesRequest:account method:@"HEAD" path:@""];
115
}
116

    
117
+ (id)getContainersRequest:(OpenStackAccount *)account {
118
    return [self filesRequest:account method:@"GET" path:@""];
119
}
120

    
121
- (NSMutableDictionary *)containers {
122
    SBJSON *parser = [[[SBJSON alloc] init] autorelease];
123
    NSArray *jsonObjects = [parser objectWithString:[self responseString]];
124
    NSMutableDictionary *containers = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]];
125
    for (NSDictionary *dict in jsonObjects) {
126
        Container *container = [Container fromJSON:dict];
127
        [containers setObject:container forKey:container.name];
128
    }
129
    return containers;
130
}
131

    
132
+ (id)writeAccountMetadataRequest:(OpenStackAccount *)account withAccountInfo:(NSDictionary *)accountInfo {
133
    OpenStackRequest *request = [self filesRequest:account method:@"POST" path:@""];
134

    
135
    NSMutableDictionary *groups = [accountInfo objectForKey:@"groups"];
136
    for (NSString *groupName in groups) {
137
        NSString *group = [NSString encodeToPercentEscape:[groups objectForKey:groupName]];
138
        groupName = [NSString encodeToPercentEscape:groupName];
139
        [request.requestHeaders setObject:group forKey:[NSString stringWithFormat:@"X-Account-Group-%@", groupName]];
140
    }
141
    if ([groups count] == 0)
142
        [request.requestHeaders setObject:@"" forKey:@"X-Account-Group-group"];
143
    
144
    NSMutableDictionary *accountMetadata = [accountInfo objectForKey:@"metadata"];
145
    for (NSString *metadataKey in accountMetadata) {
146
        NSString *metadataValue = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
147
        metadataKey = [NSString encodeToPercentEscape:[accountMetadata objectForKey:metadataKey]];
148
        [request.requestHeaders setObject:metadataValue forKey:[NSString stringWithFormat:@"X-Account-Meta-%@",metadataKey]];
149
    }
150
    
151
    return request;
152
}
153

    
154
#pragma mark Container
155

    
156
+ (id)getContainerInfoRequest:(OpenStackAccount *)account container:(Container *)container {
157
    return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
158
}
159

    
160
+ (id)createContainerRequest:(OpenStackAccount *)account container:(Container *)container {
161
    return [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
162
}
163

    
164
+ (id)deleteContainerRequest:(OpenStackAccount *)account container:(Container *)container {
165
    return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
166
}
167

    
168
+ (id)getObjectsRequest:(OpenStackAccount *)account container:(Container *)container {
169
    return [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
170
}
171

    
172
- (NSMutableDictionary *)objects {
173
    SBJSON *parser = [[[SBJSON alloc] init] autorelease];
174
    NSArray *jsonObjects = [parser objectWithString:[self responseString]];
175
    NSMutableDictionary *objects = [NSMutableDictionary dictionaryWithCapacity:[jsonObjects count]];
176
    for (NSDictionary *dict in jsonObjects) {
177
        StorageObject *object = [StorageObject fromJSON:dict];
178
        [objects setObject:object forKey:object.name];
179
    }
180
    return objects;
181
}
182

    
183
+ (id)writeContainerPolicyRequest:(OpenStackAccount *)account container:(Container *)container {
184
    OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@", [NSString encodeToPercentEscape:container.name]]];
185
    [request.requestHeaders setObject:container.versioning forKey:@"X-Container-Policy-Versioning"];
186
    [request.requestHeaders setObject:[NSString stringWithFormat:@"%u", container.quota] forKey:@"X-Container-Policy-Quota"];
187
    return request;
188
}
189

    
190
#pragma mark Storage Object
191

    
192
+ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
193
    NSString *objectFullPath = object.fullPath;
194
    if ([objectFullPath hasPrefix:@"/"])
195
        objectFullPath = [objectFullPath substringFromIndex:1];
196
    return [self filesRequest:account method:@"HEAD" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
197
}
198

    
199
+ (id)getObjectInfoRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version {
200
    OpenStackRequest *request = [self getObjectInfoRequest:account container:container object:object];
201
    request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]];
202
    return request;
203
}
204

    
205
+ (id)getObjectVersionsRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
206
    OpenStackRequest *request = [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
207
    request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=list", request.url.description]];
208
    return request;
209
}
210

    
211
- (NSMutableArray *)versions {
212
    SBJSON *parser = [[[SBJSON alloc] init] autorelease];
213
    NSArray *jsonVersions = [[parser objectWithString:[self responseString]] objectForKey:@"versions"];
214
    NSMutableArray *versions = [NSMutableArray arrayWithCapacity:[jsonVersions count]];
215
    for (NSArray *jsonVersion in jsonVersions) {
216
        [versions addObject:[NSDictionary dictionaryWithObjectsAndKeys:
217
                             [jsonVersion objectAtIndex:0], @"versionID",
218
                             [NSDate dateWithTimeIntervalSince1970:[[jsonVersion objectAtIndex:1] doubleValue]], @"versionDate",
219
                             nil]];
220
    }
221
    return versions;
222
}
223

    
224

    
225
+ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
226
    return [self filesRequest:account method:@"GET" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:object.fullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
227
}
228

    
229
+ (id)getObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object version:(NSString *)version {
230
    OpenStackRequest *request = [self getObjectRequest:account container:container object:object];
231
    if (version)
232
        request.url = [NSURL URLWithString:[NSString stringWithFormat:@"%@&version=%@", request.url.description, version]];
233
    return request;
234
}
235

    
236
+ (id)writeObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
237
    NSString *objectFullPath = object.fullPath;
238
    if ([objectFullPath hasPrefix:@"/"])
239
        objectFullPath = [objectFullPath substringFromIndex:1];
240
    OpenStackRequest *request = [self filesRequest:account method:@"PUT" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
241

    
242
    if (object.sharing)
243
        [request.requestHeaders setObject:object.sharing forKey:@"X-Object-Sharing"];
244

    
245
    NSString *metadataKeyHeaderPrefix;
246
    if ([objectFullPath length] == 0)
247
        metadataKeyHeaderPrefix = @"X-Container-Meta-";
248
    else
249
        metadataKeyHeaderPrefix = @"X-Object-Meta-";
250
    for (NSString *metadataKey in object.metadata) {
251
        NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
252
        metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
253
        NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
254
        [request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
255
    }
256
    
257
	[request setPostBody:[NSMutableData dataWithData:object.data]];
258
    [request.requestHeaders setObject:object.contentType forKey:@"Content-Type"];
259
	return request;
260
}
261

    
262
+ (id)writeObjectMetadataRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
263
    NSString *objectFullPath = object.fullPath;
264
    if ([objectFullPath hasPrefix:@"/"])
265
        objectFullPath = [objectFullPath substringFromIndex:1];
266
    OpenStackRequest *request = [self filesRequest:account method:@"POST" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
267
    
268
    NSString *metadataKeyHeaderPrefix;
269
    if ([objectFullPath length] == 0)
270
        metadataKeyHeaderPrefix = @"X-Container-Meta-";
271
    else
272
        metadataKeyHeaderPrefix = @"X-Object-Meta-";
273
    for (NSString *metadataKey in object.metadata) {
274
        NSString *metadataKeyHeader = [NSString stringWithFormat:@"%@%@", metadataKeyHeaderPrefix, metadataKey];
275
        metadataKeyHeader = [NSString encodeToPercentEscape:metadataKeyHeader];
276
        NSString *metadataValue = [NSString encodeToPercentEscape:[object.metadata objectForKey:metadataKey]];
277
        [request.requestHeaders setObject:metadataValue forKey:metadataKeyHeader];
278
    }
279
    
280
    if (!account.sharingAccount) {
281
        [request.requestHeaders setObject:([object.publicURI length] ? @"true" : @"false") forKey:@"X-Object-Public"];
282
        if (object.sharing) {
283
            [request.requestHeaders setObject:[NSString encodeToPercentEscape:object.sharing] forKey:@"X-Object-Sharing"];
284
        }
285
    }
286
    return request;
287
}
288

    
289
+ (id)deleteObjectRequest:(OpenStackAccount *)account container:(Container *)container object:(StorageObject *)object {
290
    NSString *objectFullPath = object.fullPath;
291
    if ([objectFullPath hasPrefix:@"/"])
292
        objectFullPath = [objectFullPath substringFromIndex:1];
293
    return [self filesRequest:account method:@"DELETE" path:[NSString stringWithFormat:@"/%@/%@", [NSString encodeToPercentEscape:container.name], [NSString encodeToPercentEscape:objectFullPath charactersToEncode:@"!*'();:@&=+$,?%#[]"]]];
294
}
295

    
296
#pragma mark - ASIHTTPRequest Overrides
297

    
298
- (void)setCompletionBlock:(ASIBasicBlock)aCompletionBlock {
299
    [super setCompletionBlock:aCompletionBlock];
300
    [backupCompletionBlock release];
301
    backupCompletionBlock = [aCompletionBlock copy];
302
}
303

    
304
- (void)setFailedBlock:(ASIBasicBlock)aFailedBlock {
305
    [super setFailedBlock:aFailedBlock];
306
    [backupFailureBlock release];
307
    backupFailureBlock = [aFailedBlock copy];
308
}
309

    
310
- (void)failWithError:(NSError *)theError {
311
    if (responseStatusCode == 503) {
312
        NSNotification *notification = [NSNotification notificationWithName:@"serviceUnavailable" object:nil userInfo:nil];
313
        [[NSNotificationCenter defaultCenter] postNotification:notification];
314
    } else if (responseStatusCode == 0) {
315
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
316
        if (![defaults boolForKey:@"already_failed_on_connection"]) {
317
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Connection Error" message:@"Please check your connection or API URL and try again." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
318
            [alert show];
319
            [alert release];
320
        }
321
        [defaults setBool:YES forKey:@"already_failed_on_connection"];
322
        [defaults synchronize];
323
    }
324
    
325
    [super failWithError:theError];
326
}
327

    
328
#pragma mark - Actions
329

    
330
- (BOOL)isSuccess {
331
	return (200 <= [self responseStatusCode]) && ([self responseStatusCode] <= 299);
332
}
333

    
334
#pragma mark - Notifications
335

    
336
- (void)notify:(NSString *)name {
337
    NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
338
    NSNotification *notification = [NSNotification notificationWithName:name object:nil userInfo:callbackUserInfo];
339
    [[NSNotificationCenter defaultCenter] postNotification:notification];
340
}
341

    
342
- (void)notify {
343
    NSString *observeName = [NSString stringWithFormat:@"%@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description]];
344
    NSString *callbackName = [NSString stringWithFormat:@"%@ %@ %@ %@", [self isSuccess] ? @"SUCCESS" : @"FAILURE", self.requestMethod, [self.url description], self.callback.uuid];
345

    
346
    NSDictionary *callbackUserInfo = [NSDictionary dictionaryWithObject:self forKey:@"response"];
347

    
348
    NSNotification *observeNotification = [NSNotification notificationWithName:observeName object:nil userInfo:callbackUserInfo];
349
    [[NSNotificationCenter defaultCenter] postNotification:observeNotification];
350

    
351
    NSNotification *callbackNotification = [NSNotification notificationWithName:callbackName object:nil userInfo:callbackUserInfo];
352
    [[NSNotificationCenter defaultCenter] postNotification:callbackNotification];
353
    
354
}
355

    
356
#pragma mark - Memory Management
357

    
358
- (void)releaseBackupBlocksOnMainThread {
359
	NSMutableArray *blocks = [NSMutableArray array];
360
	if (backupCompletionBlock) {
361
		[blocks addObject:backupCompletionBlock];
362
		[backupCompletionBlock release];
363
		backupCompletionBlock = nil;
364
	}
365
	if (backupFailureBlock) {
366
		[blocks addObject:backupFailureBlock];
367
		[backupFailureBlock release];
368
		backupFailureBlock = nil;
369
	}
370
	[[self class] performSelectorOnMainThread:@selector(releaseBackupBlocks:) withObject:blocks waitUntilDone:[NSThread isMainThread]];
371
}
372

    
373
// Always called on main thread
374
+ (void)releaseBackupBlocks:(NSArray *)blocks {
375
	// Blocks will be released when this method exits
376
}
377

    
378
- (void)dealloc {
379
    [account release];
380
    [errorAlerter release];
381
    [self releaseBackupBlocksOnMainThread];
382
    [super dealloc];
383
}
384

    
385
@end