UtilsTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. <?php
  2. declare(strict_types=1);
  3. namespace GuzzleHttp\Tests\Psr7;
  4. use GuzzleHttp\Psr7;
  5. use GuzzleHttp\Psr7\FnStream;
  6. use GuzzleHttp\Psr7\NoSeekStream;
  7. use PHPUnit\Framework\TestCase;
  8. use Psr\Http\Message\StreamInterface;
  9. class UtilsTest extends TestCase
  10. {
  11. public function testCopiesToString(): void
  12. {
  13. $s = Psr7\Utils::streamFor('foobaz');
  14. self::assertSame('foobaz', Psr7\Utils::copyToString($s));
  15. $s->seek(0);
  16. self::assertSame('foo', Psr7\Utils::copyToString($s, 3));
  17. self::assertSame('baz', Psr7\Utils::copyToString($s, 3));
  18. self::assertSame('', Psr7\Utils::copyToString($s));
  19. }
  20. public function testCopiesToStringStopsWhenReadFails(): void
  21. {
  22. $s1 = Psr7\Utils::streamFor('foobaz');
  23. $s1 = FnStream::decorate($s1, [
  24. 'read' => function () {
  25. return '';
  26. },
  27. ]);
  28. $result = Psr7\Utils::copyToString($s1);
  29. self::assertSame('', $result);
  30. }
  31. public function testCopiesToStream(): void
  32. {
  33. $s1 = Psr7\Utils::streamFor('foobaz');
  34. $s2 = Psr7\Utils::streamFor('');
  35. Psr7\Utils::copyToStream($s1, $s2);
  36. self::assertSame('foobaz', (string) $s2);
  37. $s2 = Psr7\Utils::streamFor('');
  38. $s1->seek(0);
  39. Psr7\Utils::copyToStream($s1, $s2, 3);
  40. self::assertSame('foo', (string) $s2);
  41. Psr7\Utils::copyToStream($s1, $s2, 3);
  42. self::assertSame('foobaz', (string) $s2);
  43. }
  44. public function testStopsCopyToStreamWhenWriteFails(): void
  45. {
  46. $s1 = Psr7\Utils::streamFor('foobaz');
  47. $s2 = Psr7\Utils::streamFor('');
  48. $s2 = FnStream::decorate($s2, [
  49. 'write' => function () {
  50. return 0;
  51. },
  52. ]);
  53. Psr7\Utils::copyToStream($s1, $s2);
  54. self::assertSame('', (string) $s2);
  55. }
  56. public function testStopsCopyToSteamWhenWriteFailsWithMaxLen(): void
  57. {
  58. $s1 = Psr7\Utils::streamFor('foobaz');
  59. $s2 = Psr7\Utils::streamFor('');
  60. $s2 = FnStream::decorate($s2, [
  61. 'write' => function () {
  62. return 0;
  63. },
  64. ]);
  65. Psr7\Utils::copyToStream($s1, $s2, 10);
  66. self::assertSame('', (string) $s2);
  67. }
  68. public function testCopyToStreamReadsInChunksInsteadOfAllInMemory(): void
  69. {
  70. $sizes = [];
  71. $s1 = new Psr7\FnStream([
  72. 'eof' => function () {
  73. return false;
  74. },
  75. 'read' => function ($size) use (&$sizes) {
  76. $sizes[] = $size;
  77. return str_repeat('.', $size);
  78. },
  79. ]);
  80. $s2 = Psr7\Utils::streamFor('');
  81. Psr7\Utils::copyToStream($s1, $s2, 16394);
  82. $s2->seek(0);
  83. self::assertSame(16394, strlen($s2->getContents()));
  84. self::assertSame(8192, $sizes[0]);
  85. self::assertSame(8192, $sizes[1]);
  86. self::assertSame(10, $sizes[2]);
  87. }
  88. public function testStopsCopyToSteamWhenReadFailsWithMaxLen(): void
  89. {
  90. $s1 = Psr7\Utils::streamFor('foobaz');
  91. $s1 = FnStream::decorate($s1, [
  92. 'read' => function () {
  93. return '';
  94. },
  95. ]);
  96. $s2 = Psr7\Utils::streamFor('');
  97. Psr7\Utils::copyToStream($s1, $s2, 10);
  98. self::assertSame('', (string) $s2);
  99. }
  100. public function testReadsLines(): void
  101. {
  102. $s = Psr7\Utils::streamFor("foo\nbaz\nbar");
  103. self::assertSame("foo\n", Psr7\Utils::readLine($s));
  104. self::assertSame("baz\n", Psr7\Utils::readLine($s));
  105. self::assertSame('bar', Psr7\Utils::readLine($s));
  106. }
  107. public function testReadsLinesUpToMaxLength(): void
  108. {
  109. $s = Psr7\Utils::streamFor("12345\n");
  110. self::assertSame('123', Psr7\Utils::readLine($s, 4));
  111. self::assertSame("45\n", Psr7\Utils::readLine($s));
  112. }
  113. public function testReadLinesEof(): void
  114. {
  115. // Should return empty string on EOF
  116. $s = Psr7\Utils::streamFor("foo\nbar");
  117. while (!$s->eof()) {
  118. Psr7\Utils::readLine($s);
  119. }
  120. self::assertSame('', Psr7\Utils::readLine($s));
  121. }
  122. public function testReadsLineUntilEmptyStringReturnedFromRead(): void
  123. {
  124. $s = $this->createMock(StreamInterface::class);
  125. $s->expects(self::exactly(2))
  126. ->method('read')
  127. ->willReturnCallback(function () {
  128. static $called = false;
  129. if ($called) {
  130. return '';
  131. }
  132. $called = true;
  133. return 'h';
  134. });
  135. $s->expects(self::exactly(2))
  136. ->method('eof')
  137. ->willReturn(false);
  138. self::assertSame('h', Psr7\Utils::readLine($s));
  139. }
  140. public function testCalculatesHash(): void
  141. {
  142. $s = Psr7\Utils::streamFor('foobazbar');
  143. self::assertSame(md5('foobazbar'), Psr7\Utils::hash($s, 'md5'));
  144. }
  145. public function testCalculatesHashThrowsWhenSeekFails(): void
  146. {
  147. $s = new NoSeekStream(Psr7\Utils::streamFor('foobazbar'));
  148. $s->read(2);
  149. $this->expectException(\RuntimeException::class);
  150. Psr7\Utils::hash($s, 'md5');
  151. }
  152. public function testCalculatesHashSeeksToOriginalPosition(): void
  153. {
  154. $s = Psr7\Utils::streamFor('foobazbar');
  155. $s->seek(4);
  156. self::assertSame(md5('foobazbar'), Psr7\Utils::hash($s, 'md5'));
  157. self::assertSame(4, $s->tell());
  158. }
  159. public function testOpensFilesSuccessfully(): void
  160. {
  161. $r = Psr7\Utils::tryFopen(__FILE__, 'r');
  162. self::assertIsResource($r);
  163. fclose($r);
  164. }
  165. public function testThrowsExceptionNotWarning(): void
  166. {
  167. $this->expectException(\RuntimeException::class);
  168. $this->expectExceptionMessage('Unable to open "/path/to/does/not/exist" using mode "r"');
  169. Psr7\Utils::tryFopen('/path/to/does/not/exist', 'r');
  170. }
  171. public function testThrowsExceptionNotValueError(): void
  172. {
  173. $this->expectException(\RuntimeException::class);
  174. $this->expectExceptionMessage('Unable to open "" using mode "r"');
  175. Psr7\Utils::tryFopen('', 'r');
  176. }
  177. /**
  178. * @requires PHP 7.4
  179. */
  180. public function testGetsContentsThrowExceptionWhenNotReadable(): void
  181. {
  182. $r = fopen(tempnam(sys_get_temp_dir(), 'guzzle-psr7-'), 'w');
  183. fwrite($r, 'hello world!');
  184. $this->expectException(\RuntimeException::class);
  185. $this->expectExceptionMessage('Unable to read stream contents');
  186. try {
  187. Psr7\Utils::tryGetContents($r);
  188. } finally {
  189. fclose($r);
  190. }
  191. }
  192. public function testGetsContentsThrowExceptionWhenCLosed(): void
  193. {
  194. $r = fopen(tempnam(sys_get_temp_dir(), 'guzzle-psr7-'), 'r+');
  195. fclose($r);
  196. $this->expectException(\RuntimeException::class);
  197. $this->expectExceptionMessage('Unable to read stream contents');
  198. Psr7\Utils::tryGetContents($r);
  199. }
  200. public function testCreatesUriForValue(): void
  201. {
  202. self::assertInstanceOf('GuzzleHttp\Psr7\Uri', Psr7\Utils::uriFor('/foo'));
  203. self::assertInstanceOf(
  204. 'GuzzleHttp\Psr7\Uri',
  205. Psr7\Utils::uriFor(new Psr7\Uri('/foo'))
  206. );
  207. }
  208. public function testValidatesUri(): void
  209. {
  210. $this->expectException(\InvalidArgumentException::class);
  211. Psr7\Utils::uriFor([]);
  212. }
  213. public function testKeepsPositionOfResource(): void
  214. {
  215. $h = fopen(__FILE__, 'r');
  216. fseek($h, 10);
  217. $stream = Psr7\Utils::streamFor($h);
  218. self::assertSame(10, $stream->tell());
  219. $stream->close();
  220. }
  221. public function testCreatesWithFactory(): void
  222. {
  223. $stream = Psr7\Utils::streamFor('foo');
  224. self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $stream);
  225. self::assertSame('foo', $stream->getContents());
  226. $stream->close();
  227. }
  228. public function testFactoryCreatesFromEmptyString(): void
  229. {
  230. $s = Psr7\Utils::streamFor();
  231. self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s);
  232. }
  233. public function testFactoryCreatesFromNull(): void
  234. {
  235. $s = Psr7\Utils::streamFor(null);
  236. self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s);
  237. }
  238. public function testFactoryCreatesFromResource(): void
  239. {
  240. $r = fopen(__FILE__, 'r');
  241. $s = Psr7\Utils::streamFor($r);
  242. self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s);
  243. self::assertSame(file_get_contents(__FILE__), (string) $s);
  244. }
  245. public function testFactoryCreatesFromObjectWithToString(): void
  246. {
  247. $r = new HasToString();
  248. $s = Psr7\Utils::streamFor($r);
  249. self::assertInstanceOf('GuzzleHttp\Psr7\Stream', $s);
  250. self::assertSame('foo', (string) $s);
  251. }
  252. public function testCreatePassesThrough(): void
  253. {
  254. $s = Psr7\Utils::streamFor('foo');
  255. self::assertSame($s, Psr7\Utils::streamFor($s));
  256. }
  257. public function testThrowsExceptionForUnknown(): void
  258. {
  259. $this->expectException(\InvalidArgumentException::class);
  260. Psr7\Utils::streamFor(new \stdClass());
  261. }
  262. public function testReturnsCustomMetadata(): void
  263. {
  264. $s = Psr7\Utils::streamFor('foo', ['metadata' => ['hwm' => 3]]);
  265. self::assertSame(3, $s->getMetadata('hwm'));
  266. self::assertArrayHasKey('hwm', $s->getMetadata());
  267. }
  268. public function testCanSetSize(): void
  269. {
  270. $s = Psr7\Utils::streamFor('', ['size' => 10]);
  271. self::assertSame(10, $s->getSize());
  272. }
  273. public function testCanCreateIteratorBasedStream(): void
  274. {
  275. $a = new \ArrayIterator(['foo', 'bar', '123']);
  276. $p = Psr7\Utils::streamFor($a);
  277. self::assertInstanceOf('GuzzleHttp\Psr7\PumpStream', $p);
  278. self::assertSame('foo', $p->read(3));
  279. self::assertFalse($p->eof());
  280. self::assertSame('b', $p->read(1));
  281. self::assertSame('a', $p->read(1));
  282. self::assertSame('r12', $p->read(3));
  283. self::assertFalse($p->eof());
  284. self::assertSame('3', $p->getContents());
  285. self::assertTrue($p->eof());
  286. self::assertSame(9, $p->tell());
  287. }
  288. public function testConvertsRequestsToStrings(): void
  289. {
  290. $request = new Psr7\Request('PUT', 'http://foo.com/hi?123', [
  291. 'Baz' => 'bar',
  292. 'Qux' => 'ipsum',
  293. ], 'hello', '1.0');
  294. self::assertSame(
  295. "PUT /hi?123 HTTP/1.0\r\nHost: foo.com\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello",
  296. Psr7\Message::toString($request)
  297. );
  298. }
  299. public function testConvertsResponsesToStrings(): void
  300. {
  301. $response = new Psr7\Response(200, [
  302. 'Baz' => 'bar',
  303. 'Qux' => 'ipsum',
  304. ], 'hello', '1.0', 'FOO');
  305. self::assertSame(
  306. "HTTP/1.0 200 FOO\r\nBaz: bar\r\nQux: ipsum\r\n\r\nhello",
  307. Psr7\Message::toString($response)
  308. );
  309. }
  310. public function testCorrectlyRendersSetCookieHeadersToString(): void
  311. {
  312. $response = new Psr7\Response(200, [
  313. 'Set-Cookie' => ['bar', 'baz', 'qux'],
  314. ], 'hello', '1.0', 'FOO');
  315. self::assertSame(
  316. "HTTP/1.0 200 FOO\r\nSet-Cookie: bar\r\nSet-Cookie: baz\r\nSet-Cookie: qux\r\n\r\nhello",
  317. Psr7\Message::toString($response)
  318. );
  319. }
  320. public function testCanModifyRequestWithUri(): void
  321. {
  322. $r1 = new Psr7\Request('GET', 'http://foo.com');
  323. $r2 = Psr7\Utils::modifyRequest($r1, [
  324. 'uri' => new Psr7\Uri('http://www.foo.com'),
  325. ]);
  326. self::assertSame('http://www.foo.com', (string) $r2->getUri());
  327. self::assertSame('www.foo.com', (string) $r2->getHeaderLine('host'));
  328. }
  329. public function testCanModifyRequestWithUriAndPort(): void
  330. {
  331. $r1 = new Psr7\Request('GET', 'http://foo.com:8000');
  332. $r2 = Psr7\Utils::modifyRequest($r1, [
  333. 'uri' => new Psr7\Uri('http://www.foo.com:8000'),
  334. ]);
  335. self::assertSame('http://www.foo.com:8000', (string) $r2->getUri());
  336. self::assertSame('www.foo.com:8000', (string) $r2->getHeaderLine('host'));
  337. }
  338. public function testCanModifyRequestWithCaseInsensitiveHeader(): void
  339. {
  340. $r1 = new Psr7\Request('GET', 'http://foo.com', ['User-Agent' => 'foo']);
  341. $r2 = Psr7\Utils::modifyRequest($r1, ['set_headers' => ['User-agent' => 'bar']]);
  342. self::assertSame('bar', $r2->getHeaderLine('User-Agent'));
  343. self::assertSame('bar', $r2->getHeaderLine('User-agent'));
  344. }
  345. public function testReturnsAsIsWhenNoChanges(): void
  346. {
  347. $r1 = new Psr7\Request('GET', 'http://foo.com');
  348. $r2 = Psr7\Utils::modifyRequest($r1, []);
  349. self::assertInstanceOf('GuzzleHttp\Psr7\Request', $r2);
  350. $r1 = new Psr7\ServerRequest('GET', 'http://foo.com');
  351. $r2 = Psr7\Utils::modifyRequest($r1, []);
  352. self::assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $r2);
  353. }
  354. public function testReturnsUriAsIsWhenNoChanges(): void
  355. {
  356. $r1 = new Psr7\Request('GET', 'http://foo.com');
  357. $r2 = Psr7\Utils::modifyRequest($r1, ['set_headers' => ['foo' => 'bar']]);
  358. self::assertNotSame($r1, $r2);
  359. self::assertSame('bar', $r2->getHeaderLine('foo'));
  360. }
  361. public function testRemovesHeadersFromMessage(): void
  362. {
  363. $r1 = new Psr7\Request('GET', 'http://foo.com', ['foo' => 'bar']);
  364. $r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['foo']]);
  365. self::assertNotSame($r1, $r2);
  366. self::assertFalse($r2->hasHeader('foo'));
  367. }
  368. public function testAddsQueryToUri(): void
  369. {
  370. $r1 = new Psr7\Request('GET', 'http://foo.com');
  371. $r2 = Psr7\Utils::modifyRequest($r1, ['query' => 'foo=bar']);
  372. self::assertNotSame($r1, $r2);
  373. self::assertSame('foo=bar', $r2->getUri()->getQuery());
  374. }
  375. public function testModifyRequestKeepInstanceOfRequest(): void
  376. {
  377. $r1 = new Psr7\Request('GET', 'http://foo.com');
  378. $r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['non-existent']]);
  379. self::assertInstanceOf('GuzzleHttp\Psr7\Request', $r2);
  380. $r1 = new Psr7\ServerRequest('GET', 'http://foo.com');
  381. $r2 = Psr7\Utils::modifyRequest($r1, ['remove_headers' => ['non-existent']]);
  382. self::assertInstanceOf('Psr\Http\Message\ServerRequestInterface', $r2);
  383. }
  384. public function testModifyServerRequestWithUploadedFiles(): void
  385. {
  386. $request = new Psr7\ServerRequest('GET', 'http://example.com/bla');
  387. $file = new Psr7\UploadedFile('Test', 100, \UPLOAD_ERR_OK);
  388. $request = $request->withUploadedFiles([$file]);
  389. /** @var Psr7\ServerRequest $modifiedRequest */
  390. $modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]);
  391. self::assertCount(1, $modifiedRequest->getUploadedFiles());
  392. $files = $modifiedRequest->getUploadedFiles();
  393. self::assertInstanceOf('GuzzleHttp\Psr7\UploadedFile', $files[0]);
  394. }
  395. public function testModifyServerRequestWithCookies(): void
  396. {
  397. $request = (new Psr7\ServerRequest('GET', 'http://example.com/bla'))
  398. ->withCookieParams(['name' => 'value']);
  399. /** @var Psr7\ServerRequest $modifiedRequest */
  400. $modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]);
  401. self::assertSame(['name' => 'value'], $modifiedRequest->getCookieParams());
  402. }
  403. public function testModifyServerRequestParsedBody(): void
  404. {
  405. $request = (new Psr7\ServerRequest('GET', 'http://example.com/bla'))
  406. ->withParsedBody(['name' => 'value']);
  407. /** @var Psr7\ServerRequest $modifiedRequest */
  408. $modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]);
  409. self::assertSame(['name' => 'value'], $modifiedRequest->getParsedBody());
  410. }
  411. public function testModifyServerRequestQueryParams(): void
  412. {
  413. $request = (new Psr7\ServerRequest('GET', 'http://example.com/bla'))
  414. ->withQueryParams(['name' => 'value']);
  415. /** @var Psr7\ServerRequest $modifiedRequest */
  416. $modifiedRequest = Psr7\Utils::modifyRequest($request, ['set_headers' => ['foo' => 'bar']]);
  417. self::assertSame(['name' => 'value'], $modifiedRequest->getQueryParams());
  418. }
  419. public function testModifyServerRequestRetainsAttributes(): void
  420. {
  421. $request = (new Psr7\ServerRequest('GET', 'http://example.com/bla'))
  422. ->withAttribute('foo', 'bar');
  423. /** @var Psr7\ServerRequest $modifiedRequest */
  424. $modifiedRequest = Psr7\Utils::modifyRequest($request, []);
  425. self::assertSame(['foo' => 'bar'], $modifiedRequest->getAttributes());
  426. }
  427. /**
  428. * @return list<array{0: string[], 1: array, 2: array}>
  429. */
  430. public function providesCaselessRemoveCases(): array
  431. {
  432. return [
  433. [
  434. ['foo-bar'],
  435. ['Foo-Bar' => 'hello'],
  436. [],
  437. ],
  438. [
  439. ['foo-bar'],
  440. ['hello'],
  441. ['hello'],
  442. ],
  443. [
  444. ['foo-Bar'],
  445. ['Foo-Bar' => 'hello', 123 => '', 'Foo-BAR' => 'hello123', 'foobar' => 'baz'],
  446. [123 => '', 'foobar' => 'baz'],
  447. ],
  448. [
  449. ['foo-Bar', 123],
  450. ['Foo-Bar' => 'hello', 123 => '', 'Foo-BAR' => 'hello123', 'foobar' => 'baz'],
  451. ['foobar' => 'baz'],
  452. ],
  453. ];
  454. }
  455. /**
  456. * @dataProvider providesCaselessRemoveCases
  457. *
  458. * @param string[] $keys
  459. */
  460. public function testCaselessRemove(array $keys, array $data, array $expected): void
  461. {
  462. self::assertSame($expected, Psr7\Utils::caselessRemove($keys, $data));
  463. }
  464. }