NSURLProtocol 研究
ChenghuiBai Lv3

[TOC]

API

// 这个方法是注册NSURLProtocol子类的方法.

+ (BOOL)registerClass:(Class)protocolClass;

// 这个方法是注册后,NSURLProtocol就会通过这个方法确定参数request是否需要被处理
// return : YES 需要经过这个NSURLProtocol”协议” 的处理, NO 这个 协议request不需要遵守这个NSURLProtocol”协议”
// 这个方法的左右 : 1, 筛选Request是否需要遵守这个NSURLRequest , 2, 处理http: , https等URL

+ (BOOL)canInitWithRequest:(NSURLRequest *)request;

// 这个方法就是返回request,当然这里可以处理的需求有 : 1,规范化请求头的信息 2, 处理DNS劫持,重定向App中所有的请求指向等

+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;

// 这个方法主要用来判断两个请求是否是同一个请求,如果是,则可以使用缓存数据,通常只需要调用父类的实现即可,默认为YES,而且一般不在这里做事情

+ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b;

// abstract Initializes an NSURLProtocol given request, cached response, and client.
// 开始初始化一个NSURLProtocol抽象对象, 包含请求, cachedResponse , 和建立client

- (instancetype)initWithRequest:(NSURLRequest *)request cachedResponse:(nullable NSCachedURLResponse *)cachedResponse client:(nullable id <NSURLProtocolClient>)client NS_DESIGNATED_INITIALIZER;

// 需要在该方法中发起一个请求,对于NSURLConnection来说,就是创建一个NSURLConnection,对于NSURLSession,就是发起一个NSURLSessionTask
// 另外一点就是这个方法之后,会回调协议中的方法,

- (void)startLoading

// 这个方法是和start是对应的 一般在这个方法中,断开Connection
// 另外一点就是当NSURLProtocolClient的协议方法都回调完毕后,就会开始执行这个方法了

- (void)stopLoading

多个NSURLProtocol注册

在开发过程中,遇到的一些问题总结一下

如果注册了两个NSURLProtocol,执行顺序是怎样?###

Protocols的遍历是反向的,也就是最后注册的Protocol会被优先判断。
如下图, 先注册AAAA,再注册BBBB的话优先判断的是BBBB,

719b010b9025a0687f94e8dfb5ad23b4.png

具体流程

1、发起请求
2、-(BOOL)canInitxxx
根据userAgent、Method、白名单、URL System Load 判断是否处理请求
不处理,会默认转给系统的NSURLProtocol处理
处理进入下一步
3、canonicalRequestForRequest
添加请求头信息
4、startLoading
看自己是否有缓存:
是否过期,
过期去请求数据
没过期,读取缓存,client 回调数据给webview展示
看系统是否自己有缓存:
处理同上
无缓存:
请求数据
等到数据,缓存数据并由client 回调数据给webview展示

填坑

1、关于私有API
因为WKBrowsingContextController和registerSchemeForCustomProtocol应该是私有的所以使用时候需要对字符串做下处理,用加密的方式或者其他就可以了,实测可以过审核的。

2、关于post请求(个人觉得post就过滤不处理好了)
大家会发现拦截不了post请求(拦截到的post请求body体为空),这个其实和WKWebview没有关系,这个是苹果为了提高效率加快流畅度所以在NSURLProtocol拦截之后索性就不复制body体内的东西,因为body的大小没有限制,开发者可能会把很大的数据放进去那就不好办了。

网上有人说下面的方式可以处理post请求参数的问题,我动手试了发现并不起作用,因为HTTPBodyStream都是空的。

#pragma mark -
#pragma mark 处理POST请求相关POST 用HTTPBodyStream来处理BODY体
- (NSMutableURLRequest *)handlePostRequestBodyWithRequest:(NSMutableURLRequest *)request {
NSMutableURLRequest * req = [request mutableCopy];
if ([request.HTTPMethod isEqualToString:@"POST"]) {
if (!request.HTTPBody) {
uint8_t d[1024] = {0};
NSInputStream *stream = request.HTTPBodyStream;
NSMutableData *data = [[NSMutableData alloc] init];
[stream open];
while ([stream hasBytesAvailable]) {
NSInteger len = [stream read:d maxLength:1024];
if (len > 0 && stream.streamError == nil) {
[data appendBytes:(void *)d length:len];
}
}
req.HTTPBody = [data copy];
[stream close];
}
}
return req;
}

目前我的解决办法是:把post请求参数单独保存,利用键值对保存不同post请求的参数,然后拦截post请求修改请求体。

参考

NSURLProtocol 的使用

NSURLProtocol拦截网络请求

iOS中NSURLProtocol黑魔法的使用

NSURLProtocol全攻略

WKWebView 那些坑(转自 腾讯Bugly)

可能是最全的iOS端HttpDns集成方案

iOS-网络优化(一)

  • Post title:NSURLProtocol 研究
  • Post author:ChenghuiBai
  • Create time:2019-08-06 21:08:43
  • Post link:https://baichenghui.github.io/2019/08/06/NSURLProtocol-研究/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.