From d2f33d2cfef1aff273c552c74bbb8e6f30ad0c5d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 17 Jul 2019 16:15:07 +0200 Subject: [PATCH] [Mailer] added debug info for HTTP mailers --- .../Bridge/Amazon/Http/Api/SesTransport.php | 9 ++++-- .../Bridge/Amazon/Http/SesTransport.php | 9 ++++-- .../Mailchimp/Http/Api/MandrillTransport.php | 11 ++++--- .../Mailchimp/Http/MandrillTransport.php | 11 ++++--- .../Mailgun/Http/Api/MailgunTransport.php | 9 ++++-- .../Bridge/Mailgun/Http/MailgunTransport.php | 9 ++++-- .../Postmark/Http/Api/PostmarkTransport.php | 9 ++++-- .../Sendgrid/Http/Api/SendgridTransport.php | 9 ++++-- .../Exception/HttpTransportException.php | 15 +++++++++ src/Symfony/Component/Mailer/SentMessage.php | 11 +++++++ .../Transport/Http/AbstractHttpTransport.php | 21 +++++++++++++ .../Http/Api/AbstractApiTransport.php | 31 ++++--------------- 12 files changed, 103 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php index 7df24401ee..9bc32aff6f 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/Api/SesTransport.php @@ -12,12 +12,13 @@ namespace Symfony\Component\Mailer\Bridge\Amazon\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -42,7 +43,7 @@ class SesTransport extends AbstractApiTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void + protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface { $date = gmdate('D, d M Y H:i:s e'); $auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date)); @@ -60,8 +61,10 @@ class SesTransport extends AbstractApiTransport if (200 !== $response->getStatusCode()) { $error = new \SimpleXMLElement($response->getContent(false)); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code)); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code), $response); } + + return $response; } private function getSignature(string $string): string diff --git a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php index e5ee143c54..936781a7cc 100644 --- a/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Amazon/Http/SesTransport.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Mailer\Bridge\Amazon\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -41,7 +42,7 @@ class SesTransport extends AbstractHttpTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSend(SentMessage $message): void + protected function doSendHttp(SentMessage $message): ResponseInterface { $date = gmdate('D, d M Y H:i:s e'); $auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date)); @@ -61,8 +62,10 @@ class SesTransport extends AbstractHttpTransport if (200 !== $response->getStatusCode()) { $error = new \SimpleXMLElement($response->getContent(false)); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code)); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $error->Error->Message, $error->Error->Code), $response); } + + return $response; } private function getSignature(string $string): string diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php index 116ca4dcb2..bf1c154b5d 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/Api/MandrillTransport.php @@ -12,12 +12,13 @@ namespace Symfony\Component\Mailer\Bridge\Mailchimp\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -35,7 +36,7 @@ class MandrillTransport extends AbstractApiTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void + protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface { $response = $this->client->request('POST', self::ENDPOINT, [ 'json' => $this->getPayload($email, $envelope), @@ -44,11 +45,13 @@ class MandrillTransport extends AbstractApiTransport if (200 !== $response->getStatusCode()) { $result = $response->toArray(false); if ('error' === ($result['status'] ?? false)) { - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $result['message'], $result['code'])); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $result['message'], $result['code']), $response); } - throw new TransportException(sprintf('Unable to send an email (code %s).', $result['code'])); + throw new HttpTransportException(sprintf('Unable to send an email (code %s).', $result['code']), $response); } + + return $response; } private function getPayload(Email $email, SmtpEnvelope $envelope): array diff --git a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php index fd931e97e2..c6e06b496c 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailchimp/Http/MandrillTransport.php @@ -12,11 +12,12 @@ namespace Symfony\Component\Mailer\Bridge\Mailchimp\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -33,7 +34,7 @@ class MandrillTransport extends AbstractHttpTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSend(SentMessage $message): void + protected function doSendHttp(SentMessage $message): ResponseInterface { $envelope = $message->getEnvelope(); $response = $this->client->request('POST', self::ENDPOINT, [ @@ -48,10 +49,12 @@ class MandrillTransport extends AbstractHttpTransport if (200 !== $response->getStatusCode()) { $result = $response->toArray(false); if ('error' === ($result['status'] ?? false)) { - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $result['message'], $result['code'])); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $result['message'], $result['code']), $response); } - throw new TransportException(sprintf('Unable to send an email (code %s).', $result['code'])); + throw new HttpTransportException(sprintf('Unable to send an email (code %s).', $result['code']), $response); } + + return $response; } } diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php index ba6983b02c..56f90eb97c 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/Api/MailgunTransport.php @@ -12,13 +12,14 @@ namespace Symfony\Component\Mailer\Bridge\Mailgun\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\Part\Multipart\FormDataPart; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -40,7 +41,7 @@ class MailgunTransport extends AbstractApiTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void + protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface { $body = new FormDataPart($this->getPayload($email, $envelope)); $headers = []; @@ -58,8 +59,10 @@ class MailgunTransport extends AbstractApiTransport if (200 !== $response->getStatusCode()) { $error = $response->toArray(false); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error['message'], $response->getStatusCode())); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $error['message'], $response->getStatusCode()), $response); } + + return $response; } private function getPayload(Email $email, SmtpEnvelope $envelope): array diff --git a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php index 0f9f515770..6ddf1f3ed5 100644 --- a/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Mailgun/Http/MailgunTransport.php @@ -12,13 +12,14 @@ namespace Symfony\Component\Mailer\Bridge\Mailgun\Http; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; use Symfony\Component\Mime\Part\DataPart; use Symfony\Component\Mime\Part\Multipart\FormDataPart; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -39,7 +40,7 @@ class MailgunTransport extends AbstractHttpTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSend(SentMessage $message): void + protected function doSendHttp(SentMessage $message): ResponseInterface { $body = new FormDataPart([ 'to' => implode(',', $this->stringifyAddresses($message->getEnvelope()->getRecipients())), @@ -59,7 +60,9 @@ class MailgunTransport extends AbstractHttpTransport if (200 !== $response->getStatusCode()) { $error = $response->toArray(false); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error['message'], $response->getStatusCode())); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $error['message'], $response->getStatusCode()), $response); } + + return $response; } } diff --git a/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php b/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php index 644e0d0f8e..8114395762 100644 --- a/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Postmark/Http/Api/PostmarkTransport.php @@ -12,12 +12,13 @@ namespace Symfony\Component\Mailer\Bridge\Postmark\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Email; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -35,7 +36,7 @@ class PostmarkTransport extends AbstractApiTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void + protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface { $response = $this->client->request('POST', self::ENDPOINT, [ 'headers' => [ @@ -48,8 +49,10 @@ class PostmarkTransport extends AbstractApiTransport if (200 !== $response->getStatusCode()) { $error = $response->toArray(false); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', $error['Message'], $error['ErrorCode'])); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', $error['Message'], $error['ErrorCode']), $response); } + + return $response; } private function getPayload(Email $email, SmtpEnvelope $envelope): array diff --git a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php index 86f362f303..3beec418da 100644 --- a/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php +++ b/src/Symfony/Component/Mailer/Bridge/Sendgrid/Http/Api/SendgridTransport.php @@ -12,13 +12,14 @@ namespace Symfony\Component\Mailer\Bridge\Sendgrid\Http\Api; use Psr\Log\LoggerInterface; -use Symfony\Component\Mailer\Exception\TransportException; +use Symfony\Component\Mailer\Exception\HttpTransportException; use Symfony\Component\Mailer\SmtpEnvelope; use Symfony\Component\Mailer\Transport\Http\Api\AbstractApiTransport; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Kevin Verschaeve @@ -36,7 +37,7 @@ class SendgridTransport extends AbstractApiTransport parent::__construct($client, $dispatcher, $logger); } - protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void + protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface { $response = $this->client->request('POST', self::ENDPOINT, [ 'json' => $this->getPayload($email, $envelope), @@ -46,8 +47,10 @@ class SendgridTransport extends AbstractApiTransport if (202 !== $response->getStatusCode()) { $errors = $response->toArray(false); - throw new TransportException(sprintf('Unable to send an email: %s (code %s).', implode('; ', array_column($errors['errors'], 'message')), $response->getStatusCode())); + throw new HttpTransportException(sprintf('Unable to send an email: %s (code %s).', implode('; ', array_column($errors['errors'], 'message')), $response->getStatusCode()), $response); } + + return $response; } private function getPayload(Email $email, SmtpEnvelope $envelope): array diff --git a/src/Symfony/Component/Mailer/Exception/HttpTransportException.php b/src/Symfony/Component/Mailer/Exception/HttpTransportException.php index c85d686543..f9e49aaeda 100644 --- a/src/Symfony/Component/Mailer/Exception/HttpTransportException.php +++ b/src/Symfony/Component/Mailer/Exception/HttpTransportException.php @@ -11,9 +11,24 @@ namespace Symfony\Component\Mailer\Exception; +use Symfony\Contracts\HttpClient\ResponseInterface; + /** * @author Fabien Potencier */ class HttpTransportException extends TransportException { + private $response; + + public function __construct(string $message = null, ResponseInterface $response, int $code = 0, \Exception $previous = null) + { + parent::__construct($message, $code, $previous); + + $this->response = $response; + } + + public function getResponse(): ResponseInterface + { + return $this->response; + } } diff --git a/src/Symfony/Component/Mailer/SentMessage.php b/src/Symfony/Component/Mailer/SentMessage.php index 45dfbdc2f3..5ed2acabaf 100644 --- a/src/Symfony/Component/Mailer/SentMessage.php +++ b/src/Symfony/Component/Mailer/SentMessage.php @@ -22,6 +22,7 @@ class SentMessage private $original; private $raw; private $envelope; + private $debug = ''; /** * @internal @@ -48,6 +49,16 @@ class SentMessage return $this->envelope; } + public function getDebug(): string + { + return $this->debug; + } + + public function appendDebug(string $debug): void + { + $this->debug .= $debug; + } + public function toString(): string { return $this->raw->toString(); diff --git a/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php b/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php index b948b16823..885a4ccfea 100644 --- a/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Http/AbstractHttpTransport.php @@ -13,9 +13,12 @@ namespace Symfony\Component\Mailer\Transport\Http; use Psr\Log\LoggerInterface; use Symfony\Component\HttpClient\HttpClient; +use Symfony\Component\Mailer\Exception\HttpTransportException; +use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\AbstractTransport; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Victor Bocharsky @@ -37,4 +40,22 @@ abstract class AbstractHttpTransport extends AbstractTransport parent::__construct($dispatcher, $logger); } + + abstract protected function doSendHttp(SentMessage $message): ResponseInterface; + + protected function doSend(SentMessage $message): void + { + $response = null; + try { + $response = $this->doSendHttp($message); + } catch (HttpTransportException $e) { + $response = $e->getResponse(); + + throw $e; + } finally { + if (null !== $response) { + $message->appendDebug($response->getInfo('debug')); + } + } + } } diff --git a/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php b/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php index ad371171af..081b5bdcc4 100644 --- a/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php +++ b/src/Symfony/Component/Mailer/Transport/Http/Api/AbstractApiTransport.php @@ -11,42 +11,23 @@ namespace Symfony\Component\Mailer\Transport\Http\Api; -use Psr\Log\LoggerInterface; -use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\Mailer\Exception\RuntimeException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\SmtpEnvelope; -use Symfony\Component\Mailer\Transport\AbstractTransport; +use Symfony\Component\Mailer\Transport\Http\AbstractHttpTransport; use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Email; use Symfony\Component\Mime\MessageConverter; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; /** * @author Fabien Potencier */ -abstract class AbstractApiTransport extends AbstractTransport +abstract class AbstractApiTransport extends AbstractHttpTransport { - protected $client; + abstract protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface; - public function __construct(HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) - { - $this->client = $client; - if (null === $client) { - if (!class_exists(HttpClient::class)) { - throw new \LogicException(sprintf('You cannot use "%s" as the HttpClient component is not installed. Try running "composer require symfony/http-client".', __CLASS__)); - } - - $this->client = HttpClient::create(); - } - - parent::__construct($dispatcher, $logger); - } - - abstract protected function doSendEmail(Email $email, SmtpEnvelope $envelope): void; - - protected function doSend(SentMessage $message): void + protected function doSendHttp(SentMessage $message): ResponseInterface { try { $email = MessageConverter::toEmail($message->getOriginalMessage()); @@ -54,7 +35,7 @@ abstract class AbstractApiTransport extends AbstractTransport throw new RuntimeException(sprintf('Unable to send message with the "%s" transport: %s', __CLASS__, $e->getMessage()), 0, $e); } - $this->doSendEmail($email, $message->getEnvelope()); + return $this->doSendApi($email, $message->getEnvelope()); } protected function getRecipients(Email $email, SmtpEnvelope $envelope): array