ZF-4238: Infinite loop when connection is closed

Description

When I use Zend_Http_Client it occasionally seems to get stuck in an infinite loop. When I perform an strace on the offending process I see the following system calls being called in an infinite loop:

poll([{fd=6, events=POLLIN|POLLERR|POLLHUP, revents=POLLIN|POLLHUP}], 1, 300000) = 1
recvfrom(6, "", 8192, MSG_DONTWAIT, NULL, NULL) = 0
poll([{fd=6, events=POLLIN|POLLERR|POLLHUP, revents=POLLIN|POLLHUP}], 1, 300000) = 1
recvfrom(6, "", 8192, MSG_DONTWAIT, NULL, NULL) = 0
poll([{fd=6, events=POLLIN|POLLERR|POLLHUP, revents=POLLIN|POLLHUP}], 1, 300000) = 1
recvfrom(6, "", 8192, MSG_DONTWAIT, NULL, NULL) = 0
poll([{fd=6, events=POLLIN|POLLERR|POLLHUP, revents=POLLIN|POLLHUP}], 1, 300000) = 1
recvfrom(6, "", 8192, MSG_DONTWAIT, NULL, NULL) = 0
...

I noticed that Zend_Http_Client_Adapter_Socket::read() does not check whether the socket has been closed while it reads a response. My hypothesis is that when a connection is closed while read() is in the process of reading the response, it gets stuck in an infinite loop as it is never able to finish reading $left_to_read bytes.

Comments

While I was reviewing Zend_Http_Client_Adapter_Socket::read(), I also noticed that it has lines like {{while ($line = @fgets($this->socket))}} and {{while ($buff = @fread($this->socket, 8192))}}. Due to the loose comparisons performed by these statements, I'd imagine that when fgets() and fread() return a string such as "0" the read is aborted prematurely. In addition to adding feof() checks, these conditions should probably be rewritten as {{(... === false)}} and/or {{(... === '')}}.

I added additional failure checks in changeset 11864 and an additional test to make sure lines with '0' or '' are not wrongly evaluated as FALSE. In order to fully confirm that this issue is fixed I need either a clear reproduction, or you can test with the latest revision and see if this still happens.

That seems ZF-4560 is caused by this problem.

I added some additional feof() checks to fix ZF-4560.

It seems that fread() / fgets() do not always return false when at EOF, and you need to manually check for EOF or it will just keep reading. This is the case when there is no content-length or "transfer-encoding: chunked" response, and you just need to read until the end of file, which is the case with some responses from the AudioScrobbler servers and maybe from other servers as well.

It seems it's resolved with r11886 commit.

The Unit Test file: /trunk/tests/Zend/Http/Client/_files/ZF4238-zerolineresponse.txt is missing, unit tests fail:

testZF4238FalseLinesInResponse() testHttpAuthBasic()

Changing issues in preparation for the 1.7.0 release.

If the connection times out while Zend_Http_Client_Adapter_Socket is reading from the socket, it will still go into an infinite loop. I am attaching a patch that appears to fix this.

I try to solve this issue at SVN r13013.

I will happy if I receive any reviews.