Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

!!!TASK: Remove deprecated Http objects and replace with PSR-7 implementation #1552

Merged
merged 78 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
579435b
TASK: Remove deprecated Http objects and replace with PSR-7 implement…
albe Apr 19, 2019
80c317b
Adjust modifications and add PSR-15 package
kitsunet Apr 27, 2019
2bf493e
More component split up
kitsunet May 2, 2019
0303d82
Cleanup Http components
kitsunet May 2, 2019
13afc99
SERVER defaults and header extraction in ServerRequestFactory
kitsunet May 2, 2019
d989ce4
Add merging of ActionRequests via Renderer
kitsunet May 5, 2019
de87179
Fix style errors
kitsunet May 5, 2019
ffe8057
Small fixes and refinements
kitsunet May 5, 2019
cce89d5
Move FlowUploadedFile and streamline argument merging
kitsunet May 9, 2019
c353598
Get rid of inheritance in ActionReponse nad adjust everything
kitsunet May 9, 2019
c587e08
Fix style issues
kitsunet May 9, 2019
d4606fc
TASK: Some small fixes
albe May 5, 2019
4184fc8
TASK: Some more changes
albe May 9, 2019
bb38fe1
TASK: Fix a couple of tests
albe May 9, 2019
9561d7f
TASK: Some more cleanup and refactoring
albe May 9, 2019
cf55b18
Split between CLI and Mvc(Web) dispatcher
kitsunet May 12, 2019
cf22ede
Finish separation of MVC/HTTP and CLI dispatchers
kitsunet May 12, 2019
2eb076d
Cleanup and test fixes
kitsunet May 13, 2019
c1a0fe8
More PSR-7 HTTP test fixes
kitsunet May 13, 2019
9600929
TASK: More work on tests
albe May 13, 2019
5377347
TASK: Another round of test fixes
albe May 13, 2019
0cb5934
More test fixes
kitsunet May 13, 2019
6a8e614
Fix StyleCI issues
kitsunet May 13, 2019
fdaa1ed
Merge remote-tracking branch 'origin/master' into http-psr7
kitsunet May 13, 2019
d1bdc9f
More test fixes regarding PSR-7
kitsunet May 14, 2019
7bcb3c0
Next round of fixes for Flow PSR-7
kitsunet May 14, 2019
984852a
Some additional fixeSome additional fixes
kitsunet May 14, 2019
476159e
Some more refactoring
kitsunet May 15, 2019
2ddb0f5
Views can render responses directly that will get applied correctly
kitsunet May 15, 2019
9e178c4
First batch of functional test fixes
kitsunet May 16, 2019
e1faf14
More functional tests fixes
kitsunet May 16, 2019
66766f5
Follow up fix for Browser
kitsunet May 16, 2019
e73e5c4
More test fixes and corrections in code using Requests
kitsunet May 16, 2019
02f461b
CurlEngine fixes
kitsunet May 16, 2019
a37a852
More functional test fixes
kitsunet May 16, 2019
7b7ab35
Last functional test fixes
kitsunet May 16, 2019
480788f
Another round of text and style fixes
kitsunet May 16, 2019
2988e50
Removing major parts of Flows HTTP code
kitsunet May 16, 2019
02b2621
Some more fixes regarding Uri handling with PSR-7
kitsunet May 16, 2019
5d23efe
Adjust escaping of Uris according to PSR-7
kitsunet May 16, 2019
48f38a2
Removal of Uri class
kitsunet May 16, 2019
8aba9c3
Fix Widget tests by urldecoding redirect URLs
kitsunet May 17, 2019
bce2431
Fixes for remaining style and test issues
kitsunet May 17, 2019
2426595
TASK: Some more cleanup and added type hints
albe May 18, 2019
e3ca850
TASK: Rename Cli\Request and Cli\Response
albe May 19, 2019
9761244
TASK: Fix tests again and re-add setContent/appendContent to CommandR…
albe May 19, 2019
a6ccd5e
TASK: Some fixes after last changes
albe May 19, 2019
539aa39
Merge remote-tracking branch 'upstream/master' into http-psr7
albe May 19, 2019
f474d3c
TASK: Merge master and fix tests again
albe May 19, 2019
9054823
TASK: Test fixes
albe May 19, 2019
a0f4565
TASK: Revert renaming Cli Request/Response
albe May 21, 2019
6b87cea
TASK: Remove obsolete references to Mvc\RequestInterface
albe May 21, 2019
825f347
Merge branch 'master' into http-psr7
albe May 21, 2019
0079fee
TASK: Remove ResponseDeprecationTrait
albe May 21, 2019
e044477
TASK: Fix return typehint
albe May 21, 2019
c6ba364
TASK: Remove Renderer import leftovers
albe May 21, 2019
33f1077
TASK: Remove leftover from `IntoComponentContext` renderer
albe May 22, 2019
18edfe9
Replace trait with ActionRequestFactory
kitsunet Jun 11, 2019
31946dd
Separate Uri path and query string in route UriContraints
kitsunet Jun 12, 2019
3947129
Merge branch 'master' into http-psr7
kdambekalns Jul 8, 2019
eb88071
Merge branch 'master' into http-psr7
albe Aug 11, 2019
745e798
TASK: Fix tests
albe Aug 11, 2019
98ac8ea
TASK: Remove unused import
albe Aug 11, 2019
123feeb
TASK: Fix routing tests
albe Aug 11, 2019
3f90598
TASK: Fix getPathConstraint() implementation
albe Aug 12, 2019
dcb2fb7
TASK: View may return a StreamInterface
albe Aug 12, 2019
0630b2f
Merge branch 'master' into http-psr7
albe Aug 14, 2019
e8e267d
TASK: Fix a couple of psalm issues
albe Aug 14, 2019
9ec2901
Disentangle CLI and MVC some more
kitsunet Aug 15, 2019
e2eb5a3
Adjust ActionResponse creation and parent request handling
kitsunet Aug 15, 2019
693c518
Fix error with dispatcher and AuthenticationRequiredException
kitsunet Aug 15, 2019
5b94c18
More psalm and type safety fixes
kitsunet Aug 16, 2019
cd2834d
Psalm and type safety fixes
kitsunet Aug 16, 2019
e2b5b84
More type safety fixes
kitsunet Aug 16, 2019
a8ef2fd
Unit test fixes and type fixes
kitsunet Aug 16, 2019
67c865e
Psalm and test fixes
kitsunet Aug 16, 2019
8961bf2
Fix issues with template lookups
kitsunet Aug 16, 2019
4ec2b5c
Fix template path resolving
kitsunet Aug 16, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Neos.Flow/Classes/Command/RoutingCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
* source code.
*/

use GuzzleHttp\Psr7\ServerRequest;
use GuzzleHttp\Psr7\Uri;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use Neos\Flow\Configuration\ConfigurationManager;
Expand Down Expand Up @@ -179,7 +181,7 @@ public function routePathCommand(string $path, string $method = 'GET')
'REQUEST_URI' => $path,
'REQUEST_METHOD' => $method
];
$httpRequest = new Request([], [], [], $server);
$httpRequest = new ServerRequest($method, new Uri('http://localhost/'), [], '', '1.1', $server);
$routeContext = new RouteContext($httpRequest, RouteParameters::createEmpty());

/** @var Route $route */
Expand All @@ -201,7 +203,7 @@ public function routePathCommand(string $path, string $method = 'GET')
$this->outputLine(' Action: ' . (isset($routeValues['@action']) ? $routeValues['@action'] : '-'));
$this->outputLine(' Format: ' . (isset($routeValues['@format']) ? $routeValues['@format'] : '-'));

$controllerObjectName = $this->getControllerObjectName($routeValues['@package'], (isset($routeValues['@subpackage']) ? $routeValues['@subpackage'] : null), $routeValues['@controller']);
$controllerObjectName = $this->getControllerObjectName($routeValues['@package'], (isset($routeValues['@subpackage']) ? $routeValues['@subpackage'] : ''), $routeValues['@controller']);
if ($controllerObjectName === null) {
$this->outputLine('<b>Controller Error:</b>');
$this->outputLine(' !!! No Controller Object found !!!');
Expand Down
9 changes: 5 additions & 4 deletions Neos.Flow/Classes/Core/Booting/Scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,11 @@ protected static function initializeExceptionStorage(Bootstrap $bootstrap): Thro
return $output;
}

$request = RequestInformationHelper::renderRequest($requestHandler->getHttpRequest());
$response = (string)$requestHandler->getHttpResponse();
$output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . ($request == '' ? '[request was empty]' : $request) . PHP_EOL;
$output .= PHP_EOL . 'HTTP RESPONSE:' . PHP_EOL . ($response == '' ? '[response was empty]' : $response) . PHP_EOL;
$request = $requestHandler->getHttpRequest();
$response = $requestHandler->getHttpResponse();
// TODO: Sensible error output
$output .= PHP_EOL . 'HTTP REQUEST:' . PHP_EOL . ($request ? '[request was empty]' : RequestInformationHelper::renderRequestHeaders($request)) . PHP_EOL;
$output .= PHP_EOL . 'HTTP RESPONSE:' . PHP_EOL . ($response ? '[response was empty]' : $response->getStatusCode()) . PHP_EOL;
$output .= PHP_EOL . 'PHP PROCESS:' . PHP_EOL . 'Inode: ' . getmyinode() . PHP_EOL . 'PID: ' . getmypid() . PHP_EOL . 'UID: ' . getmyuid() . PHP_EOL . 'GID: ' . getmygid() . PHP_EOL . 'User: ' . get_current_user() . PHP_EOL;

return $output;
Expand Down
4 changes: 2 additions & 2 deletions Neos.Flow/Classes/Error/AbstractExceptionHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
* source code.
*/

use GuzzleHttp\Psr7\ServerRequest;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\Response as CliResponse;
use Neos\Flow\Exception as FlowException;
use Neos\Flow\Http\Helper\ResponseInformationHelper;
use Neos\Flow\Http\Request;
use Neos\Flow\Log\SystemLoggerInterface;
use Neos\Flow\Log\ThrowableStorageInterface;
use Neos\Flow\Mvc\ActionRequest;
Expand Down Expand Up @@ -166,7 +166,7 @@ protected function buildView(\Throwable $exception, array $renderingOptions): Vi
$view = $viewClassName::createWithOptions($renderingOptions['viewOptions']);
$view = $this->applyLegacyViewOptions($view, $renderingOptions);

$httpRequest = Request::createFromEnvironment();
$httpRequest = ServerRequest::fromGlobals();
$request = new ActionRequest($httpRequest);
$request->setControllerPackageKey('Neos.Flow');
$uriBuilder = new UriBuilder();
Expand Down
15 changes: 10 additions & 5 deletions Neos.Flow/Classes/Http/Client/Browser.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
* source code.
*/

use GuzzleHttp\Psr7\ServerRequest;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Headers;
use Neos\Flow\Http\Response;
use Neos\Flow\Http\Uri;
use Neos\Flow\Http\Request;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\DomCrawler\Form;

Expand Down Expand Up @@ -121,7 +123,7 @@ public function removeAutomaticRequestHeader($name)
* @param string|Uri $uri
* @param string $method Request method, for example "GET"
* @param array $arguments Arguments to send in the request body
* @param array $files
* @param UploadedFileInterface[] $files
* @param array $server
* @param string $content
* @return Response The HTTP response
Expand All @@ -138,11 +140,14 @@ public function request($uri, $method = 'GET', array $arguments = [], array $fil
throw new \InvalidArgumentException('$uri must be a URI object or a valid string representation of a URI.', 1333443624);
}

$request = Request::create($uri, $method, $arguments, $files, $server);

if ($content !== null) {
$request->setContent($content);
$request = new ServerRequest($method, $uri, [], $content, '1.1', $server);
if (!empty($arguments)) {
$request = $request->withQueryParams($arguments);
}
if (!empty($files)) {
$request = $request->withUploadedFiles($files);
}

$response = $this->sendRequest($request);

$location = $response->getHeader('Location');
Expand Down
51 changes: 51 additions & 0 deletions Neos.Flow/Classes/Http/Component/BaseUriComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
namespace Neos\Flow\Http\Component;

use GuzzleHttp\Psr7\Uri;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Http\Helper\RequestInformationHelper;
use Neos\Flow\Http\HttpRequestHandlerInterface;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;

/**
* Defines the baseUri attribute for the current request.
*
* This is done after the TrustedProxiesComponent to make sure
* the proxy headers are evaluated correctly.
*/
class BaseUriComponent implements ComponentInterface
{
/**
* @Flow\Inject
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @inheritDoc
*/
public function handle(ComponentContext $componentContext)
{
$request = $componentContext->getHttpRequest();
$baseUri = RequestInformationHelper::generateBaseUri($request);
$baseUriSetting = self::getConfiguredBaseUri($this->objectManager);
if (!empty($baseUriSetting)) {
$baseUri = new Uri($baseUriSetting);
}

$request = $request->withAttribute(HttpRequestHandlerInterface::ATTRIBUTE_BASE_URI, $baseUri);
$componentContext->replaceHttpRequest($request);
}

/**
* @param ObjectManagerInterface $objectManager
* @return string
* @throws \Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException
*/
public static function getConfiguredBaseUri(ObjectManagerInterface $objectManager): string
{
return (string)$objectManager->get(ConfigurationManager::class)->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.http.baseUri');
}

}
99 changes: 99 additions & 0 deletions Neos.Flow/Classes/Http/Component/PoweredByComponent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
namespace Neos\Flow\Http\Component;

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\ObjectManagement\ObjectManagerInterface;
use Neos\Flow\Package\PackageManager;

/**
* Adds the "X-Flow-Powered" to the response.
*/
class PoweredByComponent implements ComponentInterface
{
/**
* @Flow\Inject
* @var ObjectManagerInterface
*/
protected $objectManager;

/**
* @inheritDoc
*/
public function handle(ComponentContext $componentContext)
{
$token = static::prepareApplicationToken($this->objectManager);
if ($token === '') {
return;
}

$response = $componentContext->getHttpResponse();
$response = $response->withAddedHeader('X-Flow-Powered', $token);
$componentContext->replaceHttpResponse($response);
}

/**
* Renders a major version out of a full version string
*
* @param string $version For example "2.3.7"
* @return string For example "2"
*/
protected static function renderMajorVersion($version)
{
preg_match('/^(\d+)/', $version, $versionMatches);

return isset($versionMatches[1]) ? $versionMatches[1] : '';
}

/**
* Renders a minor version out of a full version string
*
* @param string $version For example "2.3.7"
* @return string For example "2.3"
*/
protected static function renderMinorVersion($version)
{
preg_match('/^(\d+\.\d+)/', $version, $versionMatches);

return isset($versionMatches[1]) ? $versionMatches[1] : '';
}

/**
* Generate an application information header for the response based on settings and package versions.
* Will statically compile in production for performance benefits.
*
* @param ObjectManagerInterface $objectManager
* @return string
* @throws \Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException
* @Flow\CompileStatic
*/
public static function prepareApplicationToken(ObjectManagerInterface $objectManager): string
{
$configurationManager = $objectManager->get(ConfigurationManager::class);
$tokenSetting = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.http.applicationToken');

if (!in_array($tokenSetting, ['ApplicationName', 'MinorVersion', 'MajorVersion'])) {
return '';
}

$applicationPackageKey = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.core.applicationPackageKey');
$applicationName = $configurationManager->getConfiguration(ConfigurationManager::CONFIGURATION_TYPE_SETTINGS, 'Neos.Flow.core.applicationName');
$applicationIsNotFlow = ($applicationPackageKey !== 'Neos.Flow');

if ($tokenSetting === 'ApplicationName') {
return 'Flow' . ($applicationIsNotFlow ? ' ' . $applicationName : '');
}

$packageManager = $objectManager->get(PackageManager::class);
$flowPackage = $packageManager->getPackage('Neos.Flow');
$applicationPackage = $applicationIsNotFlow ? $packageManager->getPackage($applicationPackageKey) : null;

// At this point the $tokenSetting must be either "MinorVersion" or "MajorVersion" so lets use it.

$versionRenderer = 'render' . $tokenSetting;
$flowVersion = static::$versionRenderer($flowPackage->getInstalledVersion());
$applicationVersion = $applicationIsNotFlow ? static::$versionRenderer($applicationPackage->getInstalledVersion()) : null;

return 'Flow/' . ($flowVersion ?: 'dev') . ($applicationIsNotFlow ? ' ' . $applicationName . '/' . ($applicationVersion ?: 'dev') : '');
}
}
15 changes: 8 additions & 7 deletions Neos.Flow/Classes/Http/Component/TrustedProxiesComponent.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*/

use Neos\Flow\Annotations as Flow;
use Neos\Flow\Http\Request;
use Neos\Flow\Http\HttpRequestHandlerInterface;
use Neos\Flow\Utility\Ip as IpUtility;
use Psr\Http\Message\ServerRequestInterface;

Expand Down Expand Up @@ -46,9 +46,9 @@ public function handle(ComponentContext $componentContext)
{
$request = $componentContext->getHttpRequest();

$trustedRequest = $request->withAttribute(Request::ATTRIBUTE_TRUSTED_PROXY, $this->isFromTrustedProxy($request));
$trustedRequest = $request->withAttribute(HttpRequestHandlerInterface::ATTRIBUTE_TRUSTED_PROXY, $this->isFromTrustedProxy($request));

$trustedRequest = $trustedRequest->withAttribute(Request::ATTRIBUTE_CLIENT_IP, $this->getTrustedClientIpAddress($trustedRequest));
$trustedRequest = $trustedRequest->withAttribute(HttpRequestHandlerInterface::ATTRIBUTE_CLIENT_IP, $this->getTrustedClientIpAddress($trustedRequest));

$protocolHeader = $this->getFirstTrustedProxyHeaderValue(self::HEADER_PROTOCOL, $trustedRequest);
if ($protocolHeader !== null) {
Expand Down Expand Up @@ -94,10 +94,10 @@ protected function unquoteArray($array): array

/**
* @param string $type The header value type to retrieve from the Forwarded header value. One of the HEADER_* constants.
* @param string $headerValue The Forwarded header value, e.g. "for=192.168.178.5; host=www.acme.org:8080"
* @param array $headerValues The Forwarded header value, e.g. "for=192.168.178.5; host=www.acme.org:8080"
* @return array|null The array of values for the header type or null if the header
*/
protected function getForwardedHeader($type, $headerValue)
protected function getForwardedHeader($type, array $headerValues)
{
$patterns = [
self::HEADER_CLIENT_IP => self::FOR_PATTERN,
Expand All @@ -107,6 +107,7 @@ protected function getForwardedHeader($type, $headerValue)
if (!isset($patterns[$type])) {
return null;
}
$headerValue = reset($headerValues);
preg_match_all('/' . $patterns[$type] . '/i', $headerValue, $matches);
$matchedHeader = $this->unquoteArray($matches[1]);
if ($matchedHeader === []) {
Expand All @@ -129,7 +130,7 @@ protected function getTrustedProxyHeaderValues($type, ServerRequestInterface $re
} else {
$trustedHeaders = $this->settings['headers'][$type] ?? '';
}
if ($trustedHeaders === '' || !$request->getAttribute(Request::ATTRIBUTE_TRUSTED_PROXY)) {
if ($trustedHeaders === '' || !$request->getAttribute(HttpRequestHandlerInterface::ATTRIBUTE_TRUSTED_PROXY)) {
yield null;
return;
}
Expand All @@ -145,7 +146,7 @@ protected function getTrustedProxyHeaderValues($type, ServerRequestInterface $re
yield $forwardedHeaderValue;
}
} else {
yield array_map('trim', explode(',', $request->getHeader($trustedHeader)));
yield array_map('trim', explode(', ', implode(',', $request->getHeader($trustedHeader))));
}
}

Expand Down
42 changes: 42 additions & 0 deletions Neos.Flow/Classes/Http/Helper/RequestInformationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static function getScriptRequestPath(ServerRequestInterface $request): st
// FIXME: Shouldn't this be a simple dirname on getScriptRequestPathAndFilename
$requestPathSegments = explode('/', self::getScriptRequestPathAndFilename($request));
array_pop($requestPathSegments);

return implode('/', $requestPathSegments) . '/';
}

Expand Down Expand Up @@ -139,4 +140,45 @@ public static function getContentCharset(RequestInterface $request): string

return '';
}

/**
* @param RequestInterface $request
* @param string $headerName
* @return string
*/
public static function getFirstRequestHeaderValue(RequestInterface $request, string $headerName):? string
{
$headerValues = $request->getHeader($headerName);
$firstHeaderValue = reset($headerValues);

return $firstHeaderValue;
}

/**
* Extract header key/value pairs from a $_SERVER array.
*
* @param array $server
* @return array
*/
public static function extractHeadersFromServerVariables(array $server): array
{
$headerFields = [];
if (isset($server['PHP_AUTH_USER']) && isset($server['PHP_AUTH_PW'])) {
$headerFields['Authorization'] = 'Basic ' . base64_encode($server['PHP_AUTH_USER'] . ':' . $server['PHP_AUTH_PW']);
}

foreach ($server as $name => $value) {
if (strpos($name, 'HTTP_') === 0) {
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
$headerFields[$name] = $value;
} elseif ($name == 'REDIRECT_REMOTE_AUTHORIZATION' && !isset($headerFields['Authorization'])) {
$headerFields['Authorization'] = $value;
} elseif (in_array($name, ['CONTENT_TYPE', 'CONTENT_LENGTH'])) {
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $name))));
$headerFields[$name] = $value;
}
}

return $headerFields;
}
}
4 changes: 2 additions & 2 deletions Neos.Flow/Classes/Http/Helper/ResponseInformationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ public static function prepareHeaders(ResponseInterface $response): array
if ($headers instanceof Headers) {
$preparedHeaders = array_merge($preparedHeaders, $headers->getPreparedValues());
} else {
foreach (array_keys($headers) as $name) {
$preparedHeaders[] = $response->getHeaderLine($name);
foreach ($response->getHeaders() as $name => $values) {
$preparedHeaders[] = $name . ': ' . implode(', ', $values);
}
}

Expand Down
Loading