[Mailer] Do not ping the SMTP server before sending every message

This commit is contained in:
Michel Hunziker 2020-02-07 14:16:26 +01:00 committed by Fabien Potencier
parent cd2dec3a7f
commit 28178108d3
2 changed files with 126 additions and 1 deletions

View File

@ -12,8 +12,12 @@
namespace Symfony\Component\Mailer\Tests\Transport\Smtp;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
use Symfony\Component\Mailer\Transport\Smtp\Stream\AbstractStream;
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\RawMessage;
class SmtpTransportTest extends TestCase
{
@ -25,4 +29,95 @@ class SmtpTransportTest extends TestCase
$t = new SmtpTransport((new SocketStream())->setHost('127.0.0.1')->setPort(2525)->disableTls());
$this->assertEquals('smtp://127.0.0.1:2525', (string) $t);
}
public function testSendDoesNotPingBelowThreshold(): void
{
$stream = new DummyStream();
$envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]);
$transport = new SmtpTransport($stream);
$transport->send(new RawMessage('Message 1'), $envelope);
$transport->send(new RawMessage('Message 2'), $envelope);
$transport->send(new RawMessage('Message 3'), $envelope);
$this->assertNotContains("NOOP\r\n", $stream->getCommands());
}
public function testSendDoesPingAboveThreshold(): void
{
$stream = new DummyStream();
$envelope = new Envelope(new Address('sender@example.org'), [new Address('recipient@example.org')]);
$transport = new SmtpTransport($stream);
$transport->setPingThreshold(1);
$transport->send(new RawMessage('Message 1'), $envelope);
$transport->send(new RawMessage('Message 2'), $envelope);
$this->assertNotContains("NOOP\r\n", $stream->getCommands());
$stream->clearCommands();
sleep(1);
$transport->send(new RawMessage('Message 3'), $envelope);
$this->assertContains("NOOP\r\n", $stream->getCommands());
}
}
class DummyStream extends AbstractStream
{
/**
* @var string
*/
private $nextResponse;
/**
* @var string[]
*/
private $commands;
public function initialize(): void
{
$this->nextResponse = '220 localhost';
}
public function write(string $bytes, $debug = true): void
{
$this->commands[] = $bytes;
if (0 === strpos($bytes, 'DATA')) {
$this->nextResponse = '354 Enter message, ending with "." on a line by itself';
} elseif (0 === strpos($bytes, 'QUIT')) {
$this->nextResponse = '221 Goodbye';
} else {
$this->nextResponse = '250 OK';
}
}
public function readLine(): string
{
return $this->nextResponse;
}
public function flush(): void
{
}
/**
* @return string[]
*/
public function getCommands(): array
{
return $this->commands;
}
public function clearCommands(): void
{
$this->commands = [];
}
protected function getReadConnectionDescription(): string
{
return 'null';
}
}

View File

@ -35,6 +35,8 @@ class SmtpTransport extends AbstractTransport
private $restartThreshold = 100;
private $restartThresholdSleep = 0;
private $restartCounter;
private $pingThreshold = 100;
private $lastMessageTime = 0;
private $stream;
private $domain = '[127.0.0.1]';
@ -66,6 +68,28 @@ class SmtpTransport extends AbstractTransport
return $this;
}
/**
* Sets the minimum number of seconds required between two messages, before the server is pinged.
* If the transport wants to send a message and the time since the last message exceeds the specified threshold,
* the transport will ping the server first (NOOP command) to check if the connection is still alive.
* Otherwise the message will be sent without pinging the server first.
*
* Do not set the threshold too low, as the SMTP server may drop the connection if there are too many
* non-mail commands (like pinging the server with NOOP).
*
* By default, the threshold is set to 100 seconds.
*
* @param int $seconds The minimum number of seconds between two messages required to ping the server
*
* @return $this
*/
public function setPingThreshold(int $seconds): self
{
$this->pingThreshold = $seconds;
return $this;
}
/**
* Sets the name of the local domain that will be used in HELO.
*
@ -160,7 +184,10 @@ class SmtpTransport extends AbstractTransport
protected function doSend(SentMessage $message): void
{
$this->ping();
if (microtime(true) - $this->lastMessageTime > $this->pingThreshold) {
$this->ping();
}
if (!$this->started) {
$this->start();
}
@ -183,6 +210,8 @@ class SmtpTransport extends AbstractTransport
$e->appendDebug($this->stream->getDebug());
throw $e;
} finally {
$this->lastMessageTime = microtime(true);
}
}
@ -213,6 +242,7 @@ class SmtpTransport extends AbstractTransport
$this->assertResponseCode($this->getFullResponse(), [220]);
$this->doHeloCommand();
$this->started = true;
$this->lastMessageTime = 0;
$this->getLogger()->debug(sprintf('Email transport "%s" started', __CLASS__));
}