在x3.4当中,dfsockopen就存在一系列影响使用的问题,而这些问题在x3.5当中变得更加明显。
受条件限制无法在x3.4上搞大刀阔斧的改造,而小修小补解决不了根本问题。
作为Discuz内唯一的,也是插件等应用非常依赖的网络请求组件,它需要拥有更多的能力。
x3.5让这一切变得可能,同时也能扩展更多的可能性。
(本PR未包含UCenter等其余位置的改造,相关改造参见!675:修复 dfopen的socket相关问题(UCenter及安装程序部分) !677:修复 目前系统内存在的部分小Bug以及小需求 )
曾经的问题:
- dfsockopen无法正常连接依赖SNI的https地址(最严重的问题,直接影响可用性)
- dfsockopen无法支持ipv6,且在其原有实现机制下强行加入ipv6,会严重影响不支持ipv6的服务器的正常使用。
- dfsockopen很难调试查错,遇到问题难以解决
- dfsockopen的参数太多了
- dfsockopen不支持https证书校验,且无法修改
- dfsockopen在非curl模式下的优先级不够合理,最优先使用的模式居然功能最弱
- dfsockopen在非curl模式下直接禁用了安全校验,存在SSRF风险
- dfsockopen在非curl模式下,multipart格式有bug无法使用
- dfsockopen在curl模式下,由于x3.5所依赖的php版本原因,不支持单域名多ip功能
- dfsockopen不支持GET和POST以外的方法,也缺少自定义header等重要功能
- dfsockopen中存在的各类bug,等等
解决方案:
- 另起炉灶,新建filesock类
- filesock类同时兼容curl,socket(fopen,stream_socket_client,fsocketopen),哪怕服务器curl没开,allow_url_fopen也没开,一样能用。Guzzle都不可以这样。
- 新增SNI兼容相关代码,可以正确连接到依赖SNI的站点了。
- 重写dns查询和校验部分,针对单服务器多ip情况进行了处理,以支持ipv6。
- 由于同时支持ipv6和ipv4的站点的存在,增加ip校验功能,让服务器连接校验通过的ip。
- 为curl和fopen场景添加错误处理功能,解决难以调试的问题。
- 实现了对get和post以外的协议的支持,新增rawdata功能,可传入其他类型的body数据。
- 修复了fopen协议会重复发送数据的问题。
- 修复了socket模式下安全校验无法正常工作的问题。
- 修复了连接超时和请求超时无法区分的问题。
- 新增允许用户修改header和ua的功能。
- 修复了php5.6等低版本下curl表现与文档预期不符的问题。
- 修复了php新版本不支持
@
符号上传文件的问题。 - 修复了curl模式下不兼容socket模式上传表单形式文件的问题。
- 修复了curl模式下不兼容socket模式limit参数的问题。
- 修复了socket模式使用formdata上传文件或提交表单时无法被服务器正确识别的问题。
示例(以下写法均可使用):
//和从前完全一样的用法
echo dfsockopen('http://discuz.net');
$fs = filesock::open(['url' => 'http://discuz.net']);
echo $fs->request();
$fs = filesock::open();
echo $fs->get('http://discuz.net');
echo $fs->post('http://discuz.net');
echo $fs->put('http://discuz.net');
echo $fs->delete('http://discuz.net');
//使用post提交表单
$fs = filesock::open();
echo $fs->request(['url' => 'http://discuz.net', 'post' => ['a' => 3]]);
//这么写也可以
$fs = filesock::open();
$fs->url = 'http://discuz.net';
$fs->method = 'POST';
$fs->post = ['a' => 3];
echo $fs->request();
相关讨论
!563:【轻量级 PR】:update upload/source/function/function_filesock.php.
文档
您可以继续使用dfsockopen,它的行为应当与x3.4时期没有明显区别,但底层已经被更换为全新的filesock类。
dfsockopen的功能与以往一样,并不包含filesock的新增功能。
filesock类完整实现了dfsockopen的全部已有功能,并新增了许多功能以满足当今开发的需求。推荐在必要的位置直接使用filesock类以实现更多的功能。
filesock类不依赖curl(但推荐使用它),甚至可以在curl和allow_url_fopen都不支持的服务器上正常工作。
使用方法
配置
config_global.php文件当中与filesock相关的配置项:
$_config['security']['fsockopensafe']['port']
安全请求下允许的端口,启用安全检查的请求只能访问这些端口。
$_config['security']['fsockopensafe']['ipversion']
允许使用的ip协议(数组形式),服务器可以仅配置自己支持的协议,跳过不支持的协议以免请求过程中在上面浪费时间。
$_config['security']['fsockopensafe']['verifypeer']
是否验证证书有效性,开启可提升安全性,但需自行解决证书配置问题。
默认为关闭,开启前请务必确认php内已正确配置根证书列表。
需要在php.ini内配置
curl.cainfo
和openssl.cafile
true为开启(调用php内置的证书),false为关闭。也可以设置为一个路径,若设置为字符串路径,系统会启用证书校验,访问路径对应的证书文件并以此为准。
初始化
使用$fs = filesock::open()
即可初始化filesock类。
filesock::open($param)
中的$param
位置可以传入请求相关的参数。
注意:参数仅需要在任意一处位置传入即可,请勿多次传入。
传入参数
用户可以在初始化和发送请求的过程当中以数组形式传入参数,但也可以单独执行:
如$fs->url = 'http://www.discuz.net'
即可
发送请求
使用$fs->request()
即可发出请求
也可以使用$fs->get()
,$fs->post()
等方式发送请求,可以明确指定请求时所使用的method。
$fs->request($param)
中的$param
位置可以传入请求相关的参数。
获取内容
$fs->request()
运行以后会直接返回返回内容的body部分。
如需获取header部分,请执行$fs->filesockheader
。x3.4的$GLOBALS['filesockheader']
写法也可以正常使用。
错误处理
如果请求出错,$fs->request()
不会返回任何内容。
访问$fs->errno
可以获得错误代码,没有错误的话通常是0。
访问$fs->errstr
可以获得错误信息描述。
注意:curl和socket模式下的错误代码含义可能存在差异,表现也可能不同。
可用参数列表
如果在初始化和获取阶段传入参数,参数应当为数组形式。
只传入字符串的话,字符串会被当做url处理。
参数也可以直接在初始化后,请求之前直接传入。
- url 请求的URL地址,必须传入。
- limit 返回内容的最大长度,默认是0(不限制)
- method http请求的method,默认自动匹配(有post表单为POST,否则为GET),必须使用大写(如GET)
- post 传入的post表单,key value格式的数组,默认没有。
- cookie 传入的cookie,需要自行转换为字符串后再传入。默认为空。
- ip 指定请求所要连接的服务器ip。
- conntimeout 连接过程的超时时长(秒),默认为5
- timeout 连接成功以后的超时时长(秒),默认为15
- block 和dfsockopen里的一样
- encodetype 指定一个content type,通常在post/put等使用。可选项有URLENCODE(默认),FORMDATA,JSON,也可以填入任意有效的content type,如
multipart/form-data
,application/json
等。 - position 返回的body的起点位置,默认为0,设置以后会去除开头对应数量的字节。
- files 欲上传的文件,key value格式的数组,其中value为文件名。注意:若存在与post当中相同的key,则不会尝试按照文件名去查找实际文件,而是将post里对应的value作为文件的实际值进行上传,并继续使用files里的value做上传的文件名。
- unsafe 是否禁用安全检查。启用安全检查的情况下无法连接非标端口和服务器所在内网,增加了安全性的同时却也会引发某些情况下的不便。强烈建议不要在URL可能是用户输入的情况下禁用这个检查。
- useragent 允许用户指定一个user agent用于请求。默认使用浏览器useragent。
- header 数组形式,key value方式传入。请务必保证名称以首字母大写的方式传入,如User-Agent。
- rawdata post或put需要写入原始数据的情况下使用此值,通常配合JSON使用。
- verifypeer 是否检查https证书的有效性。通常建议在config里设置而非在此传入,修改前建议先读取此值避免不必要的覆盖。true为启用,false为禁用。也可传入一个路径,指向有效的证书文件,当某些特定业务涉及特定证书的情况下可能会有用。
- allowcurl 是否允许使用curl发送请求,默认为true。此项参数必须在初始化阶段就传入,初始化以后已经完成了类的加载,再传入就没用了。