HttpBrowser.php 4.0 KB

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