HttpBrowser.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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\BrowserKit;
  11. use Symfony\Component\HttpClient\HttpClient;
  12. use Symfony\Component\Mime\Part\AbstractPart;
  13. use Symfony\Component\Mime\Part\DataPart;
  14. use Symfony\Component\Mime\Part\Multipart\FormDataPart;
  15. use Symfony\Component\Mime\Part\TextPart;
  16. use Symfony\Contracts\HttpClient\HttpClientInterface;
  17. /**
  18. * An implementation of a browser using the HttpClient component
  19. * to make real HTTP requests.
  20. *
  21. * @author Fabien Potencier <fabien@symfony.com>
  22. */
  23. class HttpBrowser extends AbstractBrowser
  24. {
  25. private $client;
  26. public function __construct(HttpClientInterface $client = null, History $history = null, CookieJar $cookieJar = null)
  27. {
  28. if (!$client && !class_exists(HttpClient::class)) {
  29. throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__));
  30. }
  31. $this->client = $client ?? HttpClient::create();
  32. parent::__construct([], $history, $cookieJar);
  33. }
  34. protected function doRequest($request): Response
  35. {
  36. $headers = $this->getHeaders($request);
  37. [$body, $extraHeaders] = $this->getBodyAndExtraHeaders($request);
  38. $response = $this->client->request($request->getMethod(), $request->getUri(), [
  39. 'headers' => array_merge($headers, $extraHeaders),
  40. 'body' => $body,
  41. 'max_redirects' => 0,
  42. ]);
  43. return new Response($response->getContent(false), $response->getStatusCode(), $response->getHeaders(false));
  44. }
  45. /**
  46. * @return array [$body, $headers]
  47. */
  48. private function getBodyAndExtraHeaders(Request $request): array
  49. {
  50. if (\in_array($request->getMethod(), ['GET', 'HEAD'])) {
  51. return ['', []];
  52. }
  53. if (!class_exists(AbstractPart::class)) {
  54. throw new \LogicException('You cannot pass non-empty bodies as the Mime component is not installed. Try running "composer require symfony/mime".');
  55. }
  56. if (null !== $content = $request->getContent()) {
  57. $part = new TextPart($content, 'utf-8', 'plain', '8bit');
  58. return [$part->bodyToString(), $part->getPreparedHeaders()->toArray()];
  59. }
  60. $fields = $request->getParameters();
  61. $hasFile = false;
  62. foreach ($request->getFiles() as $name => $file) {
  63. if (!isset($file['tmp_name'])) {
  64. continue;
  65. }
  66. $hasFile = true;
  67. $fields[$name] = DataPart::fromPath($file['tmp_name'], $file['name']);
  68. }
  69. if ($hasFile) {
  70. $part = new FormDataPart($fields);
  71. return [$part->bodyToIterable(), $part->getPreparedHeaders()->toArray()];
  72. }
  73. if (empty($fields)) {
  74. return ['', []];
  75. }
  76. return [http_build_query($fields, '', '&', PHP_QUERY_RFC1738), ['Content-Type' => 'application/x-www-form-urlencoded']];
  77. }
  78. private function getHeaders(Request $request): array
  79. {
  80. $headers = [];
  81. foreach ($request->getServer() as $key => $value) {
  82. $key = strtolower(str_replace('_', '-', $key));
  83. $contentHeaders = ['content-length' => true, 'content-md5' => true, 'content-type' => true];
  84. if (0 === strpos($key, 'http-')) {
  85. $headers[substr($key, 5)] = $value;
  86. } elseif (isset($contentHeaders[$key])) {
  87. // CONTENT_* are not prefixed with HTTP_
  88. $headers[$key] = $value;
  89. }
  90. }
  91. $cookies = [];
  92. foreach ($this->getCookieJar()->allRawValues($request->getUri()) as $name => $value) {
  93. $cookies[] = $name.'='.$value;
  94. }
  95. if ($cookies) {
  96. $headers['cookie'] = implode('; ', $cookies);
  97. }
  98. return $headers;
  99. }
  100. }