bug #35195 [HttpClient] fix casting responses to PHP streams (nicolas-grekas)

This PR was merged into the 4.4 branch.

Discussion
----------

[HttpClient] fix casting responses to PHP streams

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

This patch is required to properly deal with casting responses to PHP streams.
This changes a public method, but we can't expect anyone to override it as it's totally internal.
Found when working on (and required by) #35115

Commits
-------

35c08ef395 [HttpClient] fix casting responses to PHP streams
This commit is contained in:
Nicolas Grekas 2020-01-04 13:29:59 +01:00
commit 6b2f4e6126
4 changed files with 22 additions and 13 deletions

View File

@ -288,7 +288,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
$pushedResponse = $pushedResponse->response;
$pushedResponse->__construct($this->multi, $url, $options, $this->logger);
} else {
$this->logger && $this->logger->debug(sprintf('Rejecting pushed response: "%s".', $url));
$this->logger && $this->logger->debug(sprintf('Rejecting pushed response: "%s"', $url));
$pushedResponse = null;
}
}
@ -412,7 +412,7 @@ final class CurlHttpClient implements HttpClientInterface, LoggerAwareInterface,
return false;
}
foreach (['proxy', 'no_proxy', 'bindto'] as $k) {
foreach (['proxy', 'no_proxy', 'bindto', 'local_cert', 'local_pk'] as $k) {
if ($options[$k] !== $pushedResponse->parentOptions[$k]) {
return false;
}

View File

@ -169,7 +169,7 @@ final class NativeHttpClient implements HttpClientInterface, LoggerAwareInterfac
$this->multi->dnsCache = $options['resolve'] + $this->multi->dnsCache;
}
$this->logger && $this->logger->info(sprintf('Request: %s %s', $method, implode('', $url)));
$this->logger && $this->logger->info(sprintf('Request: "%s %s"', $method, implode('', $url)));
[$host, $port, $url['authority']] = self::dnsResolve($url, $this->multi, $info, $onProgress);

View File

@ -204,7 +204,11 @@ trait ResponseTrait
$this->getHeaders($throw);
}
return StreamWrapper::createResource($this, null, $this->content, $this->handle && 'stream' === get_resource_type($this->handle) ? $this->handle : null);
$stream = StreamWrapper::createResource($this);
stream_get_meta_data($stream)['wrapper_data']
->bindHandles($this->handle, $this->content);
return $stream;
}
/**

View File

@ -43,13 +43,9 @@ class StreamWrapper
/**
* Creates a PHP stream resource from a ResponseInterface.
*
* @param resource|null $contentBuffer The seekable resource where the response body is buffered
* @param resource|null $selectHandle The resource handle that should be monitored when
* stream_select() is used on the created stream
*
* @return resource
*/
public static function createResource(ResponseInterface $response, HttpClientInterface $client = null, $contentBuffer = null, $selectHandle = null)
public static function createResource(ResponseInterface $response, HttpClientInterface $client = null)
{
if (null === $client && !method_exists($response, 'stream')) {
throw new \InvalidArgumentException(sprintf('Providing a client to "%s()" is required when the response doesn\'t have any "stream()" method.', __CLASS__));
@ -63,8 +59,6 @@ class StreamWrapper
$context = [
'client' => $client ?? $response,
'response' => $response,
'content' => $contentBuffer,
'handle' => $selectHandle,
];
return fopen('symfony://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context])) ?: null;
@ -78,6 +72,17 @@ class StreamWrapper
return $this->response;
}
/**
* @param resource|null $handle The resource handle that should be monitored when
* stream_select() is used on the created stream
* @param resource|null $content The seekable resource where the response body is buffered
*/
public function bindHandles(&$handle, &$content): void
{
$this->handle = &$handle;
$this->content = &$content;
}
public function stream_open(string $path, string $mode, int $options): bool
{
if ('r' !== $mode) {
@ -91,8 +96,6 @@ class StreamWrapper
$context = stream_context_get_options($this->context)['symfony'] ?? null;
$this->client = $context['client'] ?? null;
$this->response = $context['response'] ?? null;
$this->content = $context['content'] ?? null;
$this->handle = $context['handle'] ?? null;
$this->context = null;
if (null !== $this->client && null !== $this->response) {
@ -238,6 +241,8 @@ class StreamWrapper
public function stream_cast(int $castAs)
{
if (STREAM_CAST_FOR_SELECT === $castAs) {
$this->response->getHeaders(false);
return $this->handle ?? false;
}