首先补充点东西。
main 方法中的[self buildRequestHeaders];
- (void)buildRequestHeaders { if ([self haveBuiltRequestHeaders]) { return; } [self setHaveBuiltRequestHeaders:YES]; if ([self mainRequest]) { for (NSString *header in [[self mainRequest] requestHeaders]) { [self addRequestHeader:header value:[[[self mainRequest] requestHeaders] valueForKey:header]]; } return; } [self applyCookieHeader]; // Build and set the user agent string if the request does not already have a custom user agent specified if (![[self requestHeaders] objectForKey:@"User-Agent"]) { NSString *userAgentString = [ASIHTTPRequest defaultUserAgentString]; if (userAgentString) { [self addRequestHeader:@"User-Agent" value:userAgentString]; } } // Accept a compressed response if ([self allowCompressedResponse]) { [self addRequestHeader:@"Accept-Encoding" value:@"gzip"]; } // Configure a compressed request body if ([self shouldCompressRequestBody]) { [self addRequestHeader:@"Content-Encoding" value:@"gzip"]; } // Should this request resume an existing download? [self updatePartialDownloadSize]; if ([self partialDownloadSize]) { [self addRequestHeader:@"Range" value:[NSString stringWithFormat:@"bytes=%llu-",[self partialDownloadSize]]]; } } - (void)updatePartialDownloadSize { NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; if ([self allowResumeForFileDownloads] && [self downloadDestinationPath] && [self temporaryFileDownloadPath] && [fileManager fileExistsAtPath:[self temporaryFileDownloadPath]]) { NSError *err = nil; [self setPartialDownloadSize:[[fileManager attributesOfItemAtPath:[self temporaryFileDownloadPath] error:&err] fileSize]]; if (err) { [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",[self temporaryFileDownloadPath]],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]]; return; } } }
buildRequestHeaders 主要是设置gzip,断点续传
断点续传需要设置的属性如下:
allowResumeForFileDownloads
downloadDestinationPath
temporaryFileDownloadPath
if ([self allowResumeForFileDownloads] && [self downloadDestinationPath] && [self temporaryFileDownloadPath] && [fileManager fileExistsAtPath:[self temporaryFileDownloadPath]]) { NSError *err = nil; [self setPartialDownloadSize:[[fileManager attributesOfItemAtPath:[self temporaryFileDownloadPath] error:&err] fileSize]]; if (err) { [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIFileManagementError userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"Failed to get attributes for file at path '%@'",[self temporaryFileDownloadPath]],NSLocalizedDescriptionKey,error,NSUnderlyingErrorKey,nil]]]; return; } }
- (void)startRequest { if ([self isCancelled]) { return; } [self performSelectorOnMainThread:@selector(requestStarted) withObject:nil waitUntilDone:[NSThread isMainThread]]; [self setDownloadComplete:NO]; [self setComplete:NO]; [self setTotalBytesRead:0]; [self setLastBytesRead:0]; if ([self redirectCount] == 0) { [self setOriginalURL:[self url]]; } // If we're retrying a request, let's remove any progress we made if ([self lastBytesSent] > 0) { [self removeUploadProgressSoFar]; } [self setLastBytesSent:0]; [self setContentLength:0]; [self setResponseHeaders:nil]; if (![self downloadDestinationPath]) { [self setRawResponseData:[[[NSMutableData alloc] init] autorelease]]; } // // Create the stream for the request // NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; [self setReadStreamIsScheduled:NO]; // Do we need to stream the request body from disk if ([self shouldStreamPostDataFromDisk] && [self postBodyFilePath] && [fileManager fileExistsAtPath:[self postBodyFilePath]]) { // Are we gzipping the request body? if ([self compressedPostBodyFilePath] && [fileManager fileExistsAtPath:[self compressedPostBodyFilePath]]) { [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self compressedPostBodyFilePath] request:self]]; } else { [self setPostBodyReadStream:[ASIInputStream inputStreamWithFileAtPath:[self postBodyFilePath] request:self]]; } [self setReadStream:[(NSInputStream *)CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream]) autorelease]]; } 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 necessary if ([self postBody] && [[self postBody] length] > 0) { if ([self shouldCompressRequestBody] && [self compressedPostBody]) { [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self compressedPostBody] request:self]]; } else if ([self postBody]) { [self setPostBodyReadStream:[ASIInputStream inputStreamWithData:[self postBody] request:self]]; } [self setReadStream:[(NSInputStream *)CFReadStreamCreateForStreamedHTTPRequest(kCFAllocatorDefault, request,(CFReadStreamRef)[self postBodyReadStream]) autorelease]]; } else { [self setReadStream:[(NSInputStream *)CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request) autorelease]]; } } if (![self readStream]) { [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to create read stream",NSLocalizedDescriptionKey,nil]]]; return; } // // Handle SSL certificate settings // if([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) { NSMutableDictionary *sslProperties = [NSMutableDictionary dictionaryWithCapacity:1]; // Tell CFNetwork not to validate SSL certificates if (![self validatesSecureCertificate]) { [sslProperties setObject:(NSString *)kCFBooleanFalse forKey:(NSString *)kCFStreamSSLValidatesCertificateChain]; } // Tell CFNetwork to use a client certificate if (clientCertificateIdentity) { NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[clientCertificates count]+1]; // The first object in the array is our SecIdentityRef [certificates addObject:(id)clientCertificateIdentity]; // If we've added any additional certificates, add them too for (id cert in clientCertificates) { [certificates addObject:cert]; } [sslProperties setObject:certificates forKey:(NSString *)kCFStreamSSLCertificates]; } CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySSLSettings, sslProperties); } // // Handle proxy settings // if ([self proxyHost] && [self proxyPort]) { NSString *hostKey; NSString *portKey; if (![self proxyType]) { [self setProxyType:(NSString *)kCFProxyTypeHTTP]; } if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) { hostKey = (NSString *)kCFStreamPropertySOCKSProxyHost; portKey = (NSString *)kCFStreamPropertySOCKSProxyPort; } else { hostKey = (NSString *)kCFStreamPropertyHTTPProxyHost; portKey = (NSString *)kCFStreamPropertyHTTPProxyPort; if ([[[[self url] scheme] lowercaseString] isEqualToString:@"https"]) { hostKey = (NSString *)kCFStreamPropertyHTTPSProxyHost; portKey = (NSString *)kCFStreamPropertyHTTPSProxyPort; } } NSMutableDictionary *proxyToUse = [NSMutableDictionary dictionaryWithObjectsAndKeys:[self proxyHost],hostKey,[NSNumber numberWithInt:[self proxyPort]],portKey,nil]; if ([[self proxyType] isEqualToString:(NSString *)kCFProxyTypeSOCKS]) { CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertySOCKSProxy, proxyToUse); } else { CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPProxy, proxyToUse); } } // // Handle persistent connections // [ASIHTTPRequest expirePersistentConnections]; [connectionsLock lock]; if (![[self url] host] || ![[self url] scheme]) { [self setConnectionInfo:nil]; [self setShouldAttemptPersistentConnection:NO]; } // Will store the old stream that was using this connection (if there was one) so we can clean it up once we've opened our own stream NSInputStream *oldStream = nil; // Use a persistent connection if possible if ([self shouldAttemptPersistentConnection]) { // If we are redirecting, we will re-use the current connection only if we are connecting to the same server if ([self connectionInfo]) { if (![[[self connectionInfo] objectForKey:@"host"] isEqualToString:[[self url] host]] || ![[[self connectionInfo] objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] || [(NSNumber *)[[self connectionInfo] objectForKey:@"port"] intValue] != [[[self url] port] intValue]) { [self setConnectionInfo:nil]; // Check if we should have expired this connection } else if ([[[self connectionInfo] objectForKey:@"expires"] timeIntervalSinceNow] < 0) { #if DEBUG_PERSISTENT_CONNECTIONS NSLog(@"Not re-using connection #%i because it has expired",[[[self connectionInfo] objectForKey:@"id"] intValue]); #endif [persistentConnectionsPool removeObject:[self connectionInfo]]; [self setConnectionInfo:nil]; } } if (![self connectionInfo] && [[self url] host] && [[self url] scheme]) { // We must have a proper url with a host and scheme, or this will explode // Look for a connection to the same server in the pool for (NSMutableDictionary *existingConnection in persistentConnectionsPool) { if (![existingConnection objectForKey:@"request"] && [[existingConnection objectForKey:@"host"] isEqualToString:[[self url] host]] && [[existingConnection objectForKey:@"scheme"] isEqualToString:[[self url] scheme]] && [(NSNumber *)[existingConnection objectForKey:@"port"] intValue] == [[[self url] port] intValue]) { [self setConnectionInfo:existingConnection]; } } } if ([[self connectionInfo] objectForKey:@"stream"]) { oldStream = [[[self connectionInfo] objectForKey:@"stream"] retain]; } // No free connection was found in the pool matching the server/scheme/port we're connecting to, we'll need to create a new one if (![self connectionInfo]) { [self setConnectionInfo:[NSMutableDictionary dictionary]]; nextConnectionNumberToCreate++; [[self connectionInfo] setObject:[NSNumber numberWithInt:nextConnectionNumberToCreate] forKey:@"id"]; [[self connectionInfo] setObject:[[self url] host] forKey:@"host"]; [[self connectionInfo] setObject:[NSNumber numberWithInt:[[[self url] port] intValue]] forKey:@"port"]; [[self connectionInfo] setObject:[[self url] scheme] forKey:@"scheme"]; [persistentConnectionsPool addObject:[self connectionInfo]]; } // If we are retrying this request, it will already have a requestID if (![self requestID]) { nextRequestID++; [self setRequestID:[NSNumber numberWithUnsignedInt:nextRequestID]]; } [[self connectionInfo] setObject:[self requestID] forKey:@"request"]; [[self connectionInfo] setObject:[self readStream] forKey:@"stream"]; CFReadStreamSetProperty((CFReadStreamRef)[self readStream], kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue); #if DEBUG_PERSISTENT_CONNECTIONS NSLog(@"Request #%@ will use connection #%i",[self requestID],[[[self connectionInfo] objectForKey:@"id"] intValue]); #endif // Tag the stream with an id that tells it which connection to use behind the scenes // See http://lists.apple.com/archives/macnetworkprog/2008/Dec/msg00001.html for details on this approach CFReadStreamSetProperty((CFReadStreamRef)[self readStream], CFSTR("ASIStreamID"), [[self connectionInfo] objectForKey:@"id"]); } [connectionsLock unlock]; // Schedule the stream if (![self readStreamIsScheduled] && (!throttleWakeUpTime || [throttleWakeUpTime timeIntervalSinceDate:[NSDate date]] < 0)) { [self scheduleReadStream]; } BOOL streamSuccessfullyOpened = NO; // Start the HTTP connection CFStreamClientContext ctxt = {0, self, NULL, NULL, NULL}; if (CFReadStreamSetClient((CFReadStreamRef)[self readStream], kNetworkEvents, ReadStreamClientCallBack, &ctxt)) { if (CFReadStreamOpen((CFReadStreamRef)[self readStream])) { streamSuccessfullyOpened = YES; } } // Here, we'll close the stream that was previously using this connection, if there was one // We've kept it open until now (when we've just opened a new stream) so that the new stream can make use of the old connection // http://lists.apple.com/archives/Macnetworkprog/2006/Mar/msg00119.html if (oldStream) { [oldStream close]; [oldStream release]; oldStream = nil; } if (!streamSuccessfullyOpened) { [self setConnectionCanBeReused:NO]; [self destroyReadStream]; [self failWithError:[NSError errorWithDomain:NetworkRequestErrorDomain code:ASIInternalErrorWhileBuildingRequestType userInfo:[NSDictionary dictionaryWithObjectsAndKeys:@"Unable to start HTTP connection",NSLocalizedDescriptionKey,nil]]]; return; } if (![self mainRequest]) { if ([self shouldResetUploadProgress]) { if ([self showAccurateProgress]) { [self incrementUploadSizeBy:[self postLength]]; } else { [self incrementUploadSizeBy:1]; } [ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:1]; } if ([self shouldResetDownloadProgress] && ![self partialDownloadSize]) { [ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:0 ofTotal:1]; } } // Record when the request started, so we can timeout if nothing happens [self setLastActivityTime:[NSDate date]]; [self setStatusTimer:[NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(updateStatus:) userInfo:nil repeats:YES]]; [[NSRunLoop currentRunLoop] addTimer:[self statusTimer] forMode:[self runLoopMode]]; }
更新进度条状态
if (![self mainRequest]) {
if ([self shouldResetUploadProgress]) {
if ([self showAccurateProgress]) {
[self incrementUploadSizeBy:[self postLength]];
} else {
[self incrementUploadSizeBy:1];
}
[ASIHTTPRequest updateProgressIndicator:&uploadProgressDelegate withProgress:0 ofTotal:1];
}
if ([self shouldResetDownloadProgress] && ![self partialDownloadSize]) {
[ASIHTTPRequest updateProgressIndicator:&downloadProgressDelegate withProgress:0 ofTotal:1];
}
}
都包含在if (![self mainRequest]) {}中,为什么呢??? 有个注释如下:
//Only update progress if this isn't a HEAD request used to preset the content-length
没有搞明白??????
if (![self downloadDestinationPath]) {
[self setRawResponseData:[[[NSMutableData alloc] init] autorelease]];
}
设置返回结果的存储变量 [self setRawResponseData:[[[NSMutableData alloc] init] autorelease]];
取得返回结果:
主要是从这个变量里rawResponseData
- (NSData *)responseData { if ([self isResponseCompressed] && [self shouldWaitToInflateCompressedResponses]) { return [ASIDataDecompressor uncompressData:[self rawResponseData] error:NULL]; } else { return [self rawResponseData]; } return nil; }