PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/htdocs/wp-content/plugins/wordpress-seo/vendor_prefixed/guzzlehttp/psr7/src/functions.php

https://gitlab.com/VTTE/sitios-vtte
PHP | 657 lines | 613 code | 2 blank | 42 comment | 10 complexity | e06080579957f2ca411b820dfb74cddd MD5 | raw file
  1. <?php
  2. namespace YoastSEO_Vendor\GuzzleHttp\Psr7;
  3. use YoastSEO_Vendor\Psr\Http\Message\MessageInterface;
  4. use YoastSEO_Vendor\Psr\Http\Message\RequestInterface;
  5. use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface;
  6. use YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface;
  7. use YoastSEO_Vendor\Psr\Http\Message\StreamInterface;
  8. use YoastSEO_Vendor\Psr\Http\Message\UriInterface;
  9. /**
  10. * Returns the string representation of an HTTP message.
  11. *
  12. * @param MessageInterface $message Message to convert to a string.
  13. *
  14. * @return string
  15. */
  16. function str(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message)
  17. {
  18. if ($message instanceof \YoastSEO_Vendor\Psr\Http\Message\RequestInterface) {
  19. $msg = \trim($message->getMethod() . ' ' . $message->getRequestTarget()) . ' HTTP/' . $message->getProtocolVersion();
  20. if (!$message->hasHeader('host')) {
  21. $msg .= "\r\nHost: " . $message->getUri()->getHost();
  22. }
  23. } elseif ($message instanceof \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface) {
  24. $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' . $message->getStatusCode() . ' ' . $message->getReasonPhrase();
  25. } else {
  26. throw new \InvalidArgumentException('Unknown message type');
  27. }
  28. foreach ($message->getHeaders() as $name => $values) {
  29. $msg .= "\r\n{$name}: " . \implode(', ', $values);
  30. }
  31. return "{$msg}\r\n\r\n" . $message->getBody();
  32. }
  33. /**
  34. * Returns a UriInterface for the given value.
  35. *
  36. * This function accepts a string or {@see Psr\Http\Message\UriInterface} and
  37. * returns a UriInterface for the given value. If the value is already a
  38. * `UriInterface`, it is returned as-is.
  39. *
  40. * @param string|UriInterface $uri
  41. *
  42. * @return UriInterface
  43. * @throws \InvalidArgumentException
  44. */
  45. function uri_for($uri)
  46. {
  47. if ($uri instanceof \YoastSEO_Vendor\Psr\Http\Message\UriInterface) {
  48. return $uri;
  49. } elseif (\is_string($uri)) {
  50. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($uri);
  51. }
  52. throw new \InvalidArgumentException('URI must be a string or UriInterface');
  53. }
  54. /**
  55. * Create a new stream based on the input type.
  56. *
  57. * Options is an associative array that can contain the following keys:
  58. * - metadata: Array of custom metadata.
  59. * - size: Size of the stream.
  60. *
  61. * @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data
  62. * @param array $options Additional options
  63. *
  64. * @return StreamInterface
  65. * @throws \InvalidArgumentException if the $resource arg is not valid.
  66. */
  67. function stream_for($resource = '', array $options = [])
  68. {
  69. if (\is_scalar($resource)) {
  70. $stream = \fopen('php://temp', 'r+');
  71. if ($resource !== '') {
  72. \fwrite($stream, $resource);
  73. \fseek($stream, 0);
  74. }
  75. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($stream, $options);
  76. }
  77. switch (\gettype($resource)) {
  78. case 'resource':
  79. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($resource, $options);
  80. case 'object':
  81. if ($resource instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) {
  82. return $resource;
  83. } elseif ($resource instanceof \Iterator) {
  84. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\PumpStream(function () use($resource) {
  85. if (!$resource->valid()) {
  86. return \false;
  87. }
  88. $result = $resource->current();
  89. $resource->next();
  90. return $result;
  91. }, $options);
  92. } elseif (\method_exists($resource, '__toString')) {
  93. return stream_for((string) $resource, $options);
  94. }
  95. break;
  96. case 'NULL':
  97. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream(\fopen('php://temp', 'r+'), $options);
  98. }
  99. if (\is_callable($resource)) {
  100. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\PumpStream($resource, $options);
  101. }
  102. throw new \InvalidArgumentException('Invalid resource type: ' . \gettype($resource));
  103. }
  104. /**
  105. * Parse an array of header values containing ";" separated data into an
  106. * array of associative arrays representing the header key value pair
  107. * data of the header. When a parameter does not contain a value, but just
  108. * contains a key, this function will inject a key with a '' string value.
  109. *
  110. * @param string|array $header Header to parse into components.
  111. *
  112. * @return array Returns the parsed header values.
  113. */
  114. function parse_header($header)
  115. {
  116. static $trimmed = "\"' \n\t\r";
  117. $params = $matches = [];
  118. foreach (normalize_header($header) as $val) {
  119. $part = [];
  120. foreach (\preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
  121. if (\preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
  122. $m = $matches[0];
  123. if (isset($m[1])) {
  124. $part[\trim($m[0], $trimmed)] = \trim($m[1], $trimmed);
  125. } else {
  126. $part[] = \trim($m[0], $trimmed);
  127. }
  128. }
  129. }
  130. if ($part) {
  131. $params[] = $part;
  132. }
  133. }
  134. return $params;
  135. }
  136. /**
  137. * Converts an array of header values that may contain comma separated
  138. * headers into an array of headers with no comma separated values.
  139. *
  140. * @param string|array $header Header to normalize.
  141. *
  142. * @return array Returns the normalized header field values.
  143. */
  144. function normalize_header($header)
  145. {
  146. if (!\is_array($header)) {
  147. return \array_map('trim', \explode(',', $header));
  148. }
  149. $result = [];
  150. foreach ($header as $value) {
  151. foreach ((array) $value as $v) {
  152. if (\strpos($v, ',') === \false) {
  153. $result[] = $v;
  154. continue;
  155. }
  156. foreach (\preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
  157. $result[] = \trim($vv);
  158. }
  159. }
  160. }
  161. return $result;
  162. }
  163. /**
  164. * Clone and modify a request with the given changes.
  165. *
  166. * The changes can be one of:
  167. * - method: (string) Changes the HTTP method.
  168. * - set_headers: (array) Sets the given headers.
  169. * - remove_headers: (array) Remove the given headers.
  170. * - body: (mixed) Sets the given body.
  171. * - uri: (UriInterface) Set the URI.
  172. * - query: (string) Set the query string value of the URI.
  173. * - version: (string) Set the protocol version.
  174. *
  175. * @param RequestInterface $request Request to clone and modify.
  176. * @param array $changes Changes to apply.
  177. *
  178. * @return RequestInterface
  179. */
  180. function modify_request(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $changes)
  181. {
  182. if (!$changes) {
  183. return $request;
  184. }
  185. $headers = $request->getHeaders();
  186. if (!isset($changes['uri'])) {
  187. $uri = $request->getUri();
  188. } else {
  189. // Remove the host header if one is on the URI
  190. if ($host = $changes['uri']->getHost()) {
  191. $changes['set_headers']['Host'] = $host;
  192. if ($port = $changes['uri']->getPort()) {
  193. $standardPorts = ['http' => 80, 'https' => 443];
  194. $scheme = $changes['uri']->getScheme();
  195. if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
  196. $changes['set_headers']['Host'] .= ':' . $port;
  197. }
  198. }
  199. }
  200. $uri = $changes['uri'];
  201. }
  202. if (!empty($changes['remove_headers'])) {
  203. $headers = _caseless_remove($changes['remove_headers'], $headers);
  204. }
  205. if (!empty($changes['set_headers'])) {
  206. $headers = _caseless_remove(\array_keys($changes['set_headers']), $headers);
  207. $headers = $changes['set_headers'] + $headers;
  208. }
  209. if (isset($changes['query'])) {
  210. $uri = $uri->withQuery($changes['query']);
  211. }
  212. if ($request instanceof \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface) {
  213. return (new \YoastSEO_Vendor\GuzzleHttp\Psr7\ServerRequest(isset($changes['method']) ? $changes['method'] : $request->getMethod(), $uri, $headers, isset($changes['body']) ? $changes['body'] : $request->getBody(), isset($changes['version']) ? $changes['version'] : $request->getProtocolVersion(), $request->getServerParams()))->withParsedBody($request->getParsedBody())->withQueryParams($request->getQueryParams())->withCookieParams($request->getCookieParams())->withUploadedFiles($request->getUploadedFiles());
  214. }
  215. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request(isset($changes['method']) ? $changes['method'] : $request->getMethod(), $uri, $headers, isset($changes['body']) ? $changes['body'] : $request->getBody(), isset($changes['version']) ? $changes['version'] : $request->getProtocolVersion());
  216. }
  217. /**
  218. * Attempts to rewind a message body and throws an exception on failure.
  219. *
  220. * The body of the message will only be rewound if a call to `tell()` returns a
  221. * value other than `0`.
  222. *
  223. * @param MessageInterface $message Message to rewind
  224. *
  225. * @throws \RuntimeException
  226. */
  227. function rewind_body(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message)
  228. {
  229. $body = $message->getBody();
  230. if ($body->tell()) {
  231. $body->rewind();
  232. }
  233. }
  234. /**
  235. * Safely opens a PHP stream resource using a filename.
  236. *
  237. * When fopen fails, PHP normally raises a warning. This function adds an
  238. * error handler that checks for errors and throws an exception instead.
  239. *
  240. * @param string $filename File to open
  241. * @param string $mode Mode used to open the file
  242. *
  243. * @return resource
  244. * @throws \RuntimeException if the file cannot be opened
  245. */
  246. function try_fopen($filename, $mode)
  247. {
  248. $ex = null;
  249. \set_error_handler(function () use($filename, $mode, &$ex) {
  250. $ex = new \RuntimeException(\sprintf('Unable to open %s using mode %s: %s', $filename, $mode, \func_get_args()[1]));
  251. });
  252. $handle = \fopen($filename, $mode);
  253. \restore_error_handler();
  254. if ($ex) {
  255. /** @var $ex \RuntimeException */
  256. throw $ex;
  257. }
  258. return $handle;
  259. }
  260. /**
  261. * Copy the contents of a stream into a string until the given number of
  262. * bytes have been read.
  263. *
  264. * @param StreamInterface $stream Stream to read
  265. * @param int $maxLen Maximum number of bytes to read. Pass -1
  266. * to read the entire stream.
  267. * @return string
  268. * @throws \RuntimeException on error.
  269. */
  270. function copy_to_string(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, $maxLen = -1)
  271. {
  272. $buffer = '';
  273. if ($maxLen === -1) {
  274. while (!$stream->eof()) {
  275. $buf = $stream->read(1048576);
  276. // Using a loose equality here to match on '' and false.
  277. if ($buf == null) {
  278. break;
  279. }
  280. $buffer .= $buf;
  281. }
  282. return $buffer;
  283. }
  284. $len = 0;
  285. while (!$stream->eof() && $len < $maxLen) {
  286. $buf = $stream->read($maxLen - $len);
  287. // Using a loose equality here to match on '' and false.
  288. if ($buf == null) {
  289. break;
  290. }
  291. $buffer .= $buf;
  292. $len = \strlen($buffer);
  293. }
  294. return $buffer;
  295. }
  296. /**
  297. * Copy the contents of a stream into another stream until the given number
  298. * of bytes have been read.
  299. *
  300. * @param StreamInterface $source Stream to read from
  301. * @param StreamInterface $dest Stream to write to
  302. * @param int $maxLen Maximum number of bytes to read. Pass -1
  303. * to read the entire stream.
  304. *
  305. * @throws \RuntimeException on error.
  306. */
  307. function copy_to_stream(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $dest, $maxLen = -1)
  308. {
  309. $bufferSize = 8192;
  310. if ($maxLen === -1) {
  311. while (!$source->eof()) {
  312. if (!$dest->write($source->read($bufferSize))) {
  313. break;
  314. }
  315. }
  316. } else {
  317. $remaining = $maxLen;
  318. while ($remaining > 0 && !$source->eof()) {
  319. $buf = $source->read(\min($bufferSize, $remaining));
  320. $len = \strlen($buf);
  321. if (!$len) {
  322. break;
  323. }
  324. $remaining -= $len;
  325. $dest->write($buf);
  326. }
  327. }
  328. }
  329. /**
  330. * Calculate a hash of a Stream
  331. *
  332. * @param StreamInterface $stream Stream to calculate the hash for
  333. * @param string $algo Hash algorithm (e.g. md5, crc32, etc)
  334. * @param bool $rawOutput Whether or not to use raw output
  335. *
  336. * @return string Returns the hash of the stream
  337. * @throws \RuntimeException on error.
  338. */
  339. function hash(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, $algo, $rawOutput = \false)
  340. {
  341. $pos = $stream->tell();
  342. if ($pos > 0) {
  343. $stream->rewind();
  344. }
  345. $ctx = \hash_init($algo);
  346. while (!$stream->eof()) {
  347. \hash_update($ctx, $stream->read(1048576));
  348. }
  349. $out = \hash_final($ctx, (bool) $rawOutput);
  350. $stream->seek($pos);
  351. return $out;
  352. }
  353. /**
  354. * Read a line from the stream up to the maximum allowed buffer length
  355. *
  356. * @param StreamInterface $stream Stream to read from
  357. * @param int $maxLength Maximum buffer length
  358. *
  359. * @return string
  360. */
  361. function readline(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, $maxLength = null)
  362. {
  363. $buffer = '';
  364. $size = 0;
  365. while (!$stream->eof()) {
  366. // Using a loose equality here to match on '' and false.
  367. if (null == ($byte = $stream->read(1))) {
  368. return $buffer;
  369. }
  370. $buffer .= $byte;
  371. // Break when a new line is found or the max length - 1 is reached
  372. if ($byte === "\n" || ++$size === $maxLength - 1) {
  373. break;
  374. }
  375. }
  376. return $buffer;
  377. }
  378. /**
  379. * Parses a request message string into a request object.
  380. *
  381. * @param string $message Request message string.
  382. *
  383. * @return Request
  384. */
  385. function parse_request($message)
  386. {
  387. $data = _parse_message($message);
  388. $matches = [];
  389. if (!\preg_match('/^[\\S]+\\s+([a-zA-Z]+:\\/\\/|\\/).*/', $data['start-line'], $matches)) {
  390. throw new \InvalidArgumentException('Invalid request string');
  391. }
  392. $parts = \explode(' ', $data['start-line'], 3);
  393. $version = isset($parts[2]) ? \explode('/', $parts[2])[1] : '1.1';
  394. $request = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($parts[0], $matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1], $data['headers'], $data['body'], $version);
  395. return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
  396. }
  397. /**
  398. * Parses a response message string into a response object.
  399. *
  400. * @param string $message Response message string.
  401. *
  402. * @return Response
  403. */
  404. function parse_response($message)
  405. {
  406. $data = _parse_message($message);
  407. // According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
  408. // between status-code and reason-phrase is required. But browsers accept
  409. // responses without space and reason as well.
  410. if (!\preg_match('/^HTTP\\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
  411. throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
  412. }
  413. $parts = \explode(' ', $data['start-line'], 3);
  414. return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($parts[1], $data['headers'], $data['body'], \explode('/', $parts[0])[1], isset($parts[2]) ? $parts[2] : null);
  415. }
  416. /**
  417. * Parse a query string into an associative array.
  418. *
  419. * If multiple values are found for the same key, the value of that key
  420. * value pair will become an array. This function does not parse nested
  421. * PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
  422. * be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
  423. *
  424. * @param string $str Query string to parse
  425. * @param int|bool $urlEncoding How the query string is encoded
  426. *
  427. * @return array
  428. */
  429. function parse_query($str, $urlEncoding = \true)
  430. {
  431. $result = [];
  432. if ($str === '') {
  433. return $result;
  434. }
  435. if ($urlEncoding === \true) {
  436. $decoder = function ($value) {
  437. return \rawurldecode(\str_replace('+', ' ', $value));
  438. };
  439. } elseif ($urlEncoding === \PHP_QUERY_RFC3986) {
  440. $decoder = 'rawurldecode';
  441. } elseif ($urlEncoding === \PHP_QUERY_RFC1738) {
  442. $decoder = 'urldecode';
  443. } else {
  444. $decoder = function ($str) {
  445. return $str;
  446. };
  447. }
  448. foreach (\explode('&', $str) as $kvp) {
  449. $parts = \explode('=', $kvp, 2);
  450. $key = $decoder($parts[0]);
  451. $value = isset($parts[1]) ? $decoder($parts[1]) : null;
  452. if (!isset($result[$key])) {
  453. $result[$key] = $value;
  454. } else {
  455. if (!\is_array($result[$key])) {
  456. $result[$key] = [$result[$key]];
  457. }
  458. $result[$key][] = $value;
  459. }
  460. }
  461. return $result;
  462. }
  463. /**
  464. * Build a query string from an array of key value pairs.
  465. *
  466. * This function can use the return value of parse_query() to build a query
  467. * string. This function does not modify the provided keys when an array is
  468. * encountered (like http_build_query would).
  469. *
  470. * @param array $params Query string parameters.
  471. * @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
  472. * to encode using RFC3986, or PHP_QUERY_RFC1738
  473. * to encode using RFC1738.
  474. * @return string
  475. */
  476. function build_query(array $params, $encoding = \PHP_QUERY_RFC3986)
  477. {
  478. if (!$params) {
  479. return '';
  480. }
  481. if ($encoding === \false) {
  482. $encoder = function ($str) {
  483. return $str;
  484. };
  485. } elseif ($encoding === \PHP_QUERY_RFC3986) {
  486. $encoder = 'rawurlencode';
  487. } elseif ($encoding === \PHP_QUERY_RFC1738) {
  488. $encoder = 'urlencode';
  489. } else {
  490. throw new \InvalidArgumentException('Invalid type');
  491. }
  492. $qs = '';
  493. foreach ($params as $k => $v) {
  494. $k = $encoder($k);
  495. if (!\is_array($v)) {
  496. $qs .= $k;
  497. if ($v !== null) {
  498. $qs .= '=' . $encoder($v);
  499. }
  500. $qs .= '&';
  501. } else {
  502. foreach ($v as $vv) {
  503. $qs .= $k;
  504. if ($vv !== null) {
  505. $qs .= '=' . $encoder($vv);
  506. }
  507. $qs .= '&';
  508. }
  509. }
  510. }
  511. return $qs ? (string) \substr($qs, 0, -1) : '';
  512. }
  513. /**
  514. * Determines the mimetype of a file by looking at its extension.
  515. *
  516. * @param $filename
  517. *
  518. * @return null|string
  519. */
  520. function mimetype_from_filename($filename)
  521. {
  522. return mimetype_from_extension(\pathinfo($filename, \PATHINFO_EXTENSION));
  523. }
  524. /**
  525. * Maps a file extensions to a mimetype.
  526. *
  527. * @param $extension string The file extension.
  528. *
  529. * @return string|null
  530. * @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
  531. */
  532. function mimetype_from_extension($extension)
  533. {
  534. static $mimetypes = ['3gp' => 'video/3gpp', '7z' => 'application/x-7z-compressed', 'aac' => 'audio/x-aac', 'ai' => 'application/postscript', 'aif' => 'audio/x-aiff', 'asc' => 'text/plain', 'asf' => 'video/x-ms-asf', 'atom' => 'application/atom+xml', 'avi' => 'video/x-msvideo', 'bmp' => 'image/bmp', 'bz2' => 'application/x-bzip2', 'cer' => 'application/pkix-cert', 'crl' => 'application/pkix-crl', 'crt' => 'application/x-x509-ca-cert', 'css' => 'text/css', 'csv' => 'text/csv', 'cu' => 'application/cu-seeme', 'deb' => 'application/x-debian-package', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dvi' => 'application/x-dvi', 'eot' => 'application/vnd.ms-fontobject', 'eps' => 'application/postscript', 'epub' => 'application/epub+zip', 'etx' => 'text/x-setext', 'flac' => 'audio/flac', 'flv' => 'video/x-flv', 'gif' => 'image/gif', 'gz' => 'application/gzip', 'htm' => 'text/html', 'html' => 'text/html', 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 'ini' => 'text/plain', 'iso' => 'application/x-iso9660-image', 'jar' => 'application/java-archive', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpg' => 'image/jpeg', 'js' => 'text/javascript', 'json' => 'application/json', 'latex' => 'application/x-latex', 'log' => 'text/plain', 'm4a' => 'audio/mp4', 'm4v' => 'video/mp4', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mov' => 'video/quicktime', 'mkv' => 'video/x-matroska', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'mp4a' => 'audio/mp4', 'mp4v' => 'video/mp4', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpg' => 'video/mpeg', 'mpg4' => 'video/mp4', 'oga' => 'audio/ogg', 'ogg' => 'audio/ogg', 'ogv' => 'video/ogg', 'ogx' => 'application/ogg', 'pbm' => 'image/x-portable-bitmap', 'pdf' => 'application/pdf', 'pgm' => 'image/x-portable-graymap', 'png' => 'image/png', 'pnm' => 'image/x-portable-anymap', 'ppm' => 'image/x-portable-pixmap', 'ppt' => 'application/vnd.ms-powerpoint', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'ps' => 'application/postscript', 'qt' => 'video/quicktime', 'rar' => 'application/x-rar-compressed', 'ras' => 'image/x-cmu-raster', 'rss' => 'application/rss+xml', 'rtf' => 'application/rtf', 'sgm' => 'text/sgml', 'sgml' => 'text/sgml', 'svg' => 'image/svg+xml', 'swf' => 'application/x-shockwave-flash', 'tar' => 'application/x-tar', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'torrent' => 'application/x-bittorrent', 'ttf' => 'application/x-font-ttf', 'txt' => 'text/plain', 'wav' => 'audio/x-wav', 'webm' => 'video/webm', 'wma' => 'audio/x-ms-wma', 'wmv' => 'video/x-ms-wmv', 'woff' => 'application/x-font-woff', 'wsdl' => 'application/wsdl+xml', 'xbm' => 'image/x-xbitmap', 'xls' => 'application/vnd.ms-excel', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xml' => 'application/xml', 'xpm' => 'image/x-xpixmap', 'xwd' => 'image/x-xwindowdump', 'yaml' => 'text/yaml', 'yml' => 'text/yaml', 'zip' => 'application/zip'];
  535. $extension = \strtolower($extension);
  536. return isset($mimetypes[$extension]) ? $mimetypes[$extension] : null;
  537. }
  538. /**
  539. * Parses an HTTP message into an associative array.
  540. *
  541. * The array contains the "start-line" key containing the start line of
  542. * the message, "headers" key containing an associative array of header
  543. * array values, and a "body" key containing the body of the message.
  544. *
  545. * @param string $message HTTP request or response to parse.
  546. *
  547. * @return array
  548. * @internal
  549. */
  550. function _parse_message($message)
  551. {
  552. if (!$message) {
  553. throw new \InvalidArgumentException('Invalid message');
  554. }
  555. $message = \ltrim($message, "\r\n");
  556. $messageParts = \preg_split("/\r?\n\r?\n/", $message, 2);
  557. if ($messageParts === \false || \count($messageParts) !== 2) {
  558. throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
  559. }
  560. list($rawHeaders, $body) = $messageParts;
  561. $rawHeaders .= "\r\n";
  562. // Put back the delimiter we split previously
  563. $headerParts = \preg_split("/\r?\n/", $rawHeaders, 2);
  564. if ($headerParts === \false || \count($headerParts) !== 2) {
  565. throw new \InvalidArgumentException('Invalid message: Missing status line');
  566. }
  567. list($startLine, $rawHeaders) = $headerParts;
  568. if (\preg_match("/(?:^HTTP\\/|^[A-Z]+ \\S+ HTTP\\/)(\\d+(?:\\.\\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
  569. // Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
  570. $rawHeaders = \preg_replace(\YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
  571. }
  572. /** @var array[] $headerLines */
  573. $count = \preg_match_all(\YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, \PREG_SET_ORDER);
  574. // If these aren't the same, then one line didn't match and there's an invalid header.
  575. if ($count !== \substr_count($rawHeaders, "\n")) {
  576. // Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
  577. if (\preg_match(\YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
  578. throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
  579. }
  580. throw new \InvalidArgumentException('Invalid header syntax');
  581. }
  582. $headers = [];
  583. foreach ($headerLines as $headerLine) {
  584. $headers[$headerLine[1]][] = $headerLine[2];
  585. }
  586. return ['start-line' => $startLine, 'headers' => $headers, 'body' => $body];
  587. }
  588. /**
  589. * Constructs a URI for an HTTP request message.
  590. *
  591. * @param string $path Path from the start-line
  592. * @param array $headers Array of headers (each value an array).
  593. *
  594. * @return string
  595. * @internal
  596. */
  597. function _parse_request_uri($path, array $headers)
  598. {
  599. $hostKey = \array_filter(\array_keys($headers), function ($k) {
  600. return \strtolower($k) === 'host';
  601. });
  602. // If no host is found, then a full URI cannot be constructed.
  603. if (!$hostKey) {
  604. return $path;
  605. }
  606. $host = $headers[\reset($hostKey)][0];
  607. $scheme = \substr($host, -4) === ':443' ? 'https' : 'http';
  608. return $scheme . '://' . $host . '/' . \ltrim($path, '/');
  609. }
  610. /**
  611. * Get a short summary of the message body
  612. *
  613. * Will return `null` if the response is not printable.
  614. *
  615. * @param MessageInterface $message The message to get the body summary
  616. * @param int $truncateAt The maximum allowed size of the summary
  617. *
  618. * @return null|string
  619. */
  620. function get_message_body_summary(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message, $truncateAt = 120)
  621. {
  622. $body = $message->getBody();
  623. if (!$body->isSeekable() || !$body->isReadable()) {
  624. return null;
  625. }
  626. $size = $body->getSize();
  627. if ($size === 0) {
  628. return null;
  629. }
  630. $summary = $body->read($truncateAt);
  631. $body->rewind();
  632. if ($size > $truncateAt) {
  633. $summary .= ' (truncated...)';
  634. }
  635. // Matches any printable character, including unicode characters:
  636. // letters, marks, numbers, punctuation, spacing, and separators.
  637. if (\preg_match('/[^\\pL\\pM\\pN\\pP\\pS\\pZ\\n\\r\\t]/', $summary)) {
  638. return null;
  639. }
  640. return $summary;
  641. }
  642. /** @internal */
  643. function _caseless_remove($keys, array $data)
  644. {
  645. $result = [];
  646. foreach ($keys as &$key) {
  647. $key = \strtolower($key);
  648. }
  649. foreach ($data as $k => $v) {
  650. if (!\in_array(\strtolower($k), $keys)) {
  651. $result[$k] = $v;
  652. }
  653. }
  654. return $result;
  655. }