ClientTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. <?php
  2. /*
  3. * This file is part of the Goutte 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 Goutte\Tests;
  11. use Goutte\Client;
  12. use GuzzleHttp\Client as GuzzleClient;
  13. use GuzzleHttp\Exception\RequestException;
  14. use GuzzleHttp\Handler\MockHandler;
  15. use GuzzleHttp\HandlerStack;
  16. use GuzzleHttp\Psr7\Response as GuzzleResponse;
  17. use GuzzleHttp\Middleware;
  18. use PHPUnit\Framework\TestCase;
  19. use Symfony\Component\BrowserKit\Cookie;
  20. /**
  21. * Goutte Client Test.
  22. *
  23. * @author Michael Dowling <michael@guzzlephp.org>
  24. * @author Charles Sarrazin <charles@sarraz.in>
  25. */
  26. class ClientTest extends TestCase
  27. {
  28. protected $history;
  29. /** @var MockHandler */
  30. protected $mock;
  31. protected function getGuzzle(array $responses = [], array $extraConfig = [])
  32. {
  33. if (empty($responses)) {
  34. $responses = [new GuzzleResponse(200, [], '<html><body><p>Hi</p></body></html>')];
  35. }
  36. $this->mock = new MockHandler($responses);
  37. $handlerStack = HandlerStack::create($this->mock);
  38. $this->history = [];
  39. $handlerStack->push(Middleware::history($this->history));
  40. $guzzle = new GuzzleClient(array_merge(array('redirect.disable' => true, 'base_uri' => '', 'handler' => $handlerStack), $extraConfig));
  41. return $guzzle;
  42. }
  43. public function testCreatesDefaultClient()
  44. {
  45. $client = new Client();
  46. $this->assertInstanceOf('GuzzleHttp\\ClientInterface', $client->getClient());
  47. }
  48. public function testUsesCustomClient()
  49. {
  50. $guzzle = new GuzzleClient();
  51. $client = new Client();
  52. $this->assertSame($client, $client->setClient($guzzle));
  53. $this->assertSame($guzzle, $client->getClient());
  54. }
  55. public function testUsesCustomHeaders()
  56. {
  57. $guzzle = $this->getGuzzle();
  58. $client = new Client();
  59. $client->setClient($guzzle);
  60. $client->setHeader('X-Test', 'test');
  61. $client->request('GET', 'http://www.example.com/');
  62. $this->assertEquals('test', end($this->history)['request']->getHeaderLine('X-Test'));
  63. }
  64. public function testCustomUserAgent()
  65. {
  66. $guzzle = $this->getGuzzle();
  67. $client = new Client();
  68. $client->setClient($guzzle);
  69. $client->setHeader('User-Agent', 'foo');
  70. $client->request('GET', 'http://www.example.com/');
  71. $this->assertEquals('foo', end($this->history)['request']->getHeaderLine('User-Agent'));
  72. }
  73. public function testUsesAuth()
  74. {
  75. $guzzle = $this->getGuzzle();
  76. $client = new Client();
  77. $client->setClient($guzzle);
  78. $client->setAuth('me', '**');
  79. $client->request('GET', 'http://www.example.com/');
  80. $request = end($this->history)['request'];
  81. $this->assertEquals('Basic bWU6Kio=', $request->getHeaderLine('Authorization'));
  82. }
  83. public function testResetsAuth()
  84. {
  85. $guzzle = $this->getGuzzle();
  86. $client = new Client();
  87. $client->setClient($guzzle);
  88. $client->setAuth('me', '**');
  89. $client->resetAuth();
  90. $client->request('GET', 'http://www.example.com/');
  91. $request = end($this->history)['request'];
  92. $this->assertEquals('', $request->getHeaderLine('authorization'));
  93. }
  94. public function testUsesCookies()
  95. {
  96. $guzzle = $this->getGuzzle();
  97. $client = new Client();
  98. $client->setClient($guzzle);
  99. $client->getCookieJar()->set(new Cookie('test', '123'));
  100. $client->request('GET', 'http://www.example.com/');
  101. $request = end($this->history)['request'];
  102. $this->assertEquals('test=123', $request->getHeaderLine('Cookie'));
  103. }
  104. public function testUsesCookiesWithCustomPort()
  105. {
  106. $guzzle = $this->getGuzzle();
  107. $client = new Client();
  108. $client->setClient($guzzle);
  109. $client->getCookieJar()->set(new Cookie('test', '123'));
  110. $client->request('GET', 'http://www.example.com:8000/');
  111. $request = end($this->history)['request'];
  112. $this->assertEquals('test=123', $request->getHeaderLine('Cookie'));
  113. }
  114. public function testUsesPostFiles()
  115. {
  116. $guzzle = $this->getGuzzle();
  117. $client = new Client();
  118. $client->setClient($guzzle);
  119. $files = array(
  120. 'test' => array(
  121. 'name' => 'test.txt',
  122. 'tmp_name' => __DIR__.'/fixtures.txt',
  123. ),
  124. );
  125. $client->request('POST', 'http://www.example.com/', array(), $files);
  126. $request = end($this->history)['request'];
  127. $stream = $request->getBody();
  128. $boundary = $stream->getBoundary();
  129. $this->assertEquals(
  130. "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"test.txt\"\r\nContent-Length: 4\r\n"
  131. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  132. $stream->getContents()
  133. );
  134. }
  135. public function testUsesPostNamedFiles()
  136. {
  137. $guzzle = $this->getGuzzle();
  138. $client = new Client();
  139. $client->setClient($guzzle);
  140. $files = array(
  141. 'test' => __DIR__.'/fixtures.txt',
  142. );
  143. $client->request('POST', 'http://www.example.com/', array(), $files);
  144. $request = end($this->history)['request'];
  145. $stream = $request->getBody();
  146. $boundary = $stream->getBoundary();
  147. $this->assertEquals(
  148. "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n"
  149. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  150. $stream->getContents()
  151. );
  152. }
  153. public function testUsesPostFilesNestedFields()
  154. {
  155. $guzzle = $this->getGuzzle();
  156. $client = new Client();
  157. $client->setClient($guzzle);
  158. $files = array(
  159. 'form' => array(
  160. 'test' => array(
  161. 'name' => 'test.txt',
  162. 'tmp_name' => __DIR__.'/fixtures.txt',
  163. ),
  164. ),
  165. );
  166. $client->request('POST', 'http://www.example.com/', array(), $files);
  167. $request = end($this->history)['request'];
  168. $stream = $request->getBody();
  169. $boundary = $stream->getBoundary();
  170. $this->assertEquals(
  171. "--$boundary\r\nContent-Disposition: form-data; name=\"form[test]\"; filename=\"test.txt\"\r\nContent-Length: 4\r\n"
  172. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  173. $stream->getContents()
  174. );
  175. }
  176. public function testPostFormWithFiles()
  177. {
  178. $guzzle = $this->getGuzzle();
  179. $client = new Client();
  180. $client->setClient($guzzle);
  181. $files = array(
  182. 'test' => __DIR__.'/fixtures.txt',
  183. );
  184. $params = array(
  185. 'foo' => 'bar',
  186. );
  187. $client->request('POST', 'http://www.example.com/', $params, $files);
  188. $request = end($this->history)['request'];
  189. $stream = $request->getBody();
  190. $boundary = $stream->getBoundary();
  191. $this->assertEquals(
  192. "--$boundary\r\nContent-Disposition: form-data; name=\"foo\"\r\nContent-Length: 3\r\n"
  193. ."\r\nbar\r\n"
  194. ."--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n"
  195. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  196. $stream->getContents());
  197. }
  198. public function testPostEmbeddedFormWithFiles()
  199. {
  200. $guzzle = $this->getGuzzle();
  201. $client = new Client();
  202. $client->setClient($guzzle);
  203. $files = array(
  204. 'test' => __DIR__.'/fixtures.txt',
  205. );
  206. $params = array(
  207. 'foo' => array(
  208. 'bar' => 'baz',
  209. ),
  210. );
  211. $client->request('POST', 'http://www.example.com/', $params, $files);
  212. $request = end($this->history)['request'];
  213. $stream = $request->getBody();
  214. $boundary = $stream->getBoundary();
  215. $this->assertEquals(
  216. "--$boundary\r\nContent-Disposition: form-data; name=\"foo[bar]\"\r\nContent-Length: 3\r\n"
  217. ."\r\nbaz\r\n"
  218. ."--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n"
  219. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  220. $stream->getContents());
  221. }
  222. public function testUsesPostFilesOnClientSide()
  223. {
  224. $guzzle = $this->getGuzzle();
  225. $client = new Client();
  226. $client->setClient($guzzle);
  227. $files = array(
  228. 'test' => __DIR__.'/fixtures.txt',
  229. );
  230. $client->request('POST', 'http://www.example.com/', array(), $files);
  231. $request = end($this->history)['request'];
  232. $stream = $request->getBody();
  233. $boundary = $stream->getBoundary();
  234. $this->assertEquals(
  235. "--$boundary\r\nContent-Disposition: form-data; name=\"test\"; filename=\"fixtures.txt\"\r\nContent-Length: 4\r\n"
  236. ."Content-Type: text/plain\r\n\r\nfoo\n\r\n--$boundary--\r\n",
  237. $stream->getContents()
  238. );
  239. }
  240. public function testUsesPostFilesUploadError()
  241. {
  242. $guzzle = $this->getGuzzle();
  243. $client = new Client();
  244. $client->setClient($guzzle);
  245. $files = array(
  246. 'test' => array(
  247. 'name' => '',
  248. 'type' => '',
  249. 'tmp_name' => '',
  250. 'error' => 4,
  251. 'size' => 0,
  252. ),
  253. );
  254. $client->request('POST', 'http://www.example.com/', array(), $files);
  255. $request = end($this->history)['request'];
  256. $stream = $request->getBody();
  257. $boundary = $stream->getBoundary();
  258. $this->assertEquals("--$boundary--\r\n", $stream->getContents());
  259. }
  260. public function testCreatesResponse()
  261. {
  262. $guzzle = $this->getGuzzle();
  263. $client = new Client();
  264. $client->setClient($guzzle);
  265. $crawler = $client->request('GET', 'http://www.example.com/');
  266. $this->assertEquals('Hi', $crawler->filter('p')->text());
  267. }
  268. public function testHandlesRedirectsCorrectly()
  269. {
  270. $guzzle = $this->getGuzzle([
  271. new GuzzleResponse(301, array(
  272. 'Location' => 'http://www.example.com/',
  273. )),
  274. new GuzzleResponse(200, [], '<html><body><p>Test</p></body></html>'),
  275. ]);
  276. $client = new Client();
  277. $client->setClient($guzzle);
  278. $crawler = $client->request('GET', 'http://www.example.com/');
  279. $this->assertEquals('Test', $crawler->filter('p')->text());
  280. // Ensure that two requests were sent
  281. $this->assertEquals(2, count($this->history));
  282. }
  283. public function testConvertsGuzzleHeadersToArrays()
  284. {
  285. $guzzle = $this->getGuzzle([
  286. new GuzzleResponse(200, array(
  287. 'Date' => 'Tue, 04 Jun 2013 13:22:41 GMT',
  288. )),
  289. ]);
  290. $client = new Client();
  291. $client->setClient($guzzle);
  292. $client->request('GET', 'http://www.example.com/');
  293. $response = $client->getResponse();
  294. $headers = $response->getHeaders();
  295. $this->assertInternalType('array', array_shift($headers), 'Header not converted from Guzzle\Http\Message\Header to array');
  296. }
  297. /**
  298. * @expectedException \GuzzleHttp\Exception\RequestException
  299. */
  300. public function testNullResponseException()
  301. {
  302. $guzzle = $this->getGuzzle([
  303. new RequestException('', $this->getMockBuilder('Psr\Http\Message\RequestInterface')->getMock()),
  304. ]);
  305. $client = new Client();
  306. $client->setClient($guzzle);
  307. $client->request('GET', 'http://www.example.com/');
  308. $client->getResponse();
  309. }
  310. public function testHttps()
  311. {
  312. $guzzle = $this->getGuzzle([
  313. new GuzzleResponse(200, [], '<html><body><p>Test</p></body></html>'),
  314. ]);
  315. $client = new Client();
  316. $client->setClient($guzzle);
  317. $crawler = $client->request('GET', 'https://www.example.com/');
  318. $this->assertEquals('Test', $crawler->filter('p')->text());
  319. }
  320. public function testCustomUserAgentConstructor()
  321. {
  322. $guzzle = $this->getGuzzle();
  323. $client = new Client([
  324. 'HTTP_HOST' => '1.2.3.4',
  325. 'HTTP_USER_AGENT' => 'SomeHost',
  326. ]);
  327. $client->setClient($guzzle);
  328. $client->request('GET', 'http://www.example.com/');
  329. $this->assertEquals('SomeHost', end($this->history)['request']->getHeaderLine('User-Agent'));
  330. }
  331. public function testResetHeaders()
  332. {
  333. $client = new Client();
  334. $client->setHeader('X-Test', 'test');
  335. $reflectionProperty = new \ReflectionProperty('Goutte\Client', 'headers');
  336. $reflectionProperty->setAccessible(true);
  337. $this->assertEquals(array('x-test' => 'test'), $reflectionProperty->getValue($client));
  338. $client->resetHeaders();
  339. $this->assertEquals([], $reflectionProperty->getValue($client));
  340. }
  341. public function testRestart()
  342. {
  343. $client = new Client();
  344. $client->setHeader('X-Test', 'test');
  345. $client->setAuth('foo', 'bar');
  346. $headersReflectionProperty = new \ReflectionProperty('Goutte\Client', 'headers');
  347. $headersReflectionProperty->setAccessible(true);
  348. $this->assertEquals(array('x-test' => 'test'), $headersReflectionProperty->getValue($client));
  349. $authReflectionProperty = new \ReflectionProperty('Goutte\Client', 'auth');
  350. $authReflectionProperty->setAccessible(true);
  351. $this->assertEquals(array('foo', 'bar', 'basic'), $authReflectionProperty->getValue($client));
  352. $client->restart();
  353. $this->assertEquals([], $headersReflectionProperty->getValue($client));
  354. $this->assertNull($authReflectionProperty->getValue($client));
  355. }
  356. public function testSetBaseUri()
  357. {
  358. $guzzle = $this->getGuzzle([], ['base_uri' => 'http://example.com/']);
  359. $client = new Client();
  360. $client->setClient($guzzle);
  361. $this->assertNull($client->getServerParameter('HTTPS', null));
  362. $this->assertSame('example.com', $client->getServerParameter('HTTP_HOST'));
  363. $client->request('GET', '/foo');
  364. $this->assertSame('http://example.com/foo', (string) end($this->history)['request']->getUri());
  365. }
  366. public function testSetHttpsBaseUri()
  367. {
  368. $guzzle = $this->getGuzzle([], ['base_uri' => 'https://example.com:1234']);
  369. $client = new Client();
  370. $client->setClient($guzzle);
  371. $this->assertSame('on', $client->getServerParameter('HTTPS'));
  372. $this->assertSame('example.com:1234', $client->getServerParameter('HTTP_HOST'));
  373. $client->request('GET', '/foo');
  374. $this->assertSame('https://example.com:1234/foo', (string) end($this->history)['request']->getUri());
  375. }
  376. /**
  377. * @expectedException \InvalidArgumentException
  378. * @expectedExceptionMessage Setting a path in the Guzzle "base_uri" config option is not supported by Goutte yet.
  379. */
  380. public function testSetBaseUriWithPath()
  381. {
  382. $guzzle = $this->getGuzzle([], ['base_uri' => 'http://example.com/foo/']);
  383. $client = new Client();
  384. $client->setClient($guzzle);
  385. }
  386. }