{{ v.name }}
{{ v.cls }}类
{{ v.price }} ¥{{ v.price }}
摘要:“ip直连方案”主要在于解决dns污染、省去dns解析时间,通常情况下我们可以在项目中使用nsurlprotocol拦截nsurlsession请求,下面将支持post请求中面临的一个挑战,以及应对策略介绍一下。
“ip直连方案”主要在于解决dns污染、省去dns解析时间,通常情况下我们可以在项目中使用nsurlprotocol拦截nsurlsession请求,下面将支持post请求中面临的一个挑战,以及应对策略介绍一下:
在支持post请求过程中会遇到丢失body的问题,有以下几种解决方法:
方案如下:
对方案做以下分析
////nsurlrequest+cylnsurlprotocolextension.h//////createdbyelonchanon28/07/2017.//copyright©2017chenyilong.allrightsreserved.//#import
上面是我给出的实现,这里注意,刚开始有人做过这样的实现:
-(void)cyl_handlepostrequestbody{if([self.httpmethodisequaltostring:@"post"]){if(!self.httpbody){uint8_td[1024]={0};nsinputstream*stream=self.httpbodystream;nsmutabledata*data=[[nsmutabledataalloc]init];[streamopen];while([streamhasbytesavailable]){nsintegerlen=[streamread:dmaxlength:1024];if(len>0&&stream.streamerror==nil){[dataappendbytes:(void*)dlength:len];}}self.httpbody=[datacopy];[streamclose];}}}
这个实现的问题在于:不能用[streamhasbytesavailable])判断,处理图片文件的时候这里的[streamhasbytesavailable]会始终返回yes,导致在while里面死循环。
apple的文档也说得很清楚:
//returnsino(1)apointertothebufferin'buffer'andbyreferencein'len'howmanybytesareavailable.thisbufferisonlyvaliduntilthenextstreamoperation.subclassersmayreturnnoforthisifitisnotappropriateforthestreamtype.thismayreturnnoifthebufferisnotavailable.@property(readonly)boolhasbytesavailable;
给出了实现,下面介绍下使用方法:
在用于拦截请求的nsurlprotocol的子类中实现方法+canonicalrequestforrequest:并处理request对象:
+(nsurlrequest*)canonicalrequestforrequest:(nsurlrequest*)request{return[requestcyl_getpostrequestincludebody];}
下面介绍下相关方法的作用:
//nsurlprotocol.h/*!@methodcaninitwithrequest:@abstractthismethoddetermineswhetherthisprotocolcanhandlethegivenrequest.@discussionaconcretesubclassshouldinspectthegivenrequestanddeterminewhetherornottheimplementationcanperformaloadwiththatrequest.thisisanabstractmethod.sublassesmustprovideanimplementation.@paramrequestarequesttoinspect.@resultyesiftheprotocolcanhandlethegivenrequest,noifnot.*/+(bool)caninitwithrequest:(nsurlrequest*)request;/*!@methodcanonicalrequestforrequest:@abstractthismethodreturnsacanonicalversionofthegivenrequest.@discussionitisuptoeachconcreteprotocolimplementationtodefinewhat"canonical"means.however,aprotocolshouldguaranteethatthesameinputrequestalwaysyieldsthesamecanonicalform.specialconsiderationshouldbegivenwhenimplementingthismethodsincethecanonicalformofarequestisusedtolookupobjectsintheurlcache,aprocesswhichperformsequalitychecksbetweennsurlrequestobjects.
thisisanabstractmethod;sublassesmustprovideanimplementation.@paramrequestarequesttomakecanonical.@resultthecanonicalformofthegivenrequest.*/+(nsurlrequest*)canonicalrequestforrequest:(nsurlrequest*)request;
翻译下:
//nsurlprotocol.h/*!*@method:创建nsurlprotocol实例,nsurlprotocol注册之后,所有的nsurlconnection都会通过这个方法检查是否持有该http请求。@parma:@return:yes:持有该http请求no:不持有该http请求*/+(bool)caninitwithrequest:(nsurlrequest*)request/*!*@method:nsurlprotocol抽象类必须要实现。通常情况下这里有一个最低的标准:即输入输出请求满足最基本的协议规范一致。因此这里简单的做法可以直接返回。一般情况下我们是不会去更改这个请求的。如果你想更改,比如给这个request添加一个title,组合成一个新的http请求。@parma:本地httprequest请求:request@return:直接转发*/+(nsurlrequest*)canonicalrequestforrequest:(nsurlrequest*)request
简单说:
这里有一个注意点:+[nsurlprotocolcanonicalrequestforrequest:]的执行条件是+[nsurlprotocolcaninitwithrequest:]返回值为yes。
注意在拦截nsurlsession请求时,需要将用于拦截请求的nsurlprotocol的子类添加到nsurlsessionconfiguration中,用法如下:
nsurlsessionconfiguration*configuration=[nsurlsessionconfigurationdefaultsessionconfiguration];nsarray*protocolarray=@[[cylurlprotocolclass]];configuration.protocolclasses=protocolarray;nsurlsession*session=[nsurlsessionsessionwithconfiguration:configurationdelegate:selfdelegatequeue:[nsoperationqueuemainqueue]];换用其他提供了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'
使用libcurl来解决,libcurl/curl至少7.18.1(2008年3月30日)在sni支持下编译一个ssl/tls工具包,curl中有一个--resolve方法可以实现使用指定ip访问https网站。
在ios实现中,代码如下
//{https域名}:443:{ip地址}nsstring*curlhost=...;_hosts_list=curl_slist_append(_hosts_list,curlhost.utf8string);curl_easy_setopt(_curl,curlopt_resolve,_hosts_list);
其中curlhost形如:
{https域名}:443:{ip地址}
_hosts_list是结构体类型hosts_list,可以设置多个ip与host之间的映射关系。curl_easy_setopt方法中传入curlopt_resolve将该映射设置到https请求中。
这样就可以达到设置sni的目的。
我在这里写了一个demo:cylcurlnetworking,里面包含了编译好的支持ipv6的libcurl包,演示了下如何通过curl来进行类似nsurlsession。
注意以上讨论不涉及wkwebview中拦截nsurlsession请求的body丢失问题。
文中提到的几个概念:
文中部分提到的域名,如果没有特殊说明均指的是fqdn。