From 7912253df619a924e7091a022d20a77ad2d3939b Mon Sep 17 00:00:00 2001 From: Jordi Boggiano Date: Wed, 31 Jul 2013 20:33:20 +0200 Subject: [PATCH] Retry downloading when a corrupt zip is found, fixes #2133, fixes #2128, fixes #2125 --- src/Composer/Downloader/ArchiveDownloader.php | 81 +++++++++++-------- src/Composer/Downloader/ZipDownloader.php | 6 +- .../Test/Downloader/ZipDownloaderTest.php | 2 +- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/Composer/Downloader/ArchiveDownloader.php b/src/Composer/Downloader/ArchiveDownloader.php index 5248227e6..8de4bdcb3 100644 --- a/src/Composer/Downloader/ArchiveDownloader.php +++ b/src/Composer/Downloader/ArchiveDownloader.php @@ -28,45 +28,60 @@ abstract class ArchiveDownloader extends FileDownloader */ public function download(PackageInterface $package, $path) { - parent::download($package, $path); - - $fileName = $this->getFileName($package, $path); - if ($this->io->isVerbose()) { - $this->io->write(' Extracting archive'); - } - $temporaryDir = $this->config->get('vendor-dir').'/composer/'.substr(md5(uniqid('', true)), 0, 8); - try { - $this->filesystem->ensureDirectoryExists($temporaryDir); + $retries = 3; + while ($retries--) { + parent::download($package, $path); + + $fileName = $this->getFileName($package, $path); + if ($this->io->isVerbose()) { + $this->io->write(' Extracting archive'); + } + try { - $this->extract($fileName, $temporaryDir); + $this->filesystem->ensureDirectoryExists($temporaryDir); + try { + $this->extract($fileName, $temporaryDir); + } catch (\Exception $e) { + // remove cache if the file was corrupted + parent::clearCache($package, $path); + throw $e; + } + + unlink($fileName); + + // get file list + $contentDir = $this->listFiles($temporaryDir); + + // only one dir in the archive, extract its contents out of it + if (1 === count($contentDir) && !is_file($contentDir[0])) { + $contentDir = $this->listFiles($contentDir[0]); + } + + // move files back out of the temp dir + foreach ($contentDir as $file) { + $this->filesystem->rename($file, $path . '/' . basename($file)); + } + + $this->filesystem->removeDirectory($temporaryDir); } catch (\Exception $e) { - // remove cache if the file was corrupted - parent::clearCache($package, $path); + // clean up + $this->filesystem->removeDirectory($path); + $this->filesystem->removeDirectory($temporaryDir); + + // retry downloading if we have an invalid zip file + if ($retries && $e instanceof \UnexpectedValueException && $e->getCode() === \ZipArchive::ER_NOZIP) { + if ($this->io->isVerbose()) { + $this->io->write(' Invalid zip file, retrying...'); + } + usleep(500000); + continue; + } + throw $e; } - unlink($fileName); - - // get file list - $contentDir = $this->listFiles($temporaryDir); - - // only one dir in the archive, extract its contents out of it - if (1 === count($contentDir) && !is_file($contentDir[0])) { - $contentDir = $this->listFiles($contentDir[0]); - } - - // move files back out of the temp dir - foreach ($contentDir as $file) { - $this->filesystem->rename($file, $path . '/' . basename($file)); - } - - $this->filesystem->removeDirectory($temporaryDir); - } catch (\Exception $e) { - // clean up - $this->filesystem->removeDirectory($path); - $this->filesystem->removeDirectory($temporaryDir); - throw $e; + break; } $this->io->write(''); diff --git a/src/Composer/Downloader/ZipDownloader.php b/src/Composer/Downloader/ZipDownloader.php index 2f204eb1b..cef985348 100644 --- a/src/Composer/Downloader/ZipDownloader.php +++ b/src/Composer/Downloader/ZipDownloader.php @@ -68,11 +68,7 @@ class ZipDownloader extends ArchiveDownloader $zipArchive = new ZipArchive(); if (true !== ($retval = $zipArchive->open($file))) { - if (ZipArchive::ER_NOZIP === $retval) { - @copy($file, $copy = sys_get_temp_dir().'/composer-zip-debug.zip'); - throw new \UnexpectedValueException($this->getErrorMessage($retval, $file).' filesize: '.filesize($file).', file copied to '.$copy.' for debugging, please report this and email us the file if possible'); - } - throw new \UnexpectedValueException($this->getErrorMessage($retval, $file)); + throw new \UnexpectedValueException($this->getErrorMessage($retval, $file), $retval); } if (true !== $zipArchive->extractTo($path)) { diff --git a/tests/Composer/Test/Downloader/ZipDownloaderTest.php b/tests/Composer/Test/Downloader/ZipDownloaderTest.php index 84e4a12ac..bbe77d7ee 100644 --- a/tests/Composer/Test/Downloader/ZipDownloaderTest.php +++ b/tests/Composer/Test/Downloader/ZipDownloaderTest.php @@ -33,7 +33,7 @@ class ZipDownloaderTest extends \PHPUnit_Framework_TestCase $io = $this->getMock('Composer\IO\IOInterface'); $config = $this->getMock('Composer\Config'); - $config->expects($this->once()) + $config->expects($this->any()) ->method('get') ->with('vendor-dir') ->will($this->returnValue(sys_get_temp_dir().'/composer-zip-test-vendor'));