HttpRequest.php 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171
  1. <?php
  2. namespace Yurun\Util;
  3. use Yurun\Util\YurunHttp\Attributes;
  4. use Yurun\Util\YurunHttp\Http\Psr7\Consts\MediaType;
  5. use Yurun\Util\YurunHttp\Http\Psr7\UploadedFile;
  6. use Yurun\Util\YurunHttp\Http\Request;
  7. class HttpRequest
  8. {
  9. /**
  10. * 处理器.
  11. *
  12. * @var \Yurun\Util\YurunHttp\Handler\IHandler|null
  13. */
  14. private $handler;
  15. /**
  16. * 需要请求的Url地址
  17. *
  18. * @var string
  19. */
  20. public $url;
  21. /**
  22. * 发送内容,可以是字符串、数组(支持键值、Yurun\Util\YurunHttp\Http\Psr7\UploadedFile,其中键值会作为html编码,文件则是上传).
  23. *
  24. * @var mixed
  25. */
  26. public $content;
  27. /**
  28. * `curl_setopt_array()`所需要的第二个参数.
  29. *
  30. * @var array
  31. */
  32. public $options = [];
  33. /**
  34. * 请求头.
  35. *
  36. * @var array
  37. */
  38. public $headers = [];
  39. /**
  40. * Cookies.
  41. *
  42. * @var array
  43. */
  44. public $cookies = [];
  45. /**
  46. * 失败重试次数,默认为0.
  47. *
  48. * @var int
  49. */
  50. public $retry = 0;
  51. /**
  52. * 是否使用代理,默认false.
  53. *
  54. * @var bool
  55. */
  56. public $useProxy = false;
  57. /**
  58. * 代理设置.
  59. *
  60. * @var array
  61. */
  62. public $proxy = [];
  63. /**
  64. * 是否验证证书.
  65. *
  66. * @var bool
  67. */
  68. public $isVerifyCA = false;
  69. /**
  70. * CA根证书路径.
  71. *
  72. * @var string|null
  73. */
  74. public $caCert;
  75. /**
  76. * 连接超时时间,单位:毫秒.
  77. *
  78. * @var int
  79. */
  80. public $connectTimeout = 30000;
  81. /**
  82. * 总超时时间,单位:毫秒.
  83. *
  84. * @var int
  85. */
  86. public $timeout = 30000;
  87. /**
  88. * 下载限速,为0则不限制,单位:字节
  89. *
  90. * @var int|null
  91. */
  92. public $downloadSpeed;
  93. /**
  94. * 上传限速,为0则不限制,单位:字节
  95. *
  96. * @var int|null
  97. */
  98. public $uploadSpeed;
  99. /**
  100. * 用于连接中需要的用户名.
  101. *
  102. * @var string|null
  103. */
  104. public $username;
  105. /**
  106. * 用于连接中需要的密码
  107. *
  108. * @var string|null
  109. */
  110. public $password;
  111. /**
  112. * 请求结果保存至文件的配置.
  113. *
  114. * @var mixed
  115. */
  116. public $saveFileOption = [];
  117. /**
  118. * 是否启用重定向.
  119. *
  120. * @var bool
  121. */
  122. public $followLocation = true;
  123. /**
  124. * 最大重定向次数.
  125. *
  126. * @var int
  127. */
  128. public $maxRedirects = 10;
  129. /**
  130. * 证书类型
  131. * 支持的格式有"PEM" (默认值), "DER"和"ENG".
  132. *
  133. * @var string
  134. */
  135. public $certType = 'pem';
  136. /**
  137. * 一个包含 PEM 格式证书的文件名.
  138. *
  139. * @var string
  140. */
  141. public $certPath = '';
  142. /**
  143. * 使用证书需要的密码
  144. *
  145. * @var string
  146. */
  147. public $certPassword = null;
  148. /**
  149. * certType规定的私钥的加密类型,支持的密钥类型为"PEM"(默认值)、"DER"和"ENG".
  150. *
  151. * @var string
  152. */
  153. public $keyType = 'pem';
  154. /**
  155. * 包含 SSL 私钥的文件名.
  156. *
  157. * @var string
  158. */
  159. public $keyPath = '';
  160. /**
  161. * SSL私钥的密码
  162. *
  163. * @var string
  164. */
  165. public $keyPassword = null;
  166. /**
  167. * 请求方法.
  168. *
  169. * @var string
  170. */
  171. public $method = 'GET';
  172. /**
  173. * Http 协议版本.
  174. *
  175. * @var string
  176. */
  177. public $protocolVersion = '1.1';
  178. /**
  179. * 是否启用连接池,默认为 null 时取全局设置.
  180. *
  181. * @var bool|null
  182. */
  183. public $connectionPool = null;
  184. /**
  185. * 代理认证方式.
  186. *
  187. * @var array
  188. */
  189. public static $proxyAuths = [];
  190. /**
  191. * 代理类型.
  192. *
  193. * @var array
  194. */
  195. public static $proxyType = [];
  196. /**
  197. * 自动扩展名标志.
  198. */
  199. const AUTO_EXT_FLAG = '.*';
  200. /**
  201. * 自动扩展名用的临时文件名.
  202. */
  203. const AUTO_EXT_TEMP_EXT = '.tmp';
  204. /**
  205. * 构造方法.
  206. *
  207. * @return mixed
  208. */
  209. public function __construct()
  210. {
  211. $this->open();
  212. }
  213. /**
  214. * 析构方法.
  215. */
  216. public function __destruct()
  217. {
  218. $this->close();
  219. }
  220. /**
  221. * 打开一个新连接,初始化所有参数。一般不需要手动调用。
  222. *
  223. * @return void
  224. */
  225. public function open()
  226. {
  227. $this->handler = YurunHttp::getHandler();
  228. $this->retry = 0;
  229. $this->headers = $this->options = [];
  230. $this->url = $this->content = '';
  231. $this->useProxy = false;
  232. $this->proxy = [
  233. 'auth' => 'basic',
  234. 'type' => 'http',
  235. ];
  236. $this->isVerifyCA = false;
  237. $this->caCert = null;
  238. $this->connectTimeout = 30000;
  239. $this->timeout = 30000;
  240. $this->downloadSpeed = null;
  241. $this->uploadSpeed = null;
  242. $this->username = null;
  243. $this->password = null;
  244. $this->saveFileOption = [];
  245. }
  246. /**
  247. * 关闭连接。一般不需要手动调用。
  248. *
  249. * @return void
  250. */
  251. public function close()
  252. {
  253. if ($this->handler)
  254. {
  255. $handler = $this->handler;
  256. $this->handler = null;
  257. $handler->close();
  258. }
  259. }
  260. /**
  261. * 创建一个新会话,等同于new.
  262. *
  263. * @return static
  264. */
  265. public static function newSession()
  266. {
  267. return new static();
  268. }
  269. /**
  270. * 获取处理器.
  271. *
  272. * @return \Yurun\Util\YurunHttp\Handler\IHandler|null
  273. */
  274. public function getHandler()
  275. {
  276. return $this->handler;
  277. }
  278. /**
  279. * 设置请求地址
  280. *
  281. * @param string $url 请求地址
  282. *
  283. * @return static
  284. */
  285. public function url($url)
  286. {
  287. $this->url = $url;
  288. return $this;
  289. }
  290. /**
  291. * 设置发送内容,requestBody的别名.
  292. *
  293. * @param mixed $content 发送内容,可以是字符串、数组
  294. *
  295. * @return static
  296. */
  297. public function content($content)
  298. {
  299. return $this->requestBody($content);
  300. }
  301. /**
  302. * 设置参数,requestBody的别名.
  303. *
  304. * @param mixed $params 发送内容,可以是字符串、数组
  305. *
  306. * @return static
  307. */
  308. public function params($params)
  309. {
  310. return $this->requestBody($params);
  311. }
  312. /**
  313. * 设置请求主体.
  314. *
  315. * @param string|string|array $requestBody 发送内容,可以是字符串、数组
  316. *
  317. * @return static
  318. */
  319. public function requestBody($requestBody)
  320. {
  321. $this->content = $requestBody;
  322. return $this;
  323. }
  324. /**
  325. * 批量设置CURL的Option.
  326. *
  327. * @param array $options curl_setopt_array()所需要的第二个参数
  328. *
  329. * @return static
  330. */
  331. public function options($options)
  332. {
  333. $thisOptions = &$this->options;
  334. foreach ($options as $key => $value)
  335. {
  336. $thisOptions[$key] = $value;
  337. }
  338. return $this;
  339. }
  340. /**
  341. * 设置CURL的Option.
  342. *
  343. * @param int $option 需要设置的CURLOPT_XXX选项
  344. * @param mixed $value 值
  345. *
  346. * @return static
  347. */
  348. public function option($option, $value)
  349. {
  350. $this->options[$option] = $value;
  351. return $this;
  352. }
  353. /**
  354. * 批量设置请求头.
  355. *
  356. * @param array $headers 键值数组
  357. *
  358. * @return static
  359. */
  360. public function headers($headers)
  361. {
  362. $thisHeaders = &$this->headers;
  363. $thisHeaders = array_merge($thisHeaders, $headers);
  364. return $this;
  365. }
  366. /**
  367. * 设置请求头.
  368. *
  369. * @param string $header 请求头名称
  370. * @param string $value 值
  371. *
  372. * @return static
  373. */
  374. public function header($header, $value)
  375. {
  376. $this->headers[$header] = $value;
  377. return $this;
  378. }
  379. /**
  380. * 批量设置请求头,.
  381. *
  382. * @param array $headers 纯文本 header 数组
  383. *
  384. * @return static
  385. */
  386. public function rawHeaders($headers)
  387. {
  388. $thisHeaders = &$this->headers;
  389. foreach ($headers as $header)
  390. {
  391. $list = explode(':', $header, 2);
  392. $thisHeaders[trim($list[0])] = trim($list[1]);
  393. }
  394. return $this;
  395. }
  396. /**
  397. * 设置请求头.
  398. *
  399. * @param string $header 纯文本 header
  400. *
  401. * @return static
  402. */
  403. public function rawHeader($header)
  404. {
  405. $list = explode(':', $header, 2);
  406. $this->headers[trim($list[0])] = trim($list[1]);
  407. return $this;
  408. }
  409. /**
  410. * 设置Accept.
  411. *
  412. * @param string $accept
  413. *
  414. * @return static
  415. */
  416. public function accept($accept)
  417. {
  418. $this->headers['Accept'] = $accept;
  419. return $this;
  420. }
  421. /**
  422. * 设置Accept-Language.
  423. *
  424. * @param string $acceptLanguage
  425. *
  426. * @return static
  427. */
  428. public function acceptLanguage($acceptLanguage)
  429. {
  430. $this->headers['Accept-Language'] = $acceptLanguage;
  431. return $this;
  432. }
  433. /**
  434. * 设置Accept-Encoding.
  435. *
  436. * @param string $acceptEncoding
  437. *
  438. * @return static
  439. */
  440. public function acceptEncoding($acceptEncoding)
  441. {
  442. $this->headers['Accept-Encoding'] = $acceptEncoding;
  443. return $this;
  444. }
  445. /**
  446. * 设置Accept-Ranges.
  447. *
  448. * @param string $acceptRanges
  449. *
  450. * @return static
  451. */
  452. public function acceptRanges($acceptRanges)
  453. {
  454. $this->headers['Accept-Ranges'] = $acceptRanges;
  455. return $this;
  456. }
  457. /**
  458. * 设置Cache-Control.
  459. *
  460. * @param string $cacheControl
  461. *
  462. * @return static
  463. */
  464. public function cacheControl($cacheControl)
  465. {
  466. $this->headers['Cache-Control'] = $cacheControl;
  467. return $this;
  468. }
  469. /**
  470. * 批量设置Cookies.
  471. *
  472. * @param array $cookies 键值对应数组
  473. *
  474. * @return static
  475. */
  476. public function cookies($cookies)
  477. {
  478. $this->cookies = array_merge($this->cookies, $cookies);
  479. return $this;
  480. }
  481. /**
  482. * 设置Cookie.
  483. *
  484. * @param string $name 名称
  485. * @param string $value 值
  486. *
  487. * @return static
  488. */
  489. public function cookie($name, $value)
  490. {
  491. $this->cookies[$name] = $value;
  492. return $this;
  493. }
  494. /**
  495. * 设置Content-Type.
  496. *
  497. * @param string $contentType
  498. *
  499. * @return static
  500. */
  501. public function contentType($contentType)
  502. {
  503. $this->headers['Content-Type'] = $contentType;
  504. return $this;
  505. }
  506. /**
  507. * 设置Range.
  508. *
  509. * @param string $range
  510. *
  511. * @return static
  512. */
  513. public function range($range)
  514. {
  515. $this->headers['Range'] = $range;
  516. return $this;
  517. }
  518. /**
  519. * 设置Referer.
  520. *
  521. * @param string $referer
  522. *
  523. * @return static
  524. */
  525. public function referer($referer)
  526. {
  527. $this->headers['Referer'] = $referer;
  528. return $this;
  529. }
  530. /**
  531. * 设置User-Agent.
  532. *
  533. * @param string $userAgent
  534. *
  535. * @return static
  536. */
  537. public function userAgent($userAgent)
  538. {
  539. $this->headers['User-Agent'] = $userAgent;
  540. return $this;
  541. }
  542. /**
  543. * 设置User-Agent,userAgent的别名.
  544. *
  545. * @param string $userAgent
  546. *
  547. * @return static
  548. */
  549. public function ua($userAgent)
  550. {
  551. return $this->userAgent($userAgent);
  552. }
  553. /**
  554. * 设置失败重试次数,状态码为5XX或者0才需要重试.
  555. *
  556. * @param string $retry
  557. *
  558. * @return static
  559. */
  560. public function retry($retry)
  561. {
  562. $this->retry = $retry < 0 ? 0 : $retry; //至少请求1次,即重试0次
  563. return $this;
  564. }
  565. /**
  566. * 代理.
  567. *
  568. * @param string $server 代理服务器地址
  569. * @param int $port 代理服务器端口
  570. * @param string $type 代理类型,支持:http、socks4、socks4a、socks5
  571. * @param string $auth 代理认证方式,支持:basic、ntlm。一般默认basic
  572. *
  573. * @return static
  574. */
  575. public function proxy($server, $port, $type = 'http', $auth = 'basic')
  576. {
  577. $this->useProxy = true;
  578. $this->proxy = [
  579. 'server' => $server,
  580. 'port' => $port,
  581. 'type' => $type,
  582. 'auth' => $auth,
  583. ];
  584. return $this;
  585. }
  586. /**
  587. * 代理认证
  588. *
  589. * @param string $username
  590. * @param string $password
  591. *
  592. * @return static
  593. */
  594. public function proxyAuth($username, $password)
  595. {
  596. $this->proxy['username'] = $username;
  597. $this->proxy['password'] = $password;
  598. return $this;
  599. }
  600. /**
  601. * 设置超时时间.
  602. *
  603. * @param int $timeout 总超时时间,单位:毫秒
  604. * @param int $connectTimeout 连接超时时间,单位:毫秒
  605. *
  606. * @return static
  607. */
  608. public function timeout($timeout = null, $connectTimeout = null)
  609. {
  610. if (null !== $timeout)
  611. {
  612. $this->timeout = $timeout;
  613. }
  614. if (null !== $connectTimeout)
  615. {
  616. $this->connectTimeout = $connectTimeout;
  617. }
  618. return $this;
  619. }
  620. /**
  621. * 限速
  622. *
  623. * @param int $download 下载速度,为0则不限制,单位:字节
  624. * @param int $upload 上传速度,为0则不限制,单位:字节
  625. *
  626. * @return static
  627. */
  628. public function limitRate($download = 0, $upload = 0)
  629. {
  630. $this->downloadSpeed = $download;
  631. $this->uploadSpeed = $upload;
  632. return $this;
  633. }
  634. /**
  635. * 设置用于连接中需要的用户名和密码
  636. *
  637. * @param string $username 用户名
  638. * @param string $password 密码
  639. *
  640. * @return static
  641. */
  642. public function userPwd($username, $password)
  643. {
  644. $this->username = $username;
  645. $this->password = $password;
  646. return $this;
  647. }
  648. /**
  649. * 保存至文件的设置.
  650. *
  651. * @param string $filePath 文件路径
  652. * @param string $fileMode 文件打开方式,默认w+
  653. *
  654. * @return static
  655. */
  656. public function saveFile($filePath, $fileMode = 'w+')
  657. {
  658. $this->saveFileOption['filePath'] = $filePath;
  659. $this->saveFileOption['fileMode'] = $fileMode;
  660. return $this;
  661. }
  662. /**
  663. * 获取文件保存路径.
  664. *
  665. * @return string|null
  666. */
  667. public function getSavePath()
  668. {
  669. $saveFileOption = $this->saveFileOption;
  670. return isset($saveFileOption['filePath']) ? $saveFileOption['filePath'] : null;
  671. }
  672. /**
  673. * 设置SSL证书.
  674. *
  675. * @param string $path 一个包含 PEM 格式证书的文件名
  676. * @param string $type 证书类型,支持的格式有”PEM”(默认值),“DER”和”ENG”
  677. * @param string $password 使用证书需要的密码
  678. *
  679. * @return static
  680. */
  681. public function sslCert($path, $type = null, $password = null)
  682. {
  683. $this->certPath = $path;
  684. if (null !== $type)
  685. {
  686. $this->certType = $type;
  687. }
  688. if (null !== $password)
  689. {
  690. $this->certPassword = $password;
  691. }
  692. return $this;
  693. }
  694. /**
  695. * 设置SSL私钥.
  696. *
  697. * @param string $path 包含 SSL 私钥的文件名
  698. * @param string $type certType规定的私钥的加密类型,支持的密钥类型为”PEM”(默认值)、”DER”和”ENG”
  699. * @param string $password SSL私钥的密码
  700. *
  701. * @return static
  702. */
  703. public function sslKey($path, $type = null, $password = null)
  704. {
  705. $this->keyPath = $path;
  706. if (null !== $type)
  707. {
  708. $this->keyType = $type;
  709. }
  710. if (null !== $password)
  711. {
  712. $this->keyPassword = $password;
  713. }
  714. return $this;
  715. }
  716. /**
  717. * 设置请求方法.
  718. *
  719. * @param string $method
  720. *
  721. * @return static
  722. */
  723. public function method($method)
  724. {
  725. $this->method = $method;
  726. return $this;
  727. }
  728. /**
  729. * 设置是否启用连接池.
  730. *
  731. * @param bool $connectionPool
  732. *
  733. * @return static
  734. */
  735. public function connectionPool($connectionPool)
  736. {
  737. $this->connectionPool = $connectionPool;
  738. return $this;
  739. }
  740. /**
  741. * 处理请求主体.
  742. *
  743. * @param string|string|array $requestBody
  744. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  745. *
  746. * @return array
  747. */
  748. protected function parseRequestBody($requestBody, $contentType)
  749. {
  750. $body = $files = [];
  751. if (\is_string($requestBody))
  752. {
  753. $body = $requestBody;
  754. }
  755. elseif (\is_array($requestBody))
  756. {
  757. switch ($contentType)
  758. {
  759. case 'json':
  760. $body = json_encode($requestBody);
  761. $this->header('Content-Type', MediaType::APPLICATION_JSON);
  762. break;
  763. default:
  764. foreach ($requestBody as $k => $v)
  765. {
  766. if ($v instanceof UploadedFile)
  767. {
  768. $files[$k] = $v;
  769. }
  770. else
  771. {
  772. $body[$k] = $v;
  773. }
  774. }
  775. $body = http_build_query($body, '', '&');
  776. }
  777. }
  778. else
  779. {
  780. throw new \InvalidArgumentException('$requestBody only can be string or array');
  781. }
  782. return [$body, $files];
  783. }
  784. /**
  785. * 构建请求类.
  786. *
  787. * @param string $url 请求地址,如果为null则取url属性值
  788. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  789. * @param string|null $method 请求方法,GET、POST等
  790. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  791. *
  792. * @return \Yurun\Util\YurunHttp\Http\Request
  793. */
  794. public function buildRequest($url = null, $requestBody = null, $method = null, $contentType = null)
  795. {
  796. if (null === $url)
  797. {
  798. $url = $this->url;
  799. }
  800. if (null === $method)
  801. {
  802. $method = $this->method;
  803. }
  804. list($body, $files) = $this->parseRequestBody(null === $requestBody ? $this->content : $requestBody, $contentType);
  805. $request = new Request($url, $this->headers, $body, $method);
  806. $saveFileOption = $this->saveFileOption;
  807. $request = $request->withUploadedFiles($files)
  808. ->withCookieParams($this->cookies)
  809. ->withAttribute(Attributes::MAX_REDIRECTS, $this->maxRedirects)
  810. ->withAttribute(Attributes::IS_VERIFY_CA, $this->isVerifyCA)
  811. ->withAttribute(Attributes::CA_CERT, $this->caCert)
  812. ->withAttribute(Attributes::CERT_PATH, $this->certPath)
  813. ->withAttribute(Attributes::CERT_PASSWORD, $this->certPassword)
  814. ->withAttribute(Attributes::CERT_TYPE, $this->certType)
  815. ->withAttribute(Attributes::KEY_PATH, $this->keyPath)
  816. ->withAttribute(Attributes::KEY_PASSWORD, $this->keyPassword)
  817. ->withAttribute(Attributes::KEY_TYPE, $this->keyType)
  818. ->withAttribute(Attributes::OPTIONS, $this->options)
  819. ->withAttribute(Attributes::SAVE_FILE_PATH, isset($saveFileOption['filePath']) ? $saveFileOption['filePath'] : null)
  820. ->withAttribute(Attributes::USE_PROXY, $this->useProxy)
  821. ->withAttribute(Attributes::USERNAME, $this->username)
  822. ->withAttribute(Attributes::PASSWORD, $this->password)
  823. ->withAttribute(Attributes::CONNECT_TIMEOUT, $this->connectTimeout)
  824. ->withAttribute(Attributes::TIMEOUT, $this->timeout)
  825. ->withAttribute(Attributes::DOWNLOAD_SPEED, $this->downloadSpeed)
  826. ->withAttribute(Attributes::UPLOAD_SPEED, $this->uploadSpeed)
  827. ->withAttribute(Attributes::FOLLOW_LOCATION, $this->followLocation)
  828. ->withAttribute(Attributes::CONNECTION_POOL, $this->connectionPool)
  829. ->withProtocolVersion($this->protocolVersion)
  830. ;
  831. foreach ($this->proxy as $name => $value)
  832. {
  833. $request = $request->withAttribute('proxy.' . $name, $value);
  834. }
  835. return $request;
  836. }
  837. /**
  838. * 发送请求,所有请求的老祖宗.
  839. *
  840. * @param string|null $url 请求地址,如果为null则取url属性值
  841. * @param string|array|null $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  842. * @param string $method 请求方法,GET、POST等
  843. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  844. *
  845. * @return \Yurun\Util\YurunHttp\Http\Response|null
  846. */
  847. public function send($url = null, $requestBody = null, $method = null, $contentType = null)
  848. {
  849. $request = $this->buildRequest($url, $requestBody, $method, $contentType);
  850. return YurunHttp::send($request, $this->handler);
  851. }
  852. /**
  853. * 发送 Http2 请求不调用 recv().
  854. *
  855. * @param string|null $url 请求地址,如果为null则取url属性值
  856. * @param string|array|null $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  857. * @param string $method 请求方法,GET、POST等
  858. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  859. *
  860. * @return \Yurun\Util\YurunHttp\Http\Response|null
  861. */
  862. public function sendHttp2WithoutRecv($url = null, $requestBody = null, $method = 'GET', $contentType = null)
  863. {
  864. $request = $this->buildRequest($url, $requestBody, $method, $contentType)
  865. ->withProtocolVersion('2.0')
  866. ->withAttribute(Attributes::HTTP2_NOT_RECV, true);
  867. return YurunHttp::send($request, $this->handler);
  868. }
  869. /**
  870. * GET请求
  871. *
  872. * @param string $url 请求地址,如果为null则取url属性值
  873. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  874. *
  875. * @return \Yurun\Util\YurunHttp\Http\Response|null
  876. */
  877. public function get($url = null, $requestBody = null)
  878. {
  879. if (!empty($requestBody))
  880. {
  881. if (strpos($url, '?'))
  882. {
  883. $url .= '&';
  884. }
  885. else
  886. {
  887. $url .= '?';
  888. }
  889. $url .= http_build_query($requestBody, '', '&');
  890. }
  891. return $this->send($url, [], 'GET');
  892. }
  893. /**
  894. * POST请求
  895. *
  896. * @param string $url 请求地址,如果为null则取url属性值
  897. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  898. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  899. *
  900. * @return \Yurun\Util\YurunHttp\Http\Response|null
  901. */
  902. public function post($url = null, $requestBody = null, $contentType = null)
  903. {
  904. return $this->send($url, $requestBody, 'POST', $contentType);
  905. }
  906. /**
  907. * HEAD请求
  908. *
  909. * @param string $url 请求地址,如果为null则取url属性值
  910. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  911. *
  912. * @return \Yurun\Util\YurunHttp\Http\Response|null
  913. */
  914. public function head($url = null, $requestBody = null)
  915. {
  916. return $this->send($url, $requestBody, 'HEAD');
  917. }
  918. /**
  919. * PUT请求
  920. *
  921. * @param string $url 请求地址,如果为null则取url属性值
  922. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  923. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  924. *
  925. * @return \Yurun\Util\YurunHttp\Http\Response|null
  926. */
  927. public function put($url = null, $requestBody = null, $contentType = null)
  928. {
  929. return $this->send($url, $requestBody, 'PUT', $contentType);
  930. }
  931. /**
  932. * PATCH请求
  933. *
  934. * @param string $url 请求地址,如果为null则取url属性值
  935. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  936. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  937. *
  938. * @return \Yurun\Util\YurunHttp\Http\Response|null
  939. */
  940. public function patch($url = null, $requestBody = null, $contentType = null)
  941. {
  942. return $this->send($url, $requestBody, 'PATCH', $contentType);
  943. }
  944. /**
  945. * DELETE请求
  946. *
  947. * @param string $url 请求地址,如果为null则取url属性值
  948. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  949. * @param string|null $contentType 内容类型,支持null/json,为null时不处理
  950. *
  951. * @return \Yurun\Util\YurunHttp\Http\Response|null
  952. */
  953. public function delete($url = null, $requestBody = null, $contentType = null)
  954. {
  955. return $this->send($url, $requestBody, 'DELETE', $contentType);
  956. }
  957. /**
  958. * 直接下载文件.
  959. *
  960. * @param string $fileName 保存路径,如果以 .* 结尾,则根据 Content-Type 自动决定扩展名
  961. * @param string $url 下载文件地址
  962. * @param string|array $requestBody 发送内容,可以是字符串、数组,如果为空则取content属性值
  963. * @param string $method 请求方法,GET、POST等,一般用GET
  964. *
  965. * @return \Yurun\Util\YurunHttp\Http\Response|null
  966. */
  967. public function download($fileName, $url = null, $requestBody = null, $method = 'GET')
  968. {
  969. $isAutoExt = self::checkDownloadIsAutoExt($fileName, $fileName);
  970. $result = $this->saveFile($fileName)->send($url, $requestBody, $method);
  971. if ($isAutoExt)
  972. {
  973. self::parseDownloadAutoExt($result, $fileName);
  974. }
  975. $this->saveFileOption = [];
  976. return $result;
  977. }
  978. /**
  979. * WebSocket.
  980. *
  981. * @param string $url
  982. *
  983. * @return \Yurun\Util\YurunHttp\WebSocket\IWebSocketClient
  984. */
  985. public function websocket($url = null)
  986. {
  987. $request = $this->buildRequest($url);
  988. return YurunHttp::websocket($request, $this->handler);
  989. }
  990. /**
  991. * 检查下载文件名是否要自动扩展名.
  992. *
  993. * @param string $fileName
  994. * @param string $tempFileName
  995. *
  996. * @return bool
  997. */
  998. public static function checkDownloadIsAutoExt($fileName, &$tempFileName)
  999. {
  1000. $flagLength = \strlen(self::AUTO_EXT_FLAG);
  1001. if (self::AUTO_EXT_FLAG !== substr($fileName, -$flagLength))
  1002. {
  1003. return false;
  1004. }
  1005. $tempFileName = substr($fileName, 0, -$flagLength) . self::AUTO_EXT_TEMP_EXT;
  1006. return true;
  1007. }
  1008. /**
  1009. * 处理下载的自动扩展名.
  1010. *
  1011. * @param \Yurun\Util\YurunHttp\Http\Response $response
  1012. * @param string $tempFileName
  1013. *
  1014. * @return void
  1015. */
  1016. public static function parseDownloadAutoExt(&$response, $tempFileName)
  1017. {
  1018. $ext = MediaType::getExt($response->getHeaderLine('Content-Type'));
  1019. if (null === $ext)
  1020. {
  1021. $ext = 'file';
  1022. }
  1023. $savedFileName = substr($tempFileName, 0, -\strlen(self::AUTO_EXT_TEMP_EXT)) . '.' . $ext;
  1024. rename($tempFileName, $savedFileName);
  1025. $response = $response->withSavedFileName($savedFileName);
  1026. }
  1027. }
  1028. if (\extension_loaded('curl'))
  1029. {
  1030. // 代理认证方式
  1031. HttpRequest::$proxyAuths = [
  1032. 'basic' => \CURLAUTH_BASIC,
  1033. 'ntlm' => \CURLAUTH_NTLM,
  1034. ];
  1035. // 代理类型
  1036. HttpRequest::$proxyType = [
  1037. 'http' => \CURLPROXY_HTTP,
  1038. 'socks4' => \CURLPROXY_SOCKS4,
  1039. 'socks4a' => 6, // CURLPROXY_SOCKS4A
  1040. 'socks5' => \CURLPROXY_SOCKS5,
  1041. ];
  1042. }