diff options
author | Aleksander Machniak <alec@alec.pl> | 2013-06-16 14:18:57 +0200 |
---|---|---|
committer | Aleksander Machniak <alec@alec.pl> | 2013-06-16 14:18:57 +0200 |
commit | 3dbe4f79e42dc655444187e22356994a0d5c76ce (patch) | |
tree | b5d17825136b10b6224edbca1753d1fee1e0645d /program | |
parent | ac1a962b51531ccbbaa6c41c329aa5f80859a39d (diff) |
Fix session issues when local and database time differs (#1486132)
Improve performance by executing session gc on script shutdown, also call session_write_close() ASAP
Diffstat (limited to 'program')
-rw-r--r-- | program/include/rcmail.php | 5 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube.php | 27 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_session.php | 94 |
3 files changed, 59 insertions, 67 deletions
diff --git a/program/include/rcmail.php b/program/include/rcmail.php index fd625ba4d..39d804d1f 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -749,11 +749,6 @@ class rcmail extends rcube $book->close(); } - // before closing the database connection, write session data - if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { - $this->session->write_close(); - } - // write performance stats to logs/console if ($this->config->get('devel_mode')) { if (function_exists('memory_get_usage')) diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 8d17827be..21b49f49b 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -99,7 +99,6 @@ class rcube protected $texts; protected $caches = array(); protected $shutdown_functions = array(); - protected $expunge_cache = false; /** @@ -462,7 +461,7 @@ class rcube // use database for storing session data $this->session = new rcube_session($this->get_dbh(), $this->config); - $this->session->register_gc_handler(array($this, 'gc_handler')); + $this->session->register_gc_handler(array($this, 'gc')); $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); $this->session->set_ip_check($this->config->get('ip_check')); @@ -512,18 +511,6 @@ class rcube /** - * Garbage collector for cache entries. - * Set flag to expunge caches on shutdown - */ - public function gc_handler() - { - // because this gc function is called before storage is initialized, - // we just set a flag to expunge storage cache on shutdown. - $this->expunge_cache = true; - } - - - /** * Runs garbage collector with probability based on * session settings. This is intended for environments * without a session. @@ -922,12 +909,16 @@ class rcube call_user_func($function); } - if (is_object($this->smtp)) { - $this->smtp->disconnect(); + // write session data as soon as possible and before + // closing database connection, don't do this before + // registered shutdown functions, they may need the session + // Note: this will run registered gc handlers (ie. cache gc) + if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) { + $this->session->write_close(); } - if ($this->expunge_cache) { - $this->gc(); + if (is_object($this->smtp)) { + $this->smtp->disconnect(); } foreach ($this->caches as $cache) { diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index 4e0682749..615ec6f0e 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -32,6 +32,7 @@ class rcube_session private $ip; private $start; private $changed; + private $time_diff = 0; private $reloaded = false; private $unsets = array(); private $gc_handlers = array(); @@ -93,7 +94,7 @@ class rcube_session array($this, 'db_read'), array($this, 'db_write'), array($this, 'db_destroy'), - array($this, 'db_gc')); + array($this, 'gc')); } } @@ -148,6 +149,11 @@ class rcube_session } session_write_close(); + + // write_close() is called on script shutdown, see rcube::shutdown() + // execute cleanup functionality if enabled by session gc handler + // we do this after closing the session for better performance + $this->gc_shutdown(); } @@ -161,14 +167,16 @@ class rcube_session public function db_read($key) { $sql_result = $this->db->query( - "SELECT vars, ip, changed FROM ".$this->db->table_name('session') - ." WHERE sess_id = ?", $key); + "SELECT vars, ip, changed, " . $this->db->now() . " AS ts" + . " FROM " . $this->db->table_name('session') + . " WHERE sess_id = ?", $key); if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { - $this->changed = strtotime($sql_arr['changed']); - $this->ip = $sql_arr['ip']; - $this->vars = base64_decode($sql_arr['vars']); - $this->key = $key; + $this->time_diff = time() - strtotime($sql_arr['ts']); + $this->changed = strtotime($sql_arr['changed']); + $this->ip = $sql_arr['ip']; + $this->vars = base64_decode($sql_arr['vars']); + $this->key = $key; return !empty($this->vars) ? (string) $this->vars : ''; } @@ -188,8 +196,9 @@ class rcube_session */ public function db_write($key, $vars) { - $ts = microtime(true); - $now = $this->db->fromunixtime((int)$ts); + $now = $this->db->now(); + $table = $this->db->table_name('session'); + $ts = microtime(true); // no session row in DB (db_read() returns false) if (!$this->key) { @@ -207,22 +216,19 @@ class rcube_session $newvars = $this->_fixvars($vars, $oldvars); if ($newvars !== $oldvars) { - $this->db->query( - sprintf("UPDATE %s SET vars=?, changed=%s WHERE sess_id=?", - $this->db->table_name('session'), $now), - base64_encode($newvars), $key); + $this->db->query("UPDATE $table " + . "SET changed = $now, vars = ? WHERE sess_id = ?", + base64_encode($newvars), $key); } - else if ($ts - $this->changed > $this->lifetime / 2) { - $this->db->query("UPDATE ".$this->db->table_name('session') - ." SET changed=$now WHERE sess_id=?", $key); + else if ($ts - $this->changed + $this->time_diff > $this->lifetime / 2) { + $this->db->query("UPDATE $table SET changed = $now" + . " WHERE sess_id = ?", $key); } } else { - $this->db->query( - sprintf("INSERT INTO %s (sess_id, vars, ip, created, changed) ". - "VALUES (?, ?, ?, %s, %s)", - $this->db->table_name('session'), $now, $now), - $key, base64_encode($vars), (string)$this->ip); + $this->db->query("INSERT INTO $table (sess_id, vars, ip, created, changed)" + . " VALUES (?, ?, ?, $now, $now)", + $key, base64_encode($vars), (string)$this->ip); } return true; @@ -282,25 +288,6 @@ class rcube_session /** - * Garbage collecting function - * - * @param string Session lifetime in seconds - * @return boolean True on success - */ - public function db_gc($maxlifetime) - { - // just delete all expired sessions - $this->db->query( - sprintf("DELETE FROM %s WHERE changed < %s", - $this->db->table_name('session'), $this->db->fromunixtime(time() - $maxlifetime))); - - $this->gc(); - - return true; - } - - - /** * Read session data from memcache * * @param string Session ID @@ -376,11 +363,11 @@ class rcube_session /** * Execute registered garbage collector routines */ - public function gc() + public function gc($maxlifetime) { - foreach ($this->gc_handlers as $fct) { - call_user_func($fct); - } + // move gc execution to the script shutdown function + // see rcube::shutdown() and rcube_session::write_close() + return $this->gc_enabled = $maxlifetime; } @@ -402,6 +389,25 @@ class rcube_session /** + * Garbage collector handler to run on script shutdown + */ + protected function gc_shutdown() + { + if ($this->gc_enabled) { + // just delete all expired sessions + if ($this->storage == 'db') { + $this->db->query("DELETE FROM " . $this->db->table_name('session') + . " WHERE changed < " . $this->db->now(-$this->gc_enabled)); + } + + foreach ($this->gc_handlers as $fct) { + call_user_func($fct); + } + } + } + + + /** * Generate and set new session id * * @param boolean $destroy If enabled the current session will be destroyed |