Bug #80931 [Ver]: file_get_contents() hangs with HTTP/1.1 if server doesn't close connection

From: Date: Wed, 14 Apr 2021 03:45:48 +0000
Subject: Bug #80931 [Ver]: file_get_contents() hangs with HTTP/1.1 if server doesn't close connection
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-233415@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
 Updated by:         twosee@php.net
 Reported by:        gilperon at gmail dot com
 Summary:            file_get_contents() hangs with HTTP/1.1 if server
                     doesn't close connection
 Status:             Verified
 Type:               Bug
 Package:            Streams related
 Operating System:   any
 PHP Version:        7.4
 Assigned To:        cmb
 Block user comment: N
 Private report:     N

 New Comment:

In my opinion, this is a PHP design problem for all versions.
file_get_content("http://*") (php http stream
wrapper) always depends on the behavior of the server. It always expects recv to return 0 and uses
this to detect the end of the response.
But, if the peer does not close the connection, it will wait for data forever.
In this case, even if we set "Connection: close" when the protocol version is 1.1, the
server still treats it as a persistent connection, although the server also has implementation
problems, it did expose the problem of PHP.
So we can reproduce this problem on almost all websites:

<?php
$context = stream_context_create(['http' => ['protocol_version' => 1.1,
'header' => ['Connection: keep-alive']]]);
echo file_get_contents("http://www.baidu.com",
0, $context); // largest search engine in China
// hang...


Previous Comments:
------------------------------------------------------------------------
[2021-04-09 14:25:24] kelunik@php.net

https://gist.github.com/kelunik/c82ce751c1c203806b10ef7326f3e56a
fixes it for chunked encoding, but still fails with a content-length or if auto_decode = false.

------------------------------------------------------------------------
[2021-04-09 13:11:04] kelunik@php.net

Simple reproduce script:

php -r '$server = stream_socket_server("tcp://127.0.0.1:8080"); while ($client =
stream_socket_accept($server)) { fwrite($client, "HTTP/1.1 200 OK\r\ncontent-length:
0\r\n\r\n"); }'

php -r 'file_get_contents("http://127.0.0.1:8080/");'

------------------------------------------------------------------------
[2021-04-08 18:34:07] gilperon at gmail dot com

I think you guys could check how CURL does it, because it handles this "buggy coreios
server" well; it does not hang.

Also, could you please take a look at https://stackoverflow.com/questions/34864179/prevent-php-http-wrapper-from-waiting-for-close-of-persistent-connection
? This looks a similar problem from 5 years ago that I just found out talking with people on discord
server.

------------------------------------------------------------------------
[2021-04-08 18:20:30] rowan dot collins at gmail dot com

To confirm the below is enough to make this particular server respond in a way that PHP handles OK:


diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
index da822d9160..7fdfa9448c 100644
--- a/ext/standard/http_fopen_wrapper.c
+++ b/ext/standard/http_fopen_wrapper.c
@@ -574,7 +574,7 @@ finish:
         * HTTP/1.0 to avoid issues when the server respond with a HTTP/1.1
         * keep-alive response, which is the preferred response type. */
        if ((have_header & HTTP_HEADER_CONNECTION) == 0) {
-               smart_str_appends(&req_buf, "Connection: close\r\n");
+               smart_str_appends(&req_buf, "Connection: Close\r\n");
        }

        if (context &&



However, the correct solution is probably to make the PHP implementation forcefully close
connections when the server defaults to Keep-Alive behaviour.

------------------------------------------------------------------------
[2021-04-08 18:00:41] rowan dot collins at gmail dot com

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

------------------------------------------------------------------------


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


Thread (35 messages)

« previous php.bugs (#233415) next »