您的位置:首页 > 其它

NSURLProtocol 拦截 NSURLSession 请求时body丢失问题解决方案探讨

2018-03-26 18:11 639 查看
“IP直连方案”主要在于解决DNS污染、省去DNS解析时间,通常情况下我们可以在项目中使用 NSURLProtocol 拦截 NSURLSession 请求,下面将支持 Post 请求中面临的一个挑战,以及应对策略介绍一下:

在支持POST请求过程中会遇到丢失 body的 问题,有以下几种解决方法:

方案如下:
换用 NSURLConnection
将 body 放进 Header 中
使用 HTTPBodyStream 获取 body,并赋值到 body 中
换用 Get 请求,不使用 Post 请求。

对方案做以下分析
换用 NSURLConnection。NSURLConnection 与 NSURLSession 相比会遇到较多的性能问题,同时Apple的一些新特性也无法使用,终究会被淘汰,不作考虑。
body放header的方法,2M以下没问题,超过2M会导致请求延迟,超过 10M 就直接 Request timeout。而且无法解决 Body 为二进制数据的问题,因为Header里都是文本数据。
换用 Get 请求,不使用 Post 请求。这个也是可行的,但是毕竟对请求方式有限制,终究还是要解决 Post 请求所存在的问题。如果是基于旧项目做修改,则侵入性太大。这种方案适合新的项目。

另一种方法是我们下面主要要讲的,使用 HTTPBodyStream 获取 body,并赋值到 body 中,具体的代码如下,可以解决上面提到的问题:





上面是我给出的实现,这里注意,刚开始有人做过这样的实现:



这个实现的问题在于:不能用 [stream hasBytesAvailable]) 判断,处理图片文件的时候这里的[stream hasBytesAvailable]会始终返回YES,导致在while里面死循环。

Apple的文档也说得很清楚:



给出了实现,下面介绍下使用方法:

在用于拦截请求的 
NSURLProtocol
 的子类中实现方法 
+canonicalRequestForRequest:
 并处理 
request
 对象:



下面介绍下相关方法的作用:



翻译下:



简单说:
+[NSURLProtocol canInitWithRequest:]
 负责筛选哪些网络请求需要被拦截
+[NSURLProtocol canonicalRequestForRequest:]
 负责对需要拦截的网络请求
NSURLRequest
 进行重新构造。

这里有一个注意点:
+[NSURLProtocol canonicalRequestForRequest:]
 的执行条件是 
+[NSURLProtocol
canInitWithRequest:]
 返回值为 
YES


注意在拦截 
NSURLSession
 请求时,需要将用于拦截请求的 NSURLProtocol 的子类添加到 
NSURLSessionConfiguration
 中,用法如下:




换用其他提供了SNI字段配置接口的更底层网络库

如果使用第三方网络库:curl, 中有一个 
-resolve
 方法可以实现使用指定 ip 访问 https 网站,iOS 中集成 curl 库,参考 curl文档 ;

另外有一点也可以注意下,它也是支持 IPv6 环境的,只需要你在 build 时添加上 
--enable-ipv6
 即可。

curl 支持指定 SNI 字段,设置 SNI 时我们需要构造的参数形如: 
{HTTPS域名}:443:{IP地址}


假设你要访问. www.example.org ,若IP为 127.0.0.1 ,那么通过这个方式来调用来设置 SNI 即可:

curl * --resolve 'www.example.org:443:127.0.0.1'


iOS CURL 库

使用libcurl 来解决,libcurl / cURL 至少 7.18.1 (2008年3月30日) 在 SNI 支持下编译一个 SSL/TLS 工具包,
curl
 中有一个 
--resolve
 方法可以实现使用指定ip访问https网站。

在iOS实现中,代码如下



其中 
curlHost
 形如:

{HTTPS域名}:443:{IP地址}


_hosts_list
 是结构体类型
hosts_list
,可以设置多个IP与Host之间的映射关系。
curl_easy_setopt
方法中传入
CURLOPT_RESOLVE
 将该映射设置到
HTTPS 请求中。

这样就可以达到设置SNI的目的。

我在这里写了一个 Demo:CYLCURLNetworking,里面包含了编译好的支持 IPv6 的 libcurl 包,演示了下如何通过curl来进行类似NSURLSession。


参考链接:

Apple
- Communicating with HTTP Servers
Apple
- HTTPS Server Trust Evaluation - Server Name Failures
Apple
- HTTPS Server Trust Evaluation - Trusting One Specific Certificate
[《HTTPDNS > 最佳实践 > HTTPS(含SNI)业务场景“IP直连”方案说明

HTTPS(含SNI)业务场景“IP直连”方案说明》]( https://help.aliyun.com/document_detail/30143.html?spm=5176.doc30141.6.591.A8B1d3 )
《在 curl 中使用指定 ip 来进行请求 https》
支持SNI与WebView的 alicloud-ios-demo
《SNI: 实现多域名虚拟主机的SSL/TLS认证》


补充说明

注意以上讨论不涉及 WKWebView 中拦截 NSURLSession 请求的 body 丢失问题。

文中提到的几个概念:



文中部分提到的域名,如果没有特殊说明均指的是 FQDN。

原文:https://yq.aliyun.com/articles/543412/?spm=a2c41.11181499.0.0
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: