Showing
2 changed files
with
95 additions
and
4 deletions
| @@ -31,8 +31,14 @@ typedef enum _ASINetworkErrorType { | @@ -31,8 +31,14 @@ typedef enum _ASINetworkErrorType { | ||
| 31 | 31 | ||
| 32 | } ASINetworkErrorType; | 32 | } ASINetworkErrorType; |
| 33 | 33 | ||
| 34 | +// The error domain that all errors generated by ASIHTTPRequest use | ||
| 34 | extern NSString* const NetworkRequestErrorDomain; | 35 | extern NSString* const NetworkRequestErrorDomain; |
| 35 | 36 | ||
| 37 | +// You can use this number to throttle upload and download bandwidth in iPhone OS apps send or receive a large amount of data | ||
| 38 | +// This may help apps that might otherwise be rejected for inclusion into the app store for using excessive bandwidth | ||
| 39 | +// This number is not official, as far as I know there is no officially documented bandwidth limit | ||
| 40 | +extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
| 41 | + | ||
| 36 | @interface ASIHTTPRequest : NSOperation { | 42 | @interface ASIHTTPRequest : NSOperation { |
| 37 | 43 | ||
| 38 | // The url for this operation, should include GET params in the query string where appropriate | 44 | // The url for this operation, should include GET params in the query string where appropriate |
| @@ -449,6 +455,15 @@ extern NSString* const NetworkRequestErrorDomain; | @@ -449,6 +455,15 @@ extern NSString* const NetworkRequestErrorDomain; | ||
| 449 | // Only works on Mac OS, will always return 'application/octet-stream' on iPhone | 455 | // Only works on Mac OS, will always return 'application/octet-stream' on iPhone |
| 450 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path; | 456 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path; |
| 451 | 457 | ||
| 458 | +#pragma mark bandwidth throttling | ||
| 459 | + | ||
| 460 | +// The maximum number of bytes ALL requests can send / receive in a second | ||
| 461 | +// This is a rough figure. The actual amount used may be slightly more | ||
| 462 | ++ (unsigned long)maxBandwidthPerSecond; | ||
| 463 | ++ (void)setMaxBandwidthPerSecond:(unsigned long)bytes; | ||
| 464 | + | ||
| 465 | + | ||
| 466 | + | ||
| 452 | @property (retain) NSString *username; | 467 | @property (retain) NSString *username; |
| 453 | @property (retain) NSString *password; | 468 | @property (retain) NSString *password; |
| 454 | @property (retain) NSString *domain; | 469 | @property (retain) NSString *domain; |
| @@ -48,12 +48,29 @@ static NSError *ASIAuthenticationError; | @@ -48,12 +48,29 @@ static NSError *ASIAuthenticationError; | ||
| 48 | static NSError *ASIUnableToCreateRequestError; | 48 | static NSError *ASIUnableToCreateRequestError; |
| 49 | static NSError *ASITooMuchRedirectionError; | 49 | static NSError *ASITooMuchRedirectionError; |
| 50 | 50 | ||
| 51 | +// Used for throttling bandwidth | ||
| 52 | +// I am assuming you don't have a connection capable of more than 4GB/second? If so, why are you reading this? Aren't you supposed to be backing up the Internet?! | ||
| 53 | +static unsigned long bandwidthUsedInLastSecond = 0; | ||
| 54 | + | ||
| 55 | +// A date one second in the future from the time it was created | ||
| 56 | +static NSDate *bandwidthThrottlingMeasurementDate = nil; | ||
| 57 | + | ||
| 58 | +// Since throttling variables are shared among all requests, we'll use a lock to mediate access | ||
| 59 | +static NSLock *bandwidthThrottlingLock = nil; | ||
| 60 | + | ||
| 61 | +// the maximum number of bytes that can be transmitted in one second | ||
| 62 | +static unsigned long maxBandwidthPerSecond = 0; | ||
| 63 | + | ||
| 64 | +// A default figure for throttling bandwidth on mobile devices | ||
| 65 | +unsigned long const ASIWWANBandwidthThrottleAmount = 14800; | ||
| 51 | 66 | ||
| 52 | // Private stuff | 67 | // Private stuff |
| 53 | @interface ASIHTTPRequest () | 68 | @interface ASIHTTPRequest () |
| 54 | 69 | ||
| 55 | - (BOOL)askDelegateForCredentials; | 70 | - (BOOL)askDelegateForCredentials; |
| 56 | - (BOOL)askDelegateForProxyCredentials; | 71 | - (BOOL)askDelegateForProxyCredentials; |
| 72 | ++ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes; | ||
| 73 | ++ (void)throttleBandwidth; | ||
| 57 | 74 | ||
| 58 | @property (assign) BOOL complete; | 75 | @property (assign) BOOL complete; |
| 59 | @property (retain) NSDictionary *responseHeaders; | 76 | @property (retain) NSDictionary *responseHeaders; |
| @@ -97,6 +114,7 @@ static NSError *ASITooMuchRedirectionError; | @@ -97,6 +114,7 @@ static NSError *ASITooMuchRedirectionError; | ||
| 97 | { | 114 | { |
| 98 | if (self == [ASIHTTPRequest class]) { | 115 | if (self == [ASIHTTPRequest class]) { |
| 99 | progressLock = [[NSRecursiveLock alloc] init]; | 116 | progressLock = [[NSRecursiveLock alloc] init]; |
| 117 | + bandwidthThrottlingLock = [[NSLock alloc] init]; | ||
| 100 | ASIRequestTimedOutError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestTimedOutErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request timed out",NSLocalizedDescriptionKey,nil]] retain]; | 118 | ASIRequestTimedOutError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestTimedOutErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request timed out",NSLocalizedDescriptionKey,nil]] retain]; |
| 101 | ASIAuthenticationError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIAuthenticationErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication needed",NSLocalizedDescriptionKey,nil]] retain]; | 119 | ASIAuthenticationError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIAuthenticationErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Authentication needed",NSLocalizedDescriptionKey,nil]] retain]; |
| 102 | ASIRequestCancelledError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]] retain]; | 120 | ASIRequestCancelledError = [[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIRequestCancelledErrorType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"The request was cancelled",NSLocalizedDescriptionKey,nil]] retain]; |
| @@ -618,7 +636,6 @@ static NSError *ASITooMuchRedirectionError; | @@ -618,7 +636,6 @@ static NSError *ASITooMuchRedirectionError; | ||
| 618 | [self startRequest]; | 636 | [self startRequest]; |
| 619 | 637 | ||
| 620 | 638 | ||
| 621 | - | ||
| 622 | // Wait for the request to finish | 639 | // Wait for the request to finish |
| 623 | while (!complete) { | 640 | while (!complete) { |
| 624 | 641 | ||
| @@ -665,6 +682,11 @@ static NSError *ASITooMuchRedirectionError; | @@ -665,6 +682,11 @@ static NSError *ASITooMuchRedirectionError; | ||
| 665 | 682 | ||
| 666 | // Find out if we've sent any more data than last time, and reset the timeout if so | 683 | // Find out if we've sent any more data than last time, and reset the timeout if so |
| 667 | if (totalBytesSent > lastBytesSent) { | 684 | if (totalBytesSent > lastBytesSent) { |
| 685 | + | ||
| 686 | + // For bandwidth throttling | ||
| 687 | + if ([ASIHTTPRequest maxBandwidthPerSecond] > 0) { | ||
| 688 | + [ASIHTTPRequest incrementBandwidthUsedInLastSecond:totalBytesSent-maxBandwidthPerSecond]; | ||
| 689 | + } | ||
| 668 | [self setLastActivityTime:[NSDate date]]; | 690 | [self setLastActivityTime:[NSDate date]]; |
| 669 | [self setLastBytesSent:totalBytesSent]; | 691 | [self setLastBytesSent:totalBytesSent]; |
| 670 | } | 692 | } |
| @@ -676,7 +698,8 @@ static NSError *ASITooMuchRedirectionError; | @@ -676,7 +698,8 @@ static NSError *ASITooMuchRedirectionError; | ||
| 676 | 698 | ||
| 677 | [self updateProgressIndicators]; | 699 | [self updateProgressIndicators]; |
| 678 | 700 | ||
| 679 | - | 701 | + // Throttle bandwidth if nescessary |
| 702 | + [ASIHTTPRequest throttleBandwidth]; | ||
| 680 | 703 | ||
| 681 | // This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does. | 704 | // This thread should wait for 1/4 second for the stream to do something. We'll stop early if it does. |
| 682 | CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES); | 705 | CFRunLoopRunInMode(ASIHTTPRequestRunMode,0.25,YES); |
| @@ -1630,6 +1653,11 @@ static NSError *ASITooMuchRedirectionError; | @@ -1630,6 +1653,11 @@ static NSError *ASITooMuchRedirectionError; | ||
| 1630 | [self setTotalBytesRead:[self totalBytesRead]+bytesRead]; | 1653 | [self setTotalBytesRead:[self totalBytesRead]+bytesRead]; |
| 1631 | [self setLastActivityTime:[NSDate date]]; | 1654 | [self setLastActivityTime:[NSDate date]]; |
| 1632 | 1655 | ||
| 1656 | + // For bandwidth throttling | ||
| 1657 | + if ([ASIHTTPRequest maxBandwidthPerSecond] > 0) { | ||
| 1658 | + [ASIHTTPRequest incrementBandwidthUsedInLastSecond:bytesRead]; | ||
| 1659 | + } | ||
| 1660 | + | ||
| 1633 | // Are we downloading to a file? | 1661 | // Are we downloading to a file? |
| 1634 | if ([self downloadDestinationPath]) { | 1662 | if ([self downloadDestinationPath]) { |
| 1635 | if (![self fileDownloadOutputStream]) { | 1663 | if (![self fileDownloadOutputStream]) { |
| @@ -2219,7 +2247,7 @@ static NSError *ASITooMuchRedirectionError; | @@ -2219,7 +2247,7 @@ static NSError *ASITooMuchRedirectionError; | ||
| 2219 | return proxies; | 2247 | return proxies; |
| 2220 | } | 2248 | } |
| 2221 | 2249 | ||
| 2222 | -#pragma mark Helper functions | 2250 | +#pragma mark mime-type detection |
| 2223 | 2251 | ||
| 2224 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path | 2252 | + (NSString *)mimeTypeForFileAtPath:(NSString *)path |
| 2225 | { | 2253 | { |
| @@ -2251,6 +2279,55 @@ static NSError *ASITooMuchRedirectionError; | @@ -2251,6 +2279,55 @@ static NSError *ASITooMuchRedirectionError; | ||
| 2251 | #endif | 2279 | #endif |
| 2252 | } | 2280 | } |
| 2253 | 2281 | ||
| 2282 | +#pragma mark bandwidth throttling | ||
| 2283 | + | ||
| 2284 | ++ (unsigned long)maxBandwidthPerSecond | ||
| 2285 | +{ | ||
| 2286 | + [bandwidthThrottlingLock lock]; | ||
| 2287 | + unsigned long amount = maxBandwidthPerSecond; | ||
| 2288 | + [bandwidthThrottlingLock unlock]; | ||
| 2289 | + return amount; | ||
| 2290 | +} | ||
| 2291 | + | ||
| 2292 | ++ (void)setMaxBandwidthPerSecond:(unsigned long)bytes | ||
| 2293 | +{ | ||
| 2294 | + [bandwidthThrottlingLock lock]; | ||
| 2295 | + maxBandwidthPerSecond = bytes; | ||
| 2296 | + [bandwidthThrottlingLock unlock]; | ||
| 2297 | +} | ||
| 2298 | + | ||
| 2299 | ++ (void)incrementBandwidthUsedInLastSecond:(unsigned long)bytes | ||
| 2300 | +{ | ||
| 2301 | + [bandwidthThrottlingLock lock]; | ||
| 2302 | + bandwidthUsedInLastSecond += bytes; | ||
| 2303 | + [bandwidthThrottlingLock unlock]; | ||
| 2304 | +} | ||
| 2305 | + | ||
| 2306 | ++ (void)throttleBandwidth | ||
| 2307 | +{ | ||
| 2308 | + // Other requests may have to wait for this lock if we're sleeping, but this is fine, since we already know they shouldn't be sending or receiving data | ||
| 2309 | + [bandwidthThrottlingLock lock]; | ||
| 2310 | + | ||
| 2311 | + // Are we performing bandwidth throttling? | ||
| 2312 | + if (maxBandwidthPerSecond > 0) { | ||
| 2313 | + if (!bandwidthThrottlingMeasurementDate) { | ||
| 2314 | + bandwidthThrottlingMeasurementDate = [NSDate dateWithTimeIntervalSinceNow:1]; | ||
| 2315 | + } | ||
| 2316 | + | ||
| 2317 | + // How much data can we still send or receive this second? | ||
| 2318 | + long long bytesRemaining = maxBandwidthPerSecond - bandwidthUsedInLastSecond; | ||
| 2319 | + | ||
| 2320 | + // Have we used up our allowance? | ||
| 2321 | + if (bytesRemaining < 0) { | ||
| 2322 | + | ||
| 2323 | + // Yes, put this request to sleep until a second is up | ||
| 2324 | + [NSThread sleepUntilDate:bandwidthThrottlingMeasurementDate]; | ||
| 2325 | + bandwidthThrottlingMeasurementDate = [NSDate dateWithTimeIntervalSinceNow:1]; | ||
| 2326 | + } | ||
| 2327 | + } | ||
| 2328 | + [bandwidthThrottlingLock unlock]; | ||
| 2329 | +} | ||
| 2330 | + | ||
| 2254 | 2331 | ||
| 2255 | 2332 | ||
| 2256 | @synthesize username; | 2333 | @synthesize username; |
| @@ -2324,7 +2401,6 @@ static NSError *ASITooMuchRedirectionError; | @@ -2324,7 +2401,6 @@ static NSError *ASITooMuchRedirectionError; | ||
| 2324 | @synthesize authenticationLock; | 2401 | @synthesize authenticationLock; |
| 2325 | @synthesize needsProxyAuthentication; | 2402 | @synthesize needsProxyAuthentication; |
| 2326 | @synthesize proxyCredentials; | 2403 | @synthesize proxyCredentials; |
| 2327 | - | ||
| 2328 | @synthesize proxyHost; | 2404 | @synthesize proxyHost; |
| 2329 | @synthesize proxyPort; | 2405 | @synthesize proxyPort; |
| 2330 | @synthesize PACurl; | 2406 | @synthesize PACurl; |
-
Please register or login to post a comment