From b579b023bda3c9b51d93b7d9e6e0c93bfaea5508 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 30 Jan 2019 10:26:19 +0100 Subject: [PATCH] [HttpKernel] add RealHttpKernel: handle requests with HttpClientInterface --- src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../Component/HttpKernel/RealHttpKernel.php | 104 ++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 src/Symfony/Component/HttpKernel/RealHttpKernel.php diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 7b5dc28332..d3007e8aec 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -21,6 +21,7 @@ CHANGELOG * renamed `GetResponseForControllerResultEvent` to `ViewEvent` * renamed `GetResponseForExceptionEvent` to `ExceptionEvent` * renamed `PostResponseEvent` to `TerminateEvent` + * added `RealHttpKernel` for handling requests with an `HttpClientInterface` instance 4.2.0 ----- diff --git a/src/Symfony/Component/HttpKernel/RealHttpKernel.php b/src/Symfony/Component/HttpKernel/RealHttpKernel.php new file mode 100644 index 0000000000..d78fda1f8d --- /dev/null +++ b/src/Symfony/Component/HttpKernel/RealHttpKernel.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Mime\Part\AbstractPart; +use Symfony\Component\Mime\Part\DataPart; +use Symfony\Component\Mime\Part\Multipart\FormDataPart; +use Symfony\Component\Mime\Part\TextPart; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * An implementation of a Symfony HTTP kernel using a "real" HTTP client. + * + * @author Fabien Potencier + */ +final class RealHttpKernel implements HttpKernelInterface +{ + private $client; + private $logger; + + public function __construct(HttpClientInterface $client = null, LoggerInterface $logger = null) + { + if (!class_exists(HttpClient::class)) { + throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__)); + } + + $this->client = $client ?? HttpClient::create(); + $this->logger = $logger ?? new NullLogger(); + } + + public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) + { + $this->logger->debug(sprintf('Request: %s %s', $request->getMethod(), $request->getUri())); + + $headers = $this->getHeaders($request); + $body = ''; + if (null !== $part = $this->getBody($request)) { + $headers = array_merge($headers, $part->getPreparedHeaders()->toArray()); + $body = $part->bodyToIterable(); + } + $response = $this->client->request($request->getMethod(), $request->getUri(), [ + 'headers' => $headers, + 'body' => $body, + 'max_redirects' => 0, + ] + $request->attributes->get('http_client_options', [])); + + $this->logger->debug(sprintf('Response: %s %s', $response->getStatusCode(), $request->getUri())); + + return new Response($response->getContent(!$catch), $response->getStatusCode(), $response->getHeaders(!$catch)); + } + + private function getBody(Request $request): ?AbstractPart + { + if (\in_array($request->getMethod(), ['GET', 'HEAD'])) { + return null; + } + + if (!class_exists(AbstractPart::class)) { + throw new \LogicException('You cannot pass non-empty bodies as the Mime component is not installed. Try running "composer require symfony/mime".'); + } + + if ($content = $request->getContent()) { + return new TextPart($content, 'utf-8', 'plain', '8bit'); + } + + $fields = $request->request->all(); + foreach ($request->files->all() as $name => $file) { + $fields[$name] = DataPart::fromPath($file->getPathname(), $file->getClientOriginalName(), $file->getClientMimeType()); + } + + return new FormDataPart($fields); + } + + private function getHeaders(Request $request): array + { + $headers = []; + foreach ($request->headers as $key => $value) { + $headers[$key] = $value; + } + $cookies = []; + foreach ($request->cookies->all() as $name => $value) { + $cookies[] = $name.'='.$value; + } + if ($cookies) { + $headers['cookie'] = implode('; ', $cookies); + } + + return $headers; + } +}