QNCFHttpClient.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. //
  2. // QNHttpClient.m
  3. // AppTest
  4. //
  5. // Created by yangsen on 2020/4/7.
  6. // Copyright © 2020 com.qiniu. All rights reserved.
  7. //
  8. #import "QNCFHttpClient.h"
  9. #import "NSURLRequest+QNRequest.h"
  10. @interface QNCFHttpClient()<NSStreamDelegate>
  11. @property(nonatomic, strong)NSMutableURLRequest *request;
  12. @property(nonatomic, assign)BOOL isReadResponseHeader;
  13. @property(nonatomic, assign)BOOL isInputStreamEvaluated;
  14. @property(nonatomic, strong)NSInputStream *inputStream;
  15. @property(nonatomic, strong)NSRunLoop *inputStreamRunLoop;
  16. // 模拟上传进度
  17. @property(nonatomic, strong)NSTimer *progressTimer; // 进度定时器
  18. @property(nonatomic, assign)int64_t totalBytesSent; // 已上传大小
  19. @property(nonatomic, assign)int64_t bytesSent; // 模拟每次上传大小
  20. @property(nonatomic, assign)int64_t maxBytesSent; // 模拟上传最大值
  21. @property(nonatomic, assign)int64_t totalBytesExpectedToSend; // 总大小
  22. @end
  23. @implementation QNCFHttpClient
  24. + (instancetype)client:(NSURLRequest *)request{
  25. if (!request) {
  26. return nil;
  27. }
  28. QNCFHttpClient *client = [[QNCFHttpClient alloc] init];
  29. [client setup:request];
  30. return client;
  31. }
  32. - (void)setup:(NSURLRequest *)request{
  33. @autoreleasepool {
  34. self.request = [request mutableCopy];
  35. NSInputStream *inputStream = [self createInputStream:self.request];
  36. NSString *host = [self.request qn_domain];
  37. if ([self.request qn_isHttps]) {
  38. [self setInputStreamSNI:inputStream sni:host];
  39. }
  40. [self setupProgress];
  41. self.inputStream = inputStream;
  42. }
  43. }
  44. - (void)startLoading{
  45. [self openInputStream];
  46. [self startProgress];
  47. }
  48. - (void)stopLoading{
  49. [self closeInputStream];
  50. [self endProgress:YES];
  51. }
  52. //MARK: -- rquest -> stream
  53. - (NSInputStream *)createInputStream:(NSURLRequest *)urlRequest{
  54. CFStringRef urlString = (__bridge CFStringRef) [urlRequest.URL absoluteString];
  55. CFURLRef url = CFURLCreateWithString(kCFAllocatorDefault,
  56. urlString,
  57. NULL);
  58. CFStringRef httpMethod = (__bridge CFStringRef) urlRequest.HTTPMethod;
  59. CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
  60. httpMethod,
  61. url,
  62. kCFHTTPVersion1_1);
  63. CFRelease(url);
  64. NSDictionary *headFieldInfo = self.request.qn_allHTTPHeaderFields;
  65. for (NSString *headerField in headFieldInfo) {
  66. CFStringRef headerFieldP = (__bridge CFStringRef)headerField;
  67. CFStringRef headerFieldValueP = (__bridge CFStringRef)(headFieldInfo[headerField]);
  68. CFHTTPMessageSetHeaderFieldValue(request, headerFieldP, headerFieldValueP);
  69. }
  70. NSData *httpBody = [self.request qn_getHttpBody];
  71. if (httpBody) {
  72. CFDataRef bodyData = (__bridge CFDataRef) httpBody;
  73. CFHTTPMessageSetBody(request, bodyData);
  74. }
  75. CFReadStreamRef readStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, request);
  76. NSInputStream *inputStream = (__bridge_transfer NSInputStream *) readStream;
  77. CFRelease(request);
  78. return inputStream;
  79. }
  80. - (void)setInputStreamSNI:(NSInputStream *)inputStream sni:(NSString *)sni{
  81. if (!sni || sni.length == 0) {
  82. return;
  83. }
  84. NSMutableDictionary *settings = [NSMutableDictionary dictionary];
  85. [settings setObject:NSStreamSocketSecurityLevelNegotiatedSSL
  86. forKey:NSStreamSocketSecurityLevelKey];
  87. [settings setObject:sni
  88. forKey:(NSString *)kCFStreamSSLPeerName];
  89. [inputStream setProperty:settings forKey:(NSString *)CFBridgingRelease(kCFStreamPropertySSLSettings)];
  90. }
  91. //MARK: -- stream action
  92. - (void)openInputStream{
  93. if (!self.inputStreamRunLoop) {
  94. self.inputStreamRunLoop = [NSRunLoop currentRunLoop];
  95. }
  96. [self.inputStream scheduleInRunLoop:self.inputStreamRunLoop
  97. forMode:NSRunLoopCommonModes];
  98. self.inputStream.delegate = self;
  99. [self.inputStream open];
  100. }
  101. - (void)closeInputStream {
  102. [self.inputStream removeFromRunLoop:self.inputStreamRunLoop forMode:NSRunLoopCommonModes];
  103. [self.inputStream setDelegate:nil];
  104. [self.inputStream close];
  105. self.inputStream = nil;
  106. }
  107. - (BOOL)shouldEvaluateInputStreamServerTrust{
  108. if (![self.request qn_isHttps] || self.isInputStreamEvaluated) {
  109. return NO;
  110. } else {
  111. return YES;
  112. }
  113. }
  114. - (void)evaluateInputStreamServerTrust{
  115. if (self.isInputStreamEvaluated) {
  116. return;
  117. }
  118. SecTrustRef trust = (__bridge SecTrustRef) [self.inputStream propertyForKey:(__bridge NSString *) kCFStreamPropertySSLPeerTrust];
  119. NSString *host = [self.request allHTTPHeaderFields][@"host"];
  120. if ([self delegate_evaluateServerTrust:trust forDomain:host]) {
  121. self.isInputStreamEvaluated = YES;
  122. } else {
  123. [self delegate_onError:[NSError errorWithDomain:@"CFNetwork SSLHandshake failed"
  124. code:-9807
  125. userInfo:nil]];
  126. }
  127. }
  128. - (void)inputStreamGetAndNotifyHttpResponse{
  129. CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
  130. CFHTTPMessageRef httpMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
  131. CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(httpMessage);
  132. NSDictionary *headInfo = (__bridge_transfer NSDictionary *)headerFields;
  133. CFStringRef httpVersion = CFHTTPMessageCopyVersion(httpMessage);
  134. NSString *httpVersionInfo = (__bridge_transfer NSString *)httpVersion;
  135. CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(httpMessage);
  136. if (![self isHttpRedirectStatusCode:statusCode]) {
  137. NSHTTPURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL statusCode:statusCode HTTPVersion:httpVersionInfo headerFields:headInfo];
  138. [self delegate_onReceiveResponse:response];
  139. }
  140. CFRelease(httpMessage);
  141. }
  142. - (void)inputStreamGetAndNotifyHttpData{
  143. UInt8 buffer[16 * 1024];
  144. UInt8 *buf = NULL;
  145. NSUInteger length = 0;
  146. if (![self.inputStream getBuffer:&buf length:&length]) {
  147. NSInteger amount = [self.inputStream read:buffer maxLength:sizeof(buffer)];
  148. buf = buffer;
  149. length = amount;
  150. }
  151. NSData *data = [[NSData alloc] initWithBytes:buf length:length];
  152. [self delegate_didLoadData:data];
  153. }
  154. - (void)inputStreamDidLoadHttpResponse{
  155. [self delegate_didFinish];
  156. }
  157. - (BOOL)isInputStreamHttpResponseHeaderComplete{
  158. CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
  159. CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
  160. BOOL isComplete = CFHTTPMessageIsHeaderComplete(responseMessage);
  161. CFRelease(responseMessage);
  162. return isComplete;
  163. }
  164. - (BOOL)shouldInputStreamRedirect{
  165. CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
  166. CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
  167. CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage);
  168. CFRelease(responseMessage);
  169. return [self isHttpRedirectStatusCode:statusCode];
  170. }
  171. - (BOOL)isHttpRedirectStatusCode:(NSInteger)code{
  172. if (code >= 300 && code < 400) {
  173. return YES;
  174. } else {
  175. return NO;
  176. }
  177. }
  178. - (void)inputStreamRedirect{
  179. CFReadStreamRef readStream = (__bridge CFReadStreamRef)self.inputStream;
  180. CFHTTPMessageRef responseMessage = (CFHTTPMessageRef)CFReadStreamCopyProperty(readStream, kCFStreamPropertyHTTPResponseHeader);
  181. CFDictionaryRef headerFields = CFHTTPMessageCopyAllHeaderFields(responseMessage);
  182. NSDictionary *headInfo = (__bridge_transfer NSDictionary *)headerFields;
  183. NSString *urlString = headInfo[@"Location"];
  184. if (!urlString) {
  185. urlString = headInfo[@"location"];
  186. }
  187. if (!urlString) {
  188. return;
  189. }
  190. CFStringRef httpVersion = CFHTTPMessageCopyVersion(responseMessage);
  191. NSString *httpVersionString = (__bridge_transfer NSString *)httpVersion;
  192. CFIndex statusCode = CFHTTPMessageGetResponseStatusCode(responseMessage);
  193. NSURL *url = [NSURL URLWithString:urlString];
  194. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
  195. request.HTTPMethod = @"GET";
  196. NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:self.request.URL
  197. statusCode:statusCode
  198. HTTPVersion:httpVersionString
  199. headerFields:headInfo];
  200. [self delegate_redirectedToRequest:request redirectResponse:response];
  201. CFRelease(responseMessage);
  202. }
  203. //MARK: -- NSStreamDelegate
  204. - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode{
  205. @autoreleasepool {
  206. switch (eventCode) {
  207. case NSStreamEventHasBytesAvailable:{
  208. if (![self isInputStreamHttpResponseHeaderComplete]) {
  209. break;
  210. }
  211. if ([self shouldEvaluateInputStreamServerTrust]) {
  212. [self evaluateInputStreamServerTrust];
  213. }
  214. if (self.isReadResponseHeader == NO) {
  215. self.isReadResponseHeader = YES;
  216. [self inputStreamGetAndNotifyHttpResponse];
  217. }
  218. [self inputStreamGetAndNotifyHttpData];
  219. }
  220. break;
  221. case NSStreamEventHasSpaceAvailable:
  222. break;
  223. case NSStreamEventErrorOccurred:{
  224. [self endProgress: YES];
  225. [self closeInputStream];
  226. [self delegate_onError:[self.inputStream streamError]];
  227. }
  228. break;
  229. case NSStreamEventEndEncountered:{
  230. if ([self shouldInputStreamRedirect]) {
  231. [self inputStreamRedirect];
  232. } else {
  233. [self endProgress: NO];
  234. [self inputStreamDidLoadHttpResponse];
  235. }
  236. }
  237. break;
  238. default:
  239. break;
  240. }
  241. }
  242. }
  243. //MARK: -- progress and timer action
  244. - (void)setupProgress{
  245. self.bytesSent = 256 * 1024;
  246. self.totalBytesExpectedToSend = [self.request.qn_getHttpBody length];
  247. self.maxBytesSent = self.totalBytesExpectedToSend * 0.5;
  248. }
  249. - (void)startProgress{
  250. [self createTimer];
  251. }
  252. - (void)endProgress:(BOOL)hasError{
  253. [self invalidateTimer];
  254. if (!hasError) {
  255. [self delegate_didSendBodyData:self.totalBytesExpectedToSend - self.totalBytesSent
  256. totalBytesSent:self.totalBytesExpectedToSend
  257. totalBytesExpectedToSend:self.totalBytesExpectedToSend];
  258. }
  259. }
  260. - (void)createTimer{
  261. if (_progressTimer) {
  262. [self invalidateTimer];
  263. }
  264. __weak typeof(self) weakSelf = self;
  265. NSTimer *timer = [NSTimer timerWithTimeInterval:0.5
  266. target:weakSelf
  267. selector:@selector(timerAction)
  268. userInfo:nil
  269. repeats:YES];
  270. [[NSRunLoop currentRunLoop] addTimer:timer
  271. forMode:NSDefaultRunLoopMode];
  272. [self timerAction];
  273. _progressTimer = timer;
  274. }
  275. - (void)invalidateTimer{
  276. [self.progressTimer invalidate];
  277. self.progressTimer = nil;
  278. }
  279. - (void)timerAction{
  280. self.totalBytesSent += self.bytesSent;
  281. if (self.totalBytesSent < self.maxBytesSent) {
  282. [self delegate_didSendBodyData:self.bytesSent
  283. totalBytesSent:self.totalBytesSent
  284. totalBytesExpectedToSend:self.totalBytesExpectedToSend];
  285. }
  286. }
  287. //MARK: -- delegate action
  288. - (void)delegate_redirectedToRequest:(NSURLRequest *)request
  289. redirectResponse:(NSURLResponse *)redirectResponse{
  290. if ([self.delegate respondsToSelector:@selector(redirectedToRequest:redirectResponse:)]) {
  291. [self.delegate redirectedToRequest:request redirectResponse:redirectResponse];
  292. }
  293. }
  294. - (BOOL)delegate_evaluateServerTrust:(SecTrustRef)serverTrust
  295. forDomain:(NSString *)domain{
  296. if ([self.delegate respondsToSelector:@selector(evaluateServerTrust:forDomain:)]) {
  297. return [self.delegate evaluateServerTrust:serverTrust forDomain:domain];
  298. } else {
  299. return NO;
  300. }
  301. }
  302. - (void)delegate_onError:(NSError *)error{
  303. if ([self.delegate respondsToSelector:@selector(onError:)]) {
  304. [self.delegate onError:error];
  305. }
  306. }
  307. - (void)delegate_didSendBodyData:(int64_t)bytesSent
  308. totalBytesSent:(int64_t)totalBytesSent
  309. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  310. if ([self.delegate respondsToSelector:@selector(didSendBodyData:
  311. totalBytesSent:
  312. totalBytesExpectedToSend:)]) {
  313. [self.delegate didSendBodyData:bytesSent
  314. totalBytesSent:totalBytesSent
  315. totalBytesExpectedToSend:totalBytesExpectedToSend];
  316. }
  317. }
  318. - (void)delegate_onReceiveResponse:(NSURLResponse *)response{
  319. if ([self.delegate respondsToSelector:@selector(onReceiveResponse:)]) {
  320. [self.delegate onReceiveResponse:response];
  321. }
  322. }
  323. - (void)delegate_didLoadData:(NSData *)data{
  324. if ([self.delegate respondsToSelector:@selector(didLoadData:)]) {
  325. [self.delegate didLoadData:data];
  326. }
  327. }
  328. - (void)delegate_didFinish{
  329. if ([self.delegate respondsToSelector:@selector(didFinish)]) {
  330. [self.delegate didFinish];
  331. }
  332. }
  333. @end