瀏覽代碼

Merge pull request #2766 from Seldaek/rfs_errors

Capture response bodies in exceptions when http requests fail
Nils Adermann 11 年之前
父節點
當前提交
edbc22f6bb

+ 11 - 0
src/Composer/Downloader/TransportException.php

@@ -18,6 +18,7 @@ namespace Composer\Downloader;
 class TransportException extends \RuntimeException
 {
     protected $headers;
+    protected $response;
 
     public function setHeaders($headers)
     {
@@ -28,4 +29,14 @@ class TransportException extends \RuntimeException
     {
         return $this->headers;
     }
+
+    public function setResponse($response)
+    {
+        $this->response = $response;
+    }
+
+    public function getResponse()
+    {
+        return $this->response;
+    }
 }

+ 16 - 8
src/Composer/Util/RemoteFilesystem.php

@@ -124,6 +124,9 @@ class RemoteFilesystem
             $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['github-token'];
             unset($options['github-token']);
         }
+        if (isset($options['http'])) {
+            $options['http']['ignore_errors'] = true;
+        }
         $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
 
         if ($this->progress) {
@@ -145,6 +148,10 @@ class RemoteFilesystem
             if ($e instanceof TransportException && !empty($http_response_header[0])) {
                 $e->setHeaders($http_response_header);
             }
+            if ($e instanceof TransportException && $result !== false) {
+                $e->setResponse($result);
+            }
+            $result = false;
         }
         if ($errorMessage && !ini_get('allow_url_fopen')) {
             $errorMessage = 'allow_url_fopen must be enabled in php.ini ('.$errorMessage.')';
@@ -154,10 +161,16 @@ class RemoteFilesystem
             throw $e;
         }
 
-        // fix for 5.4.0 https://bugs.php.net/bug.php?id=61336
+        // fail 4xx and 5xx responses and capture the response
         if (!empty($http_response_header[0]) && preg_match('{^HTTP/\S+ ([45]\d\d)}i', $http_response_header[0], $match)) {
-            $result = false;
             $errorCode = $match[1];
+            if (!$this->retry) {
+                $e = new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.$http_response_header[0].')', $errorCode);
+                $e->setHeaders($http_response_header);
+                $e->setResponse($result);
+                throw $e;
+            }
+            $result = false;
         }
 
         // decode gzip
@@ -250,12 +263,7 @@ class RemoteFilesystem
                     $this->promptAuthAndRetry();
                     break;
                 }
-
-                if ($notificationCode === STREAM_NOTIFY_AUTH_REQUIRED) {
-                    break;
-                }
-
-                throw new TransportException('The "'.$this->fileUrl.'" file could not be downloaded ('.trim($message).')', $messageCode);
+                break;
 
             case STREAM_NOTIFY_AUTH_RESULT:
                 if (403 === $messageCode) {

+ 2 - 9
tests/Composer/Test/Util/RemoteFilesystemTest.php

@@ -130,18 +130,11 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
         $this->assertAttributeEquals(50, 'lastProgress', $fs);
     }
 
-    public function testCallbackGetNotifyFailure404()
+    public function testCallbackGetPassesThrough404()
     {
         $fs = new RemoteFilesystem($this->getMock('Composer\IO\IOInterface'));
 
-        try {
-            $this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0);
-            $this->fail();
-        } catch (\Exception $e) {
-            $this->assertInstanceOf('Composer\Downloader\TransportException', $e);
-            $this->assertEquals(404, $e->getCode());
-            $this->assertContains('HTTP/1.1 404 Not Found', $e->getMessage());
-        }
+        $this->assertNull($this->callCallbackGet($fs, STREAM_NOTIFY_FAILURE, 0, 'HTTP/1.1 404 Not Found', 404, 0, 0));
     }
 
     public function testCaptureAuthenticationParamsFromUrl()