bug #39599 [Cache] Fix Redis TLS scheme `rediss` for Redis connection (misaert)

This PR was submitted for the 5.x branch but it was merged into the 4.4 branch instead.

Discussion
----------

[Cache] Fix Redis TLS scheme `rediss` for Redis connection

| Q             | A
| ------------- | ---
| Branch?       | 5.x
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       |
| License       | MIT
| Doc PR        | https://github.com/symfony/symfony-docs/pull/14728

Like https://github.com/symfony/symfony/pull/35503 on Symfony Messenger, this will enable TLS support for Redis adapter.

The implementation just prefix the host with `tls://` as described here: https://github.com/phpredis/phpredis#connect-open

I don't know how to test it because I guess I need a TLS Redis in `src/Symfony/Component/Cache/Tests/Adapter/RedisAdapterTest.php`.

Commits
-------

3288897e0f [Cache] Fix Redis TLS scheme `rediss` for Redis connection
This commit is contained in:
Nicolas Grekas 2021-02-22 19:01:56 +01:00
commit 1688e5d9b4
2 changed files with 45 additions and 25 deletions

View File

@ -36,36 +36,46 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
return $adapter;
}
/**
* @dataProvider provideValidSchemes
*/
public function testCreateConnection(string $dsnScheme)
public function testCreateConnection()
{
$redis = RedisAdapter::createConnection($dsnScheme.':?host[h1]&host[h2]&host[/foo:]');
$redis = RedisAdapter::createConnection('redis:?host[h1]&host[h2]&host[/foo:]');
$this->assertInstanceOf(\RedisArray::class, $redis);
$this->assertSame(['h1:6379', 'h2:6379', '/foo'], $redis->_hosts());
@$redis = null; // some versions of phpredis connect on destruct, let's silence the warning
$redisHost = getenv('REDIS_HOST');
$redis = RedisAdapter::createConnection($dsnScheme.'://'.$redisHost);
$redis = RedisAdapter::createConnection('redis://'.$redisHost);
$this->assertInstanceOf(\Redis::class, $redis);
$this->assertTrue($redis->isConnected());
$this->assertSame(0, $redis->getDbNum());
$redis = RedisAdapter::createConnection($dsnScheme.'://'.$redisHost.'/2');
$redis = RedisAdapter::createConnection('redis://'.$redisHost.'/2');
$this->assertSame(2, $redis->getDbNum());
$redis = RedisAdapter::createConnection($dsnScheme.'://'.$redisHost, ['timeout' => 3]);
$redis = RedisAdapter::createConnection('redis://'.$redisHost, ['timeout' => 3]);
$this->assertEquals(3, $redis->getTimeout());
$redis = RedisAdapter::createConnection($dsnScheme.'://'.$redisHost.'?timeout=4');
$redis = RedisAdapter::createConnection('redis://'.$redisHost.'?timeout=4');
$this->assertEquals(4, $redis->getTimeout());
$redis = RedisAdapter::createConnection($dsnScheme.'://'.$redisHost, ['read_timeout' => 5]);
$redis = RedisAdapter::createConnection('redis://'.$redisHost, ['read_timeout' => 5]);
$this->assertEquals(5, $redis->getReadTimeout());
}
public function testCreateTlsConnection()
{
$redis = RedisAdapter::createConnection('rediss:?host[h1]&host[h2]&host[/foo:]');
$this->assertInstanceOf(\RedisArray::class, $redis);
$this->assertSame(['tls://h1:6379', 'tls://h2:6379', '/foo'], $redis->_hosts());
@$redis = null; // some versions of phpredis connect on destruct, let's silence the warning
$redisHost = getenv('REDIS_HOST');
$redis = RedisAdapter::createConnection('rediss://'.$redisHost.'?lazy=1');
$this->assertInstanceOf(RedisProxy::class, $redis);
}
/**
* @dataProvider provideFailedCreateConnection
*/
@ -95,14 +105,6 @@ class RedisAdapterTest extends AbstractRedisAdapterTest
RedisAdapter::createConnection($dsn);
}
public function provideValidSchemes(): array
{
return [
['redis'],
['rediss'],
];
}
public function provideInvalidCreateConnection(): array
{
return [

View File

@ -118,6 +118,9 @@ trait RedisTrait
$query = $hosts = [];
$tls = 'rediss' === $scheme;
$tcpScheme = $tls ? 'tls' : 'tcp';
if (isset($params['query'])) {
parse_str($params['query'], $query);
@ -130,9 +133,9 @@ trait RedisTrait
parse_str($parameters, $parameters);
}
if (false === $i = strrpos($host, ':')) {
$hosts[$host] = ['scheme' => 'tcp', 'host' => $host, 'port' => 6379] + $parameters;
$hosts[$host] = ['scheme' => $tcpScheme, 'host' => $host, 'port' => 6379] + $parameters;
} elseif ($port = (int) substr($host, 1 + $i)) {
$hosts[$host] = ['scheme' => 'tcp', 'host' => substr($host, 0, $i), 'port' => $port] + $parameters;
$hosts[$host] = ['scheme' => $tcpScheme, 'host' => substr($host, 0, $i), 'port' => $port] + $parameters;
} else {
$hosts[$host] = ['scheme' => 'unix', 'path' => substr($host, 0, $i)] + $parameters;
}
@ -148,7 +151,7 @@ trait RedisTrait
}
if (isset($params['host'])) {
array_unshift($hosts, ['scheme' => 'tcp', 'host' => $params['host'], 'port' => $params['port'] ?? 6379]);
array_unshift($hosts, ['scheme' => $tcpScheme, 'host' => $params['host'], 'port' => $params['port'] ?? 6379]);
} else {
array_unshift($hosts, ['scheme' => 'unix', 'path' => $params['path']]);
}
@ -174,9 +177,16 @@ trait RedisTrait
$connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect';
$redis = new $class();
$initializer = static function ($redis) use ($connect, $params, $dsn, $auth, $hosts) {
$initializer = static function ($redis) use ($connect, $params, $dsn, $auth, $hosts, $tls) {
$host = $hosts[0]['host'] ?? $hosts[0]['path'];
$port = $hosts[0]['port'] ?? null;
if (isset($hosts[0]['host']) && $tls) {
$host = 'tls://'.$host;
}
try {
@$redis->{$connect}($hosts[0]['host'] ?? $hosts[0]['path'], $hosts[0]['port'] ?? null, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout']);
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout']);
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
$isConnected = $redis->isConnected();
@ -210,7 +220,11 @@ trait RedisTrait
}
} elseif (is_a($class, \RedisArray::class, true)) {
foreach ($hosts as $i => $host) {
$hosts[$i] = 'tcp' === $host['scheme'] ? $host['host'].':'.$host['port'] : $host['path'];
switch ($host['scheme']) {
case 'tcp': $hosts[$i] = $host['host'].':'.$host['port']; break;
case 'tls': $hosts[$i] = 'tls://'.$host['host'].':'.$host['port']; break;
default: $hosts[$i] = $host['path'];
}
}
$params['lazy_connect'] = $params['lazy'] ?? true;
$params['connect_timeout'] = $params['timeout'];
@ -227,7 +241,11 @@ trait RedisTrait
} elseif (is_a($class, \RedisCluster::class, true)) {
$initializer = static function () use ($class, $params, $dsn, $hosts) {
foreach ($hosts as $i => $host) {
$hosts[$i] = 'tcp' === $host['scheme'] ? $host['host'].':'.$host['port'] : $host['path'];
switch ($host['scheme']) {
case 'tcp': $hosts[$i] = $host['host'].':'.$host['port']; break;
case 'tls': $hosts[$i] = 'tls://'.$host['host'].':'.$host['port']; break;
default: $hosts[$i] = $host['path'];
}
}
try {