PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Symfony/Component/HttpClient/Tests/HttpClientTraitTest.php

https://github.com/FabienD/symfony
PHP | 277 lines | 212 code | 30 blank | 35 comment | 0 complexity | eef1f95e01ae3da748d170037f29d408 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpClient\Tests;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
  13. use Symfony\Component\HttpClient\HttpClientTrait;
  14. use Symfony\Contracts\HttpClient\HttpClientInterface;
  15. class HttpClientTraitTest extends TestCase
  16. {
  17. use HttpClientTrait;
  18. private const RFC3986_BASE = 'http://a/b/c/d;p?q';
  19. /**
  20. * @dataProvider providePrepareRequestUrl
  21. */
  22. public function testPrepareRequestUrl(string $expected, string $url, array $query = [])
  23. {
  24. $defaults = [
  25. 'base_uri' => 'http://example.com?c=c',
  26. 'query' => ['a' => 1, 'b' => 'b'],
  27. ];
  28. [, $defaults] = self::prepareRequest(null, null, $defaults);
  29. [$url] = self::prepareRequest(null, $url, ['query' => $query], $defaults);
  30. $this->assertSame($expected, implode('', $url));
  31. }
  32. public function providePrepareRequestUrl(): iterable
  33. {
  34. yield ['http://example.com/', 'http://example.com/'];
  35. yield ['http://example.com/?a=1&b=b', '.'];
  36. yield ['http://example.com/?a=2&b=b', '.?a=2'];
  37. yield ['http://example.com/?a=3&b=b', '.', ['a' => 3]];
  38. yield ['http://example.com/?a=3&b=b', '.?a=0', ['a' => 3]];
  39. yield ['http://example.com/', 'http://example.com/', ['a' => null]];
  40. yield ['http://example.com/?b=', 'http://example.com/', ['b' => '']];
  41. yield ['http://example.com/?b=', 'http://example.com/', ['a' => null, 'b' => '']];
  42. }
  43. /**
  44. * @dataProvider provideResolveUrl
  45. */
  46. public function testResolveUrl(string $base, string $url, string $expected)
  47. {
  48. $this->assertSame($expected, implode('', self::resolveUrl(self::parseUrl($url), self::parseUrl($base))));
  49. }
  50. /**
  51. * From https://github.com/guzzle/psr7/blob/master/tests/UriResoverTest.php.
  52. */
  53. public function provideResolveUrl(): array
  54. {
  55. return [
  56. [self::RFC3986_BASE, 'http:h', 'http:h'],
  57. [self::RFC3986_BASE, 'g', 'http://a/b/c/g'],
  58. [self::RFC3986_BASE, './g', 'http://a/b/c/g'],
  59. [self::RFC3986_BASE, 'g/', 'http://a/b/c/g/'],
  60. [self::RFC3986_BASE, '/g', 'http://a/g'],
  61. [self::RFC3986_BASE, '//g', 'http://g/'],
  62. [self::RFC3986_BASE, '?y', 'http://a/b/c/d;p?y'],
  63. [self::RFC3986_BASE, 'g?y', 'http://a/b/c/g?y'],
  64. [self::RFC3986_BASE, '#s', 'http://a/b/c/d;p?q#s'],
  65. [self::RFC3986_BASE, 'g#s', 'http://a/b/c/g#s'],
  66. [self::RFC3986_BASE, 'g?y#s', 'http://a/b/c/g?y#s'],
  67. [self::RFC3986_BASE, ';x', 'http://a/b/c/;x'],
  68. [self::RFC3986_BASE, 'g;x', 'http://a/b/c/g;x'],
  69. [self::RFC3986_BASE, 'g;x?y#s', 'http://a/b/c/g;x?y#s'],
  70. [self::RFC3986_BASE, '', self::RFC3986_BASE],
  71. [self::RFC3986_BASE, '.', 'http://a/b/c/'],
  72. [self::RFC3986_BASE, './', 'http://a/b/c/'],
  73. [self::RFC3986_BASE, '..', 'http://a/b/'],
  74. [self::RFC3986_BASE, '../', 'http://a/b/'],
  75. [self::RFC3986_BASE, '../g', 'http://a/b/g'],
  76. [self::RFC3986_BASE, '../..', 'http://a/'],
  77. [self::RFC3986_BASE, '../../', 'http://a/'],
  78. [self::RFC3986_BASE, '../../g', 'http://a/g'],
  79. [self::RFC3986_BASE, '../../../g', 'http://a/g'],
  80. [self::RFC3986_BASE, '../../../../g', 'http://a/g'],
  81. [self::RFC3986_BASE, '/./g', 'http://a/g'],
  82. [self::RFC3986_BASE, '/../g', 'http://a/g'],
  83. [self::RFC3986_BASE, 'g.', 'http://a/b/c/g.'],
  84. [self::RFC3986_BASE, '.g', 'http://a/b/c/.g'],
  85. [self::RFC3986_BASE, 'g..', 'http://a/b/c/g..'],
  86. [self::RFC3986_BASE, '..g', 'http://a/b/c/..g'],
  87. [self::RFC3986_BASE, './../g', 'http://a/b/g'],
  88. [self::RFC3986_BASE, 'foo////g', 'http://a/b/c/foo////g'],
  89. [self::RFC3986_BASE, './g/.', 'http://a/b/c/g/'],
  90. [self::RFC3986_BASE, 'g/./h', 'http://a/b/c/g/h'],
  91. [self::RFC3986_BASE, 'g/../h', 'http://a/b/c/h'],
  92. [self::RFC3986_BASE, 'g;x=1/./y', 'http://a/b/c/g;x=1/y'],
  93. [self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
  94. // dot-segments in the query or fragment
  95. [self::RFC3986_BASE, 'g?y/./x', 'http://a/b/c/g?y/./x'],
  96. [self::RFC3986_BASE, 'g?y/../x', 'http://a/b/c/g?y/../x'],
  97. [self::RFC3986_BASE, 'g#s/./x', 'http://a/b/c/g#s/./x'],
  98. [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
  99. [self::RFC3986_BASE, 'g#s/../x', 'http://a/b/c/g#s/../x'],
  100. [self::RFC3986_BASE, '?y#s', 'http://a/b/c/d;p?y#s'],
  101. // base with fragment
  102. ['http://a/b/c?q#s', '?y', 'http://a/b/c?y'],
  103. // base with user info
  104. ['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'],
  105. ['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'],
  106. // path ending with slash or no slash at all
  107. ['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'],
  108. ['http:no-slash', 'e', 'http:e'],
  109. // falsey relative parts
  110. [self::RFC3986_BASE, '//0', 'http://0/'],
  111. [self::RFC3986_BASE, '0', 'http://a/b/c/0'],
  112. [self::RFC3986_BASE, '?0', 'http://a/b/c/d;p?0'],
  113. [self::RFC3986_BASE, '#0', 'http://a/b/c/d;p?q#0'],
  114. ];
  115. }
  116. public function testResolveUrlWithoutScheme()
  117. {
  118. $this->expectException(InvalidArgumentException::class);
  119. $this->expectExceptionMessage('Invalid URL: scheme is missing in "//localhost:8080". Did you forget to add "http(s)://"?');
  120. self::resolveUrl(self::parseUrl('localhost:8080'), null);
  121. }
  122. public function testResolveBaseUrlWitoutScheme()
  123. {
  124. $this->expectException(InvalidArgumentException::class);
  125. $this->expectExceptionMessage('Invalid URL: scheme is missing in "//localhost:8081". Did you forget to add "http(s)://"?');
  126. self::resolveUrl(self::parseUrl('/foo'), self::parseUrl('localhost:8081'));
  127. }
  128. /**
  129. * @dataProvider provideParseUrl
  130. */
  131. public function testParseUrl(array $expected, string $url, array $query = [])
  132. {
  133. $expected = array_combine(['scheme', 'authority', 'path', 'query', 'fragment'], $expected);
  134. $this->assertSame($expected, self::parseUrl($url, $query));
  135. }
  136. public function provideParseUrl(): iterable
  137. {
  138. yield [['http:', '//example.com', null, null, null], 'http://Example.coM:80'];
  139. yield [['https:', '//xn--dj-kia8a.example.com:8000', '/', null, null], 'https://DÉjà.Example.com:8000/'];
  140. yield [[null, null, '/f%20o.o', '?a=b', '#c'], '/f o%2Eo?a=b#c'];
  141. yield [[null, '//a:b@foo', '/bar', null, null], '//a:b@foo/bar'];
  142. yield [['http:', null, null, null, null], 'http:'];
  143. yield [['http:', null, 'bar', null, null], 'http:bar'];
  144. yield [[null, null, 'bar', '?a=1&c=c', null], 'bar?a=a&b=b', ['b' => null, 'c' => 'c', 'a' => 1]];
  145. yield [[null, null, 'bar', '?a=b+c&b=b', null], 'bar?a=b+c', ['b' => 'b']];
  146. yield [[null, null, 'bar', '?a=b%2B%20c', null], 'bar?a=b+c', ['a' => 'b+ c']];
  147. yield [[null, null, 'bar', '?a%5Bb%5D=c', null], 'bar', ['a' => ['b' => 'c']]];
  148. yield [[null, null, 'bar', '?a%5Bb%5Bc%5D=d', null], 'bar?a[b[c]=d', []];
  149. yield [[null, null, 'bar', '?a%5Bb%5D%5Bc%5D=dd', null], 'bar?a[b][c]=d&e[f]=g', ['a' => ['b' => ['c' => 'dd']], 'e[f]' => null]];
  150. yield [[null, null, 'bar', '?a=b&a%5Bb%20c%5D=d&e%3Df=%E2%9C%93', null], 'bar?a=b', ['a' => ['b c' => 'd'], 'e=f' => '✓']];
  151. // IDNA 2008 compliance
  152. yield [['https:', '//xn--fuball-cta.test', null, null, null], 'https://fußball.test'];
  153. }
  154. /**
  155. * @dataProvider provideRemoveDotSegments
  156. */
  157. public function testRemoveDotSegments($expected, $url)
  158. {
  159. $this->assertSame($expected, self::removeDotSegments($url));
  160. }
  161. public function provideRemoveDotSegments()
  162. {
  163. yield ['', ''];
  164. yield ['', '.'];
  165. yield ['', '..'];
  166. yield ['a', './a'];
  167. yield ['a', '../a'];
  168. yield ['/a/b', '/a/./b'];
  169. yield ['/b/', '/a/../b/.'];
  170. yield ['/a//b/', '/a///../b/.'];
  171. yield ['/a/', '/a/b/..'];
  172. yield ['/a///b', '/a///b'];
  173. }
  174. public function testAuthBearerOption()
  175. {
  176. [, $options] = self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foobar'], HttpClientInterface::OPTIONS_DEFAULTS);
  177. $this->assertSame(['Accept: */*', 'Authorization: Bearer foobar'], $options['headers']);
  178. $this->assertSame(['Authorization: Bearer foobar'], $options['normalized_headers']['authorization']);
  179. }
  180. public function testInvalidAuthBearerOption()
  181. {
  182. $this->expectException(InvalidArgumentException::class);
  183. $this->expectExceptionMessage('Option "auth_bearer" must be a string, "stdClass" given.');
  184. self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => new \stdClass()], HttpClientInterface::OPTIONS_DEFAULTS);
  185. }
  186. public function testInvalidAuthBearerValue()
  187. {
  188. $this->expectException(InvalidArgumentException::class);
  189. $this->expectExceptionMessage('Invalid character found in option "auth_bearer": "a\nb".');
  190. self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => "a\nb"], HttpClientInterface::OPTIONS_DEFAULTS);
  191. }
  192. public function testSetAuthBasicAndBearerOptions()
  193. {
  194. $this->expectException(InvalidArgumentException::class);
  195. $this->expectExceptionMessage('Define either the "auth_basic" or the "auth_bearer" option, setting both is not supported.');
  196. self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foo', 'auth_basic' => 'foo:bar'], HttpClientInterface::OPTIONS_DEFAULTS);
  197. }
  198. public function testSetJSONAndBodyOptions()
  199. {
  200. $this->expectException(InvalidArgumentException::class);
  201. $this->expectExceptionMessage('Define either the "json" or the "body" option, setting both is not supported');
  202. self::prepareRequest('POST', 'http://example.com', ['json' => ['foo' => 'bar'], 'body' => '<html/>'], HttpClientInterface::OPTIONS_DEFAULTS);
  203. }
  204. public function providePrepareAuthBasic()
  205. {
  206. yield ['foo:bar', 'Zm9vOmJhcg=='];
  207. yield [['foo', 'bar'], 'Zm9vOmJhcg=='];
  208. yield ['foo', 'Zm9v'];
  209. yield [['foo'], 'Zm9v'];
  210. }
  211. /**
  212. * @dataProvider providePrepareAuthBasic
  213. */
  214. public function testPrepareAuthBasic($arg, $result)
  215. {
  216. [, $options] = $this->prepareRequest('POST', 'http://example.com', ['auth_basic' => $arg], HttpClientInterface::OPTIONS_DEFAULTS);
  217. $this->assertSame('Authorization: Basic '.$result, $options['normalized_headers']['authorization'][0]);
  218. }
  219. public function provideFingerprints()
  220. {
  221. foreach (['md5', 'sha1', 'sha256'] as $algo) {
  222. $hash = hash($algo, $algo);
  223. yield [$hash, [$algo => $hash]];
  224. }
  225. yield ['AAAA:BBBB:CCCC:DDDD:EEEE:FFFF:GGGG:HHHH:IIII:JJJJ:KKKK', ['pin-sha256' => ['AAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJKKKK']]];
  226. }
  227. /**
  228. * @dataProvider provideFingerprints
  229. */
  230. public function testNormalizePeerFingerprint($fingerprint, $expected)
  231. {
  232. self::assertSame($expected, $this->normalizePeerFingerprint($fingerprint));
  233. }
  234. public function testNormalizePeerFingerprintException()
  235. {
  236. $this->expectException(InvalidArgumentException::class);
  237. $this->expectExceptionMessage('Cannot auto-detect fingerprint algorithm for "foo".');
  238. $this->normalizePeerFingerprint('foo');
  239. }
  240. public function testNormalizePeerFingerprintTypeException()
  241. {
  242. $this->expectException(InvalidArgumentException::class);
  243. $this->expectExceptionMessage('Option "peer_fingerprint" must be string or array, "stdClass" given.');
  244. $fingerprint = new \stdClass();
  245. $this->normalizePeerFingerprint($fingerprint);
  246. }
  247. }