Ben Copsey

Refactor to fix lots of bugs, especially locking issues

... ... @@ -50,6 +50,11 @@
- (void)buildPostBody
{
if (!postData && ! fileData) {
[super buildPostBody];
return;
}
NSMutableData *body = [[[NSMutableData alloc] init] autorelease];
// Set your own boundary string only if really obsessive. We don't bother to check if post data contains the boundary, since it's pretty unlikely that it does.
... ...
... ... @@ -10,6 +10,7 @@
// Portions are based on the ImageClient example from Apple:
// See: http://developer.apple.com/samplecode/ImageClient/listing37.html
#import <Cocoa/Cocoa.h>
@interface ASIHTTPRequest : NSOperation {
... ... @@ -93,18 +94,18 @@
int responseStatusCode;
//Size of the response
unsigned int contentLength;
unsigned long long contentLength;
//Size of the POST payload
unsigned int postLength;
unsigned long long postLength;
//The total amount of downloaded data
unsigned int totalBytesRead;
unsigned long long totalBytesRead;
//Last amount of data read (used for incrementing progress)
unsigned int lastBytesRead;
unsigned long long lastBytesRead;
//Last amount of data sent (used for incrementing progress)
unsigned int lastBytesSent;
unsigned long long lastBytesSent;
//Realm for authentication when credentials are required
NSString *authenticationRealm;
... ... @@ -112,6 +113,9 @@
//This lock will block the request until the delegate supplies authentication info
NSConditionLock *authenticationLock;
//This lock prevents the operation from being cancelled at an inopportune moment
NSLock *cancelledLock;
//Called on the delegate when the request completes successfully
SEL didFinishSelector;
... ... @@ -128,7 +132,7 @@
NSAutoreleasePool *pool;
// Will be YES when a HEAD request will handle the content-length before this request starts
BOOL useCachedContentLength;
BOOL shouldResetProgressIndicators;
// Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request
ASIHTTPRequest *mainRequest;
... ... @@ -177,9 +181,9 @@
// Called on main thread to update progress delegates
- (void)updateProgressIndicators;
- (void)resetUploadProgress:(NSNumber *)max;
- (void)resetUploadProgress:(unsigned long long)value;
- (void)updateUploadProgress;
- (void)resetDownloadProgress:(NSNumber *)max;
- (void)resetDownloadProgress:(unsigned long long)value;
- (void)updateDownloadProgress;
// Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete
... ... @@ -279,10 +283,10 @@
@property (assign) NSTimeInterval timeOutSeconds;
@property (retain) NSString *requestMethod;
@property (retain,setter=setPostBody:) NSData *postBody;
@property (assign) unsigned int contentLength;
@property (assign) unsigned int postLength;
@property (assign) BOOL useCachedContentLength;
@property (assign) unsigned long long contentLength;
@property (assign) unsigned long long postLength;
@property (assign) BOOL shouldResetProgressIndicators;
@property (retain) ASIHTTPRequest *mainRequest;
@property (assign) BOOL showAccurateProgress;
@property (assign,readonly) unsigned int totalBytesRead;
@property (assign,readonly) unsigned long long totalBytesRead;
@end
... ...
This diff is collapsed. Click to expand it.
... ... @@ -109,8 +109,9 @@
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setDownloadProgressDelegate:self];
[request start];
BOOL success = (progress == 1);
STAssertTrue(success,@"Failed to properly increment download progress");
STAssertTrue(success,@"Failed to properly increment download progress %f != 1.0",progress);
}
... ... @@ -121,8 +122,9 @@
[request setPostBody:[NSMutableData dataWithLength:1024*32]];
[request setUploadProgressDelegate:self];
[request start];
BOOL success = (progress == 1);
STAssertTrue(success,@"Failed to properly increment upload progress");
STAssertTrue(success,@"Failed to properly increment upload progress %f != 1.0",progress);
}
... ...
... ... @@ -6,7 +6,7 @@
// Copyright 2008 All-Seeing Interactive. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface ASINetworkQueue : NSOperationQueue {
... ... @@ -26,19 +26,19 @@
id uploadProgressDelegate;
// Total amount uploaded so far for all requests in this queue
unsigned int uploadProgressBytes;
unsigned long long uploadProgressBytes;
// Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit
unsigned int uploadProgressTotalBytes;
unsigned long long uploadProgressTotalBytes;
// Download progress indicator, probably an NSProgressIndicator or UIProgressView
id downloadProgressDelegate;
// Total amount downloaded so far for all requests in this queue
unsigned int downloadProgressBytes;
unsigned long long downloadProgressBytes;
// Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers
unsigned int downloadProgressTotalBytes;
unsigned long long downloadProgressTotalBytes;
// When YES, the queue will cancel all requests when a request fails. Default is YES
BOOL shouldCancelAllRequestsOnFailure;
... ... @@ -61,16 +61,19 @@
- (void)addHEADOperation:(NSOperation *)operation;
// Called at the start of a request to add on the size of this upload to the total
- (void)incrementUploadSizeBy:(int)bytes;
- (void)incrementUploadSizeBy:(unsigned long long)bytes;
// Called during a request when data is written to the upload stream to increment the progress indicator
- (void)incrementUploadProgressBy:(int)bytes;
- (void)incrementUploadProgressBy:(unsigned long long)bytes;
// Called at the start of a request to add on the size of this download to the total
- (void)incrementDownloadSizeBy:(int)bytes;
- (void)incrementDownloadSizeBy:(unsigned long long)bytes;
// Called during a request when data is received to increment the progress indicator
- (void)incrementDownloadProgressBy:(int)bytes;
- (void)incrementDownloadProgressBy:(unsigned long long)bytes;
// Called during a request when authorisation fails to cancel any progress so far
- (void)decrementUploadProgressBy:(unsigned long long)bytes;
// All ASINetworkQueues are paused when created so that total size can be calculated before the queue starts
// This method will start the queue
... ...
... ... @@ -34,6 +34,7 @@
showAccurateProgress = NO;
[self setMaxConcurrentOperationCount:4];
[self setSuspended:YES];
return self;
... ... @@ -59,9 +60,6 @@
uploadProgressTotalBytes = 0;
downloadProgressBytes = 0;
downloadProgressTotalBytes = 0;
[self setUploadProgressDelegate:nil];
[self setDownloadProgressDelegate:nil];
[self setDelegate:nil];
[super cancelAllOperations];
}
... ... @@ -103,6 +101,8 @@
if ([operation isKindOfClass:[ASIHTTPRequest class]]) {
ASIHTTPRequest *request = (ASIHTTPRequest *)operation;
[request setRequestMethod:@"HEAD"];
[request setQueuePriority:10];
[request setShowAccurateProgress:YES];
if (uploadProgressDelegate) {
[request setUploadProgressDelegate:self];
... ... @@ -133,12 +133,11 @@
//If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
if ([[request requestMethod] isEqualToString:@"GET"]) {
ASIHTTPRequest *HEADRequest = [[[ASIHTTPRequest alloc] initWithURL:[request url]] autorelease];
[HEADRequest setRequestMethod:@"HEAD"];
[HEADRequest setQueuePriority:10];
[HEADRequest setMainRequest:request];
[self addHEADOperation:HEADRequest];
[request setUseCachedContentLength:YES];
//Tell the request not to reset the progress indicator when it gets a content-length, as we will get the length from the HEAD request
[request setShouldResetProgressIndicators:NO];
[request addDependency:HEADRequest];
//If we want to track uploading for this request accurately, we need to add the size of the post content to the total
... ... @@ -150,6 +149,9 @@
[request setShowAccurateProgress:showAccurateProgress];
if (uploadProgressDelegate) {
//For uploads requests, we always work out the total upload size before the queue starts, so we tell the request not to reset the progress indicator when starting each request
[request setShouldResetProgressIndicators:NO];
[request setUploadProgressDelegate:self];
} else {
[request setUploadProgressDelegate:NULL];
... ... @@ -173,7 +175,7 @@
if (requestDidFailSelector) {
[delegate performSelector:requestDidFailSelector withObject:request];
}
if (shouldCancelAllRequestsOnFailure) {
if (shouldCancelAllRequestsOnFailure && requestsCount > 0) {
[self cancelAllOperations];
}
}
... ... @@ -191,7 +193,7 @@
}
}
- (void)incrementUploadSizeBy:(int)bytes
- (void)incrementUploadSizeBy:(unsigned long long)bytes
{
if (!uploadProgressDelegate) {
return;
... ... @@ -200,7 +202,19 @@
[self incrementUploadProgressBy:0];
}
- (void)incrementUploadProgressBy:(int)bytes
- (void)decrementUploadProgressBy:(unsigned long long)bytes
{
if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
return;
}
uploadProgressBytes -= bytes;
double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
[ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
}
- (void)incrementUploadProgressBy:(unsigned long long)bytes
{
if (!uploadProgressDelegate || uploadProgressTotalBytes == 0) {
return;
... ... @@ -209,24 +223,26 @@
double progress = (uploadProgressBytes*1.0)/(uploadProgressTotalBytes*1.0);
[ASIHTTPRequest setProgress:progress forProgressIndicator:uploadProgressDelegate];
}
- (void)incrementDownloadSizeBy:(int)bytes
- (void)incrementDownloadSizeBy:(unsigned long long)bytes
{
if (!downloadProgressDelegate) {
return;
}
downloadProgressTotalBytes += bytes;
[self incrementDownloadProgressBy:0];
NSLog(@"download size is now: %qu",downloadProgressTotalBytes);
}
- (void)incrementDownloadProgressBy:(int)bytes
- (void)incrementDownloadProgressBy:(unsigned long long)bytes
{
if (!downloadProgressDelegate || downloadProgressTotalBytes == 0) {
return;
}
downloadProgressBytes += bytes;
//NSLog(@"%hu/%hu",downloadProgressBytes,downloadProgressTotalBytes);
//NSLog(@"%qu/%qu",downloadProgressBytes,downloadProgressTotalBytes);
double progress = (downloadProgressBytes*1.0)/(downloadProgressTotalBytes*1.0);
[ASIHTTPRequest setProgress:progress forProgressIndicator:downloadProgressDelegate];
}
... ...
... ... @@ -201,8 +201,6 @@
- (void)requestFailedCancellingOthers:(ASIHTTPRequest *)request
{
BOOL success = (request == requestThatShouldFail);
STAssertTrue(success,@"Wrong request failed");
complete = YES;
}
... ...