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

From: Date: Thu, 08 Apr 2021 18:34:07 +0000
Subject: Bug #80931 [Ver]: file_get_contents() hangs on PHP 8
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-233327@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
 User updated by:    gilperon 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:

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.


Previous Comments:
------------------------------------------------------------------------
[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

------------------------------------------------------------------------
[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.

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


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 (#233327) next »