Ben Copsey

More fixes for throttling

... ... @@ -21,7 +21,7 @@
#import "ASIInputStream.h"
// Automatically set on build
NSString *ASIHTTPRequestVersion = @"v1.2-33 2009-12-17";
NSString *ASIHTTPRequestVersion = @"v1.2-34 2009-12-17";
NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain";
... ... @@ -731,9 +731,9 @@ static BOOL isiPhoneOS2;
// Are we gzipping the request body?
if ([self compressedPostBodyFilePath] && [[NSFileManager defaultManager] fileExistsAtPath:[self compressedPostBodyFilePath]]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath]]];
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath] request:self]];
} else {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath]]];
[self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath] request:self]];
}
readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream]);
} else {
... ... @@ -741,9 +741,9 @@ static BOOL isiPhoneOS2;
// If we have a request body, we'll stream it from memory using our custom stream, so that we can measure bandwidth use and it can be bandwidth-throttled if nescessary
if ([self postBody] && [[self postBody] length] > 0) {
if ([self shouldCompressRequestBody] && [self compressedPostBody]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody]]];
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody] request:self]];
} else if ([self postBody]) {
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody]]];
[self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody] request:self]];
}
readStream = CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream]);
... ... @@ -917,7 +917,7 @@ static BOOL isiPhoneOS2;
[self performThrottling];
// See if we need to timeout
if (lastActivityTime && timeOutSeconds > 0 && [now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
if (readStreamIsScheduled && lastActivityTime && timeOutSeconds > 0 && [now timeIntervalSinceDate:lastActivityTime] > timeOutSeconds) {
// Prevent timeouts before 128KB* has been sent when the size of data to upload is greater than 128KB* (*32KB on iPhone 3.0 SDK)
// This is to workaround the fact that kCFStreamPropertyHTTPRequestBytesWrittenCount is the amount written to the buffer, not the amount actually sent
... ... @@ -2965,6 +2965,9 @@ static BOOL isiPhoneOS2;
#if DEBUG_THROTTLING
NSLog(@"Waking up request %@",self);
#endif
// Reset the timeout
[self setLastActivityTime:[NSDate date]];
CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt);
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
... ... @@ -2973,7 +2976,14 @@ static BOOL isiPhoneOS2;
}
}
[bandwidthThrottlingLock unlock];
}
} else if (!readStreamIsScheduled) {
// Reset the timeout
[self setLastActivityTime:[NSDate date]];
CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL};
CFReadStreamSetClient(readStream, kNetworkEvents, ReadStreamClientCallBack, &ctxt);
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
readStreamIsScheduled = YES;
}
}
+ (BOOL)isBandwidthThrottled
... ... @@ -3044,10 +3054,6 @@ static BOOL isiPhoneOS2;
+ (unsigned long)averageBandwidthUsedPerSecond
{
[bandwidthThrottlingLock lock];
if (!bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < 0) {
[self recordBandwidthUsage];
}
unsigned long amount = averageBandwidthUsedPerSecond;
[bandwidthThrottlingLock unlock];
return amount;
... ... @@ -3063,11 +3069,11 @@ static BOOL isiPhoneOS2;
}
// Are we performing bandwidth throttling?
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
if (isBandwidthThrottled || (!shouldThrottleBandwithForWWANOnly && (maxBandwidthPerSecond))) {
#else
#else
if (maxBandwidthPerSecond) {
#endif
#endif
// How much data can we still send or receive this second?
long long bytesRemaining = (long long)maxBandwidthPerSecond - (long long)bandwidthUsedInLastSecond;
... ... @@ -3082,6 +3088,32 @@ static BOOL isiPhoneOS2;
}
[bandwidthThrottlingLock unlock];
}
+ (unsigned long)maxUploadReadLength
{
[bandwidthThrottlingLock lock];
// We'll split our bandwidth allowance into 4 (which is the default for an ASINetworkQueue's max concurrent operations count) to give all running requests a fighting chance of reading data this cycle
long long toRead = maxBandwidthPerSecond/4;
if (maxBandwidthPerSecond > 0 && (bandwidthUsedInLastSecond + toRead > maxBandwidthPerSecond)) {
toRead = (long long)maxBandwidthPerSecond-(long long)bandwidthUsedInLastSecond;
if (toRead < 0) {
toRead = 0;
}
}
if (toRead == 0 || !bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) {
[throttleWakeUpTime release];
throttleWakeUpTime = [bandwidthMeasurementDate retain];
}
[bandwidthThrottlingLock unlock];
return (unsigned long)toRead;
}
#if TARGET_OS_IPHONE
+ (void)setShouldThrottleBandwidthForWWAN:(BOOL)throttle
... ... @@ -3145,31 +3177,6 @@ static BOOL isiPhoneOS2;
#endif
+ (unsigned long)maxUploadReadLength
{
[bandwidthThrottlingLock lock];
// We'll split our bandwidth allowance into 4 (which is the default for an ASINetworkQueue's max concurrent operations count) to give all running requests a fighting chance of reading data this cycle
unsigned long toRead = maxBandwidthPerSecond/4;
if (maxBandwidthPerSecond > 0 && (bandwidthUsedInLastSecond + toRead > maxBandwidthPerSecond)) {
toRead = maxBandwidthPerSecond-bandwidthUsedInLastSecond;
if (toRead < 0) {
toRead = 0;
}
}
if (toRead == 0 || !bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < -0) {
[throttleWakeUpTime release];
throttleWakeUpTime = [bandwidthMeasurementDate retain];
[self recordBandwidthUsage];
}
[bandwidthThrottlingLock unlock];
return toRead;
}
#pragma mark miscellany
+ (BOOL)isiPhoneOS2
... ...
... ... @@ -7,6 +7,27 @@
//
// ======
// Debug output configuration options
// ======
// When set to 1 ASIHTTPRequests will print information about what a request is doing
#define DEBUG_REQUEST_STATUS 0
// When set to 1, ASIFormDataRequests will print information about the request body to the console
#define DEBUG_FORM_DATA_REQUEST 0
// When set to 1, ASIHTTPRequests will print information about bandwidth throttling to the console
#define DEBUG_THROTTLING 0
// ======
// Reachability API (iPhone only)
// ======
/*
Turn off any features you don't need for a speed boost
*/
/*
ASIHTTPRequest uses Apple's Reachability class (http://developer.apple.com/iphone/library/samplecode/Reachability/) to turn bandwidth throttling on and off automatically when shouldThrottleBandwidthForWWAN is set to YES on iPhone OS
... ... @@ -25,16 +46,3 @@ This config option is not used for apps targeting Mac OS X
#define REACHABILITY_20_API 0
/*
Debug output configuration options
*/
// When set to 1 ASIHTTPRequests will print information about what a request is doing
#define DEBUG_REQUEST_STATUS 0
// When set to 1, ASIFormDataRequests will print information about the request body to the console
#define DEBUG_FORM_DATA_REQUEST 0
// When set to 1, ASIHTTPRequests will print information about bandwidth throttling to the console
#define DEBUG_THROTTLING 0
... ...
... ... @@ -8,15 +8,19 @@
#import <Foundation/Foundation.h>
@class ASIHTTPRequest;
// This is a wrapper for NSInputStream that pretends to be an NSInputStream itself
// Subclassing NSInputStream seems to be tricky, and may involve overriding undocumented methods, so we'll cheat instead.
// It is used by ASIHTTPRequest whenever we have a request body, and handles measuring and throttling the bandwidth used for uploading
@interface ASIInputStream : NSObject {
NSInputStream *stream;
ASIHTTPRequest *request;
}
+ (id)inputStreamWithFileAtPath:(NSString *)path;
+ (id)inputStreamWithData:(NSData *)data;
+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)request;
+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)request;
@property (retain) NSInputStream *stream;
@property (retain, nonatomic) NSInputStream *stream;
@property (assign, nonatomic) ASIHTTPRequest *request;
@end
... ...
... ... @@ -21,16 +21,18 @@ static NSLock *readLock = nil;
}
}
+ (id)inputStreamWithFileAtPath:(NSString *)path
+ (id)inputStreamWithFileAtPath:(NSString *)path request:(ASIHTTPRequest *)request
{
ASIInputStream *stream = [[[self alloc] init] autorelease];
[stream setRequest:request];
[stream setStream:[NSInputStream inputStreamWithFileAtPath:path]];
return stream;
}
+ (id)inputStreamWithData:(NSData *)data
+ (id)inputStreamWithData:(NSData *)data request:(ASIHTTPRequest *)request
{
ASIInputStream *stream = [[[self alloc] init] autorelease];
[stream setRequest:request];
[stream setStream:[NSInputStream inputStreamWithData:data]];
return stream;
}
... ... @@ -54,6 +56,7 @@ static NSLock *readLock = nil;
} else if (toRead == 0) {
toRead = 1;
}
[request performThrottling];
}
[ASIHTTPRequest incrementBandwidthUsedInLastSecond:toRead];
[readLock unlock];
... ... @@ -73,4 +76,5 @@ static NSLock *readLock = nil;
}
@synthesize stream;
@synthesize request;
@end
... ...
... ... @@ -192,7 +192,7 @@
[request setDidFinishSelector:@selector(topSecretFetchComplete:)];
[request setDelegate:self];
[request setUseKeychainPersistance:[keychainCheckbox state]];
[request startAsynchronous];
[request start];
}
... ...