php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #81487 feof() should return true after reading last chunk for HTTP requests
Submitted: 2021-09-29 08:36 UTC Modified: 2021-09-29 09:16 UTC
From: public+php dot net at bastelstu dot be Assigned:
Status: Duplicate Package: Streams related
PHP Version: 8.0.11 OS: Ubuntu 20.04 + Docker
Private report: No CVE-ID: None
 [2021-09-29 08:36 UTC] public+php dot net at bastelstu dot be
Description:
------------
When performing HTTP requests using `fopen()`, `feof()` will not return `true` until the server closes the connection, even if the full body has already been received.

According to RFC 7230#6.6 (https://httpwg.org/specs/rfc7230.html#persistent.tear-down):

> A client that sends a "close" connection option MUST NOT send further requests
> on that connection (after the one containing "close") and MUST close the
> connection after reading the final response message corresponding to this request.

PHP should detect that the full response message has been read and close the socket.

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

In my real world use case, I am using Guzzles StreamHandler (https://github.com/guzzle/guzzle/blob/master/src/Handler/StreamHandler.php) to perform HTTP requests, reading Open Graph metadata.

In one case I was apparently hitting a NetScaler load balancer (https://www.nextthing.org/archives/2005/08/07/fun-with-http-headers#comment-24970). These appear to ignore any `Connection: close` headers within the request and keep the connection open (for minutes). This is in violation of section RFC7230#6.6:

> A server that receives a "close" connection option MUST initiate a close of
> the connection (see below) after it sends the final response to the request
> that contained "close". The server SHOULD send a "close" connection option
> in its final response on that connection. The server MUST NOT process any
> further requests received on that connection.

*Nonetheless* PHP should also honor its side of RFC7230 and also actually close the connection after receiving the response.

cURL on the command line and HTTPie both will close the connection on their own.

Test script:
---------------
<?php

// Reproducer server in C:
// https://gist.github.com/TimWolla/0a94abd5c3a9e4c75f317910ac56fa80

$fd = fopen("http://172.17.0.1:8080", "r");
while (!feof($fd)) {
	var_dump(fread($fd, 2));
}


Expected result:
----------------
I expected the `while` loop to terminate after reading the `9` within the response, like cURL does:

$ curl -v 'localhost:8080'
*   Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 Ok
< Connection: keep-alive
< Transfer-Encoding: chunked
< 
* Connection #0 to host localhost left intact
0123456789

Actual result:
--------------
The loop / script did not terminate until I terminated the HTTP server, thus implicitly closing the socket:

$ php test.php
string(2) "01"
string(2) "23"
string(2) "45"
string(2) "67"
string(2) "89"
*hangs at this point*

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2021-09-29 09:16 UTC] requinix@php.net
-Status: Open +Status: Duplicate -Package: HTTP related +Package: Streams related
 [2021-09-29 09:16 UTC] requinix@php.net
Same problem as bug #80931
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 14 20:01:26 2024 UTC