Skip to content

Commit

Permalink
fix(Storage): ensure streams arent copied by default (#7495)
Browse files Browse the repository at this point in the history
  • Loading branch information
bshaffer authored Jul 3, 2024
1 parent b54bcad commit 33ce1b1
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
14 changes: 13 additions & 1 deletion Storage/src/Connection/Rest.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,16 @@ public function downloadObject(array $args = [])
$transcodedObj = true;
}
};
$attempt = null;
$requestOptions['restRetryListener'] = function (
\Exception $e,
$retryAttempt,
&$arguments
) use (
$resultStream,
$requestedBytes,
$invocationId
$invocationId,
&$attempt,
) {
// if the exception has a response for us to use
if ($e instanceof RequestException && $e->hasResponse()) {
Expand All @@ -331,6 +333,9 @@ public function downloadObject(array $args = [])
// modify the range headers to fetch the remaining data
$arguments[1]['headers']['Range'] = sprintf('bytes=%s-%s', $startByte, $endByte);
$arguments[0] = $this->modifyRequestForRetry($arguments[0], $retryAttempt, $invocationId);

// Copy the final result to the end of the stream
$attempt = $retryAttempt;
}
};

Expand All @@ -339,6 +344,13 @@ public function downloadObject(array $args = [])
$requestOptions
)->getBody();

// If no retry attempt was made, then we can return the stream as is.
// This is important in the case where downloadObject is called to open
// the file but not to read from it yet.
if ($attempt === null) {
return $fetchedStream;
}

// If our object is a transcoded object, then Range headers are not honoured.
// That means even if we had a partial download available, the final obj
// that was fetched will contain the complete object. So, we don't need to copy
Expand Down
25 changes: 25 additions & 0 deletions Storage/tests/Unit/StorageObjectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,31 @@ public function provideDownloadAsStreamRetryHeaders()
];
}

public function testDownloadAsStreamShouldNotReadFromStream()
{
$stream = $this->prophesize(StreamInterface::class);
$stream->eof()->shouldNotBeCalled();
$stream->read(Argument::any())->shouldNotBeCalled();

$httpHandler = function ($request, $options) use ($stream) {
return new Response(200, [], $stream->reveal());
};

$rest = new Rest([
'httpHandler' => $httpHandler,
// Mock the authHttpHandler so it doesn't make a real request
'authHttpHandler' => function () {
return new Response(200, [], '{"access_token": "abc"}');
}
]);

$object = new StorageObject($rest, 'object', 'bucket');
$stream = $object->downloadAsStream();

// assert the resulting stream looks like we'd expect
$this->assertInstanceOf(StreamInterface::class, $stream);
}

public function testGetsInfo()
{
$objectInfo = [
Expand Down

0 comments on commit 33ce1b1

Please sign in to comment.