“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 丢失问题。
文中提到的几个概念: