minor #30831 [HttpClient][Contracts] rename "raw_headers" to "response_headers" (nicolas-grekas)
This PR was merged into the 4.3-dev branch.
Discussion
----------
[HttpClient][Contracts] rename "raw_headers" to "response_headers"
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
A preliminary step before adding the `request_headers` info on responses to ease debugging.
Commits
-------
0b21268bf5
[HttpClient][Contracts] rename "raw_headers" to "response_headers"
This commit is contained in:
commit
755f41192f
@ -36,7 +36,7 @@ class TestHttpClient extends HttpBrowser
|
|||||||
|
|
||||||
return new MockResponse($this->nextResponse->getContent(), [
|
return new MockResponse($this->nextResponse->getContent(), [
|
||||||
'http_code' => $this->nextResponse->getStatusCode(),
|
'http_code' => $this->nextResponse->getStatusCode(),
|
||||||
'raw_headers' => $this->nextResponse->getHeaders(),
|
'response_headers' => $this->nextResponse->getHeaders(),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
parent::__construct($client);
|
parent::__construct($client);
|
||||||
|
@ -97,7 +97,7 @@ class CachingHttpClient implements HttpClientInterface
|
|||||||
$response = $this->cache->handle($request);
|
$response = $this->cache->handle($request);
|
||||||
$response = new MockResponse($response->getContent(), [
|
$response = new MockResponse($response->getContent(), [
|
||||||
'http_code' => $response->getStatusCode(),
|
'http_code' => $response->getStatusCode(),
|
||||||
'raw_headers' => $response->headers->allPreserveCase(),
|
'response_headers' => $response->headers->allPreserveCase(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return MockResponse::fromRequest($method, $url, $options, $response);
|
return MockResponse::fromRequest($method, $url, $options, $response);
|
||||||
|
@ -74,8 +74,8 @@ final class CurlHttpClient implements HttpClientInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $rawHeaders) use ($multi) {
|
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi) {
|
||||||
return self::handlePush($parent, $pushed, $rawHeaders, $multi);
|
return self::handlePush($parent, $pushed, $requestHeaders, $multi);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +191,7 @@ final class CurlHttpClient implements HttpClientInterface
|
|||||||
$curlopts[CURLOPT_ENCODING] = ''; // Enable HTTP compression
|
$curlopts[CURLOPT_ENCODING] = ''; // Enable HTTP compression
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($options['raw_headers'] as $header) {
|
foreach ($options['request_headers'] as $header) {
|
||||||
if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) {
|
if (':' === $header[-2] && \strlen($header) - 2 === strpos($header, ': ')) {
|
||||||
// curl requires a special syntax to send empty headers
|
// curl requires a special syntax to send empty headers
|
||||||
$curlopts[CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2);
|
$curlopts[CURLOPT_HTTPHEADER][] = substr_replace($header, ';', -2);
|
||||||
@ -282,11 +282,11 @@ final class CurlHttpClient implements HttpClientInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function handlePush($parent, $pushed, array $rawHeaders, \stdClass $multi): int
|
private static function handlePush($parent, $pushed, array $requestHeaders, \stdClass $multi): int
|
||||||
{
|
{
|
||||||
$headers = [];
|
$headers = [];
|
||||||
|
|
||||||
foreach ($rawHeaders as $h) {
|
foreach ($requestHeaders as $h) {
|
||||||
if (false !== $i = strpos($h, ':', 1)) {
|
if (false !== $i = strpos($h, ':', 1)) {
|
||||||
$headers[substr($h, 0, $i)] = substr($h, 1 + $i);
|
$headers[substr($h, 0, $i)] = substr($h, 1 + $i);
|
||||||
}
|
}
|
||||||
@ -348,12 +348,12 @@ final class CurlHttpClient implements HttpClientInterface
|
|||||||
$redirectHeaders = [];
|
$redirectHeaders = [];
|
||||||
if (0 < $options['max_redirects']) {
|
if (0 < $options['max_redirects']) {
|
||||||
$redirectHeaders['host'] = $host;
|
$redirectHeaders['host'] = $host;
|
||||||
$redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['raw_headers'], static function ($h) {
|
$redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['request_headers'], static function ($h) {
|
||||||
return 0 !== stripos($h, 'Host:');
|
return 0 !== stripos($h, 'Host:');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isset($options['headers']['authorization']) || isset($options['headers']['cookie'])) {
|
if (isset($options['headers']['authorization']) || isset($options['headers']['cookie'])) {
|
||||||
$redirectHeaders['no_auth'] = array_filter($options['raw_headers'], static function ($h) {
|
$redirectHeaders['no_auth'] = array_filter($options['request_headers'], static function ($h) {
|
||||||
return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
|
return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -361,8 +361,8 @@ final class CurlHttpClient implements HttpClientInterface
|
|||||||
|
|
||||||
return static function ($ch, string $location) use ($redirectHeaders) {
|
return static function ($ch, string $location) use ($redirectHeaders) {
|
||||||
if ($redirectHeaders && $host = parse_url($location, PHP_URL_HOST)) {
|
if ($redirectHeaders && $host = parse_url($location, PHP_URL_HOST)) {
|
||||||
$rawHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
|
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $rawHeaders);
|
curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = self::parseUrl(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
$url = self::parseUrl(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
|
||||||
|
@ -31,7 +31,7 @@ trait HttpExceptionTrait
|
|||||||
|
|
||||||
$httpCodeFound = false;
|
$httpCodeFound = false;
|
||||||
$isJson = false;
|
$isJson = false;
|
||||||
foreach (array_reverse($response->getInfo('raw_headers')) as $h) {
|
foreach (array_reverse($response->getInfo('response_headers')) as $h) {
|
||||||
if (0 === strpos($h, 'HTTP/')) {
|
if (0 === strpos($h, 'HTTP/')) {
|
||||||
if ($httpCodeFound) {
|
if ($httpCodeFound) {
|
||||||
break;
|
break;
|
||||||
|
@ -53,12 +53,12 @@ trait HttpClientTrait
|
|||||||
$options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']);
|
$options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute raw headers
|
// Compute request headers
|
||||||
$rawHeaders = $headers = [];
|
$requestHeaders = $headers = [];
|
||||||
|
|
||||||
foreach ($options['headers'] as $name => $values) {
|
foreach ($options['headers'] as $name => $values) {
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
$rawHeaders[] = $name.': '.$headers[$name][] = $value = (string) $value;
|
$requestHeaders[] = $name.': '.$headers[$name][] = $value = (string) $value;
|
||||||
|
|
||||||
if (\strlen($value) !== strcspn($value, "\r\n\0")) {
|
if (\strlen($value) !== strcspn($value, "\r\n\0")) {
|
||||||
throw new InvalidArgumentException(sprintf('Invalid header value: CR/LF/NUL found in "%s".', $value));
|
throw new InvalidArgumentException(sprintf('Invalid header value: CR/LF/NUL found in "%s".', $value));
|
||||||
@ -95,14 +95,14 @@ trait HttpClientTrait
|
|||||||
if (null !== $url) {
|
if (null !== $url) {
|
||||||
// Merge auth with headers
|
// Merge auth with headers
|
||||||
if (($options['auth_basic'] ?? false) && !($headers['authorization'] ?? false)) {
|
if (($options['auth_basic'] ?? false) && !($headers['authorization'] ?? false)) {
|
||||||
$rawHeaders[] = 'authorization: '.$headers['authorization'][] = 'Basic '.base64_encode($options['auth_basic']);
|
$requestHeaders[] = 'authorization: '.$headers['authorization'][] = 'Basic '.base64_encode($options['auth_basic']);
|
||||||
}
|
}
|
||||||
// Merge bearer with headers
|
// Merge bearer with headers
|
||||||
if (($options['auth_bearer'] ?? false) && !($headers['authorization'] ?? false)) {
|
if (($options['auth_bearer'] ?? false) && !($headers['authorization'] ?? false)) {
|
||||||
$rawHeaders[] = 'authorization: '.$headers['authorization'][] = 'Bearer '.$options['auth_bearer'];
|
$requestHeaders[] = 'authorization: '.$headers['authorization'][] = 'Bearer '.$options['auth_bearer'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$options['raw_headers'] = $rawHeaders;
|
$options['request_headers'] = $requestHeaders;
|
||||||
unset($options['auth_basic'], $options['auth_bearer']);
|
unset($options['auth_basic'], $options['auth_bearer']);
|
||||||
|
|
||||||
// Parse base URI
|
// Parse base URI
|
||||||
@ -128,7 +128,7 @@ trait HttpClientTrait
|
|||||||
*/
|
*/
|
||||||
private static function mergeDefaultOptions(array $options, array $defaultOptions, bool $allowExtraOptions = false): array
|
private static function mergeDefaultOptions(array $options, array $defaultOptions, bool $allowExtraOptions = false): array
|
||||||
{
|
{
|
||||||
unset($options['raw_headers'], $defaultOptions['raw_headers']);
|
unset($options['request_headers'], $defaultOptions['request_headers']);
|
||||||
|
|
||||||
$options['headers'] = self::normalizeHeaders($options['headers'] ?? []);
|
$options['headers'] = self::normalizeHeaders($options['headers'] ?? []);
|
||||||
|
|
||||||
|
@ -77,12 +77,12 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
$options['body'] = self::getBodyAsString($options['body']);
|
$options['body'] = self::getBodyAsString($options['body']);
|
||||||
|
|
||||||
if ('' !== $options['body'] && 'POST' === $method && !isset($options['headers']['content-type'])) {
|
if ('' !== $options['body'] && 'POST' === $method && !isset($options['headers']['content-type'])) {
|
||||||
$options['raw_headers'][] = 'content-type: application/x-www-form-urlencoded';
|
$options['request_headers'][] = 'content-type: application/x-www-form-urlencoded';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($gzipEnabled = \extension_loaded('zlib') && !isset($options['headers']['accept-encoding'])) {
|
if ($gzipEnabled = \extension_loaded('zlib') && !isset($options['headers']['accept-encoding'])) {
|
||||||
// gzip is the most widely available algo, no need to deal with deflate
|
// gzip is the most widely available algo, no need to deal with deflate
|
||||||
$options['raw_headers'][] = 'accept-encoding: gzip';
|
$options['request_headers'][] = 'accept-encoding: gzip';
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($options['peer_fingerprint']) {
|
if ($options['peer_fingerprint']) {
|
||||||
@ -94,7 +94,7 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$info = [
|
$info = [
|
||||||
'raw_headers' => [],
|
'response_headers' => [],
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
'error' => null,
|
'error' => null,
|
||||||
'http_method' => $method,
|
'http_method' => $method,
|
||||||
@ -159,7 +159,7 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
[$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress);
|
[$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress);
|
||||||
|
|
||||||
if (!isset($options['headers']['host'])) {
|
if (!isset($options['headers']['host'])) {
|
||||||
$options['raw_headers'][] = 'host: '.$host.$port;
|
$options['request_headers'][] = 'host: '.$host.$port;
|
||||||
}
|
}
|
||||||
|
|
||||||
$context = [
|
$context = [
|
||||||
@ -203,7 +203,7 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
|
|
||||||
$resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $noProxy, $info, $onProgress);
|
$resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $noProxy, $info, $onProgress);
|
||||||
$context = stream_context_create($context, ['notification' => $notification]);
|
$context = stream_context_create($context, ['notification' => $notification]);
|
||||||
self::configureHeadersAndProxy($context, $host, $options['raw_headers'], $proxy, $noProxy);
|
self::configureHeadersAndProxy($context, $host, $options['request_headers'], $proxy, $noProxy);
|
||||||
|
|
||||||
return new NativeResponse($this->multi, $context, implode('', $url), $options, $gzipEnabled, $info, $resolveRedirect, $onProgress);
|
return new NativeResponse($this->multi, $context, implode('', $url), $options, $gzipEnabled, $info, $resolveRedirect, $onProgress);
|
||||||
}
|
}
|
||||||
@ -326,12 +326,12 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
$redirectHeaders = [];
|
$redirectHeaders = [];
|
||||||
if (0 < $maxRedirects = $options['max_redirects']) {
|
if (0 < $maxRedirects = $options['max_redirects']) {
|
||||||
$redirectHeaders = ['host' => $host];
|
$redirectHeaders = ['host' => $host];
|
||||||
$redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['raw_headers'], static function ($h) {
|
$redirectHeaders['with_auth'] = $redirectHeaders['no_auth'] = array_filter($options['request_headers'], static function ($h) {
|
||||||
return 0 !== stripos($h, 'Host:');
|
return 0 !== stripos($h, 'Host:');
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isset($options['headers']['authorization']) || isset($options['headers']['cookie'])) {
|
if (isset($options['headers']['authorization']) || isset($options['headers']['cookie'])) {
|
||||||
$redirectHeaders['no_auth'] = array_filter($options['raw_headers'], static function ($h) {
|
$redirectHeaders['no_auth'] = array_filter($options['request_headers'], static function ($h) {
|
||||||
return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
|
return 0 !== stripos($h, 'Authorization:') && 0 !== stripos($h, 'Cookie:');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -376,36 +376,36 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
|
|
||||||
if (false !== (parse_url($location, PHP_URL_HOST) ?? false)) {
|
if (false !== (parse_url($location, PHP_URL_HOST) ?? false)) {
|
||||||
// Authorization and Cookie headers MUST NOT follow except for the initial host name
|
// Authorization and Cookie headers MUST NOT follow except for the initial host name
|
||||||
$rawHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
|
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
|
||||||
$rawHeaders[] = 'host: '.$host.$port;
|
$requestHeaders[] = 'host: '.$host.$port;
|
||||||
self::configureHeadersAndProxy($context, $host, $rawHeaders, $proxy, $noProxy);
|
self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, $noProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return implode('', $url);
|
return implode('', $url);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function configureHeadersAndProxy($context, string $host, array $rawHeaders, ?array $proxy, array $noProxy)
|
private static function configureHeadersAndProxy($context, string $host, array $requestHeaders, ?array $proxy, array $noProxy)
|
||||||
{
|
{
|
||||||
if (null === $proxy) {
|
if (null === $proxy) {
|
||||||
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
|
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matching "no_proxy" should follow the behavior of curl
|
// Matching "no_proxy" should follow the behavior of curl
|
||||||
|
|
||||||
foreach ($noProxy as $rule) {
|
foreach ($noProxy as $rule) {
|
||||||
if ('*' === $rule) {
|
if ('*' === $rule) {
|
||||||
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
|
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($host === $rule) {
|
if ($host === $rule) {
|
||||||
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
|
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
$rule = '.'.ltrim($rule, '.');
|
$rule = '.'.ltrim($rule, '.');
|
||||||
|
|
||||||
if (substr($host, -\strlen($rule)) === $rule) {
|
if (substr($host, -\strlen($rule)) === $rule) {
|
||||||
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
|
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,9 +413,9 @@ final class NativeHttpClient implements HttpClientInterface
|
|||||||
stream_context_set_option($context, 'http', 'request_fulluri', true);
|
stream_context_set_option($context, 'http', 'request_fulluri', true);
|
||||||
|
|
||||||
if (null !== $proxy['auth']) {
|
if (null !== $proxy['auth']) {
|
||||||
$rawHeaders[] = 'Proxy-Authorization: '.$proxy['auth'];
|
$requestHeaders[] = 'Proxy-Authorization: '.$proxy['auth'];
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
|
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
$info = &$this->info;
|
$info = &$this->info;
|
||||||
$headers = &$this->headers;
|
$headers = &$this->headers;
|
||||||
|
|
||||||
if (!$info['raw_headers']) {
|
if (!$info['response_headers']) {
|
||||||
// Used to keep track of what we're waiting for
|
// Used to keep track of what we're waiting for
|
||||||
curl_setopt($ch, CURLOPT_PRIVATE, 'headers');
|
curl_setopt($ch, CURLOPT_PRIVATE, 'headers');
|
||||||
}
|
}
|
||||||
@ -257,7 +257,7 @@ final class CurlResponse implements ResponseInterface
|
|||||||
|
|
||||||
if ("\r\n" !== $data) {
|
if ("\r\n" !== $data) {
|
||||||
// Regular header line: add it to the list
|
// Regular header line: add it to the list
|
||||||
self::addRawHeaders([substr($data, 0, -2)], $info, $headers);
|
self::addResponseHeaders([substr($data, 0, -2)], $info, $headers);
|
||||||
|
|
||||||
if (0 === strpos($data, 'HTTP') && 300 <= $info['http_code'] && $info['http_code'] < 400) {
|
if (0 === strpos($data, 'HTTP') && 300 <= $info['http_code'] && $info['http_code'] < 400) {
|
||||||
if (curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
|
if (curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {
|
||||||
|
@ -37,26 +37,26 @@ class MockResponse implements ResponseInterface
|
|||||||
* yielding an empty string simulates a timeout,
|
* yielding an empty string simulates a timeout,
|
||||||
* exceptions are turned to TransportException
|
* exceptions are turned to TransportException
|
||||||
*
|
*
|
||||||
* @see ResponseInterface::getInfo() for possible info, e.g. "raw_headers"
|
* @see ResponseInterface::getInfo() for possible info, e.g. "response_headers"
|
||||||
*/
|
*/
|
||||||
public function __construct($body = '', array $info = [])
|
public function __construct($body = '', array $info = [])
|
||||||
{
|
{
|
||||||
$this->body = \is_iterable($body) ? $body : (string) $body;
|
$this->body = \is_iterable($body) ? $body : (string) $body;
|
||||||
$this->info = $info + $this->info;
|
$this->info = $info + $this->info;
|
||||||
|
|
||||||
if (!isset($info['raw_headers'])) {
|
if (!isset($info['response_headers'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$rawHeaders = [];
|
$responseHeaders = [];
|
||||||
|
|
||||||
foreach ($info['raw_headers'] as $k => $v) {
|
foreach ($info['response_headers'] as $k => $v) {
|
||||||
foreach ((array) $v as $v) {
|
foreach ((array) $v as $v) {
|
||||||
$rawHeaders[] = (\is_string($k) ? $k.': ' : '').$v;
|
$responseHeaders[] = (\is_string($k) ? $k.': ' : '').$v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->info['raw_headers'] = $rawHeaders;
|
$this->info['response_headers'] = $responseHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -239,7 +239,7 @@ class MockResponse implements ResponseInterface
|
|||||||
// populate info related to headers
|
// populate info related to headers
|
||||||
$info = $mock->getInfo() ?: [];
|
$info = $mock->getInfo() ?: [];
|
||||||
$response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode(false) ?: 200;
|
$response->info['http_code'] = ($info['http_code'] ?? 0) ?: $mock->getStatusCode(false) ?: 200;
|
||||||
$response->addRawHeaders($info['raw_headers'] ?? [], $response->info, $response->headers);
|
$response->addResponseHeaders($info['response_headers'] ?? [], $response->info, $response->headers);
|
||||||
$dlSize = (int) ($response->headers['content-length'][0] ?? 0);
|
$dlSize = (int) ($response->headers['content-length'][0] ?? 0);
|
||||||
|
|
||||||
$response->info = [
|
$response->info = [
|
||||||
|
@ -111,7 +111,7 @@ final class NativeResponse implements ResponseInterface
|
|||||||
// Send request and follow redirects when needed
|
// Send request and follow redirects when needed
|
||||||
$this->info['fopen_time'] = microtime(true);
|
$this->info['fopen_time'] = microtime(true);
|
||||||
$this->handle = $h = fopen($url, 'r', false, $this->context);
|
$this->handle = $h = fopen($url, 'r', false, $this->context);
|
||||||
self::addRawHeaders($http_response_header, $this->info, $this->headers);
|
self::addResponseHeaders($http_response_header, $this->info, $this->headers);
|
||||||
$url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context);
|
$url = ($this->resolveRedirect)($this->multi, $this->headers['location'][0] ?? null, $this->context);
|
||||||
} while (null !== $url);
|
} while (null !== $url);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
@ -43,7 +43,7 @@ trait ResponseTrait
|
|||||||
private $content;
|
private $content;
|
||||||
|
|
||||||
private $info = [
|
private $info = [
|
||||||
'raw_headers' => [],
|
'response_headers' => [],
|
||||||
'http_code' => 0,
|
'http_code' => 0,
|
||||||
'error' => null,
|
'error' => null,
|
||||||
];
|
];
|
||||||
@ -187,9 +187,9 @@ trait ResponseTrait
|
|||||||
*/
|
*/
|
||||||
abstract protected static function select(\stdClass $multi, float $timeout): int;
|
abstract protected static function select(\stdClass $multi, float $timeout): int;
|
||||||
|
|
||||||
private static function addRawHeaders(array $rawHeaders, array &$info, array &$headers): void
|
private static function addResponseHeaders(array $responseHeaders, array &$info, array &$headers): void
|
||||||
{
|
{
|
||||||
foreach ($rawHeaders as $h) {
|
foreach ($responseHeaders as $h) {
|
||||||
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d) .*#', $h, $m)) {
|
if (11 <= \strlen($h) && '/' === $h[4] && preg_match('#^HTTP/\d+(?:\.\d+)? ([12345]\d\d) .*#', $h, $m)) {
|
||||||
$headers = [];
|
$headers = [];
|
||||||
$info['http_code'] = (int) $m[1];
|
$info['http_code'] = (int) $m[1];
|
||||||
@ -197,7 +197,7 @@ trait ResponseTrait
|
|||||||
$headers[strtolower($m[0])][] = ltrim($m[1]);
|
$headers[strtolower($m[0])][] = ltrim($m[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$info['raw_headers'][] = $h;
|
$info['response_headers'][] = $h;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$info['http_code']) {
|
if (!$info['http_code']) {
|
||||||
|
@ -38,7 +38,7 @@ class HttpExceptionTraitTest extends TestCase
|
|||||||
->will($this->returnValueMap([
|
->will($this->returnValueMap([
|
||||||
['http_code', 400],
|
['http_code', 400],
|
||||||
['url', 'http://example.com'],
|
['url', 'http://example.com'],
|
||||||
['raw_headers', [
|
['response_headers', [
|
||||||
'HTTP/1.1 400 Bad Request',
|
'HTTP/1.1 400 Bad Request',
|
||||||
'Content-Type: '.$mimeType,
|
'Content-Type: '.$mimeType,
|
||||||
]],
|
]],
|
||||||
|
@ -169,7 +169,7 @@ class HttpClientTraitTest extends TestCase
|
|||||||
{
|
{
|
||||||
[, $options] = self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foobar'], HttpClientInterface::OPTIONS_DEFAULTS);
|
[, $options] = self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foobar'], HttpClientInterface::OPTIONS_DEFAULTS);
|
||||||
$this->assertSame('Bearer foobar', $options['headers']['authorization'][0]);
|
$this->assertSame('Bearer foobar', $options['headers']['authorization'][0]);
|
||||||
$this->assertSame('authorization: Bearer foobar', $options['raw_headers'][0]);
|
$this->assertSame('authorization: Bearer foobar', $options['request_headers'][0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,7 +73,7 @@ class MockHttpClientTest extends HttpClientTestCase
|
|||||||
|
|
||||||
case 'testGetRequest':
|
case 'testGetRequest':
|
||||||
array_unshift($headers, 'HTTP/1.1 200 OK');
|
array_unshift($headers, 'HTTP/1.1 200 OK');
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
|
|
||||||
$headers = [
|
$headers = [
|
||||||
'Host: localhost:8057',
|
'Host: localhost:8057',
|
||||||
@ -81,7 +81,7 @@ class MockHttpClientTest extends HttpClientTestCase
|
|||||||
'Content-Type: application/json',
|
'Content-Type: application/json',
|
||||||
];
|
];
|
||||||
|
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'testDnsError':
|
case 'testDnsError':
|
||||||
@ -100,7 +100,7 @@ class MockHttpClientTest extends HttpClientTestCase
|
|||||||
case 'testBadRequestBody':
|
case 'testBadRequestBody':
|
||||||
case 'testOnProgressCancel':
|
case 'testOnProgressCancel':
|
||||||
case 'testOnProgressError':
|
case 'testOnProgressError':
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'testTimeoutOnAccess':
|
case 'testTimeoutOnAccess':
|
||||||
@ -113,15 +113,15 @@ class MockHttpClientTest extends HttpClientTestCase
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'testResolve':
|
case 'testResolve':
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
$responses[] = $client->request('GET', 'http://symfony.com:8057/');
|
$responses[] = $client->request('GET', 'http://symfony.com:8057/');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'testTimeoutOnStream':
|
case 'testTimeoutOnStream':
|
||||||
case 'testUncheckedTimeoutThrows':
|
case 'testUncheckedTimeoutThrows':
|
||||||
$body = ['<1>', '', '<2>'];
|
$body = ['<1>', '', '<2>'];
|
||||||
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
|
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ interface ResponseInterface
|
|||||||
* another, as the request/response progresses.
|
* another, as the request/response progresses.
|
||||||
*
|
*
|
||||||
* The following info MUST be returned:
|
* The following info MUST be returned:
|
||||||
* - raw_headers - an array modelled after the special $http_response_header variable
|
* - response_headers - an array modelled after the special $http_response_header variable
|
||||||
* - redirect_count - the number of redirects followed while executing the request
|
* - redirect_count - the number of redirects followed while executing the request
|
||||||
* - redirect_url - the resolved location of redirect responses, null otherwise
|
* - redirect_url - the resolved location of redirect responses, null otherwise
|
||||||
* - start_time - the time when the request was sent or 0.0 when it's pending
|
* - start_time - the time when the request was sent or 0.0 when it's pending
|
||||||
|
@ -41,15 +41,15 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
'user_data' => $data = new \stdClass(),
|
'user_data' => $data = new \stdClass(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertSame([], $response->getInfo('raw_headers'));
|
$this->assertSame([], $response->getInfo('response_headers'));
|
||||||
$this->assertSame($data, $response->getInfo()['user_data']);
|
$this->assertSame($data, $response->getInfo()['user_data']);
|
||||||
$this->assertSame(200, $response->getStatusCode());
|
$this->assertSame(200, $response->getStatusCode());
|
||||||
|
|
||||||
$info = $response->getInfo();
|
$info = $response->getInfo();
|
||||||
$this->assertNull($info['error']);
|
$this->assertNull($info['error']);
|
||||||
$this->assertSame(0, $info['redirect_count']);
|
$this->assertSame(0, $info['redirect_count']);
|
||||||
$this->assertSame('HTTP/1.1 200 OK', $info['raw_headers'][0]);
|
$this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
|
||||||
$this->assertSame('Host: localhost:8057', $info['raw_headers'][1]);
|
$this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
|
||||||
$this->assertSame('http://localhost:8057/', $info['url']);
|
$this->assertSame('http://localhost:8057/', $info['url']);
|
||||||
|
|
||||||
$headers = $response->getHeaders();
|
$headers = $response->getHeaders();
|
||||||
@ -105,7 +105,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertSame(200, $response->getStatusCode());
|
$this->assertSame(200, $response->getStatusCode());
|
||||||
$this->assertSame('HTTP/1.0 200 OK', $response->getInfo('raw_headers')[0]);
|
$this->assertSame('HTTP/1.0 200 OK', $response->getInfo('response_headers')[0]);
|
||||||
|
|
||||||
$body = $response->toArray();
|
$body = $response->toArray();
|
||||||
|
|
||||||
@ -252,7 +252,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
'Content-Type: application/json',
|
'Content-Type: application/json',
|
||||||
];
|
];
|
||||||
|
|
||||||
$filteredHeaders = array_values(array_filter($response->getInfo('raw_headers'), function ($h) {
|
$filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
|
||||||
return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true) && 'Content-Encoding: gzip' !== $h;
|
return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true) && 'Content-Encoding: gzip' !== $h;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -326,7 +326,7 @@ abstract class HttpClientTestCase extends TestCase
|
|||||||
'Content-Type: application/json',
|
'Content-Type: application/json',
|
||||||
];
|
];
|
||||||
|
|
||||||
$filteredHeaders = array_values(array_filter($response->getInfo('raw_headers'), function ($h) {
|
$filteredHeaders = array_values(array_filter($response->getInfo('response_headers'), function ($h) {
|
||||||
return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true);
|
return \in_array(substr($h, 0, 4), ['HTTP', 'Loca', 'Cont'], true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user