Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
-------

35c08ef [HttpClient] fix casting responses to PHP streams
  • Loading branch information
nicolas-grekas committed Jan 4, 2020
2 parents 4103c16 + 35c08ef commit 6b2f4e6
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 13 deletions.
4 changes: 2 additions & 2 deletions src/Symfony/Component/HttpClient/CurlHttpClient.php
Expand Up @@ -288,7 +288,7 @@ public function request(string $method, string $url, array $options = []): Respo
$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;
}
}
Expand Down Expand Up @@ -412,7 +412,7 @@ private static function acceptPushForRequest(string $method, array $options, Pus
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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/HttpClient/NativeHttpClient.php
Expand Up @@ -169,7 +169,7 @@ public function request(string $method, string $url, array $options = []): Respo
$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);

Expand Down
6 changes: 5 additions & 1 deletion src/Symfony/Component/HttpClient/Response/ResponseTrait.php
Expand Up @@ -204,7 +204,11 @@ public function toStream(bool $throw = true)
$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;
}

/**
Expand Down
23 changes: 14 additions & 9 deletions src/Symfony/Component/HttpClient/Response/StreamWrapper.php
Expand Up @@ -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__));
Expand All @@ -63,8 +59,6 @@ public static function createResource(ResponseInterface $response, HttpClientInt
$context = [
'client' => $client ?? $response,
'response' => $response,
'content' => $contentBuffer,
'handle' => $selectHandle,
];

return fopen('symfony://'.$response->getInfo('url'), 'r', false, stream_context_create(['symfony' => $context])) ?: null;
Expand All @@ -78,6 +72,17 @@ public function getResponse(): ResponseInterface
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) {
Expand All @@ -91,8 +96,6 @@ public function stream_open(string $path, string $mode, int $options): bool
$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) {
Expand Down Expand Up @@ -238,6 +241,8 @@ public function stream_seek(int $offset, int $whence = SEEK_SET): bool
public function stream_cast(int $castAs)
{
if (STREAM_CAST_FOR_SELECT === $castAs) {
$this->response->getHeaders(false);

return $this->handle ?? false;
}

Expand Down

0 comments on commit 6b2f4e6

Please sign in to comment.