QNURLProtocol.m 7.7 KB

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