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.
198 lines
5.8 KiB
198 lines
5.8 KiB
4 years ago
|
//
|
||
|
// 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
|