QNURLProtocol.m 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. //
  2. // QNURLProtocol.m
  3. // AppTest
  4. //
  5. // Created by yangsen on 2020/4/7.
  6. // Copyright © 2020 com.qiniu. All rights reserved.
  7. //
  8. #import "QNURLProtocol.h"
  9. #import "QNCFHttpClient.h"
  10. #import "NSURLRequest+QNRequest.h"
  11. #import "QNDnsPrefetcher.h"
  12. #import "NSObject+QNSwizzle.h"
  13. #import <objc/runtime.h>
  14. @interface QNRequestInfo : NSObject
  15. @property(nonatomic, weak)NSURLSession *session;
  16. @property(nonatomic, weak)NSURLSessionDataTask *task;
  17. @end
  18. @implementation QNRequestInfo
  19. @end
  20. @interface QNRequestInfoManager : NSObject
  21. @property(nonatomic, strong)NSMutableDictionary <NSString *, QNRequestInfo *> *infos;
  22. @end
  23. @implementation QNRequestInfoManager
  24. + (instancetype)share{
  25. static QNRequestInfoManager *manager = nil;
  26. static dispatch_once_t onceToken;
  27. dispatch_once(&onceToken, ^{
  28. manager = [[QNRequestInfoManager alloc] init];
  29. [manager setupData];
  30. });
  31. return manager;
  32. }
  33. - (void)setupData{
  34. _infos = [NSMutableDictionary dictionary];
  35. }
  36. - (void)setRequestInfo:(QNRequestInfo *)info forRequest:(NSURLRequest *)request{
  37. NSString *requestIdentifier = [request qn_identifier];
  38. if (!requestIdentifier || !info) {
  39. return;
  40. }
  41. @synchronized (self) {
  42. [self.infos setObject:info forKey:requestIdentifier];
  43. }
  44. }
  45. - (void)removeRequestInfoForRequest:(NSURLRequest *)request{
  46. NSString *requestIdentifier = [request qn_identifier];
  47. if (!requestIdentifier) {
  48. return;
  49. }
  50. @synchronized (self) {
  51. [self.infos removeObjectForKey:requestIdentifier];
  52. }
  53. }
  54. - (QNRequestInfo *)getRequestInfoForRequest:(NSURLRequest *)request{
  55. NSString *requestIdentifier = [request qn_identifier];
  56. if (!requestIdentifier) {
  57. return nil;
  58. }
  59. QNRequestInfo *info = nil;
  60. @synchronized (self) {
  61. info = self.infos[requestIdentifier];
  62. }
  63. return info;
  64. }
  65. @end
  66. @interface NSURLRequest(QNHttps)
  67. @property(nonatomic, readonly)NSURLSession *qn_session;
  68. @property(nonatomic, readonly)NSURLSessionDataTask *qn_task;
  69. @end
  70. @implementation NSURLRequest(QNHttps)
  71. - (NSURLSession *)qn_session{
  72. return [[QNRequestInfoManager share] getRequestInfoForRequest:self].session;
  73. }
  74. - (NSURLSessionDataTask *)qn_task{
  75. return [[QNRequestInfoManager share] getRequestInfoForRequest:self].task;
  76. }
  77. - (void)qn_setSession:(NSURLSession *)session task:(NSURLSessionDataTask *)task{
  78. QNRequestInfo *info = [[QNRequestInfo alloc] init];
  79. info.session = session;
  80. info.task = task;
  81. [[QNRequestInfoManager share] setRequestInfo:info forRequest:self];
  82. }
  83. - (void)qn_requestRemoveTask{
  84. [[QNRequestInfoManager share] removeRequestInfoForRequest:self];
  85. }
  86. - (BOOL)qnHttps_shouldInit{
  87. if ([self qn_isQiNiuRequest]
  88. && ([self.URL.absoluteString hasPrefix:@"http://"] || [self.URL.absoluteString hasPrefix:@"https://"])) {
  89. return YES;
  90. } else {
  91. return NO;
  92. }
  93. }
  94. @end
  95. @interface NSURLSession(QNURLProtocol)
  96. @end
  97. @implementation NSURLSession(QNURLProtocol)
  98. + (void)load{
  99. static dispatch_once_t onceToken;
  100. dispatch_once(&onceToken, ^{
  101. [self qn_swizzleInstanceMethodsOfSelectorA:@selector(dataTaskWithRequest:)
  102. selectorB:@selector(qn_dataTaskWithRequest:)];
  103. });
  104. }
  105. - (NSURLSessionDataTask *)qn_dataTaskWithRequest:(NSURLRequest *)request{
  106. NSURLSessionDataTask *task = [self qn_dataTaskWithRequest:request];
  107. if ([request qn_isQiNiuRequest]) {
  108. [request qn_setSession:self task:task];
  109. }
  110. return task;
  111. }
  112. @end
  113. @interface QNURLProtocol()<QNCFHttpClientDelegate>
  114. @property(nonatomic, strong)QNCFHttpClient *httpsClient;
  115. @end
  116. @implementation QNURLProtocol
  117. #define kQNRequestIdentifiers @"QNRequestIdentifiers"
  118. + (void)registerProtocol{
  119. [NSURLProtocol registerClass:[QNURLProtocol class]];
  120. }
  121. + (void)unregisterProtocol{
  122. [NSURLProtocol unregisterClass:[QNURLProtocol class]];
  123. }
  124. //MARK: -- overload
  125. + (BOOL)canInitWithRequest:(NSURLRequest *)request {
  126. if ([NSURLProtocol propertyForKey:kQNRequestIdentifiers inRequest:request]) {
  127. return NO;
  128. }
  129. id <QNInetAddressDelegate> address = [kQNDnsPrefetcher getInetAddressByHost:request.URL.host].firstObject;
  130. if ([request qnHttps_shouldInit]
  131. && address.ipValue && address.ipValue.length > 0) {
  132. return YES;
  133. } else {
  134. [request qn_requestRemoveTask];
  135. return NO;
  136. }
  137. }
  138. + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
  139. NSString *host = request.URL.host;
  140. NSString *ip = [kQNDnsPrefetcher getInetAddressByHost:host].firstObject.ipValue;
  141. if (!ip || ip.length == 0) {
  142. return request;
  143. }
  144. NSString *urlString = request.URL.absoluteString;
  145. urlString = [urlString stringByReplacingOccurrencesOfString:host withString:ip];
  146. NSMutableURLRequest *requestNew = [request mutableCopy];
  147. requestNew.URL = [NSURL URLWithString:urlString];
  148. return [requestNew copy];
  149. }
  150. - (void)startLoading {
  151. [self loadingRequest:self.request];
  152. }
  153. - (void)stopLoading {
  154. [self.httpsClient stopLoading];
  155. self.httpsClient = nil;
  156. }
  157. - (void)loadingRequest:(NSURLRequest *)request{
  158. self.httpsClient = [QNCFHttpClient client:request];
  159. self.httpsClient.delegate = self;
  160. [NSURLProtocol setProperty:@(YES)
  161. forKey:kQNRequestIdentifiers
  162. inRequest:self.httpsClient.request];
  163. [self.httpsClient startLoading];
  164. }
  165. //MARK: -- delegate
  166. - (void)didSendBodyData:(int64_t)bytesSent
  167. totalBytesSent:(int64_t)totalBytesSent
  168. totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend{
  169. id <NSURLSessionTaskDelegate> sessionDelegate = (id <NSURLSessionTaskDelegate>)self.request.qn_session.delegate;
  170. if ([sessionDelegate respondsToSelector:@selector(URLSession:
  171. task:
  172. didSendBodyData:
  173. totalBytesSent:
  174. totalBytesExpectedToSend:)]) {
  175. [sessionDelegate URLSession:self.request.qn_session
  176. task:self.request.qn_task
  177. didSendBodyData:bytesSent
  178. totalBytesSent:totalBytesSent
  179. totalBytesExpectedToSend:totalBytesExpectedToSend];
  180. }
  181. }
  182. - (void)didFinish {
  183. [self.client URLProtocolDidFinishLoading:self];
  184. [self.request qn_requestRemoveTask];
  185. }
  186. - (void)didLoadData:(nonnull NSData *)data {
  187. [self.client URLProtocol:self didLoadData:data];
  188. }
  189. - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
  190. NSMutableArray *policies = [NSMutableArray array];
  191. if (domain) {
  192. [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
  193. } else {
  194. [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
  195. }
  196. SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
  197. SecTrustResultType result = kSecTrustResultInvalid;
  198. OSStatus status = SecTrustEvaluate(serverTrust, &result);
  199. if (status != errSecSuccess) {
  200. return NO;
  201. }
  202. if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
  203. return YES;
  204. } else {
  205. return NO;
  206. }
  207. }
  208. - (void)onError:(nonnull NSError *)error {
  209. [self.client URLProtocol:self didFailWithError:error];
  210. }
  211. - (void)onReceiveResponse:(nonnull NSURLResponse *)response {
  212. [self.client URLProtocol:self
  213. didReceiveResponse:response
  214. cacheStoragePolicy:NSURLCacheStorageNotAllowed];
  215. }
  216. - (void)redirectedToRequest:(nonnull NSURLRequest *)request redirectResponse:(nonnull NSURLResponse *)redirectResponse {
  217. if ([self.client respondsToSelector:@selector(URLProtocol:wasRedirectedToRequest:redirectResponse:)]) {
  218. [self.client URLProtocol:self wasRedirectedToRequest:request redirectResponse:redirectResponse];
  219. [self.client URLProtocolDidFinishLoading:self];
  220. } else {
  221. [self.httpsClient stopLoading];
  222. [self loadingRequest:request];
  223. }
  224. }
  225. @end
  226. @implementation NSURLSessionConfiguration(QNURLProtocol)
  227. + (NSURLSessionConfiguration *)qn_sessionConfiguration{
  228. NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
  229. config.protocolClasses = @[[QNURLProtocol class]];
  230. return config;
  231. }
  232. @end