Ben Copsey

Improve ASINetworkQueue progress tracking behaviour when adding requests to a qu…

…eue that is already running
@@ -26,19 +26,19 @@ @@ -26,19 +26,19 @@
26 id uploadProgressDelegate; 26 id uploadProgressDelegate;
27 27
28 // Total amount uploaded so far for all requests in this queue 28 // Total amount uploaded so far for all requests in this queue
29 - unsigned long long uploadProgressBytes; 29 + unsigned long long bytesUploadedSoFar;
30 30
31 // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit 31 // Total amount to be uploaded for all requests in this queue - requests add to this figure as they work out how much data they have to transmit
32 - unsigned long long uploadProgressTotalBytes; 32 + unsigned long long totalBytesToUpload;
33 33
34 // Download progress indicator, probably an NSProgressIndicator or UIProgressView 34 // Download progress indicator, probably an NSProgressIndicator or UIProgressView
35 id downloadProgressDelegate; 35 id downloadProgressDelegate;
36 36
37 // Total amount downloaded so far for all requests in this queue 37 // Total amount downloaded so far for all requests in this queue
38 - unsigned long long downloadProgressBytes; 38 + unsigned long long bytesDownloadedSoFar;
39 39
40 // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers 40 // Total amount to be downloaded for all requests in this queue - requests add to this figure as they receive Content-Length headers
41 - unsigned long long downloadProgressTotalBytes; 41 + unsigned long long totalBytesToDownload;
42 42
43 // When YES, the queue will cancel all requests when a request fails. Default is YES 43 // When YES, the queue will cancel all requests when a request fails. Default is YES
44 BOOL shouldCancelAllRequestsOnFailure; 44 BOOL shouldCancelAllRequestsOnFailure;
@@ -108,4 +108,9 @@ @@ -108,4 +108,9 @@
108 @property (assign, readonly) int requestsCount; 108 @property (assign, readonly) int requestsCount;
109 @property (retain) NSDictionary *userInfo; 109 @property (retain) NSDictionary *userInfo;
110 110
  111 +@property (assign) unsigned long long bytesUploadedSoFar;
  112 +@property (assign) unsigned long long totalBytesToUpload;
  113 +@property (assign) unsigned long long bytesDownloadedSoFar;
  114 +@property (assign) unsigned long long totalBytesToDownload;
  115 +
111 @end 116 @end
@@ -12,10 +12,6 @@ @@ -12,10 +12,6 @@
12 // Private stuff 12 // Private stuff
13 @interface ASINetworkQueue () 13 @interface ASINetworkQueue ()
14 @property (assign) int requestsCount; 14 @property (assign) int requestsCount;
15 - @property (assign) unsigned long long uploadProgressBytes;  
16 - @property (assign) unsigned long long uploadProgressTotalBytes;  
17 - @property (assign) unsigned long long downloadProgressBytes;  
18 - @property (assign) unsigned long long downloadProgressTotalBytes;  
19 @end 15 @end
20 16
21 @implementation ASINetworkQueue 17 @implementation ASINetworkQueue
@@ -80,10 +76,10 @@ @@ -80,10 +76,10 @@
80 - (void)cancelAllOperations 76 - (void)cancelAllOperations
81 { 77 {
82 [self setRequestsCount:0]; 78 [self setRequestsCount:0];
83 - [self setUploadProgressBytes:0]; 79 + [self setBytesUploadedSoFar:0];
84 - [self setUploadProgressTotalBytes:0]; 80 + [self setTotalBytesToUpload:0];
85 - [self setDownloadProgressBytes:0]; 81 + [self setBytesDownloadedSoFar:0];
86 - [self setDownloadProgressTotalBytes:0]; 82 + [self setTotalBytesToDownload:0];
87 [super cancelAllOperations]; 83 [super cancelAllOperations];
88 [self updateNetworkActivityIndicator]; 84 [self updateNetworkActivityIndicator];
89 } 85 }
@@ -151,7 +147,8 @@ @@ -151,7 +147,8 @@
151 147
152 // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length 148 // If this is a GET request and we want accurate progress, perform a HEAD request first to get the content-length
153 // We'll only do this before the queue is started 149 // We'll only do this before the queue is started
154 - // If requests are added after the queue is started they will probably move the progress backwards anyway, so there's no value performing the HEAD requests first 150 + // 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
  151 + // Instead, they'll update the total progress if and when they recieve a content-length header
155 if ([[request requestMethod] isEqualToString:@"GET"] && [self isSuspended]) { 152 if ([[request requestMethod] isEqualToString:@"GET"] && [self isSuspended]) {
156 ASIHTTPRequest *HEADRequest = [request HEADRequest]; 153 ASIHTTPRequest *HEADRequest = [request HEADRequest];
157 [self addHEADOperation:HEADRequest]; 154 [self addHEADOperation:HEADRequest];
@@ -164,7 +161,7 @@ @@ -164,7 +161,7 @@
164 // If we want to track uploading for this request accurately, we need to add the size of the post content to the total 161 // If we want to track uploading for this request accurately, we need to add the size of the post content to the total
165 } else if (uploadProgressDelegate) { 162 } else if (uploadProgressDelegate) {
166 [request buildPostBody]; 163 [request buildPostBody];
167 - [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes]+[request postLength]]; 164 + [self setTotalBytesToUpload:[self totalBytesToUpload]+[request postLength]];
168 } 165 }
169 } 166 }
170 [request setShowAccurateProgress:[self showAccurateProgress]]; 167 [request setShowAccurateProgress:[self showAccurateProgress]];
@@ -213,7 +210,7 @@ @@ -213,7 +210,7 @@
213 if (![self uploadProgressDelegate]) { 210 if (![self uploadProgressDelegate]) {
214 return; 211 return;
215 } 212 }
216 - [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] - bytes]; 213 + [self setTotalBytesToUpload:[self totalBytesToUpload] - bytes];
217 [self incrementUploadProgressBy:0]; 214 [self incrementUploadProgressBy:0];
218 } 215 }
219 216
@@ -222,36 +219,41 @@ @@ -222,36 +219,41 @@
222 if (![self uploadProgressDelegate]) { 219 if (![self uploadProgressDelegate]) {
223 return; 220 return;
224 } 221 }
225 - [self setUploadProgressTotalBytes:[self uploadProgressTotalBytes] + bytes]; 222 + [self setTotalBytesToUpload:[self totalBytesToUpload] + bytes];
226 [self incrementUploadProgressBy:0]; 223 [self incrementUploadProgressBy:0];
227 } 224 }
228 225
229 - (void)decrementUploadProgressBy:(unsigned long long)bytes 226 - (void)decrementUploadProgressBy:(unsigned long long)bytes
230 { 227 {
231 - if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { 228 + if (![self uploadProgressDelegate] || [self totalBytesToUpload] == 0) {
232 return; 229 return;
233 } 230 }
234 - [self setUploadProgressBytes:[self uploadProgressBytes] - bytes]; 231 + [self setBytesUploadedSoFar:[self bytesUploadedSoFar] - bytes];
235 232
236 - 233 + double progress;
237 - double progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); 234 + //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
  235 + if ([ASIHTTPRequest isiPhoneOS2]) {
  236 + progress = [[NSNumber numberWithUnsignedLongLong:[self bytesUploadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToUpload]] doubleValue];
  237 + } else {
  238 + progress = ([self bytesUploadedSoFar]*1.0)/([self totalBytesToUpload]*1.0);
  239 + }
238 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; 240 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
239 } 241 }
240 242
241 243
242 - (void)incrementUploadProgressBy:(unsigned long long)bytes 244 - (void)incrementUploadProgressBy:(unsigned long long)bytes
243 { 245 {
244 - if (![self uploadProgressDelegate] || [self uploadProgressTotalBytes] == 0) { 246 + if (![self uploadProgressDelegate] || [self totalBytesToUpload] == 0) {
245 return; 247 return;
246 } 248 }
247 - [self setUploadProgressBytes:[self uploadProgressBytes] + bytes]; 249 + [self setBytesUploadedSoFar:[self bytesUploadedSoFar] + bytes];
248 250
249 double progress; 251 double progress;
250 //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0 252 //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
251 if ([ASIHTTPRequest isiPhoneOS2]) { 253 if ([ASIHTTPRequest isiPhoneOS2]) {
252 - progress = [[NSNumber numberWithUnsignedLongLong:[self uploadProgressBytes]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self uploadProgressTotalBytes]] doubleValue]; 254 + progress = [[NSNumber numberWithUnsignedLongLong:[self bytesUploadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToUpload]] doubleValue];
253 } else { 255 } else {
254 - progress = ([self uploadProgressBytes]*1.0)/([self uploadProgressTotalBytes]*1.0); 256 + progress = ([self bytesUploadedSoFar]*1.0)/([self totalBytesToUpload]*1.0);
255 } 257 }
256 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]]; 258 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self uploadProgressDelegate]];
257 259
@@ -262,23 +264,23 @@ @@ -262,23 +264,23 @@
262 if (![self downloadProgressDelegate]) { 264 if (![self downloadProgressDelegate]) {
263 return; 265 return;
264 } 266 }
265 - [self setDownloadProgressTotalBytes:[self downloadProgressTotalBytes] + bytes]; 267 + [self setTotalBytesToDownload:[self totalBytesToDownload] + bytes];
266 [self incrementDownloadProgressBy:0]; 268 [self incrementDownloadProgressBy:0];
267 } 269 }
268 270
269 - (void)incrementDownloadProgressBy:(unsigned long long)bytes 271 - (void)incrementDownloadProgressBy:(unsigned long long)bytes
270 { 272 {
271 - if (![self downloadProgressDelegate] || [self downloadProgressTotalBytes] == 0) { 273 + if (![self downloadProgressDelegate] || [self totalBytesToDownload] == 0) {
272 return; 274 return;
273 } 275 }
274 - [self setDownloadProgressBytes:[self downloadProgressBytes] + bytes]; 276 + [self setBytesDownloadedSoFar:[self bytesDownloadedSoFar] + bytes];
275 277
276 double progress; 278 double progress;
277 //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0 279 //Workaround for an issue with converting a long to a double on iPhone OS 2.2.1 with a base SDK >= 3.0
278 if ([ASIHTTPRequest isiPhoneOS2]) { 280 if ([ASIHTTPRequest isiPhoneOS2]) {
279 - progress = [[NSNumber numberWithUnsignedLongLong:[self downloadProgressBytes]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self downloadProgressTotalBytes]] doubleValue]; 281 + progress = [[NSNumber numberWithUnsignedLongLong:[self bytesDownloadedSoFar]] doubleValue]/[[NSNumber numberWithUnsignedLongLong:[self totalBytesToDownload]] doubleValue];
280 } else { 282 } else {
281 - progress = ([self downloadProgressBytes]*1.0)/([self downloadProgressTotalBytes]*1.0); 283 + progress = ([self bytesDownloadedSoFar]*1.0)/([self totalBytesToDownload]*1.0);
282 } 284 }
283 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]]; 285 [ASIHTTPRequest setProgress:progress forProgressIndicator:[self downloadProgressDelegate]];
284 } 286 }
@@ -318,10 +320,10 @@ @@ -318,10 +320,10 @@
318 320
319 321
320 @synthesize requestsCount; 322 @synthesize requestsCount;
321 -@synthesize uploadProgressBytes; 323 +@synthesize bytesUploadedSoFar;
322 -@synthesize uploadProgressTotalBytes; 324 +@synthesize totalBytesToUpload;
323 -@synthesize downloadProgressBytes; 325 +@synthesize bytesDownloadedSoFar;
324 -@synthesize downloadProgressTotalBytes; 326 +@synthesize totalBytesToDownload;
325 @synthesize shouldCancelAllRequestsOnFailure; 327 @synthesize shouldCancelAllRequestsOnFailure;
326 @synthesize uploadProgressDelegate; 328 @synthesize uploadProgressDelegate;
327 @synthesize downloadProgressDelegate; 329 @synthesize downloadProgressDelegate;
@@ -38,6 +38,9 @@ IMPORTANT @@ -38,6 +38,9 @@ IMPORTANT
38 ASINetworkQueue *postQueue; 38 ASINetworkQueue *postQueue;
39 39
40 ASINetworkQueue *testNTLMQueue; 40 ASINetworkQueue *testNTLMQueue;
  41 +
  42 + ASINetworkQueue *addMoreRequestsQueue;
  43 + int requestsFinishedCount;
41 } 44 }
42 45
43 - (void)testFailure; 46 - (void)testFailure;
@@ -71,5 +74,5 @@ IMPORTANT @@ -71,5 +74,5 @@ IMPORTANT
71 @property (retain) ASINetworkQueue *cancelQueue; 74 @property (retain) ASINetworkQueue *cancelQueue;
72 @property (retain) ASINetworkQueue *postQueue; 75 @property (retain) ASINetworkQueue *postQueue;
73 @property (retain) ASINetworkQueue *testNTLMQueue; 76 @property (retain) ASINetworkQueue *testNTLMQueue;
74 - 77 +@property (retain) ASINetworkQueue *addMoreRequestsQueue;
75 @end 78 @end
@@ -156,7 +156,68 @@ IMPORTANT @@ -156,7 +156,68 @@ IMPORTANT
156 GHAssertTrue(success,@"Failed to increment progress properly"); 156 GHAssertTrue(success,@"Failed to increment progress properly");
157 } 157 }
158 158
  159 +- (void)testAddingRequestsToQueueWhileInProgress
  160 +{
  161 + [[self addMoreRequestsQueue] cancelAllOperations];
  162 + [self setAddMoreRequestsQueue:[ASINetworkQueue queue]];
  163 + [[self addMoreRequestsQueue] setDownloadProgressDelegate:self];
  164 + [[self addMoreRequestsQueue] setDelegate:self];
  165 + [[self addMoreRequestsQueue] setShowAccurateProgress:NO];
  166 + [[self addMoreRequestsQueue] setQueueDidFinishSelector:@selector(addMoreRequestsQueueFinished:)];
  167 +
  168 + requestsFinishedCount = 0;
  169 +
  170 + complete = NO;
  171 + progress = 0;
  172 + [[self addMoreRequestsQueue] setShowAccurateProgress:YES];
  173 +
  174 + int i;
  175 + for (i=0; i<5; i++) {
  176 + NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"] autorelease];
  177 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  178 + [request setDelegate:self];
  179 + [request setDidFinishSelector:@selector(addedRequestComplete:)];
  180 + [[self addMoreRequestsQueue] addOperation:request];
  181 + }
  182 + [[self addMoreRequestsQueue] go];
159 183
  184 + // Add another request to the queue each second for 5 seconds
  185 + for (i=0; i<5; i++) {
  186 + [self performSelector:@selector(addAnotherRequest) withObject:nil afterDelay:i];
  187 + }
  188 +
  189 + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
  190 +}
  191 +
  192 +- (void)addAnotherRequest
  193 +{
  194 + NSURL *url = [[[NSURL alloc] initWithString:@"http://allseeing-i.com/ASIHTTPRequest/tests/the_great_american_novel_(abridged).txt"] autorelease];
  195 + ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
  196 + [request setDelegate:self];
  197 + [request setDidFinishSelector:@selector(addedRequestComplete:)];
  198 + [[self addMoreRequestsQueue] addOperation:request];
  199 +}
  200 +
  201 +- (void)addedRequestComplete:(ASIHTTPRequest *)request
  202 +{
  203 + requestsFinishedCount++;
  204 +}
  205 +
  206 +- (void)addMoreRequestsQueueFinished:(ASINetworkQueue *)queue
  207 +{
  208 + // This might get called multiple times if the queue finishes before all the requests can be added
  209 + // So we'll make sure they're all done first
  210 + while (requestsFinishedCount < 10) {
  211 + [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
  212 + }
  213 +
  214 + BOOL success = (progress == 1.0);
  215 + GHAssertTrue(success,@"Failed to increment progress properly");
  216 +
  217 + // The file we downloaded 10 times is 130050 bytes long
  218 + success = ([queue totalBytesToDownload] == 130050*10);
  219 + GHAssertTrue(success,@"Failed to increment total download size properly");
  220 +}
160 221
161 222
162 - (void)uploadFailed:(ASIHTTPRequest *)request 223 - (void)uploadFailed:(ASIHTTPRequest *)request
@@ -229,12 +290,6 @@ IMPORTANT @@ -229,12 +290,6 @@ IMPORTANT
229 290
230 - (void)setProgress:(float)newProgress 291 - (void)setProgress:(float)newProgress
231 { 292 {
232 - NSLog(@"%f",newProgress);  
233 - if (newProgress < progress) {  
234 - GHFail(@"Progress went backwards!");  
235 - }  
236 -  
237 -  
238 progress = newProgress; 293 progress = newProgress;
239 } 294 }
240 295
@@ -988,5 +1043,5 @@ IMPORTANT @@ -988,5 +1043,5 @@ IMPORTANT
988 @synthesize cancelQueue; 1043 @synthesize cancelQueue;
989 @synthesize postQueue; 1044 @synthesize postQueue;
990 @synthesize testNTLMQueue; 1045 @synthesize testNTLMQueue;
991 - 1046 +@synthesize addMoreRequestsQueue;
992 @end 1047 @end