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

Using "stream" => true in options makes PSR7 responses read-once #3180

Closed
garethellis36 opened this issue Sep 27, 2023 · 3 comments
Closed

Comments

@garethellis36
Copy link

Guzzle version(s) affected: 7.5.0
PHP version: 8.1.21
cURL version: 7.61.1

Description
We have enabled streaming of responses as per this Request Option.

While debugging another problem, I enabled the log middleware, and configured it to write the full response body to the log. I discovered that the response body could not be read a second time, because the StreamInterface implementation is not seekable. I believe that this is because StreamHandler uses fopen() to create a stream wrapper, and HTTP stream wrappers are inherently not seekable - however I do admit that I do not completely understand the mechanics of this. I spent quite a long time reading Guzzle source code trying to figure out how everything fits together.

How to reproduce

$handlerStack = HandlerStack::create();
$handlerStack->push(Middleware::log($logger, new MessageFormatter(MessageFormatter::DEBUG)));

$options = ['handler' => $handlerStack];
$client = new \GuzzleHttp\Client($options);

$response = $client->get($url, ['stream' => true]);
$body = (string)$response->getBody();
// `$body` is an empty string because the stream has already been fully read by the log middleware, and it can't be rewound

Additional context
If my understanding is correct, I do not think there is a solution for this, it's an inherent drawback of streaming responses. If this is the case then I think a documentation update would be useful because I spent a lot of time debugging this and trying to understand why the responses were read-once.

We are using stream here so that we can download large files and return the remote HTTP response directly from our application as another stream, without having to load the entire response into memory.

@GrahamCampbell
Copy link
Member

GrahamCampbell commented Sep 27, 2023

You should disable the stream option. It doesn't solve the problem you are asking about. We already buffer things to disk when it exceeds some threshold which I don't remember (something like 1MB).

@garethellis36
Copy link
Author

Thank you; out of interest, can you point me towards the code which buffers to disk so I can understand how this works?

@GrahamCampbell
Copy link
Member

If you're using ext-curl, this happens here:

$options['sink'] = \GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+');
. The threshold where by we move from in-memory to on disk depends on PHP config, the default being 2MB according to https://www.php.net/manual/en/wrappers.php.php.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants