Added support for persistent http connections
More performance tests
Showing
4 changed files
with
192 additions
and
13 deletions
@@ -331,6 +331,14 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -331,6 +331,14 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
331 | 331 | ||
332 | // The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0) | 332 | // The number of times this request has retried (when numberOfTimesToRetryOnTimeout > 0) |
333 | int retryCount; | 333 | int retryCount; |
334 | + | ||
335 | + // When YES, requests will keep the connection to the server alive for a while to allow subsequent requests to re-use it for a substatial speed-boost | ||
336 | + // Persistent connections only work when the server sends a 'Keep-Alive' header | ||
337 | + // Default is YES | ||
338 | + BOOL shouldAttemptPersistentConnection; | ||
339 | + | ||
340 | + // Set to yes when an appropriate keep-alive header is found | ||
341 | + BOOL canUsePersistentConnection; | ||
334 | } | 342 | } |
335 | 343 | ||
336 | #pragma mark init / dealloc | 344 | #pragma mark init / dealloc |
@@ -666,4 +674,5 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | @@ -666,4 +674,5 @@ extern unsigned long const ASIWWANBandwidthThrottleAmount; | ||
666 | @property (assign) BOOL shouldRunInBackgroundThread; | 674 | @property (assign) BOOL shouldRunInBackgroundThread; |
667 | @property (assign) int numberOfTimesToRetryOnTimeout; | 675 | @property (assign) int numberOfTimesToRetryOnTimeout; |
668 | @property (assign, readonly) int retryCount; | 676 | @property (assign, readonly) int retryCount; |
677 | +@property (assign) BOOL shouldAttemptPersistentConnection; | ||
669 | @end | 678 | @end |
@@ -21,7 +21,7 @@ | @@ -21,7 +21,7 @@ | ||
21 | #import "ASIInputStream.h" | 21 | #import "ASIInputStream.h" |
22 | 22 | ||
23 | // Automatically set on build | 23 | // Automatically set on build |
24 | -NSString *ASIHTTPRequestVersion = @"v1.2-52 2009-12-19"; | 24 | +NSString *ASIHTTPRequestVersion = @"v1.2-53 2009-12-19"; |
25 | 25 | ||
26 | NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; | 26 | NSString* const NetworkRequestErrorDomain = @"ASIHTTPRequestErrorDomain"; |
27 | 27 | ||
@@ -154,6 +154,7 @@ static BOOL isiPhoneOS2; | @@ -154,6 +154,7 @@ static BOOL isiPhoneOS2; | ||
154 | @property (assign) BOOL isSynchronous; | 154 | @property (assign) BOOL isSynchronous; |
155 | @property (assign) BOOL inProgress; | 155 | @property (assign) BOOL inProgress; |
156 | @property (assign) int retryCount; | 156 | @property (assign) int retryCount; |
157 | +@property (assign) BOOL canUsePersistentConnection; | ||
157 | @end | 158 | @end |
158 | 159 | ||
159 | 160 | ||
@@ -191,6 +192,7 @@ static BOOL isiPhoneOS2; | @@ -191,6 +192,7 @@ static BOOL isiPhoneOS2; | ||
191 | self = [super init]; | 192 | self = [super init]; |
192 | [self setRequestMethod:@"GET"]; | 193 | [self setRequestMethod:@"GET"]; |
193 | 194 | ||
195 | + [self setShouldAttemptPersistentConnection:YES]; | ||
194 | [self setShouldPresentCredentialsBeforeChallenge:YES]; | 196 | [self setShouldPresentCredentialsBeforeChallenge:YES]; |
195 | [self setShouldRedirect:YES]; | 197 | [self setShouldRedirect:YES]; |
196 | [self setShowAccurateProgress:YES]; | 198 | [self setShowAccurateProgress:YES]; |
@@ -808,6 +810,9 @@ static BOOL isiPhoneOS2; | @@ -808,6 +810,9 @@ static BOOL isiPhoneOS2; | ||
808 | CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]); | 810 | CFReadStreamSetProperty(readStream, kCFStreamPropertySSLSettings, [NSMutableDictionary dictionaryWithObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]); |
809 | } | 811 | } |
810 | 812 | ||
813 | + // Use a persistent connection if possible | ||
814 | + CFReadStreamSetProperty(readStream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); | ||
815 | + | ||
811 | 816 | ||
812 | // Handle proxy settings | 817 | // Handle proxy settings |
813 | 818 | ||
@@ -930,10 +935,20 @@ static BOOL isiPhoneOS2; | @@ -930,10 +935,20 @@ static BOOL isiPhoneOS2; | ||
930 | [self scheduleReadStream]; | 935 | [self scheduleReadStream]; |
931 | } | 936 | } |
932 | 937 | ||
933 | - while (!complete) { | 938 | +// if ([NSThread isMainThread]) { |
934 | - [self checkRequestStatus]; | 939 | +// [NSTimer scheduledTimerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]; |
935 | - CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, NO); | 940 | +// CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeOutSeconds, NO); |
936 | - } | 941 | +// |
942 | +// } else if (!uploadProgressDelegate && !downloadProgressDelegate) { | ||
943 | +// CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeOutSeconds, NO); | ||
944 | +// [self checkRequestStatus]; | ||
945 | +// } else { | ||
946 | + | ||
947 | + while (!complete) { | ||
948 | + [self checkRequestStatus]; | ||
949 | + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, NO); | ||
950 | + } | ||
951 | + //} | ||
937 | } | 952 | } |
938 | 953 | ||
939 | // This gets fired every 1/4 of a second in asynchronous requests to update the progress and work out if we need to timeout | 954 | // This gets fired every 1/4 of a second in asynchronous requests to update the progress and work out if we need to timeout |
@@ -1594,11 +1609,32 @@ static BOOL isiPhoneOS2; | @@ -1594,11 +1609,32 @@ static BOOL isiPhoneOS2; | ||
1594 | 1609 | ||
1595 | } | 1610 | } |
1596 | 1611 | ||
1612 | + // Handle connection persistence | ||
1613 | + if ([self shouldAttemptPersistentConnection] && [[[self responseHeaders] objectForKey:@"Connection"] isEqualToString:@"Keep-Alive"]) { | ||
1614 | + NSString *keepAliveHeader = [[self responseHeaders] objectForKey:@"Keep-Alive"]; | ||
1615 | + if (keepAliveHeader) { | ||
1616 | + int timeout = 0; | ||
1617 | + int max = 0; | ||
1618 | + NSScanner *scanner = [NSScanner scannerWithString:keepAliveHeader]; | ||
1619 | + [scanner scanString:@"timeout=" intoString:NULL]; | ||
1620 | + [scanner scanInt:&timeout]; | ||
1621 | + [scanner scanUpToString:@"max=" intoString:NULL]; | ||
1622 | + [scanner scanString:@"max=" intoString:NULL]; | ||
1623 | + [scanner scanInt:&max]; | ||
1624 | + if (max > 5) { | ||
1625 | + [self setCanUsePersistentConnection:YES]; | ||
1626 | + CFRetain(readStream); | ||
1627 | + [NSTimer scheduledTimerWithTimeInterval:max target:[self class] selector:@selector(closePersistentConnection:) userInfo:(id)readStream repeats:NO]; | ||
1628 | + } | ||
1629 | + } | ||
1630 | + } | ||
1597 | } | 1631 | } |
1632 | + | ||
1633 | + | ||
1634 | + | ||
1598 | CFRelease(headers); | 1635 | CFRelease(headers); |
1599 | return isAuthenticationChallenge; | 1636 | return isAuthenticationChallenge; |
1600 | } | 1637 | } |
1601 | - | ||
1602 | 1638 | ||
1603 | #pragma mark http authentication | 1639 | #pragma mark http authentication |
1604 | 1640 | ||
@@ -2222,12 +2258,14 @@ static BOOL isiPhoneOS2; | @@ -2222,12 +2258,14 @@ static BOOL isiPhoneOS2; | ||
2222 | if ([self needsRedirect]) { | 2258 | if ([self needsRedirect]) { |
2223 | return; | 2259 | return; |
2224 | } | 2260 | } |
2225 | - long long bufferSize = 2048; | 2261 | +// long long bufferSize = 2048; |
2226 | - if (contentLength > 262144) { | 2262 | +// if (contentLength > 262144) { |
2227 | - bufferSize = 65536; | 2263 | +// bufferSize = 65536; |
2228 | - } else if (contentLength > 65536) { | 2264 | +// } else if (contentLength > 65536) { |
2229 | - bufferSize = 16384; | 2265 | +// bufferSize = 16384; |
2230 | - } | 2266 | +// } |
2267 | + | ||
2268 | + long long bufferSize = 262144; | ||
2231 | 2269 | ||
2232 | // Reduce the buffer size if we're receiving data too quickly when bandwidth throttling is active | 2270 | // Reduce the buffer size if we're receiving data too quickly when bandwidth throttling is active |
2233 | // This just augments the throttling done in measureBandwidthUsage to reduce the amount we go over the limit | 2271 | // This just augments the throttling done in measureBandwidthUsage to reduce the amount we go over the limit |
@@ -2397,7 +2435,9 @@ static BOOL isiPhoneOS2; | @@ -2397,7 +2435,9 @@ static BOOL isiPhoneOS2; | ||
2397 | if (readStreamIsScheduled) { | 2435 | if (readStreamIsScheduled) { |
2398 | CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); | 2436 | CFReadStreamUnscheduleFromRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); |
2399 | } | 2437 | } |
2400 | - CFReadStreamClose(readStream); | 2438 | + if (!canUsePersistentConnection) { |
2439 | + CFReadStreamClose(readStream); | ||
2440 | + } | ||
2401 | CFRelease(readStream); | 2441 | CFRelease(readStream); |
2402 | readStream = NULL; | 2442 | readStream = NULL; |
2403 | } | 2443 | } |
@@ -2421,6 +2461,13 @@ static BOOL isiPhoneOS2; | @@ -2421,6 +2461,13 @@ static BOOL isiPhoneOS2; | ||
2421 | } | 2461 | } |
2422 | } | 2462 | } |
2423 | 2463 | ||
2464 | ++ (void)closePersistentConnection:(NSTimer *)timer | ||
2465 | +{ | ||
2466 | + CFReadStreamRef stream = (CFReadStreamRef)[timer userInfo]; | ||
2467 | + CFReadStreamClose(stream); | ||
2468 | + CFRelease(stream); | ||
2469 | +} | ||
2470 | + | ||
2424 | #pragma mark NSCopying | 2471 | #pragma mark NSCopying |
2425 | 2472 | ||
2426 | - (id)copyWithZone:(NSZone *)zone | 2473 | - (id)copyWithZone:(NSZone *)zone |
@@ -3427,6 +3474,8 @@ static BOOL isiPhoneOS2; | @@ -3427,6 +3474,8 @@ static BOOL isiPhoneOS2; | ||
3427 | @synthesize shouldRunInBackgroundThread; | 3474 | @synthesize shouldRunInBackgroundThread; |
3428 | @synthesize numberOfTimesToRetryOnTimeout; | 3475 | @synthesize numberOfTimesToRetryOnTimeout; |
3429 | @synthesize retryCount; | 3476 | @synthesize retryCount; |
3477 | +@synthesize shouldAttemptPersistentConnection; | ||
3478 | +@synthesize canUsePersistentConnection; | ||
3430 | @end | 3479 | @end |
3431 | 3480 | ||
3432 | 3481 |
@@ -10,6 +10,8 @@ | @@ -10,6 +10,8 @@ | ||
10 | #import "ASITestCase.h" | 10 | #import "ASITestCase.h" |
11 | 11 | ||
12 | @interface PerformanceTests : ASITestCase { | 12 | @interface PerformanceTests : ASITestCase { |
13 | + NSURL *testURL; | ||
14 | + | ||
13 | NSDate *testStartDate; | 15 | NSDate *testStartDate; |
14 | int requestsComplete; | 16 | int requestsComplete; |
15 | NSMutableArray *responseData; | 17 | NSMutableArray *responseData; |
@@ -19,6 +21,7 @@ | @@ -19,6 +21,7 @@ | ||
19 | - (void)testASIHTTPRequestAsyncPerformance; | 21 | - (void)testASIHTTPRequestAsyncPerformance; |
20 | - (void)testNSURLConnectionAsyncPerformance; | 22 | - (void)testNSURLConnectionAsyncPerformance; |
21 | 23 | ||
24 | +@property (retain,nonatomic) NSURL *testURL; | ||
22 | @property (retain,nonatomic) NSDate *testStartDate; | 25 | @property (retain,nonatomic) NSDate *testStartDate; |
23 | @property (assign,nonatomic) int requestsComplete; | 26 | @property (assign,nonatomic) int requestsComplete; |
24 | @property (retain,nonatomic) NSMutableArray *responseData; | 27 | @property (retain,nonatomic) NSMutableArray *responseData; |
@@ -9,6 +9,7 @@ | @@ -9,6 +9,7 @@ | ||
9 | #import "PerformanceTests.h" | 9 | #import "PerformanceTests.h" |
10 | #import "ASIHTTPRequest.h" | 10 | #import "ASIHTTPRequest.h" |
11 | 11 | ||
12 | + | ||
12 | @interface NSURLConnectionSubclass : NSURLConnection { | 13 | @interface NSURLConnectionSubclass : NSURLConnection { |
13 | int tag; | 14 | int tag; |
14 | } | 15 | } |
@@ -21,11 +22,105 @@ | @@ -21,11 +22,105 @@ | ||
21 | 22 | ||
22 | @implementation PerformanceTests | 23 | @implementation PerformanceTests |
23 | 24 | ||
25 | +- (void)setUp | ||
26 | +{ | ||
27 | + [self setTestURL:[NSURL URLWithString:@"http://allseeing-i.com"]]; | ||
28 | +} | ||
29 | + | ||
30 | +- (void)testASIHTTPRequestSynchronousPerformance | ||
31 | +{ | ||
32 | + [self performSelectorOnMainThread:@selector(runSynchronousASIHTTPRequests) withObject:nil waitUntilDone:YES]; | ||
33 | +} | ||
34 | + | ||
35 | + | ||
36 | +- (void)runSynchronousASIHTTPRequests | ||
37 | +{ | ||
38 | + int runTimes = 10; | ||
39 | + NSTimeInterval times[runTimes]; | ||
40 | + int i; | ||
41 | + for (i=0; i<runTimes; i++) { | ||
42 | + NSDate *startTime = [NSDate date]; | ||
43 | + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:testURL]; | ||
44 | + //Send the same headers as NSURLRequest | ||
45 | + [request addRequestHeader:@"Pragma" value:@"no-cache"]; | ||
46 | + [request addRequestHeader:@"Accept" value:@"*/*"]; | ||
47 | + [request addRequestHeader:@"Accept-Language" value:@"en/us"]; | ||
48 | + [request setUseCookiePersistance:NO]; | ||
49 | + [request setUseSessionPersistance:NO]; | ||
50 | + //[request setShouldRunInBackgroundThread:YES]; | ||
51 | + [request startSynchronous]; | ||
52 | + if ([request error]) { | ||
53 | + NSLog(@"Request failed - cannot proceed with test"); | ||
54 | + return; | ||
55 | + } | ||
56 | + times[i] = [[NSDate date] timeIntervalSinceDate:startTime]; | ||
57 | + } | ||
58 | + NSTimeInterval bestTime = 1000; | ||
59 | + NSTimeInterval worstTime = 0; | ||
60 | + NSTimeInterval totalTime = 0; | ||
61 | + for (i=0; i<runTimes; i++) { | ||
62 | + if (times[i] < bestTime) { | ||
63 | + bestTime = times[i]; | ||
64 | + } | ||
65 | + if (times[i] > worstTime) { | ||
66 | + worstTime = times[i]; | ||
67 | + } | ||
68 | + totalTime += times[i]; | ||
69 | + } | ||
70 | + NSLog(@"Ran runTimes requests in %f seconds (average time: %f secs / best time: %f secs / worst time: %f secs)",totalTime,totalTime/runTimes,bestTime,worstTime); | ||
71 | +} | ||
72 | + | ||
73 | + | ||
74 | +- (void)testNSURLConnectionSynchronousPerformance | ||
75 | +{ | ||
76 | + [self performSelectorOnMainThread:@selector(runSynchronousNSURLConnections) withObject:nil waitUntilDone:YES]; | ||
77 | +} | ||
78 | + | ||
79 | + | ||
80 | +- (void)runSynchronousNSURLConnections | ||
81 | +{ | ||
82 | + int runTimes = 10; | ||
83 | + NSTimeInterval times[runTimes]; | ||
84 | + int i; | ||
85 | + for (i=0; i<runTimes; i++) { | ||
86 | + NSDate *startTime = [NSDate date]; | ||
87 | + | ||
88 | + NSURLResponse *response = nil; | ||
89 | + NSError *error = nil; | ||
90 | + NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:testURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10]; | ||
91 | + [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; | ||
92 | + if (error) { | ||
93 | + NSLog(@"Request failed - cannot proceed with test"); | ||
94 | + return; | ||
95 | + } | ||
96 | + times[i] = [[NSDate date] timeIntervalSinceDate:startTime]; | ||
97 | + } | ||
98 | + NSTimeInterval bestTime = 1000; | ||
99 | + NSTimeInterval worstTime = 0; | ||
100 | + NSTimeInterval totalTime = 0; | ||
101 | + for (i=0; i<runTimes; i++) { | ||
102 | + if (times[i] < bestTime) { | ||
103 | + bestTime = times[i]; | ||
104 | + } | ||
105 | + if (times[i] > worstTime) { | ||
106 | + worstTime = times[i]; | ||
107 | + } | ||
108 | + totalTime += times[i]; | ||
109 | + } | ||
110 | + NSLog(@"Ran runTimes requests in %f seconds (average time: %f secs / best time: %f secs / worst time: %f secs)",totalTime,totalTime/runTimes,bestTime,worstTime); | ||
111 | +} | ||
112 | + | ||
113 | + | ||
24 | - (void)testASIHTTPRequestAsyncPerformance | 114 | - (void)testASIHTTPRequestAsyncPerformance |
25 | { | 115 | { |
26 | [self performSelectorOnMainThread:@selector(startASIHTTPRequests) withObject:nil waitUntilDone:NO]; | 116 | [self performSelectorOnMainThread:@selector(startASIHTTPRequests) withObject:nil waitUntilDone:NO]; |
27 | } | 117 | } |
28 | 118 | ||
119 | +- (void)testASIHTTPRequestAsyncPerformanceWithQueue | ||
120 | +{ | ||
121 | + [self performSelectorOnMainThread:@selector(startASIHTTPRequestsWithQueue) withObject:nil waitUntilDone:NO]; | ||
122 | +} | ||
123 | + | ||
29 | 124 | ||
30 | - (void)startASIHTTPRequests | 125 | - (void)startASIHTTPRequests |
31 | { | 126 | { |
@@ -35,11 +130,33 @@ | @@ -35,11 +130,33 @@ | ||
35 | int i; | 130 | int i; |
36 | for (i=0; i<10; i++) { | 131 | for (i=0; i<10; i++) { |
37 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; | 132 | ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; |
133 | + //Send the same headers as NSURLRequest | ||
134 | + [request addRequestHeader:@"Pragma" value:@"no-cache"]; | ||
135 | + [request addRequestHeader:@"Accept" value:@"*/*"]; | ||
136 | + [request addRequestHeader:@"Accept-Language" value:@"en/us"]; | ||
38 | [request setDelegate:self]; | 137 | [request setDelegate:self]; |
39 | [request start]; | 138 | [request start]; |
40 | } | 139 | } |
41 | } | 140 | } |
42 | 141 | ||
142 | +- (void)startASIHTTPRequestsWithQueue | ||
143 | +{ | ||
144 | + bytesDownloaded = 0; | ||
145 | + [self setRequestsComplete:0]; | ||
146 | + [self setTestStartDate:[NSDate date]]; | ||
147 | + int i; | ||
148 | + NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease]; | ||
149 | + for (i=0; i<10; i++) { | ||
150 | + ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"]]; | ||
151 | + //Send the same headers as NSURLRequest | ||
152 | + [request addRequestHeader:@"Pragma" value:@"no-cache"]; | ||
153 | + [request addRequestHeader:@"Accept" value:@"*/*"]; | ||
154 | + [request addRequestHeader:@"Accept-Language" value:@"en/us"]; | ||
155 | + [request setDelegate:self]; | ||
156 | + [queue addOperation:request]; | ||
157 | + } | ||
158 | +} | ||
159 | + | ||
43 | - (void)requestFailed:(ASIHTTPRequest *)request | 160 | - (void)requestFailed:(ASIHTTPRequest *)request |
44 | { | 161 | { |
45 | GHFail(@"Cannot proceed with ASIHTTPRequest test - a request failed"); | 162 | GHFail(@"Cannot proceed with ASIHTTPRequest test - a request failed"); |
@@ -99,6 +216,7 @@ | @@ -99,6 +216,7 @@ | ||
99 | } | 216 | } |
100 | } | 217 | } |
101 | 218 | ||
219 | +@synthesize testURL; | ||
102 | @synthesize requestsComplete; | 220 | @synthesize requestsComplete; |
103 | @synthesize testStartDate; | 221 | @synthesize testStartDate; |
104 | @synthesize responseData; | 222 | @synthesize responseData; |
-
Please register or login to post a comment