Skip to content

Commit 378abfa

Browse files
committed
feature #33424 [Mailer] Change the DSN semantics (fabpot)
This PR was merged into the 4.4 branch. Discussion ---------- [Mailer] Change the DSN semantics | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | yes | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | n/a | License | MIT | Doc PR | symfony/symfony-docs#12258 I'm not very satisfied with the current DSNs for the Mailer. First, the scheme/protocol should use the provider name, then, there is no way to change the host, which would be nice to debug more easily (using a requestb.in service for instance). Before: ``` smtp://USERNAME:PASSWORD@mailgun http://KEY:DOMAIN@mailgun ``` After: ``` mailgun+smtp://USERNAME:PASSWORD@default mailgun+http://KEY:DOMAIN@default # New mailgun+http://KEY:DOMAIN@somewhere.com:99 ``` SMTP DSNs did not change, but the special sendmail one did: Before: ``` smtp://sendmail ``` After: ``` sendmail+smtp://default ``` And for the `null` transport: Before: ``` smtp://null ``` After: ``` null://null ``` Commits ------- 469c989 [Mailer] Change the DSN semantics
2 parents ef2b65a + 469c989 commit 378abfa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+704
-187
lines changed

src/Symfony/Bundle/FrameworkBundle/Tests/Functional/app/Mailer/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ imports:
44

55
framework:
66
mailer:
7-
dsn: 'smtp://null'
7+
dsn: 'null://null'
88
envelope:
99
sender: sender@example.org
1010
recipients:
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Amazon\Tests\Transport;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiTransport;
16+
17+
class SesApiTransportTest extends TestCase
18+
{
19+
/**
20+
* @dataProvider getTransportData
21+
*/
22+
public function testToString(SesApiTransport $transport, string $expected)
23+
{
24+
$this->assertSame($expected, (string) $transport);
25+
}
26+
27+
public function getTransportData()
28+
{
29+
return [
30+
[
31+
new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'),
32+
'ses+api://ACCESS_KEY@email.eu-west-1.amazonaws.com',
33+
],
34+
[
35+
new SesApiTransport('ACCESS_KEY', 'SECRET_KEY', 'us-east-1'),
36+
'ses+api://ACCESS_KEY@email.us-east-1.amazonaws.com',
37+
],
38+
[
39+
(new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com'),
40+
'ses+api://ACCESS_KEY@example.com',
41+
],
42+
[
43+
(new SesApiTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com')->setPort(99),
44+
'ses+api://ACCESS_KEY@example.com:99',
45+
],
46+
];
47+
}
48+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Amazon\Tests\Transport;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpTransport;
16+
17+
class SesHttpTransportTest extends TestCase
18+
{
19+
/**
20+
* @dataProvider getTransportData
21+
*/
22+
public function testToString(SesHttpTransport $transport, string $expected)
23+
{
24+
$this->assertSame($expected, (string) $transport);
25+
}
26+
27+
public function getTransportData()
28+
{
29+
return [
30+
[
31+
new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'),
32+
'ses+https://ACCESS_KEY@email.eu-west-1.amazonaws.com',
33+
],
34+
[
35+
new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY', 'us-east-1'),
36+
'ses+https://ACCESS_KEY@email.us-east-1.amazonaws.com',
37+
],
38+
[
39+
(new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com'),
40+
'ses+https://ACCESS_KEY@example.com',
41+
],
42+
[
43+
(new SesHttpTransport('ACCESS_KEY', 'SECRET_KEY'))->setHost('example.com')->setPort(99),
44+
'ses+https://ACCESS_KEY@example.com:99',
45+
],
46+
];
47+
}
48+
}

src/Symfony/Component/Mailer/Bridge/Amazon/Tests/Transport/SesTransportFactoryTest.php

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,28 +29,33 @@ public function getFactory(): TransportFactoryInterface
2929
public function supportsProvider(): iterable
3030
{
3131
yield [
32-
new Dsn('api', 'ses'),
32+
new Dsn('ses+api', 'default'),
3333
true,
3434
];
3535

3636
yield [
37-
new Dsn('http', 'ses'),
37+
new Dsn('ses+https', 'default'),
3838
true,
3939
];
4040

4141
yield [
42-
new Dsn('smtp', 'ses'),
42+
new Dsn('ses', 'default'),
4343
true,
4444
];
4545

4646
yield [
47-
new Dsn('smtps', 'ses'),
47+
new Dsn('ses+smtp', 'default'),
4848
true,
4949
];
5050

5151
yield [
52-
new Dsn('smtp', 'example.com'),
53-
false,
52+
new Dsn('ses+smtps', 'default'),
53+
true,
54+
];
55+
56+
yield [
57+
new Dsn('ses+smtp', 'example.com'),
58+
true,
5459
];
5560
}
5661

@@ -61,53 +66,68 @@ public function createProvider(): iterable
6166
$logger = $this->getLogger();
6267

6368
yield [
64-
new Dsn('api', 'ses', self::USER, self::PASSWORD),
69+
new Dsn('ses+api', 'default', self::USER, self::PASSWORD),
6570
new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
6671
];
6772

6873
yield [
69-
new Dsn('api', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
74+
new Dsn('ses+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
7075
new SesApiTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
7176
];
7277

7378
yield [
74-
new Dsn('http', 'ses', self::USER, self::PASSWORD),
79+
new Dsn('ses+api', 'example.com', self::USER, self::PASSWORD, 8080),
80+
(new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
81+
];
82+
83+
yield [
84+
new Dsn('ses+https', 'default', self::USER, self::PASSWORD),
7585
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
7686
];
7787

7888
yield [
79-
new Dsn('http', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
89+
new Dsn('ses', 'default', self::USER, self::PASSWORD),
90+
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
91+
];
92+
93+
yield [
94+
new Dsn('ses+https', 'example.com', self::USER, self::PASSWORD, 8080),
95+
(new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
96+
];
97+
98+
yield [
99+
new Dsn('ses+https', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
80100
new SesHttpTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
81101
];
82102

83103
yield [
84-
new Dsn('smtp', 'ses', self::USER, self::PASSWORD),
104+
new Dsn('ses+smtp', 'default', self::USER, self::PASSWORD),
85105
new SesSmtpTransport(self::USER, self::PASSWORD, null, $dispatcher, $logger),
86106
];
87107

88108
yield [
89-
new Dsn('smtp', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
109+
new Dsn('ses+smtp', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
90110
new SesSmtpTransport(self::USER, self::PASSWORD, 'eu-west-1', $dispatcher, $logger),
91111
];
92112

93113
yield [
94-
new Dsn('smtps', 'ses', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
114+
new Dsn('ses+smtps', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
95115
new SesSmtpTransport(self::USER, self::PASSWORD, 'eu-west-1', $dispatcher, $logger),
96116
];
97117
}
98118

99119
public function unsupportedSchemeProvider(): iterable
100120
{
101121
yield [
102-
new Dsn('foo', 'ses', self::USER, self::PASSWORD),
103-
'The "foo" scheme is not supported for mailer "ses". Supported schemes are: "api", "http", "smtp", "smtps".',
122+
new Dsn('ses+foo', 'default', self::USER, self::PASSWORD),
123+
'The "ses+foo" scheme is not supported. Supported schemes for mailer "ses" are: "ses", "ses+api", "ses+https", "ses+smtp", "ses+smtps".',
104124
];
105125
}
106126

107127
public function incompleteDsnProvider(): iterable
108128
{
109-
yield [new Dsn('smtp', 'ses', self::USER)];
129+
yield [new Dsn('ses+smtp', 'default', self::USER)];
110130

111-
yield [new Dsn('smtp', 'ses', null, self::PASSWORD)];
131+
yield [new Dsn('ses+smtp', 'default', null, self::PASSWORD)];
112132
}
113133
}

src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesApiTransport.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
*/
2626
class SesApiTransport extends AbstractApiTransport
2727
{
28-
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
28+
private const HOST = 'email.%region%.amazonaws.com';
2929

3030
private $accessKey;
3131
private $secretKey;
@@ -45,16 +45,15 @@ public function __construct(string $accessKey, string $secretKey, string $region
4545

4646
public function __toString(): string
4747
{
48-
return sprintf('api://%s@ses?region=%s', $this->accessKey, $this->region);
48+
return sprintf('ses+api://%s@%s', $this->accessKey, $this->getEndpoint());
4949
}
5050

5151
protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInterface
5252
{
5353
$date = gmdate('D, d M Y H:i:s e');
5454
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
5555

56-
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
57-
$response = $this->client->request('POST', $endpoint, [
56+
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), [
5857
'headers' => [
5958
'X-Amzn-Authorization' => $auth,
6059
'Date' => $date,
@@ -72,6 +71,11 @@ protected function doSendApi(Email $email, SmtpEnvelope $envelope): ResponseInte
7271
return $response;
7372
}
7473

74+
private function getEndpoint(): ?string
75+
{
76+
return ($this->host ?: str_replace('%region%', $this->region, self::HOST)).($this->port ? ':'.$this->port : '');
77+
}
78+
7579
private function getSignature(string $string): string
7680
{
7781
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));

src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesHttpTransport.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*/
2525
class SesHttpTransport extends AbstractHttpTransport
2626
{
27-
private const ENDPOINT = 'https://email.%region%.amazonaws.com';
27+
private const HOST = 'email.%region%.amazonaws.com';
2828

2929
private $accessKey;
3030
private $secretKey;
@@ -44,16 +44,15 @@ public function __construct(string $accessKey, string $secretKey, string $region
4444

4545
public function __toString(): string
4646
{
47-
return sprintf('http://%s@ses?region=%s', $this->accessKey, $this->region);
47+
return sprintf('ses+https://%s@%s', $this->accessKey, $this->getEndpoint());
4848
}
4949

5050
protected function doSendHttp(SentMessage $message): ResponseInterface
5151
{
5252
$date = gmdate('D, d M Y H:i:s e');
5353
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
5454

55-
$endpoint = str_replace('%region%', $this->region, self::ENDPOINT);
56-
$response = $this->client->request('POST', $endpoint, [
55+
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), [
5756
'headers' => [
5857
'X-Amzn-Authorization' => $auth,
5958
'Date' => $date,
@@ -73,6 +72,11 @@ protected function doSendHttp(SentMessage $message): ResponseInterface
7372
return $response;
7473
}
7574

75+
private function getEndpoint(): ?string
76+
{
77+
return ($this->host ?: str_replace('%region%', $this->region, self::HOST)).($this->port ? ':'.$this->port : '');
78+
}
79+
7680
private function getSignature(string $string): string
7781
{
7882
return base64_encode(hash_hmac('sha256', $string, $this->secretKey, true));

src/Symfony/Component/Mailer/Bridge/Amazon/Transport/SesTransportFactory.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,26 @@ public function create(Dsn $dsn): TransportInterface
2727
$user = $this->getUser($dsn);
2828
$password = $this->getPassword($dsn);
2929
$region = $dsn->getOption('region');
30+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
31+
$port = $dsn->getPort();
3032

31-
if ('api' === $scheme) {
32-
return new SesApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
33+
if ('ses+api' === $scheme) {
34+
return (new SesApiTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
3335
}
3436

35-
if ('http' === $scheme) {
36-
return new SesHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger);
37+
if ('ses+https' === $scheme || 'ses' === $scheme) {
38+
return (new SesHttpTransport($user, $password, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port);
3739
}
3840

39-
if ('smtp' === $scheme || 'smtps' === $scheme) {
41+
if ('ses+smtp' === $scheme || 'ses+smtps' === $scheme) {
4042
return new SesSmtpTransport($user, $password, $region, $this->dispatcher, $this->logger);
4143
}
4244

43-
throw new UnsupportedSchemeException($dsn, ['api', 'http', 'smtp', 'smtps']);
45+
throw new UnsupportedSchemeException($dsn, 'ses', $this->getSupportedSchemes());
4446
}
4547

46-
public function supports(Dsn $dsn): bool
48+
protected function getSupportedSchemes(): array
4749
{
48-
return 'ses' === $dsn->getHost();
50+
return ['ses', 'ses+api', 'ses+https', 'ses+smtp', 'ses+smtps'];
4951
}
5052
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy