Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add degraded mode to try and bypass ipv6/gzip issues, refs #4142, refs
  • Loading branch information
Seldaek committed Jul 20, 2015
1 parent 4175160 commit ff84b32
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 15 deletions.
22 changes: 22 additions & 0 deletions doc/articles/troubleshooting.md
Expand Up @@ -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.
68 changes: 53 additions & 15 deletions src/Composer/Util/RemoteFilesystem.php
Expand Up @@ -37,6 +37,7 @@ class RemoteFilesystem
private $retryAuthFailure;
private $lastHeaders;
private $storeAuth;
private $degradedMode = false;

/**
* Constructor.
Expand Down Expand Up @@ -155,6 +156,10 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file
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) {
Expand Down Expand Up @@ -186,6 +191,16 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file
}
restore_error_handler();
if (isset($e) && !$this->retry) {
if (false !== strpos($e->getMessage(), 'Operation timed out')) {
$this->degradedMode = true;

This comment has been minimized.

Copy link
@stof

stof Jul 21, 2015

Contributor

what if we were already in degraded mode ?

This comment has been minimized.

Copy link
@alcohol

alcohol Jul 21, 2015

Member

Check the follow-up commit. I think he realised that too ;-).

This comment has been minimized.

Copy link
@alcohol

alcohol Jul 21, 2015

Member
$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;
}

Expand All @@ -201,36 +216,45 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file
$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) {
Expand Down Expand Up @@ -269,6 +293,16 @@ protected function get($originUrl, $fileUrl, $additionalOptions = array(), $file
$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;
}

Expand Down Expand Up @@ -404,8 +438,12 @@ protected function getOptionsForUrl($originUrl, $additionalOptions)
}

$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);
Expand Down

0 comments on commit ff84b32

Please sign in to comment.