You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
197 lines
5.8 KiB
197 lines
5.8 KiB
// |
|
// TFURLProtocol.m |
|
// WXReader |
|
// |
|
// Created by 谢腾飞 on 2020/12/3. |
|
// Copyright © 2020 xtfei_2011@126.com. All rights reserved. |
|
// |
|
|
|
#import "TFURLProtocol.h" |
|
#import "AppDelegate.h" |
|
#import <objc/runtime.h> |
|
|
|
// 拦截标识符,避免无限循环 |
|
static NSString * const kProtocolIdentifier = @"kProtocolIdentifier"; |
|
// 默认微信Token请求接口前缀 |
|
static NSString * const kDefaultTokenURL = @"https://api.weixin.qq.com/sns/oauth2/access_token?"; |
|
|
|
@interface TFURLProtocol ()<NSURLConnectionDelegate, NSURLConnectionDataDelegate, NSURLSessionDelegate> |
|
|
|
@property (nonatomic ,strong) NSURLSessionDataTask *dataTask; |
|
@property (nonatomic ,strong) NSOperationQueue *sessionDelegateQueue; |
|
@property (nonatomic ,strong) NSURLResponse *response; |
|
@property (nonatomic ,weak) AppDelegate *delegate; |
|
|
|
@end |
|
|
|
@implementation TFURLProtocol |
|
|
|
+ (void)startMonitor |
|
{ |
|
[NSURLProtocol registerClass:self]; |
|
[self sharedInstance]; |
|
[_instance load]; |
|
} |
|
|
|
+ (void)stopMonitor |
|
{ |
|
[NSURLProtocol unregisterClass:self]; |
|
[self sharedInstance]; |
|
[_instance unload]; |
|
} |
|
|
|
static TFURLProtocol *_instance; |
|
+ (instancetype)sharedInstance |
|
{ |
|
return [[self alloc] init]; |
|
} |
|
|
|
+ (instancetype)allocWithZone:(struct _NSZone *)zone |
|
{ |
|
static dispatch_once_t onceToken; |
|
dispatch_once(&onceToken, ^{ |
|
_instance = [super allocWithZone:zone]; |
|
}); |
|
|
|
return _instance; |
|
} |
|
|
|
/// 拦截网络请求 |
|
/// @param request 需要拦截的请求 |
|
+ (BOOL)canInitWithRequest:(NSURLRequest *)request |
|
{ |
|
if ([NSURLProtocol propertyForKey:kProtocolIdentifier inRequest:request]) { |
|
return NO; |
|
} |
|
|
|
// 拦截微信获取Token的网络请求。 |
|
dispatch_semaphore_t signal = dispatch_semaphore_create(0); |
|
AppDelegate * __block delegate; |
|
dispatch_async(dispatch_get_main_queue(), ^{ |
|
delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; |
|
_instance.delegate = delegate; |
|
dispatch_semaphore_signal(signal); |
|
}); |
|
dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER); |
|
NSString *prefix = delegate.checkSettingModel.wechatTokenURL ?: kDefaultTokenURL; |
|
if ([request.URL.absoluteString hasPrefix:prefix]) { |
|
NSURLComponents *components = [[NSURLComponents alloc] initWithString:request.URL.absoluteString]; |
|
[components.queryItems enumerateObjectsUsingBlock:^(NSURLQueryItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { |
|
NSString *name = obj.name; |
|
NSString *value = obj.value; |
|
if ([name isEqualToString:@"code"]) { |
|
delegate.wechatCode = value; |
|
} |
|
}]; |
|
return YES; |
|
} |
|
return NO; |
|
} |
|
|
|
/// 返回自定义的网络请求 |
|
/// @param request 网络请求 |
|
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request |
|
{ |
|
return [[NSURLRequest alloc] init]; |
|
} |
|
|
|
// 重写父类的开始加载方法 |
|
- (void)startLoading |
|
{ |
|
NSURLSessionConfiguration *configuration = |
|
[NSURLSessionConfiguration defaultSessionConfiguration]; |
|
|
|
self.sessionDelegateQueue = [[NSOperationQueue alloc] init]; |
|
self.sessionDelegateQueue.maxConcurrentOperationCount = 1; |
|
self.sessionDelegateQueue.name = @"com.beiwo.queue"; |
|
|
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:self.sessionDelegateQueue]; |
|
|
|
self.dataTask = [session dataTaskWithRequest:self.request]; |
|
[self.dataTask resume]; |
|
} |
|
|
|
// 结束加载 |
|
- (void)stopLoading |
|
{ |
|
[self.dataTask cancel]; |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error |
|
{ |
|
if (!error) { |
|
[self.client URLProtocolDidFinishLoading:self]; |
|
} else if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { |
|
} else { |
|
[self.client URLProtocol:self didFailWithError:error]; |
|
} |
|
self.dataTask = nil; |
|
} |
|
|
|
// 当服务端返回信息时,这个回调函数会被ULS调用,在这里实现http返回信息的截取 |
|
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask |
|
didReceiveData:(NSData *)data |
|
{ |
|
[self.client URLProtocol:self didLoadData:data]; |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler |
|
{ |
|
[[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed]; |
|
completionHandler(NSURLSessionResponseAllow); |
|
|
|
self.response = response; |
|
} |
|
|
|
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler |
|
{ |
|
if (response != nil) { |
|
self.response = response; |
|
|
|
[[self client] URLProtocol:self wasRedirectedToRequest:request redirectResponse:response]; |
|
} |
|
} |
|
|
|
- (void)load |
|
{ |
|
if (_isSwizzle == YES) return; |
|
|
|
_isSwizzle = YES; |
|
|
|
[TFSessionConfig swizzleSelector]; |
|
} |
|
|
|
- (void)unload |
|
{ |
|
if (_isSwizzle == NO) return; |
|
|
|
_isSwizzle = NO; |
|
|
|
[TFSessionConfig swizzleSelector]; |
|
} |
|
|
|
@end |
|
|
|
|
|
|
|
|
|
@implementation TFSessionConfig |
|
|
|
+ (void)swizzleSelector |
|
{ |
|
Class originCls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration"); |
|
Method fromMethod = class_getInstanceMethod(originCls, @selector(protocolClasses)); |
|
Method toMethed = class_getInstanceMethod(self.class, @selector(protocolClasses)); |
|
|
|
if (!fromMethod || !toMethed) { |
|
[NSException raise:NSInternalInconsistencyException format:@"无法加载NEURLSessionConfiguration。"]; |
|
} |
|
method_exchangeImplementations(fromMethod, toMethed); |
|
} |
|
|
|
- (NSArray *)protocolClasses |
|
{ |
|
return @[[TFURLProtocol class]]; |
|
} |
|
|
|
@end
|
|
|