`

asihttp 源码分析 之三 startRequest

 
阅读更多

首先补充点东西。

 

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

 

分享到:
评论

相关推荐

    ASiHTTP.zip

    综上所述,ASiHTTP.zip的内容可能包括ASiHTTP库的源码和示例,供开发者学习如何在ARC环境下解决兼容性问题,或者如何将其转换为静态库以避免这些问题。对于初学者或有经验的iOS开发者来说,理解ARC的工作原理,以及...

    ASIHttp.rar_asihttp

    ASIHttp是一个针对iOS平台的网络请求库,基于ASIHttpRequest框架实现。这个框架为开发者提供了强大的网络请求处理能力,包括多任务下载、断点续传等功能,对于构建客户端应用中的网络功能非常有用。以下是对ASIHttp...

    ASIHTTP资源包

    2. **设置请求头**:可以添加自定义请求头,例如`[request setHeaderValue:@"application/json" forHTTPHeaderField:@"Content-Type"]`。 3. **发送POST数据**:可以方便地附加POST参数,`[request appendPostData:...

    ASIHTTP demo

    ASIHTTP 是一个已停止维护但在过去非常流行的iOS和Mac OS X平台上的第三方网络库,它为开发者提供了方便的API来处理HTTP请求。这个"ASIHTTP demo"是一个示例项目,展示了如何利用ASIHTTP库进行异步下载图片并显示...

    ASIHTTPRequest+UITableView实现多个下载任务

    在iOS开发中,实现多个下载任务是常见的需求,特别是在处理数据量大或者用户需要离线浏览的情况下。本项目使用了ASIHTTPRequest库与UITableView相结合来实现这一功能。...而UITableView则是iOS中用于展示列表数据的组件...

    ASIhttp得力助手

    4. **处理cookies**:ASIHTTP自动处理服务器返回的cookies,可以通过`- (void)addCookie:(NSHTTPCookie *)cookie`手动添加cookie,或者通过`- (NSArray *)cookies`获取当前请求的所有cookies。 5. **请求队列管理**...

    ios-ASIHttp天气预报.zip

    利用ASIHttp第三方库,,获取网络请求,,获得天气预报,,,代码显示天气预报效果默认是河南郑州,,亲们可以自己输入并查询自己想知道的城市天气情况,,堪比真机天气

    ASIHTTP+JSON+Google地图,多线程实现加载DEMO

    在iOS开发中,ASIHTTP和JSON是两个非常重要的技术组件,常常用于网络数据的请求与解析,而Google地图API则提供了强大的地图展示功能。在这个"ASIHTTP+JSON+Google地图,多线程实现加载DEMO"中,我们将探讨如何将这些...

    ios_ASIHttp框架详解

    ASIHTTPRequest的详细介绍还包括 同步 异步 缓存 请求网络数据与传输数据

    iOS用ASIHTTP框架异步队列下载图片显示进度条

    request.failureBlock = ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"Error: %@", error); // 错误处理逻辑 }; ``` 综上所述,通过ASIHTTPRequest框架,我们可以轻松地...

    ios开发常用第三方类库集合

    整理的一些市面上常见的第三方类库,快速集成可以方便开发,这些都是大家常用的。 里面包含: GTM 各种加密 ASIHttp http请求 KissXml xml解析 json oc版 json cpp版 Reachabiliy 网络状态判断 libcurl c语言静态...

    ASI库+练习Demo+服务器

    ASIHTTP库是一个经典的Objective-C编写的网络请求框架,主要用于iOS和Mac OS X平台。这个库在iOS开发领域曾经非常流行,特别是在HTTP请求处理方面,它提供了简单易用的API,支持GET、POST等多种HTTP方法,同时也包含...

    ASIHTTPRequest第三方库

    ASIHTTPRequest是一个广泛使用的Objective-C第三方库,专门设计用于简化iOS和macOS平台上的HTTP网络请求。这个库由Sam Soffes开发,它提供了一个简单、直观的接口来发起HTTP请求,处理响应,并管理网络连接。在移动...

    ASIHTTPRequest常用类包

    1. **获取源码**:可以从GitHub或其他源获取ASIHTTPRequest的源代码压缩包,如"ASIHttpRequest"。 2. **导入项目**:将下载的源代码解压后,将包含的"ASIHTTPRequest"文件夹导入到Xcode工程中。 3. **链接库**:在...

    asihttprequest使用指南

    [queue start]; ``` ##### 1.5 在委托方法中处理多个请求的成功和失败 委托方法提供了更细粒度的控制,可以在请求成功或失败时执行特定的操作。 ```objective-c - (void)requestFinished:(ASIHTTPRequest *)...

    进入网络请求,对返回的XML数据进入处理

    本文将深入探讨"进入网络请求,对返回的XML数据进行处理"这一主题,包括如何使用ASIHttp进行网络请求以及如何利用XMLParserSDK解析XML数据。 首先,让我们了解网络请求的基础。在网络编程中,HTTP协议是最常用的一...

    中国电视节目查询

    在iOS开发领域,"中国电视节目查询"这个项目涉及到ASIHttp的使用、XML解析以及自定义Cell这三大关键知识点。接下来,我们将详细探讨这些技术及其在实际应用中的重要性。 首先,ASIHttp是一个广泛使用的Objective-C...

    同步与异步

    相比于ASIHTTP,NSURLConnection更符合苹果的最新编程指南,但在某些场景下可能需要更多的代码来实现相同的功能。 在WebService开发中,无论是同步还是异步,都应根据实际需求和性能考虑选择合适的策略。对于快速...

    IOS ASIHttpRequest资源包

    ASIHTTPRequest是简单易用的,它封装了CFNetwork API。使得与Web服务器通信变得更简单。它是用Objective-C编写的,可以在MAC OS X和iPhone应用中使用。 ...ASIFormDataRequest子类可以简单的实现提交数据和文件。

    使用ASIhttprequest类库上传文件到php服务器之iOS客户端方法

    这时ios中的使用asihttp类库上传文件到php服务器,关于php服务器接收文件见我的另一篇文章

Global site tag (gtag.js) - Google Analytics