composer / composer

Dependency Manager for PHP
https://getcomposer.org/
MIT License
28.37k stars 4.49k forks source link

zlib_decode(): data error #4121

Closed Ugoku closed 8 years ago

Ugoku commented 9 years ago

I realise this issue has been raised before (#3006, #3270) but no replies were given there and the results are many months old. Since a while, I'm getting the error mentioned in the issue title, when doing a composer require xxx/yyy or composer install Stack trace from the "very verbose" mode is as follows:

  [ErrorException]
  zlib_decode(): data error

Exception trace:
 () at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Util/RemoteFilesystem.php:218
 Composer\Util\ErrorHandler::handle() at n/a:n/a
 zlib_decode() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Util/RemoteFilesystem.php:218
 Composer\Util\RemoteFilesystem->get() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Util/Remote
Filesystem.php:83
 Composer\Util\RemoteFilesystem->getContents() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Rep
ository/ComposerRepository.php:587
 Composer\Repository\ComposerRepository->fetchFile() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Compos
er/Repository/ComposerRepository.php:296
 Composer\Repository\ComposerRepository->whatProvides() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Com
poser/DependencyResolver/Pool.php:191
 Composer\DependencyResolver\Pool->computeWhatProvides() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Co
mposer/DependencyResolver/Pool.php:180
 Composer\DependencyResolver\Pool->whatProvides() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/
DependencyResolver/RuleSetGenerator.php:221
 Composer\DependencyResolver\RuleSetGenerator->addRulesForPackage() at phar://C:/ProgramData/ComposerSetup/bin/composer.
phar/src/Composer/DependencyResolver/RuleSetGenerator.php:293
 Composer\DependencyResolver\RuleSetGenerator->addRulesForJobs() at phar://C:/ProgramData/ComposerSetup/bin/composer.pha
r/src/Composer/DependencyResolver/RuleSetGenerator.php:333
 Composer\DependencyResolver\RuleSetGenerator->getRulesFor() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/sr
c/Composer/DependencyResolver/Solver.php:172
 Composer\DependencyResolver\Solver->solve() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Insta
ller.php:505
 Composer\Installer->doInstall() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Installer.php:230

 Composer\Installer->run() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Command/RequireCommand.
php:158
 Composer\Command\RequireCommand->execute() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/vendor/symfony/cons
ole/Symfony/Component/Console/Command/Command.php:257
 Symfony\Component\Console\Command\Command->run() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/vendor/symfon
y/console/Symfony/Component/Console/Application.php:874
 Symfony\Component\Console\Application->doRunCommand() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/vendor/s
ymfony/console/Symfony/Component/Console/Application.php:195
 Symfony\Component\Console\Application->doRun() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Co
nsole/Application.php:146
 Composer\Console\Application->doRun() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/vendor/symfony/console/S
ymfony/Component/Console/Application.php:126
 Symfony\Component\Console\Application->run() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/src/Composer/Cons
ole/Application.php:82
 Composer\Console\Application->run() at phar://C:/ProgramData/ComposerSetup/bin/composer.phar/bin/composer:43
 require() at C:\ProgramData\ComposerSetup\bin\composer.phar:25

Any thoughts on why this happens and/or how to fix it? I'm only getting this on one of my systems, and it worked fine in the past, I'm not sure what changed since. I have reinstalled Composer just now, so I'm using the latest version. Specs of my system: Windows 8.1 x64, PHP 5.6.5 x64

Edit: when I require a new dependency on an other machine, then run composer install on my own system, that works just fine.

Edit 2: I just noticed Composer is connecting to http://packagist.org, despite having openssl enabled.

Ugoku commented 9 years ago

Sure :)

alcohol commented 9 years ago

Weird, your 'unread_bytes' count (before the stream_get_contents() call) is completely different. Can you repeat the process several times and see if it is different for each iteration?

alcohol commented 9 years ago

Oh it is different for me too (7917), but consistently the same on multiple iterations.

Ugoku commented 9 years ago

No problem, here is Iteration / Before / After / Content size (fwiw) 1 / 1185 / 0 / 31837 2 / 1185 / 0 / 18705 3 / 1185 / 0 / 21625 4 / 1185 / 0 / 27457 5 / 1185 / 0 / 14325 So yeah, unread_bytes is consistent, but strlen($content) is not.

frederikbosch commented 9 years ago

@Ugoku @alcohol Good to know. This might not even be an actual PHP problem. We should find out whether all data actually gets to your computer. @Ugoku Do you have some experience with a tcpdump variant on Windows? Maybe WinDump, or similar. In the past there was something like Ethereal, I believe it is now called Wireshark.

If the data does get to your computer, there is a problem getting the correct data to PHP. If not, there is a problem with request to the packagist server (by PHP) or with the response of the packagist server.

Could you run the script again and enable the tcp inspection?

alcohol commented 9 years ago

Weird, the strlen($content) value is also consistent for me (41528).

Actually, I've just ran the iteration a lot more times, and I did notice that unread_bytes sometimes fluctuates to a different value for me. But the end result is always the same. So even when unread_bytes fluctuated, the strlen($content) value remained identical.

/tmp $ tail -1 test.php
printf("%d:%d\n", $headersBeforeContents['unread_bytes'], strlen($content));
/tmp $ for i in {1..100}; do php test.php; done
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
2461:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
7917:41528
5197:41528
2461:41528
5197:41528
7917:41528
7917:41528
7917:41528
..snipped..
7917:41528
7917:41528
1093:41528
1093:41528
7917:41528
7917:41528
7917:41528
7917:41528
2461:41528
7917:41528
7917:41528
5197:41528
7917:41528
7917:41528
7917:41528
Ugoku commented 9 years ago

@frederikbosch I've used Wireshark before though it's been a while. I can see traffic to and from 87.98.253.214 (packagist.org) but what should I be looking for exactly?

frederikbosch commented 9 years ago

@alcohol I confirm your findings. I think it is definitely not the PHP script, so it is not a composer bug. It is either network related, PHP bug or webserver problem.

frederikbosch commented 9 years ago

@Ugoku You should inspect the specific packages and see if you see a complete json is coming in.

Ugoku commented 9 years ago

Right, thanks. Yes, the line with 200 OK gives an "Uncompressed entity body" of 698634 bytes which is identical in both size and content to when I download it in the browser.

Glad to see it's not a Composer bug. Thanks so much for all the help! Should I close the issue or should we wait until @BloomPhilippe from #4176 shares his information?

alcohol commented 9 years ago

Also interesting to try out is https://github.com/simsong/tcpflow

frederikbosch commented 9 years ago

@Ugoku We are not there yet. It is maybe not a composer bug, but maybe we can work around. Since there are multiple people affected, we might come to a fix or or hack.

But for this hack, I wonder the following. Why does his computer receive the correct data but is PHP not able to read it correctly? What could be wrong? Is it a bug in PHP? Are there any other possible explanations?

frederikbosch commented 9 years ago

@Ugoku Just to be sure: you see the correct JSON file in Wireshark, right?

alcohol commented 9 years ago

For what it is worth, I did notice that for me, the connection handler arbitrarily picks ipv4 or ipv6 (since my laptop is also setup with ipv6 connectivity). This doesn't seem to affect the final result (for me), but figured it might be worth mentioning.

Ugoku commented 9 years ago

@frederikbosch Yes, Wireshark gives the correct JSON file and the exact same contents as the JSON file downloaded through a browser.

For what it's worth, I just installed PHP 5.6.10 (TS x64) and ran composer update again, didn't help.

frederikbosch commented 9 years ago

@Ugoku @alcohol I wonder how many people are affected by this. If all Windows users were affected, we should have had much more problems. Alright, there have been multiple reports of Windows users affected, but the group is minor I believe. But it must be related to Windows, right? And since we can rule out network, there must be something really specific with his PHP version in combination with Windows that is causing this problem.

And because he receives a correct json file, I cannot figure how can overcome this problem by creating a hack. Changing the request to packagist will not affect the problem.

If nobody can come up with another possibility, I think we should report a bug at php.net.

frederikbosch commented 9 years ago

@Seldaek You are the pro here. What do you think?

Seldaek commented 9 years ago

Can we have detailed environment info for everyone thats affected? OS with version. PHP version and TS or NTS for PHP on windows. Then maybe we can begin to see commonalities.. But it is definitely strange if you see the complete json in wireshark.

Can you also just to confirm all make sure this only occurs when using gzip transfer encoding and also if possible give info on whether ipv4 or ipv6 usage matters to your problem.

An interesting thing to do as well would be to host the file somewhere else to see if the behavior of the web server might have an impact or not..

Ugoku commented 9 years ago

My specs:

I reported this on June 8, it began happening I think about a week before that. It definitely worked on the same machine a while before that.

I'll try your last suggestion, will report on that. I will also post my home specs when I get the chance.

frederikbosch commented 9 years ago

@Ugoku Did you update anything on your computer related to PHP between June 1 and June 8?

Seldaek commented 9 years ago

@Ugoku also did you try with composer alpha10 for example? Just to rule out any recent changes. Sorry if you did already I can't read the whole backlog right now :)

frederikbosch commented 9 years ago

@Ugoku You might also try to modify some headers in the script. Maybe you will get different results.

Ugoku commented 9 years ago

@frederikbosch I don't remember changing anything PHP related... definitely not PHP itself, that's been running since February.

@Seldaek no need to apologise :) no, I've been running the latest master for as long as I remember. I'll try with alpha10.

I've uploaded the original JSON file to https://dashboard.shore2ship.com/json.json (gzip is enabled) and when I run the script on that URL, it works fine (output here). I don't have access to an nginx server though, this is on an IIS 8 server (Azure). I can try on an Apache server too, if that helps.

frederikbosch commented 9 years ago

@Ugoku in order to replicate consistently, you should use 'http' not 'https'.

Seldaek commented 9 years ago

On your server can you keep the full name as well just in case the length, $ sign trips, or something like that causes the issue?

alcohol commented 9 years ago

You can find the file here behind an nginx server:

http://dump.robbast.nl/finder$a4bc31ef5d391952af0e7202b072f6d587ae89623277f89aefad0f9d49a9bc2c.json

$ nginx -V
nginx version: nginx/1.8.0
built with OpenSSL 1.0.2a 19 Mar 2015 (running with OpenSSL 1.0.2c 12 Jun 2015)
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --sbin-path=/usr/bin/nginx --pid-path=/run/nginx.pid --lock-path=/run/lock/nginx.lock --user=http --group=http --http-log-path=/var/log/nginx/access.log --error-log-path=stderr --http-client-body-temp-path=/var/lib/nginx/client-body --http-proxy-temp-path=/var/lib/nginx/proxy --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-imap --with-imap_ssl_module --with-ipv6 --with-pcre-jit --with-file-aio --with-http_dav_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_realip_module --with-http_spdy_module --with-http_ssl_module --with-http_stub_status_module --with-http_addition_module --with-http_degradation_module --with-http_flv_module --with-http_mp4_module --with-http_secure_link_module --with-http_sub_module
alcohol commented 9 years ago

Odd, the gist script returns a much smaller strlen($content) on the one from my server. I simply grabbed it from Packagist using wget.

frederikbosch commented 9 years ago

@alcohol Then the contents cannot be equal.

alcohol commented 9 years ago

They are though. I just checked..

frederikbosch commented 9 years ago

@alcohol I mean the contents of $content must be different, because it tells you a different strlen.

alcohol commented 9 years ago

It's a significant difference though; 41528 from packagist.org and 27875 from my domain. Isn't $content supposed to reflect the json? Or what?

alcohol commented 9 years ago

Oh maybe I use different gzip configuration than packagist does;

    gzip on;
    gzip_vary on;
    gzip_min_length 10240;
    gzip_comp_level 6;
    gzip_http_version 1.1;
    gzip_proxied expired no-cache no-store private auth;
    gzip_types text/plain text/css text/xml text/javascript
        application/json application/x-javascript application/javascript
        application/xml application/xml+rss application/xhtml+xml application/rss+xml;

My server block;

    server {
        server_name dump.robbast.nl;
        access_log /var/log/nginx/robbast.nl.log custom;
        error_log /var/log/nginx/robbast.nl.err;
        root /srv/http/robbast.nl/dump;
        autoindex on;
        location = /robots.txt  { access_log off; log_not_found off; }
        location = /favicon.ico { access_log off; log_not_found off; }
    }
Seldaek commented 9 years ago

BTW for everyone, I copied that file somewhere to avoid it disappearing in the future, just so we can all work with the same file for tests: it is now available at http://packagist.org/_p/symfony/finder%24a4bc31ef5d391952af0e7202b072f6d587ae89623277f89aefad0f9d49a9bc2c.json

@alcohol maybe your server always sends gzipped? In a browser both responses look the same to me (packagist vs your server). Here is the gzip config I have:

    gzip on;
    gzip_comp_level 3;
    gzip_disable msie6;
    gzip_http_version 1.0;
    gzip_min_length 1400;
    gzip_proxied any;
    gzip_types text/plain application/json text/css application/x-javascript application/javascript text/xml applicatio$
    gzip_vary on;

The comp_level probably is the difference, and that's interesting that the result is so different, maybe I should bump that a bit.

frederikbosch commented 9 years ago

@alcohol Could you compare the value $content after grabbing the json from your own server to that of packagist? I mean the Content-Length could be different, but the resulting value in $content must be the same! It should be the full json contents.

alcohol commented 9 years ago

Not really comparable. $content is the gzip output. @Seldaek just changed his compression level on Packagist to the same as mine and now I get the exact same content length for both my file and his file.

frederikbosch commented 9 years ago

@alcohol Of course, my bad, but the central question still is how can @Ugoku end up with a corrupted file while the full file went over his network?

alcohol commented 9 years ago

The problem is though that @Ugoku is not even receiving the full gzip output. His $content length is different every time he makes a request. If he could submit several requests to my server, I can check the logs to see if it sends the full file (I have bytes send in my logs). Then we can determine if the client aborts the connection prematurely (if my server cannot send the full file), or if something else cuts off data at his end (if my server does send the full file).

frederikbosch commented 9 years ago

@alcohol He already watched with Wireshark. He inspected his tcp packages. Wireshark reports the full file going over his network. But the response is not interpreted correctly by PHP.

alcohol commented 9 years ago

Well then it must definitely be a PHP issue, or a related lib it relies on.

frederikbosch commented 9 years ago

@alcohol But since it worked for him in the past, there must be a specific setting that triggers this bug. If we want to report a PHP issue, we at least should find out what that is.

alcohol commented 9 years ago

It still seems very weird though that it just "suddenly" started to happen. Something must have changed in the environment that causes things to now be broken.

frederikbosch commented 9 years ago

@alcohol Very true that, and it must be in the very tiny script that we extracted from composer as he claims that there were no changes to his system.

sbuzonas commented 9 years ago

The content type sort was there because composer moves it to the end due to a bug in php. I suppose it doesn't apply here.

It would be interesting to confirm that this can be reproduced on ipv4 and ipv6. I'm still not convinced that this isn't a bad gateway somewhere between your connection to your isp and packagist.

What are the ISPs of the individuals affected? I know comcast just enabled ipv6 in my area at the beginning of the month.

frederikbosch commented 9 years ago

@slbmeh The ISP is not important in my opinion. @Ugoku reports that Wireshark shows that he receives the file correctly. So there is no ISP problem. The packagist servers sends correctly, the ISP delivers the file accordingly but PHP truncates the file somehow.

Nonetheless, it is striking that it worked before. So something is triggering this behaviour, but we are not able to find it (yet).

Ugoku commented 9 years ago

The file @alcohol serves from robbast.nl works. The static file from @Seldaek on packagist.org does not, but I can see the full file in Wireshark. So yes, it's served correctly by the server and received correctly by my computer, but between there and PHP processing the file, something goes wrong.

As I said, I changed from PHP 5.6.5 to 5.6.10 yesterday but that didn't change anything. Disabling antivirus does not help.

-edit- Whoah... changed from ethernet to wifi (same network!) and now everything works again, composer update too. This is very consistent, if I turn off wifi and use the ethernet connection it fails again. This is definitely weird, but I can live with this now that I know how to fix it.

If you think this is something that Composer could work around, or should be submitted as a PHP issue, I am more than happy to try anything that might help. Otherwise, I could close the issue.

pierrejoye commented 9 years ago

Replying as I have been asked about it on twitter.

If it works using one connection but fails with another then it is definitely a OS configuration problem. Maybe one uses a proxy, firewall or something related.

I do not see how it could be a php bug, on windows or other.

frederikbosch commented 9 years ago

@pierrejoye First of all, thanks a lot for providing help, much appreciated. Am I correct you think it is a server problem on pacakgist.org? I think it is not a client OS configuration issue. There have been filed multiple issues, but all have been closed as duplicate of this one, so it already affects multiple people. I am not ruling that out completely, just think is not likely.

Let us assume you are right, and it is a server issue. What nginx configuration can cause the behaviour as reported by @Ugoku? He receives the full file (Wireshark proves that) but it is truncated in PHP and only by PHP on Windows?

frederikbosch commented 9 years ago

Maybe @BloomPhilippe can confirm that the behaviour. @BloomPhilippe If you use the script and download the file from both servers (in top of the file). What do you see?

frederikbosch commented 9 years ago

@BloomPhilippe Use this file to test both at the same time.

alcohol commented 9 years ago

I think @Seldaek already lowered his compression rate due to the hit on cpu it incurred, so you will receive different gzip content. On Jun 25, 2015 10:33 AM, "Frederik Bosch" notifications@github.com wrote:

@BloomPhilippe https://github.com/BloomPhilippe Use this file https://gist.github.com/frederikbosch/2b75db3aa837c2ede873 to test both at the same time.

— Reply to this email directly or view it on GitHub https://github.com/composer/composer/issues/4121#issuecomment-115162090.