Ben Copsey

Change error handling to use different error codes for each type of error

Error userInfo now uses the standard localizedDescription for the description of the error
Stream errors now embed the original error in userInfo dictionary
@@ -10,117 +10,132 @@ @@ -10,117 +10,132 @@
10 // Portions are based on the ImageClient example from Apple: 10 // Portions are based on the ImageClient example from Apple:
11 // See: http://developer.apple.com/samplecode/ImageClient/listing37.html 11 // See: http://developer.apple.com/samplecode/ImageClient/listing37.html
12 12
  13 +#import <Cocoa/Cocoa.h>
  14 +
  15 +
  16 +typedef enum _ASINetworkErrorType {
  17 + ASIConnectionFailureErrorType = 1,
  18 + ASIRequestTimedOutErrorType = 2,
  19 + ASIAuthenticationErrorType = 3,
  20 + ASIRequestCancelledErrorType = 4,
  21 + ASIUnableToCreateRequestErrorType = 5,
  22 + ASIInternalErrorWhileBuildingRequestType = 6,
  23 + ASIInternalErrorWhileApplyingCredentialsType = 7
  24 +
  25 +} ASINetworkErrorType;
  26 +
13 @interface ASIHTTPRequest : NSOperation { 27 @interface ASIHTTPRequest : NSOperation {
14 28
15 - //The url for this operation, should include GET params in the query string where appropriate 29 + // The url for this operation, should include GET params in the query string where appropriate
16 NSURL *url; 30 NSURL *url;
17 31
18 - //The delegate, you need to manage setting and talking to your delegate in your subclasses 32 + // The delegate, you need to manage setting and talking to your delegate in your subclasses
19 id delegate; 33 id delegate;
20 34
21 - //HTTP method to use (GET / POST / PUT / DELETE). Defaults to GET 35 + // HTTP method to use (GET / POST / PUT / DELETE). Defaults to GET
22 NSString *requestMethod; 36 NSString *requestMethod;
23 37
24 - //Request body 38 + // Request body
25 NSData *postBody; 39 NSData *postBody;
26 40
27 - //Dictionary for custom HTTP request headers 41 + // Dictionary for custom HTTP request headers
28 NSMutableDictionary *requestHeaders; 42 NSMutableDictionary *requestHeaders;
29 43
30 - //Will be populated with HTTP response headers from the server 44 + // Will be populated with HTTP response headers from the server
31 NSDictionary *responseHeaders; 45 NSDictionary *responseHeaders;
32 46
33 - //Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you 47 + // Can be used to manually insert cookie headers to a request, but it's more likely that sessionCookies will do this for you
34 NSMutableArray *requestCookies; 48 NSMutableArray *requestCookies;
35 49
36 - //Will be populated with Cookies 50 + // Will be populated with Cookies
37 NSArray *responseCookies; 51 NSArray *responseCookies;
38 52
39 - //If use cokie persistance is true, network requests will present valid cookies from previous requests 53 + // If use cokie persistance is true, network requests will present valid cookies from previous requests
40 BOOL useCookiePersistance; 54 BOOL useCookiePersistance;
41 55
42 - //If useKeychainPersistance is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented 56 + // If useKeychainPersistance is true, network requests will attempt to read credentials from the keychain, and will save them in the keychain when they are successfully presented
43 BOOL useKeychainPersistance; 57 BOOL useKeychainPersistance;
44 58
45 - //If useSessionPersistance is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called) 59 + // If useSessionPersistance is true, network requests will save credentials and reuse for the duration of the session (until clearSession is called)
46 BOOL useSessionPersistance; 60 BOOL useSessionPersistance;
47 61
48 - //When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location 62 + // When downloadDestinationPath is set, the result of this request will be downloaded to the file at this location
49 - //If downloadDestinationPath is not set, download data will be stored in memory 63 + // If downloadDestinationPath is not set, download data will be stored in memory
50 NSString *downloadDestinationPath; 64 NSString *downloadDestinationPath;
51 65
52 - //Used for writing data to a file when downloadDestinationPath is set 66 + // Used for writing data to a file when downloadDestinationPath is set
53 NSOutputStream *outputStream; 67 NSOutputStream *outputStream;
54 68
55 - //When the request fails or completes successfully, complete will be true 69 + // When the request fails or completes successfully, complete will be true
56 BOOL complete; 70 BOOL complete;
57 71
58 - //If an error occurs, error will contain an NSError 72 + // If an error occurs, error will contain an NSError
  73 + // If error code is = ASIConnectionFailureErrorType (1, Connection failure occurred) - inspect [[error userInfo] objectForKey:NSUnderlyingErrorKey] for more information
59 NSError *error; 74 NSError *error;
60 75
61 - //If an authentication error occurs, we give the delegate a chance to handle it, ignoreError will be set to true 76 + // If an authentication error occurs, we give the delegate a chance to handle it, ignoreError will be set to true
62 BOOL ignoreError; 77 BOOL ignoreError;
63 78
64 - //Username and password used for authentication 79 + // Username and password used for authentication
65 NSString *username; 80 NSString *username;
66 NSString *password; 81 NSString *password;
67 - 82 +
68 - //Domain used for NTLM authentication 83 + // Domain used for NTLM authentication
69 NSString *domain; 84 NSString *domain;
70 85
71 - //Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) 86 + // Delegate for displaying upload progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
72 id uploadProgressDelegate; 87 id uploadProgressDelegate;
73 88
74 - //Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself) 89 + // Delegate for displaying download progress (usually an NSProgressIndicator, but you can supply a different object and handle this yourself)
75 id downloadProgressDelegate; 90 id downloadProgressDelegate;
76 - 91 +
77 // Whether we've seen the headers of the response yet 92 // Whether we've seen the headers of the response yet
78 BOOL haveExaminedHeaders; 93 BOOL haveExaminedHeaders;
79 94
80 - //Data we receive will be stored here 95 + // Data we receive will be stored here
81 NSMutableData *receivedData; 96 NSMutableData *receivedData;
82 97
83 - //Used for sending and receiving data 98 + // Used for sending and receiving data
84 CFHTTPMessageRef request; 99 CFHTTPMessageRef request;
85 CFReadStreamRef readStream; 100 CFReadStreamRef readStream;
86 101
87 // Authentication currently being used for prompting and resuming 102 // Authentication currently being used for prompting and resuming
88 CFHTTPAuthenticationRef requestAuthentication; 103 CFHTTPAuthenticationRef requestAuthentication;
89 NSMutableDictionary *requestCredentials; 104 NSMutableDictionary *requestCredentials;
90 - 105 +
91 // HTTP status code, eg: 200 = OK, 404 = Not found etc 106 // HTTP status code, eg: 200 = OK, 404 = Not found etc
92 int responseStatusCode; 107 int responseStatusCode;
93 108
94 - //Size of the response 109 + // Size of the response
95 unsigned long long contentLength; 110 unsigned long long contentLength;
96 - 111 +
97 - //Size of the POST payload 112 + // Size of the POST payload
98 unsigned long long postLength; 113 unsigned long long postLength;
99 114
100 - //The total amount of downloaded data 115 + // The total amount of downloaded data
101 unsigned long long totalBytesRead; 116 unsigned long long totalBytesRead;
102 117
103 - //Last amount of data read (used for incrementing progress) 118 + // Last amount of data read (used for incrementing progress)
104 unsigned long long lastBytesRead; 119 unsigned long long lastBytesRead;
105 - //Last amount of data sent (used for incrementing progress) 120 + // Last amount of data sent (used for incrementing progress)
106 unsigned long long lastBytesSent; 121 unsigned long long lastBytesSent;
107 122
108 - //Realm for authentication when credentials are required 123 + // Realm for authentication when credentials are required
109 NSString *authenticationRealm; 124 NSString *authenticationRealm;
110 - 125 +
111 - //This lock will block the request until the delegate supplies authentication info 126 + // This lock will block the request until the delegate supplies authentication info
112 NSConditionLock *authenticationLock; 127 NSConditionLock *authenticationLock;
113 128
114 - //This lock prevents the operation from being cancelled at an inopportune moment 129 + // This lock prevents the operation from being cancelled at an inopportune moment
115 NSLock *cancelledLock; 130 NSLock *cancelledLock;
116 131
117 - //Called on the delegate when the request completes successfully 132 + // Called on the delegate when the request completes successfully
118 SEL didFinishSelector; 133 SEL didFinishSelector;
119 134
120 - //Called on the delegate when the request fails 135 + // Called on the delegate when the request fails
121 SEL didFailSelector; 136 SEL didFailSelector;
122 137
123 - //Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate 138 + // Used for recording when something last happened during the request, we will compare this value with the current date to time out requests when appropriate
124 NSDate *lastActivityTime; 139 NSDate *lastActivityTime;
125 140
126 // Number of seconds to wait before timing out - default is 10 141 // Number of seconds to wait before timing out - default is 10
@@ -141,10 +156,10 @@ @@ -141,10 +156,10 @@
141 // Also see the comments in ASINetworkQueue.h 156 // Also see the comments in ASINetworkQueue.h
142 BOOL showAccurateProgress; 157 BOOL showAccurateProgress;
143 158
144 - //Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO 159 + // Used to ensure the progress indicator is only incremented once when showAccurateProgress = NO
145 BOOL updatedProgress; 160 BOOL updatedProgress;
146 161
147 - //Prevents the body of the post being built more than once (largely for subclasses) 162 + // Prevents the body of the post being built more than once (largely for subclasses)
148 BOOL haveBuiltPostBody; 163 BOOL haveBuiltPostBody;
149 } 164 }
150 165
@@ -155,16 +170,14 @@ @@ -155,16 +170,14 @@
155 170
156 #pragma mark setup request 171 #pragma mark setup request
157 172
158 -//Add a custom header to the request 173 +// Add a custom header to the request
159 - (void)addRequestHeader:(NSString *)header value:(NSString *)value; 174 - (void)addRequestHeader:(NSString *)header value:(NSString *)value;
160 175
161 - (void)buildPostBody; 176 - (void)buildPostBody;
162 177
163 #pragma mark get information about this request 178 #pragma mark get information about this request
164 179
165 -- (BOOL)isFinished; //Same thing, for NSOperationQueues to read 180 +// Returns the contents of the result as an NSString (not appropriate for binary data - used receivedData instead)
166 -  
167 -// Returns the contents of the result as an NSString (not appropriate for binary data!)  
168 - (NSString *)dataString; 181 - (NSString *)dataString;
169 182
170 #pragma mark request logic 183 #pragma mark request logic
@@ -192,11 +205,11 @@ @@ -192,11 +205,11 @@
192 205
193 #pragma mark handling request complete / failure 206 #pragma mark handling request complete / failure
194 207
195 -// Called when a request completes successfully - defaults to: @selector(requestFinished:) 208 +// Called when a request completes successfully, lets the delegate now via didFinishSelector
196 - (void)requestFinished; 209 - (void)requestFinished;
197 210
198 -// Called when a request fails - defaults to: @selector(requestFailed:) 211 +// Called when a request fails, and lets the delegate now via didFailSelector
199 -- (void)failWithProblem:(NSString *)problem; 212 +- (void)failWithError:(NSError *)theError;
200 213
201 #pragma mark http authentication stuff 214 #pragma mark http authentication stuff
202 215
@@ -216,8 +229,6 @@ @@ -216,8 +229,6 @@
216 // Apply authentication information and resume the request after an authentication challenge 229 // Apply authentication information and resume the request after an authentication challenge
217 - (void)attemptToApplyCredentialsAndResume; 230 - (void)attemptToApplyCredentialsAndResume;
218 231
219 -// Customise or overidde this to have a generic error for authentication failure  
220 -- (NSError *)authenticationError;  
221 232
222 #pragma mark stream status handlers 233 #pragma mark stream status handlers
223 234
@@ -18,10 +18,7 @@ static CFStringRef ASIHTTPRequestRunMode = CFSTR("ASIHTTPRequest"); @@ -18,10 +18,7 @@ static CFStringRef ASIHTTPRequestRunMode = CFSTR("ASIHTTPRequest");
18 18
19 static NSString *NetworkRequestErrorDomain = @"com.Your-Company.Your-Product.NetworkError."; 19 static NSString *NetworkRequestErrorDomain = @"com.Your-Company.Your-Product.NetworkError.";
20 20
21 -static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted | 21 +static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred;
22 - kCFStreamEventHasBytesAvailable |  
23 - kCFStreamEventEndEncountered |  
24 - kCFStreamEventErrorOccurred;  
25 22
26 static CFHTTPAuthenticationRef sessionAuthentication = NULL; 23 static CFHTTPAuthenticationRef sessionAuthentication = NULL;
27 static NSMutableDictionary *sessionCredentials = nil; 24 static NSMutableDictionary *sessionCredentials = nil;
@@ -35,6 +32,11 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy @@ -35,6 +32,11 @@ static void ReadStreamClientCallBack(CFReadStreamRef readStream, CFStreamEventTy
35 // This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa 32 // This lock prevents the operation from being cancelled while it is trying to update the progress, and vice versa
36 static NSRecursiveLock *progressLock; 33 static NSRecursiveLock *progressLock;
37 34
  35 +static NSError *ASIRequestCancelledError;
  36 +static NSError *ASIRequestTimedOutError;
  37 +static NSError *ASIAuthenticationError;
  38 +static NSError *ASIUnableToCreateRequestError;
  39 +
38 @implementation ASIHTTPRequest 40 @implementation ASIHTTPRequest
39 41
40 42
@@ -44,6 +46,12 @@ static NSRecursiveLock *progressLock; @@ -44,6 +46,12 @@ static NSRecursiveLock *progressLock;
44 + (void)initialize 46 + (void)initialize
45 { 47 {
46 progressLock = [[NSRecursiveLock alloc] init]; 48 progressLock = [[NSRecursiveLock alloc] init];
  49 +
  50 + ASIRequestTimedOutError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestTimedOutErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request timed out",NSLocalizedDescriptionKey,nil]] retain];
  51 + ASIAuthenticationError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIAuthenticationErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication needed",NSLocalizedDescriptionKey,nil]] retain];
  52 + ASIRequestCancelledError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]] retain];
  53 + ASIUnableToCreateRequestError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIUnableToCreateRequestErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create request (bad url?)",NSLocalizedDescriptionKey,nil]] retain];
  54 +
47 } 55 }
48 56
49 - (id)initWithURL:(NSURL *)newURL 57 - (id)initWithURL:(NSURL *)newURL
@@ -147,7 +155,7 @@ static NSRecursiveLock *progressLock; @@ -147,7 +155,7 @@ static NSRecursiveLock *progressLock;
147 155
148 - (void)cancel 156 - (void)cancel
149 { 157 {
150 - [self failWithProblem:@"The request was cancelled"]; 158 + [self failWithError:ASIRequestCancelledError];
151 [self cancelLoad]; 159 [self cancelLoad];
152 complete = YES; 160 complete = YES;
153 [super cancel]; 161 [super cancel];
@@ -178,7 +186,7 @@ static NSRecursiveLock *progressLock; @@ -178,7 +186,7 @@ static NSRecursiveLock *progressLock;
178 // Create a new HTTP request. 186 // Create a new HTTP request.
179 request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)requestMethod, (CFURLRef)url, kCFHTTPVersion1_1); 187 request = CFHTTPMessageCreateRequest(kCFAllocatorDefault, (CFStringRef)requestMethod, (CFURLRef)url, kCFHTTPVersion1_1);
180 if (!request) { 188 if (!request) {
181 - [self failWithProblem:[NSString stringWithFormat:@"Unable to create request for: %@",url]]; 189 + [self failWithError:ASIUnableToCreateRequestError];
182 return; 190 return;
183 } 191 }
184 192
@@ -213,8 +221,8 @@ static NSRecursiveLock *progressLock; @@ -213,8 +221,8 @@ static NSRecursiveLock *progressLock;
213 [self addRequestHeader:@"Cookie" value:cookieHeader]; 221 [self addRequestHeader:@"Cookie" value:cookieHeader];
214 } 222 }
215 } 223 }
216 - 224 +
217 - 225 +
218 if (!haveBuiltPostBody) { 226 if (!haveBuiltPostBody) {
219 [self buildPostBody]; 227 [self buildPostBody];
220 } 228 }
@@ -225,13 +233,14 @@ static NSRecursiveLock *progressLock; @@ -225,13 +233,14 @@ static NSRecursiveLock *progressLock;
225 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]); 233 CFHTTPMessageSetHeaderFieldValue(request, (CFStringRef)header, (CFStringRef)[requestHeaders objectForKey:header]);
226 } 234 }
227 235
  236 +
228 // If this is a post request and we have data to send, add it to the request 237 // If this is a post request and we have data to send, add it to the request
229 if ([self postBody]) { 238 if ([self postBody]) {
230 CFHTTPMessageSetBody(request, (CFDataRef)postBody); 239 CFHTTPMessageSetBody(request, (CFDataRef)postBody);
231 } 240 }
232 - 241 +
233 [self loadRequest]; 242 [self loadRequest];
234 - 243 +
235 } 244 }
236 245
237 246
@@ -270,7 +279,7 @@ static NSRecursiveLock *progressLock; @@ -270,7 +279,7 @@ static NSRecursiveLock *progressLock;
270 readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,readStream); 279 readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,readStream);
271 if (!readStream) { 280 if (!readStream) {
272 [cancelledLock unlock]; 281 [cancelledLock unlock];
273 - [self failWithProblem:@"Unable to create read stream"]; 282 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create read stream",NSLocalizedDescriptionKey,nil]]];
274 return; 283 return;
275 } 284 }
276 285
@@ -280,7 +289,7 @@ static NSRecursiveLock *progressLock; @@ -280,7 +289,7 @@ static NSRecursiveLock *progressLock;
280 CFRelease(readStream); 289 CFRelease(readStream);
281 readStream = NULL; 290 readStream = NULL;
282 [cancelledLock unlock]; 291 [cancelledLock unlock];
283 - [self failWithProblem:@"Unable to setup read stream"]; 292 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to setup read stream",NSLocalizedDescriptionKey,nil]]];
284 return; 293 return;
285 } 294 }
286 295
@@ -294,7 +303,7 @@ static NSRecursiveLock *progressLock; @@ -294,7 +303,7 @@ static NSRecursiveLock *progressLock;
294 CFRelease(readStream); 303 CFRelease(readStream);
295 readStream = NULL; 304 readStream = NULL;
296 [cancelledLock unlock]; 305 [cancelledLock unlock];
297 - [self failWithProblem:@"Unable to start http connection"]; 306 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]];
298 return; 307 return;
299 } 308 }
300 [cancelledLock unlock]; 309 [cancelledLock unlock];
@@ -307,8 +316,8 @@ static NSRecursiveLock *progressLock; @@ -307,8 +316,8 @@ static NSRecursiveLock *progressLock;
307 } 316 }
308 [self resetUploadProgress:amount]; 317 [self resetUploadProgress:amount];
309 } 318 }
310 - 319 +
311 - 320 +
312 321
313 // Record when the request started, so we can timeout if nothing happens 322 // Record when the request started, so we can timeout if nothing happens
314 [self setLastActivityTime:[NSDate date]]; 323 [self setLastActivityTime:[NSDate date]];
@@ -325,7 +334,7 @@ static NSRecursiveLock *progressLock; @@ -325,7 +334,7 @@ static NSRecursiveLock *progressLock;
325 // See if we need to timeout 334 // See if we need to timeout
326 if (lastActivityTime && timeOutSeconds > 0) { 335 if (lastActivityTime && timeOutSeconds > 0) {
327 if ([now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) { 336 if ([now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
328 - [self failWithProblem:@"Request timed out"]; 337 + [self failWithError:ASIRequestTimedOutError];
329 [self cancelLoad]; 338 [self cancelLoad];
330 complete = YES; 339 complete = YES;
331 break; 340 break;
@@ -334,15 +343,12 @@ static NSRecursiveLock *progressLock; @@ -334,15 +343,12 @@ static NSRecursiveLock *progressLock;
334 343
335 // See if our NSOperationQueue told us to cancel 344 // See if our NSOperationQueue told us to cancel
336 if ([self isCancelled]) { 345 if ([self isCancelled]) {
337 - [self failWithProblem:@"The request was cancelled"];  
338 - [self cancelLoad];  
339 - complete = YES;  
340 break; 346 break;
341 } 347 }
342 348
343 [self updateProgressIndicators]; 349 [self updateProgressIndicators];
344 350
345 - //This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does. 351 + // This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does.
346 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES); 352 CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES);
347 [now release]; 353 [now release];
348 } 354 }
@@ -366,7 +372,7 @@ static NSRecursiveLock *progressLock; @@ -366,7 +372,7 @@ static NSRecursiveLock *progressLock;
366 if (receivedData) { 372 if (receivedData) {
367 [self setReceivedData:nil]; 373 [self setReceivedData:nil];
368 374
369 - //If we were downloading to a file, let's remove it 375 + // If we were downloading to a file, let's remove it
370 } else if (downloadDestinationPath) { 376 } else if (downloadDestinationPath) {
371 [outputStream close]; 377 [outputStream close];
372 [[NSFileManager defaultManager] removeFileAtPath:downloadDestinationPath handler:nil]; 378 [[NSFileManager defaultManager] removeFileAtPath:downloadDestinationPath handler:nil];
@@ -383,6 +389,7 @@ static NSRecursiveLock *progressLock; @@ -383,6 +389,7 @@ static NSRecursiveLock *progressLock;
383 389
384 - (void)updateProgressIndicators 390 - (void)updateProgressIndicators
385 { 391 {
  392 +
386 //Only update progress if this isn't a HEAD request used to preset the content-length 393 //Only update progress if this isn't a HEAD request used to preset the content-length
387 if (!mainRequest) { 394 if (!mainRequest) {
388 if (showAccurateProgress || (complete && !updatedProgress)) { 395 if (showAccurateProgress || (complete && !updatedProgress)) {
@@ -390,6 +397,7 @@ static NSRecursiveLock *progressLock; @@ -390,6 +397,7 @@ static NSRecursiveLock *progressLock;
390 [self updateDownloadProgress]; 397 [self updateDownloadProgress];
391 } 398 }
392 } 399 }
  400 +
393 } 401 }
394 402
395 403
@@ -460,7 +468,7 @@ static NSRecursiveLock *progressLock; @@ -460,7 +468,7 @@ static NSRecursiveLock *progressLock;
460 468
461 if (uploadProgressDelegate) { 469 if (uploadProgressDelegate) {
462 470
463 - //We're using a progress queue or compatible controller to handle progress 471 + // We're using a progress queue or compatible controller to handle progress
464 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) { 472 if ([uploadProgressDelegate respondsToSelector:@selector(incrementUploadProgressBy:)]) {
465 unsigned long long value = 0; 473 unsigned long long value = 0;
466 if (showAccurateProgress) { 474 if (showAccurateProgress) {
@@ -478,21 +486,21 @@ static NSRecursiveLock *progressLock; @@ -478,21 +486,21 @@ static NSRecursiveLock *progressLock;
478 [invocation setArgument:&value atIndex:2]; 486 [invocation setArgument:&value atIndex:2];
479 [invocation invoke]; 487 [invocation invoke];
480 488
481 - //We aren't using a queue, we should just set progress of the indicator 489 + // We aren't using a queue, we should just set progress of the indicator
482 } else { 490 } else {
483 [ASIHTTPRequest setProgress:(double)(byteCount/postLength) forProgressIndicator:uploadProgressDelegate]; 491 [ASIHTTPRequest setProgress:(double)(byteCount/postLength) forProgressIndicator:uploadProgressDelegate];
484 } 492 }
485 493
486 } 494 }
487 lastBytesSent = byteCount; 495 lastBytesSent = byteCount;
488 - 496 +
489 } 497 }
490 498
491 499
492 - (void)resetDownloadProgress:(unsigned long long)value 500 - (void)resetDownloadProgress:(unsigned long long)value
493 { 501 {
494 [progressLock lock]; 502 [progressLock lock];
495 - //We're using a progress queue or compatible controller to handle progress 503 + // We're using a progress queue or compatible controller to handle progress
496 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) { 504 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadSizeBy:)]) {
497 SEL selector = @selector(incrementDownloadSizeBy:); 505 SEL selector = @selector(incrementDownloadSizeBy:);
498 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 506 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
@@ -501,7 +509,7 @@ static NSRecursiveLock *progressLock; @@ -501,7 +509,7 @@ static NSRecursiveLock *progressLock;
501 [invocation setSelector:selector]; 509 [invocation setSelector:selector];
502 [invocation setArgument:&value atIndex:2]; 510 [invocation setArgument:&value atIndex:2];
503 [invocation invoke]; 511 [invocation invoke];
504 - 512 +
505 } else { 513 } else {
506 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate]; 514 [ASIHTTPRequest setProgress:0 forProgressIndicator:downloadProgressDelegate];
507 } 515 }
@@ -520,7 +528,8 @@ static NSRecursiveLock *progressLock; @@ -520,7 +528,8 @@ static NSRecursiveLock *progressLock;
520 } 528 }
521 529
522 if (downloadProgressDelegate) { 530 if (downloadProgressDelegate) {
523 - 531 +
  532 +
524 // We're using a progress queue or compatible controller to handle progress 533 // We're using a progress queue or compatible controller to handle progress
525 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) { 534 if ([downloadProgressDelegate respondsToSelector:@selector(incrementDownloadProgressBy:)]) {
526 535
@@ -534,7 +543,7 @@ static NSRecursiveLock *progressLock; @@ -534,7 +543,7 @@ static NSRecursiveLock *progressLock;
534 updatedProgress = YES; 543 updatedProgress = YES;
535 } 544 }
536 545
537 - 546 +
538 547
539 SEL selector = @selector(incrementDownloadProgressBy:); 548 SEL selector = @selector(incrementDownloadProgressBy:);
540 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector]; 549 NSMethodSignature *signature = [[downloadProgressDelegate class] instanceMethodSignatureForSelector:selector];
@@ -546,7 +555,7 @@ static NSRecursiveLock *progressLock; @@ -546,7 +555,7 @@ static NSRecursiveLock *progressLock;
546 555
547 [thePool release]; 556 [thePool release];
548 557
549 - // We aren't using a queue, we should just set progress of the indicator to 0 558 + // We aren't using a queue, we should just set progress of the indicator to 0
550 } else if (contentLength > 0) { 559 } else if (contentLength > 0) {
551 [ASIHTTPRequest setProgress:(double)(bytesReadSoFar/contentLength) forProgressIndicator:downloadProgressDelegate]; 560 [ASIHTTPRequest setProgress:(double)(bytesReadSoFar/contentLength) forProgressIndicator:downloadProgressDelegate];
552 } 561 }
@@ -554,7 +563,7 @@ static NSRecursiveLock *progressLock; @@ -554,7 +563,7 @@ static NSRecursiveLock *progressLock;
554 563
555 lastBytesRead = bytesReadSoFar; 564 lastBytesRead = bytesReadSoFar;
556 } 565 }
557 - 566 +
558 } 567 }
559 568
560 -(void)removeUploadProgressSoFar 569 -(void)removeUploadProgressSoFar
@@ -571,7 +580,7 @@ static NSRecursiveLock *progressLock; @@ -571,7 +580,7 @@ static NSRecursiveLock *progressLock;
571 [invocation setArgument:&value atIndex:2]; 580 [invocation setArgument:&value atIndex:2];
572 [invocation invoke]; 581 [invocation invoke];
573 582
574 - // We aren't using a queue, we should just set progress of the indicator to 0 583 + // We aren't using a queue, we should just set progress of the indicator to 0
575 } else { 584 } else {
576 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate]; 585 [ASIHTTPRequest setProgress:0 forProgressIndicator:uploadProgressDelegate];
577 } 586 }
@@ -598,7 +607,7 @@ static NSRecursiveLock *progressLock; @@ -598,7 +607,7 @@ static NSRecursiveLock *progressLock;
598 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]]; 607 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]];
599 608
600 609
601 - // Cocoa: NSProgressIndicator 610 + // Cocoa: NSProgressIndicator
602 } else if ([indicator respondsToSelector:@selector(setDoubleValue:)]) { 611 } else if ([indicator respondsToSelector:@selector(setDoubleValue:)]) {
603 selector = @selector(setDoubleValue:); 612 selector = @selector(setDoubleValue:);
604 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector]; 613 NSMethodSignature *signature = [[indicator class] instanceMethodSignatureForSelector:selector];
@@ -606,7 +615,7 @@ static NSRecursiveLock *progressLock; @@ -606,7 +615,7 @@ static NSRecursiveLock *progressLock;
606 [invocation setSelector:selector]; 615 [invocation setSelector:selector];
607 [invocation setArgument:&progress atIndex:2]; 616 [invocation setArgument:&progress atIndex:2];
608 617
609 - //If we're running in the main thread, update the progress straight away. Otherwise, it's not that urgent 618 + // If we're running in the main thread, update the progress straight away. Otherwise, it's not that urgent
610 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]]; 619 [invocation performSelectorOnMainThread:@selector(invokeWithTarget:) withObject:indicator waitUntilDone:[NSThread isMainThread]];
611 620
612 } 621 }
@@ -629,15 +638,12 @@ static NSRecursiveLock *progressLock; @@ -629,15 +638,12 @@ static NSRecursiveLock *progressLock;
629 638
630 // Subclasses can override this method to perform error handling in the same thread 639 // Subclasses can override this method to perform error handling in the same thread
631 // If not overidden, it will call the didFailSelector on the delegate (by default requestFailed:)` 640 // If not overidden, it will call the didFailSelector on the delegate (by default requestFailed:)`
632 -- (void)failWithProblem:(NSString *)problem 641 +- (void)failWithError:(NSError *)theError
633 { 642 {
634 complete = YES; 643 complete = YES;
635 if (!error) { 644 if (!error) {
636 - [self setError:[NSError errorWithDomain:NetworkRequestErrorDomain 645 +
637 - code:1 646 + [self setError:theError];
638 - userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"An error occurred",@"Title",  
639 - problem,@"Description",nil]]];  
640 - NSLog(problem);  
641 647
642 if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) { 648 if (didFailSelector && ![self isCancelled] && [delegate respondsToSelector:didFailSelector]) {
643 [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]]; 649 [delegate performSelectorOnMainThread:didFailSelector withObject:self waitUntilDone:[NSThread isMainThread]];
@@ -661,7 +667,7 @@ static NSRecursiveLock *progressLock; @@ -661,7 +667,7 @@ static NSRecursiveLock *progressLock;
661 667
662 // We won't reset the download progress delegate if we got an authentication challenge 668 // We won't reset the download progress delegate if we got an authentication challenge
663 if (!isAuthenticationChallenge) { 669 if (!isAuthenticationChallenge) {
664 - 670 +
665 // See if we got a Content-length header 671 // See if we got a Content-length header
666 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"]; 672 NSString *cLength = [responseHeaders valueForKey:@"Content-Length"];
667 if (cLength) { 673 if (cLength) {
@@ -677,7 +683,7 @@ static NSRecursiveLock *progressLock; @@ -677,7 +683,7 @@ static NSRecursiveLock *progressLock;
677 // Handle cookies 683 // Handle cookies
678 NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url]; 684 NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:responseHeaders forURL:url];
679 [self setResponseCookies:cookies]; 685 [self setResponseCookies:cookies];
680 - 686 +
681 if (useCookiePersistance) { 687 if (useCookiePersistance) {
682 688
683 // Store cookies in global persistent store 689 // Store cookies in global persistent store
@@ -718,6 +724,7 @@ static NSRecursiveLock *progressLock; @@ -718,6 +724,7 @@ static NSRecursiveLock *progressLock;
718 if (newCredentials && requestAuthentication && request) { 724 if (newCredentials && requestAuthentication && request) {
719 // Apply whatever credentials we've built up to the old request 725 // Apply whatever credentials we've built up to the old request
720 if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) { 726 if (CFHTTPMessageApplyCredentialDictionary(request, requestAuthentication, (CFMutableDictionaryRef)newCredentials, NULL)) {
  727 +
721 //If we have credentials and they're ok, let's save them to the keychain 728 //If we have credentials and they're ok, let's save them to the keychain
722 if (useKeychainPersistance) { 729 if (useKeychainPersistance) {
723 [self saveCredentialsToKeychain:newCredentials]; 730 [self saveCredentialsToKeychain:newCredentials];
@@ -737,7 +744,7 @@ static NSRecursiveLock *progressLock; @@ -737,7 +744,7 @@ static NSRecursiveLock *progressLock;
737 - (NSMutableDictionary *)findCredentials 744 - (NSMutableDictionary *)findCredentials
738 { 745 {
739 NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease]; 746 NSMutableDictionary *newCredentials = [[[NSMutableDictionary alloc] init] autorelease];
740 - 747 +
741 // Is an account domain needed? (used currently for NTLM only) 748 // Is an account domain needed? (used currently for NTLM only)
742 if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) { 749 if (CFHTTPAuthenticationRequiresAccountDomain(requestAuthentication)) {
743 [newCredentials setObject:domain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain]; 750 [newCredentials setObject:domain forKey:(NSString *)kCFHTTPAuthenticationAccountDomain];
@@ -750,17 +757,17 @@ static NSRecursiveLock *progressLock; @@ -750,17 +757,17 @@ static NSRecursiveLock *progressLock;
750 authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(requestAuthentication); 757 authenticationRealm = (NSString *)CFHTTPAuthenticationCopyRealm(requestAuthentication);
751 } 758 }
752 759
753 - //First, let's look at the url to see if the username and password were included 760 + // First, let's look at the url to see if the username and password were included
754 NSString *user = [url user]; 761 NSString *user = [url user];
755 NSString *pass = [url password]; 762 NSString *pass = [url password];
756 763
757 - //If the username and password weren't in the url, let's try to use the ones set in this object 764 + // If the username and password weren't in the url, let's try to use the ones set in this object
758 if ((!user || !pass) && username && password) { 765 if ((!user || !pass) && username && password) {
759 user = username; 766 user = username;
760 pass = password; 767 pass = password;
761 } 768 }
762 769
763 - //Ok, that didn't work, let's try the keychain 770 + // Ok, that didn't work, let's try the keychain
764 if ((!user || !pass) && useKeychainPersistance) { 771 if ((!user || !pass) && useKeychainPersistance) {
765 NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:443 protocol:[url scheme] realm:authenticationRealm]; 772 NSURLCredential *authenticationCredentials = [ASIHTTPRequest savedCredentialsForHost:[url host] port:443 protocol:[url scheme] realm:authenticationRealm];
766 if (authenticationCredentials) { 773 if (authenticationCredentials) {
@@ -770,7 +777,7 @@ static NSRecursiveLock *progressLock; @@ -770,7 +777,7 @@ static NSRecursiveLock *progressLock;
770 777
771 } 778 }
772 779
773 - //If we have a username and password, let's apply them to the request and continue 780 + // If we have a username and password, let's apply them to the request and continue
774 if (user && pass) { 781 if (user && pass) {
775 782
776 [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername]; 783 [newCredentials setObject:user forKey:(NSString *)kCFHTTPAuthenticationUsername];
@@ -789,16 +796,16 @@ static NSRecursiveLock *progressLock; @@ -789,16 +796,16 @@ static NSRecursiveLock *progressLock;
789 796
790 - (void)attemptToApplyCredentialsAndResume 797 - (void)attemptToApplyCredentialsAndResume
791 { 798 {
792 - 799 +
793 // Read authentication data 800 // Read authentication data
794 if (!requestAuthentication) { 801 if (!requestAuthentication) {
795 CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader); 802 CFHTTPMessageRef responseHeader = (CFHTTPMessageRef) CFReadStreamCopyProperty(readStream,kCFStreamPropertyHTTPResponseHeader);
796 requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader); 803 requestAuthentication = CFHTTPAuthenticationCreateFromResponse(NULL, responseHeader);
797 CFRelease(responseHeader); 804 CFRelease(responseHeader);
798 } 805 }
799 - 806 +
800 if (!requestAuthentication) { 807 if (!requestAuthentication) {
801 - [self failWithProblem:@"Failed to get authentication object from response headers"]; 808 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to get authentication object from response headers",NSLocalizedDescriptionKey,nil]]];
802 return; 809 return;
803 } 810 }
804 811
@@ -826,23 +833,23 @@ static NSRecursiveLock *progressLock; @@ -826,23 +833,23 @@ static NSRecursiveLock *progressLock;
826 return; 833 return;
827 } 834 }
828 } 835 }
829 - [self setError:[self authenticationError]]; 836 + [self setError:ASIAuthenticationError];
830 complete = YES; 837 complete = YES;
831 return; 838 return;
832 } 839 }
833 - 840 +
834 [self cancelLoad]; 841 [self cancelLoad];
835 842
836 if (requestCredentials) { 843 if (requestCredentials) {
837 if ([self applyCredentials:requestCredentials]) { 844 if ([self applyCredentials:requestCredentials]) {
838 [self loadRequest]; 845 [self loadRequest];
839 } else { 846 } else {
840 - [self failWithProblem:@"Failed to apply credentials to request"]; 847 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
841 } 848 }
842 - 849 +
843 - // Are a user name & password needed? 850 + // Are a user name & password needed?
844 } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) { 851 } else if (CFHTTPAuthenticationRequiresUserNameAndPassword(requestAuthentication)) {
845 - 852 +
846 NSMutableDictionary *newCredentials = [self findCredentials]; 853 NSMutableDictionary *newCredentials = [self findCredentials];
847 854
848 //If we have some credentials to use let's apply them to the request and continue 855 //If we have some credentials to use let's apply them to the request and continue
@@ -851,11 +858,11 @@ static NSRecursiveLock *progressLock; @@ -851,11 +858,11 @@ static NSRecursiveLock *progressLock;
851 if ([self applyCredentials:newCredentials]) { 858 if ([self applyCredentials:newCredentials]) {
852 [self loadRequest]; 859 [self loadRequest];
853 } else { 860 } else {
854 - [self failWithProblem:@"Failed to apply credentials to request"]; 861 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileApplyingCredentialsType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Failed to apply credentials to request",NSLocalizedDescriptionKey,nil]]];
855 } 862 }
856 return; 863 return;
857 } 864 }
858 - 865 +
859 // We've got no credentials, let's ask the delegate to sort this out 866 // We've got no credentials, let's ask the delegate to sort this out
860 ignoreError = YES; 867 ignoreError = YES;
861 if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) { 868 if ([delegate respondsToSelector:@selector(authorizationNeededForRequest:)]) {
@@ -867,23 +874,13 @@ static NSRecursiveLock *progressLock; @@ -867,23 +874,13 @@ static NSRecursiveLock *progressLock;
867 } 874 }
868 875
869 // The delegate isn't interested, we'll have to give up 876 // The delegate isn't interested, we'll have to give up
870 - [self setError:[self authenticationError]]; 877 + [self setError:ASIAuthenticationError];
871 complete = YES; 878 complete = YES;
872 return; 879 return;
873 } 880 }
874 881
875 } 882 }
876 883
877 -- (NSError *)authenticationError  
878 -{  
879 - return [NSError errorWithDomain:NetworkRequestErrorDomain  
880 - code:2  
881 - userInfo:[NSDictionary dictionaryWithObjectsAndKeys: @"Permission Denied",@"Title",  
882 - @"Your username and password were incorrect.",@"Description",nil]];  
883 -  
884 -}  
885 -  
886 -  
887 #pragma mark stream status handlers 884 #pragma mark stream status handlers
888 885
889 886
@@ -921,17 +918,17 @@ static NSRecursiveLock *progressLock; @@ -921,17 +918,17 @@ static NSRecursiveLock *progressLock;
921 918
922 UInt8 buffer[2048]; 919 UInt8 buffer[2048];
923 CFIndex bytesRead = CFReadStreamRead(readStream, buffer, sizeof(buffer)); 920 CFIndex bytesRead = CFReadStreamRead(readStream, buffer, sizeof(buffer));
924 - 921 +
925 922
926 // Less than zero is an error 923 // Less than zero is an error
927 if (bytesRead < 0) { 924 if (bytesRead < 0) {
928 [self handleStreamError]; 925 [self handleStreamError];
929 - 926 +
930 - // If zero bytes were read, wait for the EOF to come. 927 + // If zero bytes were read, wait for the EOF to come.
931 } else if (bytesRead) { 928 } else if (bytesRead) {
932 - 929 +
933 totalBytesRead += bytesRead; 930 totalBytesRead += bytesRead;
934 - 931 +
935 // Are we downloading to a file? 932 // Are we downloading to a file?
936 if (downloadDestinationPath) { 933 if (downloadDestinationPath) {
937 if (!outputStream) { 934 if (!outputStream) {
@@ -940,18 +937,20 @@ static NSRecursiveLock *progressLock; @@ -940,18 +937,20 @@ static NSRecursiveLock *progressLock;
940 } 937 }
941 [outputStream write:buffer maxLength:bytesRead]; 938 [outputStream write:buffer maxLength:bytesRead];
942 939
943 - //Otherwise, let's add the data to our in-memory store 940 + //Otherwise, let's add the data to our in-memory store
944 } else { 941 } else {
945 [receivedData appendBytes:buffer length:bytesRead]; 942 [receivedData appendBytes:buffer length:bytesRead];
946 } 943 }
947 } 944 }
948 - 945 +
949 - 946 +
950 } 947 }
951 948
952 949
953 - (void)handleStreamComplete 950 - (void)handleStreamComplete
954 { 951 {
  952 +
  953 +
955 //Try to read the headers (if this is a HEAD request handleBytesAvailable available may not be called) 954 //Try to read the headers (if this is a HEAD request handleBytesAvailable available may not be called)
956 if (!responseHeaders) { 955 if (!responseHeaders) {
957 if ([self readResponseHeadersReturningAuthenticationFailure]) { 956 if ([self readResponseHeadersReturningAuthenticationFailure]) {
@@ -962,7 +961,7 @@ static NSRecursiveLock *progressLock; @@ -962,7 +961,7 @@ static NSRecursiveLock *progressLock;
962 [progressLock lock]; 961 [progressLock lock];
963 complete = YES; 962 complete = YES;
964 [self updateProgressIndicators]; 963 [self updateProgressIndicators];
965 - 964 +
966 if (readStream) { 965 if (readStream) {
967 CFReadStreamClose(readStream); 966 CFReadStreamClose(readStream);
968 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL); 967 CFReadStreamSetClient(readStream, kCFStreamEventNone, NULL, NULL);
@@ -977,19 +976,20 @@ static NSRecursiveLock *progressLock; @@ -977,19 +976,20 @@ static NSRecursiveLock *progressLock;
977 } 976 }
978 [progressLock unlock]; 977 [progressLock unlock];
979 [self requestFinished]; 978 [self requestFinished];
980 - 979 +
981 } 980 }
982 981
983 982
984 - (void)handleStreamError 983 - (void)handleStreamError
985 { 984 {
986 complete = YES; 985 complete = YES;
987 - NSError *err = [(NSError *)CFReadStreamCopyError(readStream) autorelease]; 986 + NSError *underlyingError = [(NSError *)CFReadStreamCopyError(readStream) autorelease];
988 - 987 +
989 [self cancelLoad]; 988 [self cancelLoad];
990 989
991 if (!error) { // We may already have handled this error 990 if (!error) { // We may already have handled this error
992 - [self failWithProblem:[NSString stringWithFormat: @"An error occurred: %@",[err localizedDescription]]]; 991 +
  992 + [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIConnectionFailureErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"A connection failure occurred",NSLocalizedDescriptionKey,underlyingError,NSUnderlyingErrorKey,nil]]];
993 } 993 }
994 } 994 }
995 995
@@ -1017,12 +1017,12 @@ static NSRecursiveLock *progressLock; @@ -1017,12 +1017,12 @@ static NSRecursiveLock *progressLock;
1017 + (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm 1017 + (void)saveCredentials:(NSURLCredential *)credentials forHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
1018 { 1018 {
1019 NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host 1019 NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host
1020 - port:port 1020 + port:port
1021 - protocol:protocol 1021 + protocol:protocol
1022 - realm:realm 1022 + realm:realm
1023 - authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; 1023 + authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
1024 - 1024 +
1025 - 1025 +
1026 NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage]; 1026 NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage];
1027 [storage setDefaultCredential:credentials forProtectionSpace:protectionSpace]; 1027 [storage setDefaultCredential:credentials forProtectionSpace:protectionSpace];
1028 } 1028 }
@@ -1030,12 +1030,12 @@ static NSRecursiveLock *progressLock; @@ -1030,12 +1030,12 @@ static NSRecursiveLock *progressLock;
1030 + (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm 1030 + (NSURLCredential *)savedCredentialsForHost:(NSString *)host port:(int)port protocol:(NSString *)protocol realm:(NSString *)realm
1031 { 1031 {
1032 NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host 1032 NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:host
1033 - port:port 1033 + port:port
1034 - protocol:protocol 1034 + protocol:protocol
1035 - realm:realm 1035 + realm:realm
1036 - authenticationMethod:NSURLAuthenticationMethodDefault] autorelease]; 1036 + authenticationMethod:NSURLAuthenticationMethodDefault] autorelease];
1037 - 1037 +
1038 - 1038 +
1039 NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage]; 1039 NSURLCredentialStorage *storage = [NSURLCredentialStorage sharedCredentialStorage];
1040 return [storage defaultCredentialForProtectionSpace:protectionSpace]; 1040 return [storage defaultCredentialForProtectionSpace:protectionSpace];
1041 } 1041 }
@@ -1059,7 +1059,7 @@ static NSRecursiveLock *progressLock; @@ -1059,7 +1059,7 @@ static NSRecursiveLock *progressLock;
1059 { 1059 {
1060 return sessionCookies; 1060 return sessionCookies;
1061 } 1061 }
1062 - 1062 +
1063 + (void)setSessionCookies:(NSMutableArray *)newSessionCookies 1063 + (void)setSessionCookies:(NSMutableArray *)newSessionCookies
1064 { 1064 {
1065 // Remove existing cookies from the persistent store 1065 // Remove existing cookies from the persistent store
@@ -18,36 +18,40 @@ @@ -18,36 +18,40 @@
18 18
19 - (void)testBasicDownload 19 - (void)testBasicDownload
20 { 20 {
21 - //Grab data  
22 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease]; 21 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com"] autorelease];
23 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 22 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
24 [request start]; 23 [request start];
25 NSString *html = [request dataString]; 24 NSString *html = [request dataString];
26 STAssertNotNil(html,@"Basic synchronous request failed"); 25 STAssertNotNil(html,@"Basic synchronous request failed");
27 26
28 - //Check we're getting the correct response headers 27 + // Check we're getting the correct response headers
29 NSString *pingBackHeader = [[request responseHeaders] objectForKey:@"X-Pingback"]; 28 NSString *pingBackHeader = [[request responseHeaders] objectForKey:@"X-Pingback"];
30 BOOL success = [pingBackHeader isEqualToString:@"http://allseeing-i.com/Ping-Back"]; 29 BOOL success = [pingBackHeader isEqualToString:@"http://allseeing-i.com/Ping-Back"];
31 STAssertTrue(success,@"Failed to populate response headers"); 30 STAssertTrue(success,@"Failed to populate response headers");
32 31
33 - //Check we're getting back the correct status code 32 + // Check we're getting back the correct status code
34 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/a-page-that-does-not-exist"] autorelease]; 33 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/a-page-that-does-not-exist"] autorelease];
35 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 34 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
36 [request start]; 35 [request start];
37 success = ([request responseStatusCode] == 404); 36 success = ([request responseStatusCode] == 404);
38 STAssertTrue(success,@"Didn't get correct status code"); 37 STAssertTrue(success,@"Didn't get correct status code");
39 38
40 - //Check data 39 + // Check data is as expected
41 NSRange notFound = NSMakeRange(NSNotFound, 0); 40 NSRange notFound = NSMakeRange(NSNotFound, 0);
42 success = !NSEqualRanges([html rangeOfString:@"All-Seeing Interactive"],notFound); 41 success = !NSEqualRanges([html rangeOfString:@"All-Seeing Interactive"],notFound);
43 STAssertTrue(success,@"Failed to download the correct data"); 42 STAssertTrue(success,@"Failed to download the correct data");
44 43
45 - //Attempt to grab from bad url 44 + // Attempt to grab from bad url
46 url = [[[NSURL alloc] initWithString:@""] autorelease]; 45 url = [[[NSURL alloc] initWithString:@""] autorelease];
47 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 46 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
48 [request start]; 47 [request start];
49 - NSError *error = [request error]; 48 + success = [[request error] code] == ASIInternalErrorWhileBuildingRequestType;
50 - STAssertNotNil(error,@"Failed to generate an error for a bad host"); 49 + STAssertTrue(success,@"Failed to generate an error for a bad host");
  50 +
  51 + request = [[[ASIHTTPRequest alloc] initWithURL:nil] autorelease];
  52 + [request start];
  53 + success = [[request error] code] == ASIUnableToCreateRequestErrorType;
  54 + STAssertTrue(success,@"Failed to generate an error for a bad host");
51 } 55 }
52 56
53 - (void)testTimeOut 57 - (void)testTimeOut
@@ -57,9 +61,8 @@ @@ -57,9 +61,8 @@
57 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 61 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
58 [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout 62 [request setTimeOutSeconds:0.0001]; //It's pretty unlikely we will be able to grab the data this quickly, so the request should timeout
59 [request start]; 63 [request start];
60 - NSError *error = [request error]; 64 +
61 - STAssertNotNil(error,@"Request didn't timeout"); 65 + BOOL success = [[request error] code] == ASIRequestTimedOutErrorType;
62 - BOOL success = [[[error userInfo] valueForKey:@"Description"] isEqualToString:@"Request timed out"];  
63 STAssertTrue(success,@"Timeout didn't generate the correct error"); 66 STAssertTrue(success,@"Timeout didn't generate the correct error");
64 67
65 } 68 }
@@ -139,7 +142,7 @@ @@ -139,7 +142,7 @@
139 { 142 {
140 BOOL success; 143 BOOL success;
141 144
142 - //Set setting a cookie 145 + // Set setting a cookie
143 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/set_cookie"] autorelease]; 146 NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/set_cookie"] autorelease];
144 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 147 ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
145 [request setUseCookiePersistance:YES]; 148 [request setUseCookiePersistance:YES];
@@ -148,12 +151,12 @@ @@ -148,12 +151,12 @@
148 success = [html isEqualToString:@"I have set a cookie"]; 151 success = [html isEqualToString:@"I have set a cookie"];
149 STAssertTrue(success,@"Failed to set a cookie"); 152 STAssertTrue(success,@"Failed to set a cookie");
150 153
151 - //Test a cookie is stored in responseCookies 154 + // Test a cookie is stored in responseCookies
152 NSArray *cookies = [request responseCookies]; 155 NSArray *cookies = [request responseCookies];
153 STAssertNotNil(cookies,@"Failed to store cookie data in responseCookies"); 156 STAssertNotNil(cookies,@"Failed to store cookie data in responseCookies");
154 157
155 158
156 - //Test the cookie contains the correct data 159 + // Test the cookie contains the correct data
157 NSHTTPCookie *cookie = nil; 160 NSHTTPCookie *cookie = nil;
158 BOOL foundCookie = NO; 161 BOOL foundCookie = NO;
159 for (cookie in cookies) { 162 for (cookie in cookies) {
@@ -174,7 +177,7 @@ @@ -174,7 +177,7 @@
174 return; 177 return;
175 } 178 }
176 179
177 - //Test a cookie is presented when manually added to the request 180 + // Test a cookie is presented when manually added to the request
178 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease]; 181 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease];
179 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 182 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
180 [request setUseCookiePersistance:NO]; 183 [request setUseCookiePersistance:NO];
@@ -184,7 +187,7 @@ @@ -184,7 +187,7 @@
184 success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"]; 187 success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"];
185 STAssertTrue(success,@"Cookie not presented to the server with cookie persistance OFF"); 188 STAssertTrue(success,@"Cookie not presented to the server with cookie persistance OFF");
186 189
187 - //Test a cookie is presented from the persistent store 190 + // Test a cookie is presented from the persistent store
188 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease]; 191 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease];
189 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 192 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
190 [request setUseCookiePersistance:YES]; 193 [request setUseCookiePersistance:YES];
@@ -193,7 +196,7 @@ @@ -193,7 +196,7 @@
193 success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"]; 196 success = [html isEqualToString:@"I have 'This is the value' as the value of 'ASIHTTPRequestTestCookie'"];
194 STAssertTrue(success,@"Cookie not presented to the server with cookie persistance ON"); 197 STAssertTrue(success,@"Cookie not presented to the server with cookie persistance ON");
195 198
196 - //Test removing a cookie 199 + // Test removing a cookie
197 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/remove_cookie"] autorelease]; 200 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/remove_cookie"] autorelease];
198 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 201 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
199 [request start]; 202 [request start];
@@ -201,7 +204,7 @@ @@ -201,7 +204,7 @@
201 success = [html isEqualToString:@"I have removed a cookie"]; 204 success = [html isEqualToString:@"I have removed a cookie"];
202 STAssertTrue(success,@"Failed to remove a cookie"); 205 STAssertTrue(success,@"Failed to remove a cookie");
203 206
204 - //Test making sure cookie was properly removed 207 + // Test making sure cookie was properly removed
205 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease]; 208 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease];
206 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 209 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
207 [request start]; 210 [request start];
@@ -209,7 +212,7 @@ @@ -209,7 +212,7 @@
209 success = [html isEqualToString:@"No cookie exists"]; 212 success = [html isEqualToString:@"No cookie exists"];
210 STAssertTrue(success,@"Cookie presented to the server when it should have been removed"); 213 STAssertTrue(success,@"Cookie presented to the server when it should have been removed");
211 214
212 - //Test setting a custom cookie works 215 + // Test setting a custom cookie works
213 NSDictionary *cookieProperties = [[[NSMutableDictionary alloc] init] autorelease]; 216 NSDictionary *cookieProperties = [[[NSMutableDictionary alloc] init] autorelease];
214 [cookieProperties setValue:@"Test Value" forKey:NSHTTPCookieValue]; 217 [cookieProperties setValue:@"Test Value" forKey:NSHTTPCookieValue];
215 [cookieProperties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName]; 218 [cookieProperties setValue:@"ASIHTTPRequestTestCookie" forKey:NSHTTPCookieName];
@@ -227,7 +230,7 @@ @@ -227,7 +230,7 @@
227 success = [html isEqualToString:@"I have 'Test Value' as the value of 'ASIHTTPRequestTestCookie'"]; 230 success = [html isEqualToString:@"I have 'Test Value' as the value of 'ASIHTTPRequestTestCookie'"];
228 STAssertTrue(success,@"Custom cookie not presented to the server with cookie persistance OFF"); 231 STAssertTrue(success,@"Custom cookie not presented to the server with cookie persistance OFF");
229 232
230 - //Test removing all cookies works 233 + // Test removing all cookies works
231 [ASIHTTPRequest clearSession]; 234 [ASIHTTPRequest clearSession];
232 235
233 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease]; 236 url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/asi-http-request/tests/read_cookie"] autorelease];
@@ -250,7 +253,8 @@ @@ -250,7 +253,8 @@
250 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 253 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
251 [request setUseKeychainPersistance:NO]; 254 [request setUseKeychainPersistance:NO];
252 [request start]; 255 [request start];
253 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 256 +
  257 + success = [[request error] code] == ASIAuthenticationErrorType;
254 STAssertTrue(success,@"Failed to generate permission denied error with no credentials"); 258 STAssertTrue(success,@"Failed to generate permission denied error with no credentials");
255 259
256 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 260 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -258,7 +262,7 @@ @@ -258,7 +262,7 @@
258 [request setUsername:@"wrong"]; 262 [request setUsername:@"wrong"];
259 [request setPassword:@"wrong"]; 263 [request setPassword:@"wrong"];
260 [request start]; 264 [request start];
261 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 265 + success = [[request error] code] == ASIAuthenticationErrorType;
262 STAssertTrue(success,@"Failed to generate permission denied error with wrong credentials"); 266 STAssertTrue(success,@"Failed to generate permission denied error with wrong credentials");
263 267
264 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 268 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -274,7 +278,7 @@ @@ -274,7 +278,7 @@
274 [request setUseSessionPersistance:NO]; 278 [request setUseSessionPersistance:NO];
275 [request setUseKeychainPersistance:NO]; 279 [request setUseKeychainPersistance:NO];
276 [request start]; 280 [request start];
277 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 281 + success = [[request error] code] == ASIAuthenticationErrorType;
278 STAssertTrue(success,@"Reused credentials when we shouldn't have"); 282 STAssertTrue(success,@"Reused credentials when we shouldn't have");
279 283
280 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 284 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -289,10 +293,10 @@ @@ -289,10 +293,10 @@
289 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 293 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
290 [request setUseKeychainPersistance:NO]; 294 [request setUseKeychainPersistance:NO];
291 [request start]; 295 [request start];
292 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 296 + success = [[request error] code] == ASIAuthenticationErrorType;
293 STAssertTrue(success,@"Failed to clear credentials"); 297 STAssertTrue(success,@"Failed to clear credentials");
294 298
295 - //This test may show a dialog! 299 + // This test may show a dialog!
296 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 300 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
297 [request setUseKeychainPersistance:YES]; 301 [request setUseKeychainPersistance:YES];
298 [request start]; 302 [request start];
@@ -314,7 +318,7 @@ @@ -314,7 +318,7 @@
314 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 318 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
315 [request setUseKeychainPersistance:NO]; 319 [request setUseKeychainPersistance:NO];
316 [request start]; 320 [request start];
317 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 321 + success = [[request error] code] == ASIAuthenticationErrorType;
318 STAssertTrue(success,@"Failed to generate permission denied error with no credentials"); 322 STAssertTrue(success,@"Failed to generate permission denied error with no credentials");
319 323
320 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 324 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -322,7 +326,7 @@ @@ -322,7 +326,7 @@
322 [request setUsername:@"wrong"]; 326 [request setUsername:@"wrong"];
323 [request setPassword:@"wrong"]; 327 [request setPassword:@"wrong"];
324 [request start]; 328 [request start];
325 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 329 + success = [[request error] code] == ASIAuthenticationErrorType;
326 STAssertTrue(success,@"Failed to generate permission denied error with wrong credentials"); 330 STAssertTrue(success,@"Failed to generate permission denied error with wrong credentials");
327 331
328 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 332 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -338,7 +342,7 @@ @@ -338,7 +342,7 @@
338 [request setUseSessionPersistance:NO]; 342 [request setUseSessionPersistance:NO];
339 [request setUseKeychainPersistance:NO]; 343 [request setUseKeychainPersistance:NO];
340 [request start]; 344 [request start];
341 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 345 + success = [[request error] code] == ASIAuthenticationErrorType;
342 STAssertTrue(success,@"Reused credentials when we shouldn't have"); 346 STAssertTrue(success,@"Reused credentials when we shouldn't have");
343 347
344 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 348 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
@@ -353,7 +357,7 @@ @@ -353,7 +357,7 @@
353 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease]; 357 request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
354 [request setUseKeychainPersistance:NO]; 358 [request setUseKeychainPersistance:NO];
355 [request start]; 359 [request start];
356 - success = ([[[[request error] userInfo] objectForKey:@"Description"] isEqualToString:@"Your username and password were incorrect."]); 360 + success = [[request error] code] == ASIAuthenticationErrorType;
357 STAssertTrue(success,@"Failed to clear credentials"); 361 STAssertTrue(success,@"Failed to clear credentials");
358 362
359 } 363 }
@@ -48,7 +48,6 @@ @@ -48,7 +48,6 @@
48 STAssertTrue(success,@"Failed to increment progress properly"); 48 STAssertTrue(success,@"Failed to increment progress properly");
49 49
50 //Now test again with accurate progress 50 //Now test again with accurate progress
51 -  
52 [networkQueue cancelAllOperations]; 51 [networkQueue cancelAllOperations];
53 [networkQueue setShowAccurateProgress:YES]; 52 [networkQueue setShowAccurateProgress:YES];
54 53