Эх сурвалжийг харах

Add degraded mode to try and bypass ipv6/gzip issues, refs #4142, refs #4121

Jordi Boggiano 10 жил өмнө
parent
commit
ff84b32097

+ 22 - 0
doc/articles/troubleshooting.md

@@ -168,3 +168,25 @@ To enable the swap you can use for example:
 /sbin/mkswap /var/swap.1
 /sbin/swapon /var/swap.1
 ```
+
+## Degraded Mode
+
+Due to some intermittent issues on Travis and other systems, we introduced a
+degraded network mode which helps Composer finish successfully but disables
+a few optimizations. This is enabled automatically when an issue is first
+detected.
+
+If you have been pointed to this page, you want to check a few things:
+
+- If you are using ESET antivirus, go in "Advanced Settings" and disable "HTTP-scanner"
+  under "web access protection"
+- If you are using IPv6, try disabling it. If that solves your issues, get in touch
+  with your ISP or server host, the problem is not at the Packagist level but in the
+  routing rules between you and Packagist (i.e. the internet at large). The best way to get
+  these fixed is raise awareness to the network engineers that have the power to fix it.
+
+  To disable IPv6 on Linux, try using this command which appends a
+  rule prefering IPv4 over IPv6 to your config:
+
+  `sudo sh -c "echo 'precedence ::ffff:0:0/96 100' >> /etc/gai.conf"`
+- If none of the above helped, please report the error.

+ 53 - 15
src/Composer/Util/RemoteFilesystem.php

@@ -37,6 +37,7 @@ class RemoteFilesystem
     private $retryAuthFailure;
     private $lastHeaders;
     private $storeAuth;
+    private $degradedMode = false;
 
     /**
      * Constructor.
@@ -155,6 +156,10 @@ class RemoteFilesystem
         if (isset($options['http'])) {
             $options['http']['ignore_errors'] = true;
         }
+        if ($this->degradedMode && substr($fileUrl, 0, 21) === 'http://packagist.org/') {
+            // access packagist using the resolved IPv4 instead of the hostname to force IPv4 protocol
+            $fileUrl = 'http://' . gethostbyname('packagist.org') . substr($fileUrl, 20);
+        }
         $ctx = StreamContextFactory::getContext($fileUrl, $options, array('notification' => array($this, 'callbackGet')));
 
         if ($this->progress) {
@@ -186,6 +191,16 @@ class RemoteFilesystem
         }
         restore_error_handler();
         if (isset($e) && !$this->retry) {
+            if (false !== strpos($e->getMessage(), 'Operation timed out')) {
+                $this->degradedMode = true;
+                $this->io->writeError(array(
+                    '<error>'.$e->getMessage().'</error>',
+                    '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'
+                ));
+
+                return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
+            }
+
             throw $e;
         }
 
@@ -201,36 +216,45 @@ class RemoteFilesystem
             $result = false;
         }
 
+        if ($this->progress && !$this->retry) {
+            $this->io->overwriteError("    Downloading: <comment>100%</comment>");
+        }
+
         // decode gzip
         if ($result && extension_loaded('zlib') && substr($fileUrl, 0, 4) === 'http') {
             $decode = false;
             foreach ($http_response_header as $header) {
                 if (preg_match('{^content-encoding: *gzip *$}i', $header)) {
                     $decode = true;
-                    continue;
                 } elseif (preg_match('{^HTTP/}i', $header)) {
                     $decode = false;
                 }
             }
 
             if ($decode) {
-                if (PHP_VERSION_ID >= 50400) {
-                    $result = zlib_decode($result);
-                } else {
-                    // work around issue with gzuncompress & co that do not work with all gzip checksums
-                    $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
-                }
+                try {
+                    if (PHP_VERSION_ID >= 50400) {
+                        $result = zlib_decode($result);
+                    } else {
+                        // work around issue with gzuncompress & co that do not work with all gzip checksums
+                        $result = file_get_contents('compress.zlib://data:application/octet-stream;base64,'.base64_encode($result));
+                    }
 
-                if (!$result) {
-                    throw new TransportException('Failed to decode zlib stream');
+                    if (!$result) {
+                        throw new TransportException('Failed to decode zlib stream');
+                    }
+                } catch (\Exception $e) {
+                    $this->degradedMode = true;
+                    $this->io->writeError(array(
+                        '<error>Failed to decode response: '.$e->getMessage().'</error>',
+                        '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'
+                    ));
+
+                    return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
                 }
             }
         }
 
-        if ($this->progress && !$this->retry) {
-            $this->io->overwriteError("    Downloading: <comment>100%</comment>");
-        }
-
         // handle copy command if download was successful
         if (false !== $result && null !== $fileName) {
             if ('' === $result) {
@@ -269,6 +293,16 @@ class RemoteFilesystem
                 $e->setHeaders($http_response_header);
             }
 
+            if (false !== strpos($e->getMessage(), 'Operation timed out')) {
+                $this->degradedMode = true;
+                $this->io->writeError(array(
+                    '<error>'.$e->getMessage().'</error>',
+                    '<error>Retrying with degraded mode, check https://getcomposer.org/doc/articles/troubleshooting.md#degraded-mode for more info</error>'
+                ));
+
+                return $this->get($this->originUrl, $this->fileUrl, $additionalOptions, $this->fileName, $this->progress);
+            }
+
             throw $e;
         }
 
@@ -404,8 +438,12 @@ class RemoteFilesystem
         }
 
         $options = array_replace_recursive($this->options, $additionalOptions);
-        $options['http']['protocol_version'] = 1.1;
-        $headers[] = 'Connection: close';
+        if (!$this->degradedMode) {
+            // degraded mode disables HTTP/1.1 which causes issues with some bad
+            // proxies/software due to the use of chunked encoding
+            $options['http']['protocol_version'] = 1.1;
+            $headers[] = 'Connection: close';
+        }
 
         if ($this->io->hasAuthentication($originUrl)) {
             $auth = $this->io->getAuthentication($originUrl);