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

//
// 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