Request.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. <?php
  2. namespace AlibabaCloud\Client\Request;
  3. use AlibabaCloud\Client\AlibabaCloud;
  4. use AlibabaCloud\Client\Credentials\Providers\CredentialsProvider;
  5. use AlibabaCloud\Client\Encode;
  6. use AlibabaCloud\Client\Exception\ClientException;
  7. use AlibabaCloud\Client\Exception\ServerException;
  8. use AlibabaCloud\Client\Filter\ApiFilter;
  9. use AlibabaCloud\Client\Filter\ClientFilter;
  10. use AlibabaCloud\Client\Filter\Filter;
  11. use AlibabaCloud\Client\Filter\HttpFilter;
  12. use AlibabaCloud\Client\Request\Traits\AcsTrait;
  13. use AlibabaCloud\Client\Request\Traits\ClientTrait;
  14. use AlibabaCloud\Client\Request\Traits\DeprecatedTrait;
  15. use AlibabaCloud\Client\Request\Traits\RetryTrait;
  16. use AlibabaCloud\Client\Result\Result;
  17. use AlibabaCloud\Client\SDK;
  18. use AlibabaCloud\Client\Traits\ArrayAccessTrait;
  19. use AlibabaCloud\Client\Traits\HttpTrait;
  20. use AlibabaCloud\Client\Traits\ObjectAccessTrait;
  21. use AlibabaCloud\Client\Traits\RegionTrait;
  22. use ArrayAccess;
  23. use Exception;
  24. use GuzzleHttp\Client;
  25. use GuzzleHttp\Exception\GuzzleException;
  26. use GuzzleHttp\HandlerStack;
  27. use GuzzleHttp\MessageFormatter;
  28. use GuzzleHttp\Middleware;
  29. use GuzzleHttp\Promise\PromiseInterface;
  30. use GuzzleHttp\Psr7\Uri;
  31. use Psr\Http\Message\ResponseInterface;
  32. /**
  33. * Class Request
  34. *
  35. * @package AlibabaCloud\Client\Request
  36. *
  37. * @method string stringToSign()
  38. * @method string resolveParameter()
  39. */
  40. abstract class Request implements ArrayAccess
  41. {
  42. use DeprecatedTrait;
  43. use HttpTrait;
  44. use RegionTrait;
  45. use ClientTrait;
  46. use AcsTrait;
  47. use ArrayAccessTrait;
  48. use ObjectAccessTrait;
  49. use RetryTrait;
  50. /**
  51. * Request Connect Timeout
  52. */
  53. const CONNECT_TIMEOUT = 5;
  54. /**
  55. * Request Timeout
  56. */
  57. const TIMEOUT = 10;
  58. /**
  59. * @var string HTTP Method
  60. */
  61. public $method = 'GET';
  62. /**
  63. * @var string
  64. */
  65. public $format = 'JSON';
  66. /**
  67. * @var string HTTP Scheme
  68. */
  69. protected $scheme = 'http';
  70. /**
  71. * @var string
  72. */
  73. public $client;
  74. /**
  75. * @var Uri
  76. */
  77. public $uri;
  78. /**
  79. * @var array The original parameters of the request.
  80. */
  81. public $data = [];
  82. /**
  83. * @var array
  84. */
  85. private $userAgent = [];
  86. /**
  87. * Request constructor.
  88. *
  89. * @param array $options
  90. *
  91. * @throws ClientException
  92. */
  93. public function __construct(array $options = [])
  94. {
  95. $this->client = CredentialsProvider::getDefaultName();
  96. $this->uri = new Uri();
  97. $this->uri = $this->uri->withScheme($this->scheme);
  98. $this->options['http_errors'] = false;
  99. $this->options['connect_timeout'] = self::CONNECT_TIMEOUT;
  100. $this->options['timeout'] = self::TIMEOUT;
  101. // Turn on debug mode based on environment variable.
  102. if (null !== \AlibabaCloud\Client\env('DEBUG') && strtolower(\AlibabaCloud\Client\env('DEBUG')) === 'sdk') {
  103. $this->options['debug'] = true;
  104. }
  105. // Rewrite configuration if the user has a configuration.
  106. if ($options !== []) {
  107. $this->options($options);
  108. }
  109. }
  110. /**
  111. * @param string $name
  112. * @param string $value
  113. *
  114. * @return $this
  115. * @throws ClientException
  116. */
  117. public function appendUserAgent($name, $value)
  118. {
  119. $filter_name = Filter::name($name);
  120. if (!UserAgent::isGuarded($filter_name)) {
  121. $this->userAgent[$filter_name] = Filter::value($value);
  122. }
  123. return $this;
  124. }
  125. /**
  126. * @param array $userAgent
  127. *
  128. * @return $this
  129. */
  130. public function withUserAgent(array $userAgent)
  131. {
  132. $this->userAgent = UserAgent::clean($userAgent);
  133. return $this;
  134. }
  135. /**
  136. * Set Accept format.
  137. *
  138. * @param string $format
  139. *
  140. * @return $this
  141. * @throws ClientException
  142. */
  143. public function format($format)
  144. {
  145. $this->format = ApiFilter::format($format);
  146. return $this;
  147. }
  148. /**
  149. * @param $contentType
  150. *
  151. * @return $this
  152. * @throws ClientException
  153. */
  154. public function contentType($contentType)
  155. {
  156. $this->options['headers']['Content-Type'] = HttpFilter::contentType($contentType);
  157. return $this;
  158. }
  159. /**
  160. * @param string $accept
  161. *
  162. * @return $this
  163. * @throws ClientException
  164. */
  165. public function accept($accept)
  166. {
  167. $this->options['headers']['Accept'] = HttpFilter::accept($accept);
  168. return $this;
  169. }
  170. /**
  171. * Set the request body.
  172. *
  173. * @param string $body
  174. *
  175. * @return $this
  176. * @throws ClientException
  177. */
  178. public function body($body)
  179. {
  180. $this->options['body'] = HttpFilter::body($body);
  181. return $this;
  182. }
  183. /**
  184. * Set the json as body.
  185. *
  186. * @param array|object $content
  187. *
  188. * @return $this
  189. * @throws ClientException
  190. */
  191. public function jsonBody($content)
  192. {
  193. if (!\is_array($content) && !\is_object($content)) {
  194. throw new ClientException(
  195. 'jsonBody only accepts an array or object',
  196. SDK::INVALID_ARGUMENT
  197. );
  198. }
  199. return $this->body(\json_encode($content));
  200. }
  201. /**
  202. * Set the request scheme.
  203. *
  204. * @param string $scheme
  205. *
  206. * @return $this
  207. * @throws ClientException
  208. */
  209. public function scheme($scheme)
  210. {
  211. $this->scheme = HttpFilter::scheme($scheme);
  212. $this->uri = $this->uri->withScheme($scheme);
  213. return $this;
  214. }
  215. /**
  216. * Set the request host.
  217. *
  218. * @param string $host
  219. *
  220. * @return $this
  221. * @throws ClientException
  222. */
  223. public function host($host)
  224. {
  225. $this->uri = $this->uri->withHost(HttpFilter::host($host));
  226. return $this;
  227. }
  228. /**
  229. * @param string $method
  230. *
  231. * @return $this
  232. * @throws ClientException
  233. */
  234. public function method($method)
  235. {
  236. $this->method = HttpFilter::method($method);
  237. return $this;
  238. }
  239. /**
  240. * @param string $clientName
  241. *
  242. * @return $this
  243. * @throws ClientException
  244. */
  245. public function client($clientName)
  246. {
  247. $this->client = ClientFilter::clientName($clientName);
  248. return $this;
  249. }
  250. /**
  251. * @return bool
  252. * @throws ClientException
  253. */
  254. public function isDebug()
  255. {
  256. if (isset($this->options['debug'])) {
  257. return $this->options['debug'] === true;
  258. }
  259. if (isset($this->httpClient()->options['debug'])) {
  260. return $this->httpClient()->options['debug'] === true;
  261. }
  262. return false;
  263. }
  264. /**
  265. * @throws ClientException
  266. * @throws ServerException
  267. */
  268. public function resolveOption()
  269. {
  270. $this->options['headers']['User-Agent'] = UserAgent::toString($this->userAgent);
  271. $this->cleanQuery();
  272. $this->cleanFormParams();
  273. $this->resolveHost();
  274. $this->resolveParameter();
  275. if (isset($this->options['form_params'])) {
  276. if (function_exists('\GuzzleHttp\Psr7\parse_query')) {
  277. $this->options['form_params'] = \GuzzleHttp\Psr7\parse_query(
  278. Encode::create($this->options['form_params'])->toString()
  279. );
  280. } else {
  281. $this->options['form_params'] = \GuzzleHttp\Psr7\Query::parse(
  282. Encode::create($this->options['form_params'])->toString()
  283. );
  284. }
  285. }
  286. $this->mergeOptionsIntoClient();
  287. }
  288. /**
  289. * @return Result
  290. * @throws ClientException
  291. * @throws ServerException
  292. */
  293. public function request()
  294. {
  295. $this->resolveOption();
  296. $result = $this->response();
  297. if ($this->shouldServerRetry($result)) {
  298. return $this->request();
  299. }
  300. if (!$result->isSuccess()) {
  301. throw new ServerException($result);
  302. }
  303. return $result;
  304. }
  305. /***
  306. * @return PromiseInterface
  307. * @throws Exception
  308. */
  309. public function requestAsync()
  310. {
  311. $this->resolveOption();
  312. return self::createClient($this)->requestAsync(
  313. $this->method,
  314. (string)$this->uri,
  315. $this->options
  316. );
  317. }
  318. /**
  319. * @param Request $request
  320. *
  321. * @return Client
  322. * @throws Exception
  323. */
  324. public static function createClient(Request $request = null)
  325. {
  326. if (AlibabaCloud::hasMock()) {
  327. $stack = HandlerStack::create(AlibabaCloud::getMock());
  328. } else {
  329. $stack = HandlerStack::create();
  330. }
  331. if (AlibabaCloud::isRememberHistory()) {
  332. $stack->push(Middleware::history(AlibabaCloud::referenceHistory()));
  333. }
  334. if (AlibabaCloud::getLogger()) {
  335. $stack->push(Middleware::log(
  336. AlibabaCloud::getLogger(),
  337. new MessageFormatter(AlibabaCloud::getLogFormat())
  338. ));
  339. }
  340. $stack->push(Middleware::mapResponse(static function (ResponseInterface $response) use ($request) {
  341. return new Result($response, $request);
  342. }));
  343. self::$config['handler'] = $stack;
  344. return new Client(self::$config);
  345. }
  346. /**
  347. * @throws ClientException
  348. * @throws Exception
  349. */
  350. private function response()
  351. {
  352. try {
  353. return self::createClient($this)->request(
  354. $this->method,
  355. (string)$this->uri,
  356. $this->options
  357. );
  358. } catch (GuzzleException $exception) {
  359. if ($this->shouldClientRetry($exception)) {
  360. return $this->response();
  361. }
  362. throw new ClientException(
  363. $exception->getMessage(),
  364. SDK::SERVER_UNREACHABLE,
  365. $exception
  366. );
  367. }
  368. }
  369. /**
  370. * Remove redundant Query
  371. *
  372. * @codeCoverageIgnore
  373. */
  374. private function cleanQuery()
  375. {
  376. if (isset($this->options['query']) && $this->options['query'] === []) {
  377. unset($this->options['query']);
  378. }
  379. }
  380. /**
  381. * Remove redundant Headers
  382. *
  383. * @codeCoverageIgnore
  384. */
  385. private function cleanFormParams()
  386. {
  387. if (isset($this->options['form_params']) && $this->options['form_params'] === []) {
  388. unset($this->options['form_params']);
  389. }
  390. }
  391. }