mirror of
https://github.com/Seldaek/monolog.git
synced 2025-08-05 20:57:36 +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:
@@ -25,6 +25,8 @@ class SocketHandler extends AbstractProcessingHandler
|
|||||||
private $connectionTimeout;
|
private $connectionTimeout;
|
||||||
private $resource;
|
private $resource;
|
||||||
private $timeout = 0;
|
private $timeout = 0;
|
||||||
|
private $writingTimeout = 0;
|
||||||
|
private $lastSentBytes = null;
|
||||||
private $persistent = false;
|
private $persistent = false;
|
||||||
private $errno;
|
private $errno;
|
||||||
private $errstr;
|
private $errstr;
|
||||||
@@ -113,6 +115,18 @@ class SocketHandler extends AbstractProcessingHandler
|
|||||||
$this->timeout = (float) $seconds;
|
$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
|
* Get current connection string
|
||||||
*
|
*
|
||||||
@@ -153,6 +167,16 @@ class SocketHandler extends AbstractProcessingHandler
|
|||||||
return $this->timeout;
|
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.
|
* Check to see if the socket is currently available.
|
||||||
*
|
*
|
||||||
@@ -262,6 +286,7 @@ class SocketHandler extends AbstractProcessingHandler
|
|||||||
{
|
{
|
||||||
$length = strlen($data);
|
$length = strlen($data);
|
||||||
$sent = 0;
|
$sent = 0;
|
||||||
|
$this->lastSentBytes = $sent;
|
||||||
while ($this->isConnected() && $sent < $length) {
|
while ($this->isConnected() && $sent < $length) {
|
||||||
if (0 == $sent) {
|
if (0 == $sent) {
|
||||||
$chunk = $this->fwrite($data);
|
$chunk = $this->fwrite($data);
|
||||||
@@ -276,9 +301,35 @@ class SocketHandler extends AbstractProcessingHandler
|
|||||||
if ($socketInfo['timed_out']) {
|
if ($socketInfo['timed_out']) {
|
||||||
throw new \RuntimeException("Write 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) {
|
if (!$this->isConnected() && $sent < $length) {
|
||||||
throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent $sent of $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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -235,6 +235,26 @@ class SocketHandlerTest extends TestCase
|
|||||||
$this->assertTrue(is_resource($this->res));
|
$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)
|
private function createHandler($connectionString)
|
||||||
{
|
{
|
||||||
$this->handler = new SocketHandler($connectionString);
|
$this->handler = new SocketHandler($connectionString);
|
||||||
|
Reference in New Issue
Block a user