1
0
mirror of https://github.com/Seldaek/monolog.git synced 2025-08-01 10:50:21 +02:00

Avoid infinite loops when no data is written on a socket for a time greater than writingTimeout settings

This commit is contained in:
Danilo Silva
2015-10-19 12:02:32 +02:00
parent fa74e171dc
commit 5c129a7f7f
2 changed files with 71 additions and 0 deletions

View File

@@ -25,6 +25,8 @@ class SocketHandler extends AbstractProcessingHandler
private $connectionTimeout;
private $resource;
private $timeout = 0;
private $writingTimeout = 0;
private $lastSentBytes = null;
private $persistent = false;
private $errno;
private $errstr;
@@ -113,6 +115,18 @@ class SocketHandler extends AbstractProcessingHandler
$this->timeout = (float) $seconds;
}
/**
* Set writing timeout. Only has effect during connection in the writing cycle.
*
* @param float $seconds 0 for no timeout
*
*/
public function setWritingTimeout($seconds)
{
$this->validateTimeout($seconds);
$this->writingTimeout = (float) $seconds;
}
/**
* Get current connection string
*
@@ -153,6 +167,16 @@ class SocketHandler extends AbstractProcessingHandler
return $this->timeout;
}
/**
* Get current local writing timeout
*
* @return float
*/
public function getWritingTimeout()
{
return $this->writingTimeout;
}
/**
* Check to see if the socket is currently available.
*
@@ -262,6 +286,7 @@ class SocketHandler extends AbstractProcessingHandler
{
$length = strlen($data);
$sent = 0;
$this->lastSentBytes = $sent;
while ($this->isConnected() && $sent < $length) {
if (0 == $sent) {
$chunk = $this->fwrite($data);
@@ -276,9 +301,35 @@ class SocketHandler extends AbstractProcessingHandler
if ($socketInfo['timed_out']) {
throw new \RuntimeException("Write timed-out");
}
if ($this->writingIsTimedOut($sent)) {
throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent $sent of $length)");
}
}
if (!$this->isConnected() && $sent < $length) {
throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $length)");
}
}
private function writingIsTimedOut($sent)
{
$writingTimeout = (int) floor($this->writingTimeout);
if (0 === $writingTimeout) {
return false;
}
if ($sent !== $this->lastSentBytes) {
$this->lastWritingAt = time();
$this->lastSentBytes = $sent;
} else {
usleep(100);
}
if ((time() - $this->lastWritingAt) >= $writingTimeout) {
$this->closeSocket();
return true;
}
return false;
}
}

View File

@@ -235,6 +235,26 @@ class SocketHandlerTest extends TestCase
$this->assertTrue(is_resource($this->res));
}
/**
* @expectedException \RuntimeException
*/
public function testAvoidInfiniteLoopWhenNoDataIsWrittenForAWritingTimeoutSeconds()
{
$this->setMockHandler(array('fwrite', 'streamGetMetadata'));
$this->handler->expects($this->any())
->method('fwrite')
->will($this->returnValue(0));
$this->handler->expects($this->any())
->method('streamGetMetadata')
->will($this->returnValue(array('timed_out' => false)));
$this->handler->setWritingTimeout(1);
$this->writeRecord('Hello world');
}
private function createHandler($connectionString)
{
$this->handler = new SocketHandler($connectionString);