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:
Nicolas Grekas 2019-04-02 13:11:42 +02:00
commit 755f41192f
15 changed files with 65 additions and 65 deletions

View File

@ -36,7 +36,7 @@ class TestHttpClient extends HttpBrowser
return new MockResponse($this->nextResponse->getContent(), [
'http_code' => $this->nextResponse->getStatusCode(),
'raw_headers' => $this->nextResponse->getHeaders(),
'response_headers' => $this->nextResponse->getHeaders(),
]);
});
parent::__construct($client);

View File

@ -97,7 +97,7 @@ class CachingHttpClient implements HttpClientInterface
$response = $this->cache->handle($request);
$response = new MockResponse($response->getContent(), [
'http_code' => $response->getStatusCode(),
'raw_headers' => $response->headers->allPreserveCase(),
'response_headers' => $response->headers->allPreserveCase(),
]);
return MockResponse::fromRequest($method, $url, $options, $response);

View File

@ -74,8 +74,8 @@ final class CurlHttpClient implements HttpClientInterface
return;
}
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $rawHeaders) use ($multi) {
return self::handlePush($parent, $pushed, $rawHeaders, $multi);
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi) {
return self::handlePush($parent, $pushed, $requestHeaders, $multi);
});
}
@ -191,7 +191,7 @@ final class CurlHttpClient implements HttpClientInterface
$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, ': ')) {
// curl requires a special syntax to send empty headers
$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 = [];
foreach ($rawHeaders as $h) {
foreach ($requestHeaders as $h) {
if (false !== $i = strpos($h, ':', 1)) {
$headers[substr($h, 0, $i)] = substr($h, 1 + $i);
}
@ -348,12 +348,12 @@ final class CurlHttpClient implements HttpClientInterface
$redirectHeaders = [];
if (0 < $options['max_redirects']) {
$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:');
});
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:');
});
}
@ -361,8 +361,8 @@ final class CurlHttpClient implements HttpClientInterface
return static function ($ch, string $location) use ($redirectHeaders) {
if ($redirectHeaders && $host = parse_url($location, PHP_URL_HOST)) {
$rawHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
curl_setopt($ch, CURLOPT_HTTPHEADER, $rawHeaders);
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
}
$url = self::parseUrl(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));

View File

@ -31,7 +31,7 @@ trait HttpExceptionTrait
$httpCodeFound = 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 ($httpCodeFound) {
break;

View File

@ -53,12 +53,12 @@ trait HttpClientTrait
$options['peer_fingerprint'] = self::normalizePeerFingerprint($options['peer_fingerprint']);
}
// Compute raw headers
$rawHeaders = $headers = [];
// Compute request headers
$requestHeaders = $headers = [];
foreach ($options['headers'] as $name => $values) {
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")) {
throw new InvalidArgumentException(sprintf('Invalid header value: CR/LF/NUL found in "%s".', $value));
@ -95,14 +95,14 @@ trait HttpClientTrait
if (null !== $url) {
// Merge auth with headers
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
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']);
// Parse base URI
@ -128,7 +128,7 @@ trait HttpClientTrait
*/
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'] ?? []);

View File

@ -77,12 +77,12 @@ final class NativeHttpClient implements HttpClientInterface
$options['body'] = self::getBodyAsString($options['body']);
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'])) {
// 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']) {
@ -94,7 +94,7 @@ final class NativeHttpClient implements HttpClientInterface
}
$info = [
'raw_headers' => [],
'response_headers' => [],
'url' => $url,
'error' => null,
'http_method' => $method,
@ -159,7 +159,7 @@ final class NativeHttpClient implements HttpClientInterface
[$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress);
if (!isset($options['headers']['host'])) {
$options['raw_headers'][] = 'host: '.$host.$port;
$options['request_headers'][] = 'host: '.$host.$port;
}
$context = [
@ -203,7 +203,7 @@ final class NativeHttpClient implements HttpClientInterface
$resolveRedirect = self::createRedirectResolver($options, $host, $proxy, $noProxy, $info, $onProgress);
$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);
}
@ -326,12 +326,12 @@ final class NativeHttpClient implements HttpClientInterface
$redirectHeaders = [];
if (0 < $maxRedirects = $options['max_redirects']) {
$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:');
});
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:');
});
}
@ -376,36 +376,36 @@ final class NativeHttpClient implements HttpClientInterface
if (false !== (parse_url($location, PHP_URL_HOST) ?? false)) {
// Authorization and Cookie headers MUST NOT follow except for the initial host name
$rawHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
$rawHeaders[] = 'host: '.$host.$port;
self::configureHeadersAndProxy($context, $host, $rawHeaders, $proxy, $noProxy);
$requestHeaders = $redirectHeaders['host'] === $host ? $redirectHeaders['with_auth'] : $redirectHeaders['no_auth'];
$requestHeaders[] = 'host: '.$host.$port;
self::configureHeadersAndProxy($context, $host, $requestHeaders, $proxy, $noProxy);
}
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) {
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
foreach ($noProxy as $rule) {
if ('*' === $rule) {
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
}
if ($host === $rule) {
return stream_context_set_option($context, 'http', 'header', $rawHeaders);
return stream_context_set_option($context, 'http', 'header', $requestHeaders);
}
$rule = '.'.ltrim($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);
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);
}
}

View File

@ -48,7 +48,7 @@ final class CurlResponse implements ResponseInterface
$info = &$this->info;
$headers = &$this->headers;
if (!$info['raw_headers']) {
if (!$info['response_headers']) {
// Used to keep track of what we're waiting for
curl_setopt($ch, CURLOPT_PRIVATE, 'headers');
}
@ -257,7 +257,7 @@ final class CurlResponse implements ResponseInterface
if ("\r\n" !== $data) {
// 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 (curl_getinfo($ch, CURLINFO_REDIRECT_COUNT) === $options['max_redirects']) {

View File

@ -37,26 +37,26 @@ class MockResponse implements ResponseInterface
* yielding an empty string simulates a timeout,
* 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 = [])
{
$this->body = \is_iterable($body) ? $body : (string) $body;
$this->info = $info + $this->info;
if (!isset($info['raw_headers'])) {
if (!isset($info['response_headers'])) {
return;
}
$rawHeaders = [];
$responseHeaders = [];
foreach ($info['raw_headers'] as $k => $v) {
foreach ($info['response_headers'] as $k => $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
$info = $mock->getInfo() ?: [];
$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);
$response->info = [

View File

@ -111,7 +111,7 @@ final class NativeResponse implements ResponseInterface
// Send request and follow redirects when needed
$this->info['fopen_time'] = microtime(true);
$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);
} while (null !== $url);
} catch (\Throwable $e) {

View File

@ -43,7 +43,7 @@ trait ResponseTrait
private $content;
private $info = [
'raw_headers' => [],
'response_headers' => [],
'http_code' => 0,
'error' => null,
];
@ -187,9 +187,9 @@ trait ResponseTrait
*/
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)) {
$headers = [];
$info['http_code'] = (int) $m[1];
@ -197,7 +197,7 @@ trait ResponseTrait
$headers[strtolower($m[0])][] = ltrim($m[1]);
}
$info['raw_headers'][] = $h;
$info['response_headers'][] = $h;
}
if (!$info['http_code']) {

View File

@ -38,7 +38,7 @@ class HttpExceptionTraitTest extends TestCase
->will($this->returnValueMap([
['http_code', 400],
['url', 'http://example.com'],
['raw_headers', [
['response_headers', [
'HTTP/1.1 400 Bad Request',
'Content-Type: '.$mimeType,
]],

View File

@ -169,7 +169,7 @@ class HttpClientTraitTest extends TestCase
{
[, $options] = self::prepareRequest('POST', 'http://example.com', ['auth_bearer' => 'foobar'], HttpClientInterface::OPTIONS_DEFAULTS);
$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]);
}
/**

View File

@ -73,7 +73,7 @@ class MockHttpClientTest extends HttpClientTestCase
case 'testGetRequest':
array_unshift($headers, 'HTTP/1.1 200 OK');
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
$headers = [
'Host: localhost:8057',
@ -81,7 +81,7 @@ class MockHttpClientTest extends HttpClientTestCase
'Content-Type: application/json',
];
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
break;
case 'testDnsError':
@ -100,7 +100,7 @@ class MockHttpClientTest extends HttpClientTestCase
case 'testBadRequestBody':
case 'testOnProgressCancel':
case 'testOnProgressError':
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
break;
case 'testTimeoutOnAccess':
@ -113,15 +113,15 @@ class MockHttpClientTest extends HttpClientTestCase
break;
case 'testResolve':
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
$responses[] = $client->request('GET', 'http://symfony.com:8057/');
break;
case 'testTimeoutOnStream':
case 'testUncheckedTimeoutThrows':
$body = ['<1>', '', '<2>'];
$responses[] = new MockResponse($body, ['raw_headers' => $headers]);
$responses[] = new MockResponse($body, ['response_headers' => $headers]);
break;
}

View File

@ -79,7 +79,7 @@ interface ResponseInterface
* another, as the request/response progresses.
*
* 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_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

View File

@ -41,15 +41,15 @@ abstract class HttpClientTestCase extends TestCase
'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(200, $response->getStatusCode());
$info = $response->getInfo();
$this->assertNull($info['error']);
$this->assertSame(0, $info['redirect_count']);
$this->assertSame('HTTP/1.1 200 OK', $info['raw_headers'][0]);
$this->assertSame('Host: localhost:8057', $info['raw_headers'][1]);
$this->assertSame('HTTP/1.1 200 OK', $info['response_headers'][0]);
$this->assertSame('Host: localhost:8057', $info['response_headers'][1]);
$this->assertSame('http://localhost:8057/', $info['url']);
$headers = $response->getHeaders();
@ -105,7 +105,7 @@ abstract class HttpClientTestCase extends TestCase
]);
$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();
@ -252,7 +252,7 @@ abstract class HttpClientTestCase extends TestCase
'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;
}));
@ -326,7 +326,7 @@ abstract class HttpClientTestCase extends TestCase
'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);
}));