Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/phpredis/phpredis.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorPavlo Yatsukhnenko <yatsukhnenko@gmail.com>2018-03-24 18:45:51 +0300
committerPavlo Yatsukhnenko <yatsukhnenko@gmail.com>2018-03-24 18:45:51 +0300
commit300c72510c48e210338826b713f260a4eda8abc7 (patch)
tree2addd5f65234763f6ea0ee72c48b97000b3894e8 /tests
parentfd93e26fc42ae0be6c5d5510516933e7d9350722 (diff)
parent29edc7db158412239aa5f36a63f342fdcac6c13c (diff)
Merge branch 'session-locking' into develop
Conflicts: redis_session.c tests/RedisTest.php
Diffstat (limited to 'tests')
-rw-r--r--tests/RedisClusterTest.php20
-rw-r--r--tests/RedisTest.php376
-rw-r--r--tests/getSessionData.php19
-rw-r--r--tests/regenerateSessionId.php83
-rw-r--r--tests/startSession.php38
5 files changed, 528 insertions, 8 deletions
diff --git a/tests/RedisClusterTest.php b/tests/RedisClusterTest.php
index eb364dbc..f841fa26 100644
--- a/tests/RedisClusterTest.php
+++ b/tests/RedisClusterTest.php
@@ -23,6 +23,11 @@ class Redis_Cluster_Test extends Redis_Test {
RedisCluster::FAILOVER_DISTRIBUTE
);
+ /**
+ * @var string
+ */
+ protected $sessionPrefix = 'PHPREDIS_CLUSTER_SESSION:';
+
/* Tests we'll skip all together in the context of RedisCluster. The
* RedisCluster class doesn't implement specialized (non-redis) commands
* such as sortAsc, or sortDesc and other commands such as SELECT are
@@ -37,6 +42,21 @@ class Redis_Cluster_Test extends Redis_Test {
public function testSwapDB() { return $this->markTestSkipped(); }
public function testConnectException() { return $this->markTestSkipped(); }
+ /* Session locking feature is currently not supported in in context of Redis Cluster.
+ The biggest issue for this is the distribution nature of Redis cluster */
+ public function testSession_savedToRedis() { return $this->markTestSkipped(); }
+ public function testSession_lockKeyCorrect() { return $this->markTestSkipped(); }
+ public function testSession_lockingDisabledByDefault() { return $this->markTestSkipped(); }
+ public function testSession_lockReleasedOnClose() { return $this->markTestSkipped(); }
+ public function testSession_ttlMaxExecutionTime() { return $this->markTestSkipped(); }
+ public function testSession_ttlLockExpire() { return $this->markTestSkipped(); }
+ public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock() { return $this->markTestSkipped(); }
+ public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock() { return $this->markTestSkipped(); }
+ public function testSession_correctLockRetryCount() { return $this->markTestSkipped(); }
+ public function testSession_defaultLockRetryCount() { return $this->markTestSkipped(); }
+ public function testSession_noUnlockOfOtherProcess() { return $this->markTestSkipped(); }
+ public function testSession_lockWaitTime() { return $this->markTestSkipped(); }
+
/* Load our seeds on construction */
public function __construct() {
$str_nodemap_file = dirname($_SERVER['PHP_SELF']) . '/nodes/nodemap';
diff --git a/tests/RedisTest.php b/tests/RedisTest.php
index 4d807a78..0475f867 100644
--- a/tests/RedisTest.php
+++ b/tests/RedisTest.php
@@ -21,6 +21,11 @@ class Redis_Test extends TestSuite
*/
public $redis;
+ /**
+ * @var string
+ */
+ protected $sessionPrefix = 'PHPREDIS_SESSION:';
+
public function setUp() {
$this->redis = $this->newInstance();
$info = $this->redis->info();
@@ -5142,15 +5147,176 @@ class Redis_Test extends TestSuite
$this->assertEquals($this->redis->lrange('mylist', 0, -1), Array('A','B','C','D'));
}
- public function testSession()
+ public function testSession_savedToRedis()
{
- ini_set('session.save_handler', 'redis');
- ini_set('session.save_path', 'tcp://localhost:6379');
- if (!@session_start()) {
- return $this->markTestSkipped();
- }
- session_write_close();
- $this->assertTrue($this->redis->exists('PHPREDIS_SESSION:' . session_id()));
+ $this->setSessionHandler();
+
+ $sessionId = $this->generateSessionId();
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
+
+ $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId));
+ $this->assertTrue($sessionSuccessful);
+ }
+
+ public function testSession_lockKeyCorrect()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 5, true);
+ usleep(100000);
+
+ $this->assertTrue($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
+ }
+
+ public function testSession_lockingDisabledByDefault()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 5, true, 300, false);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, false);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
+ $this->assertTrue($elapsedTime < 1);
+ $this->assertTrue($sessionSuccessful);
+ }
+
+ public function testSession_lockReleasedOnClose()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 1, true);
+ usleep(1100000);
+
+ $this->assertFalse($this->redis->exists($this->sessionPrefix . $sessionId . '_LOCK'));
+ }
+
+ public function testSession_ttlMaxExecutionTime()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 10, true, 2);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime < 3);
+ $this->assertTrue($sessionSuccessful);
+ }
+
+ public function testSession_ttlLockExpire()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 10, true, 300, true, null, -1, 2);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime < 3);
+ $this->assertTrue($sessionSuccessful);
+ }
+
+ public function testSession_lockHoldCheckBeforeWrite_otherProcessHasLock()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 2, true, 300, true, null, -1, 1, 'firstProcess');
+ usleep(1500000); // 1.5 sec
+ $writeSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 10, 'secondProcess');
+ sleep(1);
+
+ $this->assertTrue($writeSuccessful);
+ $this->assertEquals('secondProcess', $this->getSessionData($sessionId));
+ }
+
+ public function testSession_lockHoldCheckBeforeWrite_nobodyHasLock()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $writeSuccessful = $this->startSessionProcess($sessionId, 2, false, 300, true, null, -1, 1, 'firstProcess');
+
+ $this->assertFalse($writeSuccessful);
+ $this->assertTrue('firstProcess' !== $this->getSessionData($sessionId));
+ }
+
+ public function testSession_correctLockRetryCount()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 10, true);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 1000000, 3);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime > 3 && $elapsedTime < 4);
+ $this->assertFalse($sessionSuccessful);
+ }
+
+ public function testSession_defaultLockRetryCount()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 10, true);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 10, true, 200000, 0);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime > 2 && $elapsedTime < 3);
+ $this->assertFalse($sessionSuccessful);
+ }
+
+ public function testSession_noUnlockOfOtherProcess()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 3, true, 1); // Process 1
+ usleep(100000);
+ $this->startSessionProcess($sessionId, 5, true); // Process 2
+
+ $start = microtime(true);
+ // Waiting until TTL of process 1 ended and process 2 locked the session,
+ // because is not guaranteed which waiting process gets the next lock
+ sleep(1);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime > 5);
+ $this->assertTrue($sessionSuccessful);
+ }
+
+ public function testSession_lockWaitTime()
+ {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 1, true, 300);
+ usleep(100000);
+
+ $start = microtime(true);
+ $sessionSuccessful = $this->startSessionProcess($sessionId, 0, false, 300, true, 3000000);
+ $end = microtime(true);
+ $elapsedTime = $end - $start;
+
+ $this->assertTrue($elapsedTime > 2.5);
+ $this->assertTrue($elapsedTime < 3.5);
+ $this->assertTrue($sessionSuccessful);
}
public function testMultipleConnect() {
@@ -5175,5 +5341,199 @@ class Redis_Test extends TestSuite
$this->assertTrue(strpos($e, "timed out") !== false);
}
}
+
+ public function testSession_regenerateSessionId_noLock_noDestroy() {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_noLock_withDestroy() {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, false, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_withLock_noDestroy() {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_withLock_withDestroy() {
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, true, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_noLock_noDestroy_withProxy() {
+ if (!interface_exists('SessionHandlerInterface')) {
+ $this->markTestSkipped('session handler interface not available in PHP < 5.4');
+ }
+
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, false, false, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_noLock_withDestroy_withProxy() {
+ if (!interface_exists('SessionHandlerInterface')) {
+ $this->markTestSkipped('session handler interface not available in PHP < 5.4');
+ }
+
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, false, true, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_withLock_noDestroy_withProxy() {
+ if (!interface_exists('SessionHandlerInterface')) {
+ $this->markTestSkipped('session handler interface not available in PHP < 5.4');
+ }
+
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, true, false, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ public function testSession_regenerateSessionId_withLock_withDestroy_withProxy() {
+ if (!interface_exists('SessionHandlerInterface')) {
+ $this->markTestSkipped('session handler interface not available in PHP < 5.4');
+ }
+
+ $this->setSessionHandler();
+ $sessionId = $this->generateSessionId();
+ $this->startSessionProcess($sessionId, 0, false, 300, true, null, -1, 1, 'bar');
+
+ $newSessionId = $this->regenerateSessionId($sessionId, true, true, true);
+
+ $this->assertTrue($newSessionId !== $sessionId);
+ $this->assertEquals('bar', $this->getSessionData($newSessionId));
+ }
+
+ private function setSessionHandler()
+ {
+ $host = $this->getHost() ?: 'localhost';
+
+ ini_set('session.save_handler', 'redis');
+ ini_set('session.save_path', 'tcp://' . $host . ':6379');
+ }
+
+ /**
+ * @return string
+ */
+ private function generateSessionId()
+ {
+ if (function_exists('session_create_id')) {
+ return session_create_id();
+ } else {
+ $encoded = bin2hex(openssl_random_pseudo_bytes(8));
+ return $encoded;
+ }
+ }
+
+ /**
+ * @param string $sessionId
+ * @param int $sleepTime
+ * @param bool $background
+ * @param int $maxExecutionTime
+ * @param bool $locking_enabled
+ * @param int $lock_wait_time
+ * @param int $lock_retries
+ * @param int $lock_expires
+ * @param string $sessionData
+ *
+ * @return bool
+ */
+ private function startSessionProcess($sessionId, $sleepTime, $background, $maxExecutionTime = 300, $locking_enabled = true, $lock_wait_time = null, $lock_retries = -1, $lock_expires = 0, $sessionData = '')
+ {
+ if (substr(php_uname(), 0, 7) == "Windows"){
+ $this->markTestSkipped();
+ return true;
+ } else {
+ $commandParameters = array($this->getHost(), $sessionId, $sleepTime, $maxExecutionTime, $lock_retries, $lock_expires, $sessionData);
+ if ($locking_enabled) {
+ $commandParameters[] = '1';
+
+ if ($lock_wait_time != null) {
+ $commandParameters[] = $lock_wait_time;
+ }
+ }
+ $commandParameters = array_map('escapeshellarg', $commandParameters);
+
+ $command = 'php ' . __DIR__ . '/startSession.php ' . implode(' ', $commandParameters);
+ $command .= $background ? ' 2>/dev/null > /dev/null &' : ' 2>&1';
+
+ exec($command, $output);
+ return ($background || (count($output) == 1 && $output[0] == 'SUCCESS')) ? true : false;
+ }
+ }
+
+ /**
+ * @param string $sessionId
+ *
+ * @return string
+ */
+ private function getSessionData($sessionId)
+ {
+ $command = 'php ' . __DIR__ . '/getSessionData.php ' . escapeshellarg($this->getHost()) . ' ' . escapeshellarg($sessionId);
+ exec($command, $output);
+
+ return $output[0];
+ }
+
+ /**
+ * @param string $sessionId
+ * @param bool $locking
+ * @param bool $destroyPrevious
+ * @param bool $sessionProxy
+ *
+ * @return string
+ */
+ private function regenerateSessionId($sessionId, $locking = false, $destroyPrevious = false, $sessionProxy = false)
+ {
+ $args = array_map('escapeshellarg', array($sessionId, $locking, $destroyPrevious, $sessionProxy));
+
+ $command = 'php --no-php-ini --define extension=igbinary.so --define extension=' . __DIR__ . '/../modules/redis.so ' . __DIR__ . '/regenerateSessionId.php ' . escapeshellarg($this->getHost()) . ' ' . implode(' ', $args);
+
+ exec($command, $output);
+
+ return $output[0];
+ }
}
?>
diff --git a/tests/getSessionData.php b/tests/getSessionData.php
new file mode 100644
index 00000000..b5bea74a
--- /dev/null
+++ b/tests/getSessionData.php
@@ -0,0 +1,19 @@
+<?php
+error_reporting(E_ERROR | E_WARNING);
+
+$redisHost = $argv[1];
+$sessionId = $argv[2];
+
+if (empty($redisHost)) {
+ $redisHost = 'localhost';
+}
+
+ini_set('session.save_handler', 'redis');
+ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
+
+session_id($sessionId);
+if (!session_start()) {
+ echo "session_start() was nut successful";
+} else {
+ echo isset($_SESSION['redis_test']) ? $_SESSION['redis_test'] : 'Key redis_test not found';
+}
diff --git a/tests/regenerateSessionId.php b/tests/regenerateSessionId.php
new file mode 100644
index 00000000..6d49c0ab
--- /dev/null
+++ b/tests/regenerateSessionId.php
@@ -0,0 +1,83 @@
+<?php
+error_reporting(E_ERROR | E_WARNING);
+
+$redisHost = $argv[1];
+$sessionId = $argv[2];
+$locking = !!$argv[3];
+$destroyPrevious = !!$argv[4];
+$sessionProxy = !!$argv[5];
+
+if (empty($redisHost)) {
+ $redisHost = 'localhost';
+}
+
+ini_set('session.save_handler', 'redis');
+ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
+
+if ($locking) {
+ ini_set('redis.session.locking_enabled', true);
+}
+
+if (interface_exists('SessionHandlerInterface')) {
+ class TestHandler implements SessionHandlerInterface
+ {
+ /**
+ * @var SessionHandler
+ */
+ private $handler;
+
+ public function __construct()
+ {
+ $this->handler = new SessionHandler();
+ }
+
+ public function close()
+ {
+ return $this->handler->close();
+ }
+
+ public function destroy($session_id)
+ {
+ return $this->handler->destroy($session_id);
+ }
+
+ public function gc($maxlifetime)
+ {
+ return $this->handler->gc($maxlifetime);
+ }
+
+ public function open($save_path, $name)
+ {
+ return $this->handler->open($save_path, $name);
+ }
+
+ public function read($session_id)
+ {
+ return $this->handler->read($session_id);
+ }
+
+ public function write($session_id, $session_data)
+ {
+ return $this->handler->write($session_id, $session_data);
+ }
+ }
+}
+
+if ($sessionProxy) {
+ $handler = new TestHandler();
+ session_set_save_handler($handler);
+}
+
+session_id($sessionId);
+if (!session_start()) {
+ $result = "FAILED: session_start()";
+}
+elseif (!session_regenerate_id($destroyPrevious)) {
+ $result = "FAILED: session_regenerate_id()";
+}
+else {
+ $result = session_id();
+}
+session_write_close();
+echo $result;
+
diff --git a/tests/startSession.php b/tests/startSession.php
new file mode 100644
index 00000000..081a6951
--- /dev/null
+++ b/tests/startSession.php
@@ -0,0 +1,38 @@
+<?php
+error_reporting(E_ERROR | E_WARNING);
+
+$redisHost = $argv[1];
+$sessionId = $argv[2];
+$sleepTime = $argv[3];
+$maxExecutionTime = $argv[4];
+$lock_retries = $argv[5];
+$lock_expire = $argv[6];
+$sessionData = $argv[7];
+
+if (empty($redisHost)) {
+ $redisHost = 'localhost';
+}
+
+ini_set('session.save_handler', 'redis');
+ini_set('session.save_path', 'tcp://' . $redisHost . ':6379');
+ini_set('max_execution_time', $maxExecutionTime);
+ini_set('redis.session.lock_retries', $lock_retries);
+ini_set('redis.session.lock_expire', $lock_expire);
+
+if (isset($argv[8])) {
+ ini_set('redis.session.locking_enabled', $argv[8]);
+}
+
+if (isset($argv[9])) {
+ ini_set('redis.session.lock_wait_time', $argv[9]);
+}
+
+session_id($sessionId);
+$sessionStartSuccessful = session_start();
+sleep($sleepTime);
+if (!empty($sessionData)) {
+ $_SESSION['redis_test'] = $sessionData;
+}
+session_write_close();
+
+echo $sessionStartSuccessful ? 'SUCCESS' : 'FAILURE'; \ No newline at end of file