Bug #80931 [Com]: file_get_contents() hangs on PHP 8

From: Date: Thu, 08 Apr 2021 18:00:41 +0000
Subject: Bug #80931 [Com]: file_get_contents() hangs on PHP 8
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-233325@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=80931&edit=1 ID: 80931 Comment by: rowan dot collins at gmail dot com Reported by: gilperon at gmail dot com Summary: file_get_contents() hangs on PHP 8 Status: Verified Type: Bug Package: Streams related Operating System: any PHP Version: 7.4 Block user comment: N Private report: N New Comment: OK, I think I have figured out what's happening here: * If you send the server an HTTP/1.1 request with the header "Connection: Close", it acknowledges with "connection: close"; if you send "Connection: close" (as PHP does), it does not acknowledge it, and presumably defaults to Connection: Keep-Alive * RFC 7230 clearly states that "Connection options are case-insensitive." so this is definitely a bug in the server. https://tools.ietf.org/html/rfc7230#section-6.1 * A local and up to date IIS server does not exhibit the bug. * The server at ws.correios.com.br is probably running an old version of IIS. The headers include "x-aspnet-version: 4.0.30319" which was released sometime between 2010 and 2012 Previous Comments: ------------------------------------------------------------------------ [2021-04-08 13:43:25] rowan dot collins at gmail dot com Note that the PHP client code always sends a "Connection: Close" header in the request, for both HTTP/1.0 and HTTP/1.1 requests: https://heap.space/xref/php-src/ext/standard/http_fopen_wrapper.c?r=5787f91c#570 For some reason, the server appears to only be honouring that for HTTP/1.0 requests, which makes no sense, because it's an HTTP/1.1 feature. ------------------------------------------------------------------------ [2021-04-08 12:57:23] danack@php.net Okay, so looking at the packets, what is happening, from the response on is: # http 1_0 protocol 6. server sends response. 7. php acks 6. 8. server sends finack. 9. php sends finack. 10. servers acks 9. # http 1_1 protocol 6. server sends response. 7. php acks 10. 8. php sends finack. 9. server acks 8. 10. server sends finack. 11. php acks 10. That all looks correct, but the difference is that for http 1.1 the client is initiating the connection close. In http 1.0 the server is intiating the connection close. All the packets look okay, according to https://gitlab.com/wireshark/wireshark/-/wikis/TCP-4-times-close The problem seems to be that for whatever reason, after sending the last ack, PHP is sitting around doing nothing. btw it does time out after 2 * 60 seconds, which probably confirms the socket is in the appropriate TIME-WAIT status. ------------------------------------------------------------------------ [2021-04-08 12:23:56] cmb@php.net First, this is not a regression in PHP 8.0, but rather a general issue with HTTP/1.1. It seems to me the problem is that the server does *not* close the connection right away after having sent the response under HTTP/1.1, what appears to be legit behavior. After having received the full response, our HTTP stream implementation still tries to select(2) the sole readfd, but the server won't send more data, so the timeout occurs. FWIW, if I add a Connection:keep-alive header to the context options, I can reproduce the behavior of the server locally. ------------------------------------------------------------------------ [2021-04-07 20:43:12] gilperon at gmail dot com danack@php.net Respectfully, setting manually the protocol to 1.0 is more like a hack to me than anything else. Devs 99% of the time, don't have control over the API response, they cannot fix server's misconfiguration only so their code works. Devs expect file_get_contents to simply work. Curl works just fine with this URL while file_get_contents is buggy. I don't expect Curl to have lots of hacks and I am pretty sure Curl has a nice workaround about server not closing connection - and so PHP should do. No hacks, I agree with you, hacks are terrible and probably will break something in the future. ------------------------------------------------------------------------ [2021-04-07 20:11:49] danack@php.net Setting the protocol back to 1.0 with the protocol version option appears to make it work on 8. I'll need to actually inspect the packets to have a deeper look. > But I am pretty sure it will start bothering many other devs as they > update their PHP to 8.x and start seeing their code breaking. To set your expectation, if this is a bug on the remote server, then it's unlikely we would write a hack around it. People can either use a workaround in their code, stay on PHP7, or ask the person who owns that site to fix it. It's not feasible to put work arounds in php core for all buggy servers out there. <?php $context = stream_context_create(array( "http" => array( "protocol_version" => "1.0" ) )); $response = file_get_contents("http://ws.correios.com.br/calculador/CalcPrecoPrazo.aspx?nCdEmpresa=&sDsSenha=&sCepOrigem=11661690&sCepDestino=88070-480&nVlPeso=1&nCdFormato=1&nVlComprimento=25&nVlAltura=3&nVlLargura=25&sCdMaoPropria=N&nVlValorDeclarado=0&sCdAvisoRecebimento=N&nCdServico=04014&nVlDiametro=0&StrRetorno=xml", 0, $context); echo "response length is " . strlen($response) . "\n"; ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=80931 -- Edit this bug report at https://bugs.php.net/bug.php?id=80931&edit=1

« previous php.bugs (#233325) next »