Skip to content

Commit

Permalink
More tweaks to progress tracking
Browse files Browse the repository at this point in the history
Run synchronous requests in custom runloop mode again
  • Loading branch information
pokeb committed Apr 15, 2010
1 parent bd94484 commit 3cbd8d6
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 85 deletions.
13 changes: 9 additions & 4 deletions Classes/ASIHTTPRequest.h
Expand Up @@ -266,7 +266,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
NSTimeInterval timeOutSeconds;

// Will be YES when a HEAD request will handle the content-length before this request starts
BOOL shouldResetProgressIndicators;
BOOL shouldResetUploadProgress;
BOOL shouldResetDownloadProgress;

// Used by HEAD requests when showAccurateProgress is YES to preset the content-length for this request
ASIHTTPRequest *mainRequest;
Expand Down Expand Up @@ -385,6 +386,9 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;

// An ID that uniquely identifies this request - primarily used for debugging persistent connections
NSNumber *requestID;

// Will be ASIHTTPRequestRunLoopMode for synchronous requests, NSDefaultRunLoopMode for all other requests
NSString *runLoopMode;
}

#pragma mark init / dealloc
Expand Down Expand Up @@ -466,10 +470,10 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
// Called when authorisation is needed, as we only find out we don't have permission to something when the upload is complete
- (void)removeUploadProgressSoFar;

// Called when we get a content-length header and shouldResetProgressIndicators is true
// Called when we get a content-length header and shouldResetDownloadProgress is true
- (void)incrementDownloadSizeBy:(long long)length;

// Called when a request starts and shouldResetProgressIndicators is true
// Called when a request starts and shouldResetUploadProgress is true
// Also called (with a negative length) to remove the size of the underlying buffer used for uploading
- (void)incrementUploadSizeBy:(long long)length;

Expand Down Expand Up @@ -723,7 +727,8 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount;
@property (retain) NSMutableData *postBody;
@property (assign,readonly) unsigned long long contentLength;
@property (assign) unsigned long long postLength;
@property (assign) BOOL shouldResetProgressIndicators;
@property (assign) BOOL shouldResetDownloadProgress;
@property (assign) BOOL shouldResetUploadProgress;
@property (assign) ASIHTTPRequest *mainRequest;
@property (assign) BOOL showAccurateProgress;
@property (assign,readonly) unsigned long long totalBytesRead;
Expand Down
75 changes: 44 additions & 31 deletions Classes/ASIHTTPRequest.m
Expand Up @@ -23,10 +23,12 @@


// Automatically set on build
NSString *ASIHTTPRequestVersion = @"v1.6.1-15 2010-04-14";
NSString *ASIHTTPRequestVersion = @"v1.6.1-16 2010-04-15";

NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";

static NSString *ASIHTTPRequestRunLoopMode = @"ASIHTTPRequestRunLoopMode";

static const CFOptionFlags kNetworkEvents = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventEndEncountered | kCFStreamEventErrorOccurred;

// In memory caches of credentials, used on when useSessionPersistence is YES
Expand Down Expand Up @@ -187,6 +189,7 @@ + (void)reachabilityChanged:(NSNotification *)note;
@property (retain, nonatomic) NSTimer *statusTimer;
@property (assign, nonatomic) BOOL downloadComplete;
@property (retain) NSNumber *requestID;
@property (assign, nonatomic) NSString *runLoopMode;
@end


Expand Down Expand Up @@ -225,12 +228,14 @@ - (id)initWithURL:(NSURL *)newURL
self = [self init];
[self setRequestMethod:@"GET"];

[self setRunLoopMode:NSDefaultRunLoopMode];
[self setShouldAttemptPersistentConnection:YES];
[self setPersistentConnectionTimeoutSeconds:60.0];
[self setShouldPresentCredentialsBeforeChallenge:YES];
[self setShouldRedirect:YES];
[self setShowAccurateProgress:YES];
[self setShouldResetProgressIndicators:YES];
[self setShouldResetDownloadProgress:YES];
[self setShouldResetUploadProgress:YES];
[self setAllowCompressedResponse:YES];
[self setDefaultResponseEncoding:NSISOLatin1StringEncoding];
[self setShouldPresentProxyAuthenticationDialog:YES];
Expand Down Expand Up @@ -498,6 +503,7 @@ - (void)startSynchronous
#if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
NSLog(@"Starting synchronous request %@",self);
#endif
[self setRunLoopMode:ASIHTTPRequestRunLoopMode];
[self setInProgress:YES];
@try {
if (![self isCancelled] && ![self complete]) {
Expand All @@ -516,7 +522,6 @@ - (void)start
{
#if TARGET_OS_IPHONE
[self performSelectorInBackground:@selector(startAsynchronous) withObject:nil];

#else

SInt32 versionMajor;
Expand Down Expand Up @@ -1015,7 +1020,7 @@ - (void)startRequest
// We've kept it open until now (when we've just opened a new stream) so that the new stream can make use of the old connection
// http://lists.apple.com/archives/Macnetworkprog/2006/Mar/msg00119.html
if (oldStream) {
CFReadStreamClose((CFReadStreamRef)oldStream);
[oldStream close];
[oldStream release];
oldStream = nil;
}
Expand All @@ -1031,14 +1036,16 @@ - (void)startRequest

[[self cancelledLock] unlock];

if (![self mainRequest] && [self shouldResetProgressIndicators]) {
if ([self showAccurateProgress]) {
[self incrementUploadSizeBy:[self postLength]];
} else {
[self incrementUploadSizeBy:1];
if (![self mainRequest]) {
if ([self shouldResetUploadProgress]) {
if ([self showAccurateProgress]) {
[self incrementUploadSizeBy:[self postLength]];
} else {
[self incrementUploadSizeBy:1];
}
[ASIHTTPRequest updateProgressIndicator:[self uploadProgressDelegate] withProgress:0 ofTotal:1];
}
[ASIHTTPRequest updateProgressIndicator:[self uploadProgressDelegate] withProgress:0 ofTotal:1];
if (![self partialDownloadSize]) {
if ([self shouldResetDownloadProgress] && ![self partialDownloadSize]) {
[ASIHTTPRequest updateProgressIndicator:[self downloadProgressDelegate] withProgress:0 ofTotal:1];
}
}
Expand All @@ -1048,12 +1055,13 @@ - (void)startRequest
[self setLastActivityTime:[NSDate date]];


[self setStatusTimer:[NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]];

[self setStatusTimer:[NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]];
[[NSRunLoop currentRunLoop] addTimer:[self statusTimer] forMode:[self runLoopMode]];

// If we're running asynchronously on the main thread, the runloop will already be running and we can return control
if (![NSThread isMainThread] || [self isSynchronous]) {
if (![NSThread isMainThread] || [self isSynchronous] || ![[self runLoopMode] isEqualToString:NSDefaultRunLoopMode]) {
while (!complete) {
CFRunLoopRun();
[[NSRunLoop currentRunLoop] runMode:[self runLoopMode] beforeDate:[NSDate distantFuture]];
}
}
}
Expand Down Expand Up @@ -1322,6 +1330,9 @@ - (void)updateDownloadProgress
value = 1;
[self setUpdatedProgress:YES];
}
if (!value) {
return;
}

[ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:[self queue] withObject:self amount:&value];
[ASIHTTPRequest performSelector:@selector(request:didReceiveBytes:) onTarget:[self downloadProgressDelegate] withObject:self amount:&value];
Expand Down Expand Up @@ -1358,6 +1369,10 @@ - (void)updateUploadProgress
[self setUpdatedProgress:YES];
}

if (!value) {
return;
}

[ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:[self queue] withObject:self amount:&value];
[ASIHTTPRequest performSelector:@selector(request:didSendBytes:) onTarget:[self uploadProgressDelegate] withObject:self amount:&value];
[ASIHTTPRequest updateProgressIndicator:[self uploadProgressDelegate] withProgress:[self totalBytesSent]-[self uploadBufferSize] ofTotal:[self postLength]];
Expand All @@ -1368,15 +1383,13 @@ - (void)incrementDownloadSizeBy:(long long)length
{
[ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:[self queue] withObject:self amount:&length];
[ASIHTTPRequest performSelector:@selector(request:incrementDownloadSizeBy:) onTarget:[self uploadProgressDelegate] withObject:self amount:&length];
[ASIHTTPRequest updateProgressIndicator:[self uploadProgressDelegate] withProgress:0 ofTotal:length];
}


- (void)incrementUploadSizeBy:(long long)length
{
[ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:[self queue] withObject:self amount:&length];
[ASIHTTPRequest performSelector:@selector(request:incrementUploadSizeBy:) onTarget:[self uploadProgressDelegate] withObject:self amount:&length];
[ASIHTTPRequest updateProgressIndicator:[self uploadProgressDelegate] withProgress:0 ofTotal:length];
}


Expand Down Expand Up @@ -1625,18 +1638,18 @@ - (void)readResponseHeaders
SInt32 length = CFStringGetIntValue((CFStringRef)cLength);

// Workaround for Apache HEAD requests for dynamically generated content returning the wrong Content-Length when using gzip
if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetProgressIndicators]) {
if ([self mainRequest] && [self allowCompressedResponse] && length == 20 && [self showAccurateProgress] && [self shouldResetDownloadProgress]) {
[[self mainRequest] setShowAccurateProgress:NO];
[[self mainRequest] incrementDownloadSizeBy:1];

} else {
[theRequest setContentLength:length];
if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) {
if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) {
[theRequest incrementDownloadSizeBy:[theRequest contentLength]+[theRequest partialDownloadSize]];
}
}

} else if ([self showAccurateProgress] && [self shouldResetProgressIndicators]) {
} else if ([self showAccurateProgress] && [self shouldResetDownloadProgress]) {
[theRequest setShowAccurateProgress:NO];
[theRequest incrementDownloadSizeBy:1];
}
Expand Down Expand Up @@ -2434,7 +2447,7 @@ - (void)handleBytesAvailable


UInt8 buffer[bufferSize];
CFIndex bytesRead = CFReadStreamRead((CFReadStreamRef)[self readStream], buffer, sizeof(buffer));
NSInteger bytesRead = [[self readStream] read:buffer maxLength:sizeof(buffer)];

// Less than zero is an error
if (bytesRead < 0) {
Expand Down Expand Up @@ -2663,12 +2676,10 @@ - (void)destroyReadStream
[connectionsLock lock];

if (![self connectionCanBeReused]) {
CFReadStreamUnscheduleFromRunLoop((CFReadStreamRef)[self readStream], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamClose((CFReadStreamRef)[self readStream]);
[[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
[[self readStream] close];
[self setReadStreamIsScheduled:NO];

}

[self setReadStream:nil];
[connectionsLock unlock];
}
Expand All @@ -2679,15 +2690,15 @@ - (void)scheduleReadStream
if ([self readStream] && ![self readStreamIsScheduled]) {
// Reset the timeout
[self setLastActivityTime:[NSDate date]];
CFReadStreamScheduleWithRunLoop((CFReadStreamRef)[self readStream], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
[[self readStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
[self setReadStreamIsScheduled:YES];
}
}

- (void)unscheduleReadStream
{
if ([self readStream] && [self readStreamIsScheduled]) {
CFReadStreamUnscheduleFromRunLoop((CFReadStreamRef)[self readStream], CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
[[self readStream] removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:[self runLoopMode]];
[self setReadStreamIsScheduled:NO];
}
}
Expand All @@ -2709,7 +2720,7 @@ + (void)expirePersistentConnections
#endif
NSInputStream *stream = [existingConnection objectForKey:@"stream"];
if (stream) {
CFReadStreamClose((CFReadStreamRef)stream);
[stream close];
}
[persistentConnectionsPool removeObject:existingConnection];
i--;
Expand Down Expand Up @@ -2755,7 +2766,8 @@ - (id)copyWithZone:(NSZone *)zone
[newRequest setDidFinishSelector:[self didFinishSelector]];
[newRequest setDidFailSelector:[self didFailSelector]];
[newRequest setTimeOutSeconds:[self timeOutSeconds]];
[newRequest setShouldResetProgressIndicators:[self shouldResetProgressIndicators]];
[newRequest setShouldResetDownloadProgress:[self shouldResetDownloadProgress]];
[newRequest setShouldResetUploadProgress:[self shouldResetUploadProgress]];
[newRequest setShowAccurateProgress:[self showAccurateProgress]];
[newRequest setDefaultResponseEncoding:[self defaultResponseEncoding]];
[newRequest setAllowResumeForFileDownloads:[self allowResumeForFileDownloads]];
Expand Down Expand Up @@ -3585,7 +3597,6 @@ + (void)registerForNetworkReachabilityNotifications
+ (void)unsubscribeFromNetworkReachabilityNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"kNetworkReachabilityChangedNotification" object:nil];

}

+ (BOOL)isNetworkReachableViaWWAN
Expand Down Expand Up @@ -3691,7 +3702,8 @@ + (NSString*)base64forData:(NSData*)theData {
@synthesize contentLength;
@synthesize partialDownloadSize;
@synthesize postLength;
@synthesize shouldResetProgressIndicators;
@synthesize shouldResetDownloadProgress;
@synthesize shouldResetUploadProgress;
@synthesize mainRequest;
@synthesize totalBytesRead;
@synthesize totalBytesSent;
Expand Down Expand Up @@ -3748,4 +3760,5 @@ + (NSString*)base64forData:(NSData*)theData {
@synthesize shouldUseRFC2616RedirectBehaviour;
@synthesize downloadComplete;
@synthesize requestID;
@synthesize runLoopMode;
@end
2 changes: 1 addition & 1 deletion Classes/ASINetworkQueue.h
Expand Up @@ -55,7 +55,7 @@
int requestsCount;

// When NO, this request will only update the progress indicator when it completes
// When YES, this request will update the progress indicator according to how much data it has recieved so far
// When YES, this request will update the progress indicator according to how much data it has received so far
// When YES, the queue will first perform HEAD requests for all GET requests in the queue, so it can calculate the total download size before it starts
// NO means better performance, because it skips this step for GET requests, and it won't waste time updating the progress indicator until a request completes
// Set to YES if the size of a requests in the queue varies greatly for much more accurate results
Expand Down
60 changes: 23 additions & 37 deletions Classes/ASINetworkQueue.m
Expand Up @@ -71,24 +71,12 @@ - (void)reset
[self setRequestDidFailSelector:NULL];
[self setRequestDidFinishSelector:NULL];
[self setQueueDidFinishSelector:NULL];
[self setTotalBytesToUpload:0];
[self setBytesUploadedSoFar:0];
[self setTotalBytesToDownload:0];
[self setBytesDownloadedSoFar:0];
[self setSuspended:YES];
}


- (void)go
{
if (![self showAccurateProgress]) {
if ([self downloadProgressDelegate]) {
[self setTotalBytesToDownload:[self requestsCount]];
}
if ([self uploadProgressDelegate]) {
[self setTotalBytesToUpload:[self requestsCount]];
}
}
[self setSuspended:NO];
}

Expand Down Expand Up @@ -172,35 +160,33 @@ - (void)addOperation:(NSOperation *)operation
// If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
// We'll only do this before the queue is started
// If requests are added after the queue is started they will probably move the overall progress backwards anyway, so there's no value performing the HEAD requests first
// Instead, they'll update the total progress if and when they recieve a content-length header
if ([[request requestMethod] isEqualToString:@"GET"] && [self isSuspended]) {
ASIHTTPRequest *HEADRequest = [request HEADRequest];
[self addHEADOperation:HEADRequest];

if ([request shouldResetProgressIndicators]) {
[self resetProgressDelegate:[request downloadProgressDelegate]];
}

//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
} else if (uploadProgressDelegate) {
[request buildPostBody];
[self setTotalBytesToUpload:[self totalBytesToUpload]+[request postLength]];


if ([request shouldResetProgressIndicators]) {
[self resetProgressDelegate:[request uploadProgressDelegate]];
// Instead, they'll update the total progress if and when they receive a content-length header
if ([[request requestMethod] isEqualToString:@"GET"]) {
if ([self isSuspended]) {
ASIHTTPRequest *HEADRequest = [request HEADRequest];
[self addHEADOperation:HEADRequest];
[request addDependency:HEADRequest];
if ([request shouldResetDownloadProgress]) {
[self resetProgressDelegate:[request downloadProgressDelegate]];
[request setShouldResetDownloadProgress:NO];
}
}

[request setShouldResetProgressIndicators:NO];
}
[request buildPostBody];
[self request:nil incrementUploadSizeBy:[request postLength]];


} else {
[self request:nil incrementDownloadSizeBy:1];
[self request:nil incrementUploadSizeBy:1];
}
// Tell the request not to increment the upload size when it starts, as we've already added its length
if ([request shouldResetUploadProgress]) {
[self resetProgressDelegate:[request uploadProgressDelegate]];
[request setShouldResetUploadProgress:NO];
}

[request setShowAccurateProgress:[self showAccurateProgress]];


[request setQueue:self];
[super addOperation:request];
Expand Down

0 comments on commit 3cbd8d6

Please sign in to comment.