[php-src] Issue #9994: stream_copy_to_stream() returns on socket timeout
| From: | negram | Date: | Wed, 23 Nov 2022 17:44:38 +0000 |
| Subject: | [php-src] Issue #9994: stream_copy_to_stream() returns on socket timeout | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-242891@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/9994
Author: negram
### Description
The code is a little big huge, so let me explain first.
If the source socket was created as blocking,
stream_copy_to_stream returns as soon as
default_socket_timeout is reached, before the actual EOF happened.
That's probably the expected behaviour but we couldn't find this in the documentation,
maybe that should be improved.
The behaviour changed recently if the source socket was created from
stream_socket_accept on a non-blocking socket on Linux (it looks related to #8472,
#9252).
The following code:
```php
<?php
const SEND_BYTES = 10 * 1024;
if (pcntl_fork() == 0) {
doSender();
}
doServer();
pcntl_wait($status); // wait for the sender process
function doSender() {
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 9638);
$string = str_repeat("0", SEND_BYTES);
$written = 0;
for ($i = 0; $i < 2; ++ $i) {
$bytes = socket_write($socket, $string . "\n");
assert($bytes !== false, "server died");
$written += $bytes;
sleep((int)ini_get('default_socket_timeout') * 2);
}
printf("written: %d\n", $written);
exit;
}
function doServer() {
$server = new Server;
$server->doServer();
}
class Server
{
private $socket_server;
function doServer()
{
$this->socket_server = $this->startSocketServer();
while (true) {
//stream_socket_accept emits a warning if interrupted by a signal
if (@$client = stream_socket_accept($this->socket_server, 0)) {
$this->handleClient($client);
break;
}
usleep(10000); //0.01 sec
}
fclose($this->socket_server);
}
function startSocketServer()
{
$socket_server = stream_socket_server("tcp://0.0.0.0:9638", $errno, $errstr);
assert(!empty($socket_server), "Could not start socket server [$errno]: $errstr");
if (!stream_set_blocking($socket_server, false)) {
assert(false, "Could not set socket server to non-blocking mode");
}
return $socket_server;
}
protected function handleClient($client)
{
$handle = fopen("/dev/null", 'w');
assert(!empty($handle), "Could not open client output stream for write");
$bytes_copied = stream_copy_to_stream($client, $handle);
printf("read %d bytes\n", $bytes_copied);
fclose($handle);
fclose($client);
}
}
```
Resulted in this output:
```
$ php -ddefault_socket_timeout=2 socket-test.php
read 10241 bytes
written: 20482
```
But I expected this output instead PHP 8.0.22 returned:
```
$ php -ddefault_socket_timeout=2 socket-test.php
written: 20482
Read 20482 bytes
```
### PHP Version
PHP 8.0.25
### Operating System
CentOS Stream 8