-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use Composer's HttpDownloader natively (#77)
- Loading branch information
1 parent
57df719
commit 56e372d
Showing
4 changed files
with
122 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace Tuf\ComposerIntegration; | ||
|
||
use Composer\Downloader\MaxFileSizeExceededException; | ||
use Composer\Downloader\TransportException; | ||
use Composer\Util\HttpDownloader; | ||
use GuzzleHttp\Psr7\Utils; | ||
use Psr\Http\Message\StreamInterface; | ||
use Tuf\Exception\DownloadSizeException; | ||
use Tuf\Exception\RepoFileNotFound; | ||
use Tuf\Loader\LoaderInterface; | ||
|
||
/** | ||
* Defines a data loader that wraps around Composer's HttpDownloader. | ||
*/ | ||
class Loader implements LoaderInterface | ||
{ | ||
public function __construct(private HttpDownloader $downloader, private string $baseUrl = '') | ||
{ | ||
} | ||
|
||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public function load(string $locator, int $maxBytes): StreamInterface | ||
{ | ||
$url = $this->baseUrl . $locator; | ||
|
||
try { | ||
// Add 1 to $maxBytes to work around a bug in Composer. | ||
// @see \Tuf\ComposerIntegration\ComposerCompatibleUpdater::getLength() | ||
$content = $this->downloader->get($url, ['max_file_size' => $maxBytes + 1]) | ||
->getBody(); | ||
return Utils::streamFor($content); | ||
} catch (TransportException $e) { | ||
if ($e->getStatusCode() === 404) { | ||
throw new RepoFileNotFound("$locator not found"); | ||
} elseif ($e instanceof MaxFileSizeExceededException) { | ||
throw new DownloadSizeException("$locator exceeded $maxBytes bytes"); | ||
} else { | ||
throw new \RuntimeException($e->getMessage(), $e->getCode(), $e); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
<?php | ||
|
||
namespace Tuf\ComposerIntegration\Tests; | ||
|
||
use Composer\Downloader\MaxFileSizeExceededException; | ||
use Composer\Downloader\TransportException; | ||
use Composer\Util\HttpDownloader; | ||
use GuzzleHttp\Psr7\Response; | ||
use PHPUnit\Framework\TestCase; | ||
use Prophecy\PhpUnit\ProphecyTrait; | ||
use Psr\Http\Message\StreamInterface; | ||
use Tuf\ComposerIntegration\Loader; | ||
use Tuf\Exception\DownloadSizeException; | ||
use Tuf\Exception\RepoFileNotFound; | ||
|
||
/** | ||
* @covers \Tuf\ComposerIntegration\Loader | ||
*/ | ||
class LoaderTest extends TestCase | ||
{ | ||
use ProphecyTrait; | ||
|
||
public function testLoader(): void | ||
{ | ||
$downloader = $this->prophesize(HttpDownloader::class); | ||
$loader = new Loader($downloader->reveal(), '/metadata/'); | ||
|
||
$downloader->get('/metadata/root.json', ['max_file_size' => 129]) | ||
->willReturn(new Response()) | ||
->shouldBeCalled(); | ||
$this->assertInstanceOf(StreamInterface::class, $loader->load('root.json', 128)); | ||
|
||
// Any TransportException with a 404 error could should be converted | ||
// into a RepoFileNotFound exception. | ||
$exception = new TransportException(); | ||
$exception->setStatusCode(404); | ||
$downloader->get('/metadata/bogus.txt', ['max_file_size' => 11]) | ||
->willThrow($exception) | ||
->shouldBeCalled(); | ||
try { | ||
$loader->load('bogus.txt', 10); | ||
$this->fail('Expected a RepoFileNotFound exception, but none was thrown.'); | ||
} catch (RepoFileNotFound $e) { | ||
$this->assertSame('bogus.txt not found', $e->getMessage()); | ||
} | ||
|
||
// A MaxFileSizeExceededException should be converted into a | ||
// DownloadSizeException. | ||
$downloader->get('/metadata/too_big.txt', ['max_file_size' => 11]) | ||
->willThrow(new MaxFileSizeExceededException()) | ||
->shouldBeCalled(); | ||
try { | ||
$loader->load('too_big.txt', 10); | ||
$this->fail('Expected a DownloadSizeException, but none was thrown.'); | ||
} catch (DownloadSizeException $e) { | ||
$this->assertSame('too_big.txt exceeded 10 bytes', $e->getMessage()); | ||
} | ||
|
||
// Any other TransportException should be wrapped in a | ||
// \RuntimeException. | ||
$originalException = new TransportException('Whiskey Tango Foxtrot', -32); | ||
$downloader->get('/metadata/wtf.txt', ['max_file_size' => 11]) | ||
->willThrow($originalException) | ||
->shouldBeCalled(); | ||
try { | ||
$loader->load('wtf.txt', 10); | ||
$this->fail('Expected a RuntimeException, but none was thrown.'); | ||
} catch (\RuntimeException $e) { | ||
$this->assertSame($originalException->getMessage(), $e->getMessage()); | ||
$this->assertSame($originalException->getCode(), $e->getCode()); | ||
$this->assertSame($originalException, $e->getPrevious()); | ||
} | ||
} | ||
} |