Skip to content

Commit

Permalink
More fixes for throttling
Browse files Browse the repository at this point in the history
  • Loading branch information
pokeb committed Dec 17, 2009
1 parent 9c964b6 commit f8e6637
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 58 deletions.
85 changes: 46 additions & 39 deletions Classes/ASIHTTPRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -731,19 +731,19 @@ - (void)startRequest

// 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 {

// 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]);

Expand Down Expand Up @@ -917,7 +917,7 @@ - (void)checkRequestStatus
[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
Expand Down Expand Up @@ -2965,6 +2965,9 @@ - (void)performThrottling
#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);
Expand All @@ -2973,7 +2976,14 @@ - (void)performThrottling
}
}
[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
Expand Down Expand Up @@ -3044,10 +3054,6 @@ + (void)recordBandwidthUsage
+ (unsigned long)averageBandwidthUsedPerSecond
{
[bandwidthThrottlingLock lock];

if (!bandwidthMeasurementDate || [bandwidthMeasurementDate timeIntervalSinceNow] < 0) {
[self recordBandwidthUsage];
}
unsigned long amount = averageBandwidthUsedPerSecond;
[bandwidthThrottlingLock unlock];
return amount;
Expand All @@ -3063,11 +3069,11 @@ + (void)measureBandwidthUsage
}

// 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;

Expand All @@ -3082,6 +3088,32 @@ + (void)measureBandwidthUsage
}
[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
Expand Down Expand Up @@ -3145,31 +3177,6 @@ + (void)reachabilityChanged:(NSNotification *)note
#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
Expand Down
34 changes: 21 additions & 13 deletions Classes/ASIHTTPRequestConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

10 changes: 7 additions & 3 deletions Classes/ASIInputStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 6 additions & 2 deletions Classes/ASIInputStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ + (void)initialize
}
}

+ (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;
}
Expand All @@ -54,6 +56,7 @@ - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)len
} else if (toRead == 0) {
toRead = 1;
}
[request performThrottling];
}
[ASIHTTPRequest incrementBandwidthUsedInLastSecond:toRead];
[readLock unlock];
Expand All @@ -73,4 +76,5 @@ - (void)forwardInvocation:(NSInvocation *)anInvocation
}

@synthesize stream;
@synthesize request;
@end
2 changes: 1 addition & 1 deletion Mac Sample/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ - (IBAction)fetchTopSecretInformation:(id)sender
[request setDidFinishSelector:@selector(topSecretFetchComplete:)];
[request setDelegate:self];
[request setUseKeychainPersistance:[keychainCheckbox state]];
[request startAsynchronous];
[request start];

}

Expand Down

0 comments on commit f8e6637

Please sign in to comment.