mirror of
https://github.com/kataras/iris.git
synced 2025-12-18 10:27:06 +00:00
Update to 8.2.0 | BoltDB session database, fix file sessiondb, faster, simpler and improvement Session Database API
Former-commit-id: 4034737a65b78a77277e4283fd9289c17f4a452e
This commit is contained in:
@@ -36,87 +36,102 @@ func (p *provider) RegisterDatabase(db Database) {
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// startAutoDestroy start a task which destoy the session when expire date is reached,
|
||||
// but only if `expires` parameter is positive. It updates the expire date of the session from `expires` parameter.
|
||||
func (p *provider) startAutoDestroy(s *Session, expires time.Duration) bool {
|
||||
res := expires > 0
|
||||
if res { // if not unlimited life duration and no -1 (cookie remove action is based on browser's session)
|
||||
expireDate := time.Now().Add(expires)
|
||||
|
||||
s.expireAt = &expireDate
|
||||
s.timer = time.AfterFunc(expires, func() {
|
||||
// the destroy makes the check if this session is exists then or not,
|
||||
// this is used to destroy the session from the server-side also
|
||||
// it's good to have here for security reasons, I didn't add it on the gc function to separate its action
|
||||
p.Destroy(s.sid)
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// newSession returns a new session from sessionid
|
||||
func (p *provider) newSession(sid string, expires time.Duration) *Session {
|
||||
values, expireAt := p.loadSessionValuesFromDB(sid)
|
||||
onExpire := func() {
|
||||
p.Destroy(sid)
|
||||
}
|
||||
|
||||
values, lifetime := p.loadSessionFromDB(sid)
|
||||
// simple and straight:
|
||||
if !lifetime.IsZero() {
|
||||
// if stored time is not zero
|
||||
// start a timer based on the stored time, if not expired.
|
||||
lifetime.Revive(onExpire)
|
||||
} else {
|
||||
// Remember: if db not exist or it has been expired
|
||||
// then the stored time will be zero(see loadSessionFromDB) and the values will be empty.
|
||||
//
|
||||
// Even if the database has an unlimited session (possible by a previous app run)
|
||||
// priority to the "expires" is given,
|
||||
// again if <=0 then it does nothing.
|
||||
lifetime.Begin(expires, onExpire)
|
||||
}
|
||||
|
||||
// I ended up without the need of a duration field on lifetime,
|
||||
// but these are some of my previous comments, they will be required for any future change.
|
||||
//
|
||||
// OK I think go has a bug when gob on embedded time.Time
|
||||
// even if we `gob.Register(LifeTime)`
|
||||
// the OriginalDuration is not saved to the gob file and it cannot be retrieved, it's always 0.
|
||||
// But if we do not embed the `time.Time` inside the `LifeTime` then
|
||||
// it's working.
|
||||
// i.e type LifeTime struct { time.Time; OriginalDuration time.Duration} -> this doesn't
|
||||
// type LifeTime struct {Time time.Time; OriginalDuration time.Duration} -> this works
|
||||
// So we have two options:
|
||||
// 1. don't embed the time.Time -> we will have to use lifetime.Time to get its functions, which doesn't seems right to me
|
||||
// 2. embed the time.Time and compare their times with `lifetime.After(time.Now().Add(expires))`, it seems right but it
|
||||
// should be slower.
|
||||
//
|
||||
// I'll use the 1. and put some common time.Time functions, like After, IsZero on the `LifeTime` type too.
|
||||
//
|
||||
// if db exists but its lifetime is bigger than the expires (very raire,
|
||||
// the source code should be compatible with the databases,
|
||||
// should we print a warning to the user? it is his/her fault
|
||||
// use the database's lifetime or the configurated?
|
||||
// if values.Len() > 0 && lifetime.OriginalDuration != expires {
|
||||
// golog.Warnf(`session database: stored expire time(dur=%d) is differnet than the configuration(dur=%d)
|
||||
// application will use the configurated one`, lifetime.OriginalDuration, expires)
|
||||
// lifetime.Reset(expires)
|
||||
// }
|
||||
|
||||
sess := &Session{
|
||||
sid: sid,
|
||||
provider: p,
|
||||
values: values,
|
||||
flashes: make(map[string]*flashMessage),
|
||||
expireAt: expireAt,
|
||||
lifetime: lifetime,
|
||||
}
|
||||
|
||||
if (len(values) > 0) && (sess.expireAt != nil) {
|
||||
// Restore expiration state
|
||||
// However, if session save in database has no expiration date,
|
||||
// therefore the expiration will be reinitialised with session configuration
|
||||
expires = sess.expireAt.Sub(time.Now())
|
||||
}
|
||||
|
||||
p.startAutoDestroy(sess, expires)
|
||||
|
||||
return sess
|
||||
}
|
||||
|
||||
// can return nil memstore
|
||||
func (p *provider) loadSessionValuesFromDB(sid string) (memstore.Store, *time.Time) {
|
||||
func (p *provider) loadSessionFromDB(sid string) (memstore.Store, LifeTime) {
|
||||
var store memstore.Store
|
||||
var expireDate *time.Time
|
||||
var lifetime LifeTime
|
||||
|
||||
firstValidIdx := 1
|
||||
for i, n := 0, len(p.databases); i < n; i++ {
|
||||
dbValues, currentExpireDate := p.databases[i].Load(sid)
|
||||
if dbValues != nil && len(dbValues) > 0 {
|
||||
for k, v := range dbValues {
|
||||
store.Set(k, v)
|
||||
}
|
||||
storeDB := p.databases[i].Load(sid)
|
||||
if storeDB.Lifetime.HasExpired() { // if expired then skip this db
|
||||
firstValidIdx++
|
||||
continue
|
||||
}
|
||||
|
||||
if (currentExpireDate != nil) && ((expireDate == nil) || expireDate.After(*currentExpireDate)) {
|
||||
expireDate = currentExpireDate
|
||||
if lifetime.IsZero() {
|
||||
// update the lifetime to the most valid
|
||||
lifetime = storeDB.Lifetime
|
||||
}
|
||||
|
||||
if n == firstValidIdx {
|
||||
// if one database then set the store as it is
|
||||
store = storeDB.Values
|
||||
} else {
|
||||
// else append this database's key-value pairs
|
||||
// to the store
|
||||
storeDB.Values.Visit(func(key string, value interface{}) {
|
||||
store.Set(key, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check if session has already expired
|
||||
if (expireDate != nil) && expireDate.Before(time.Now()) {
|
||||
return nil, nil
|
||||
}
|
||||
// Note: if one database and it's being expired then the lifetime will be zero(unlimited)
|
||||
// this by itself is wrong but on the `newSession` we make check of this case too and update the lifetime
|
||||
// if the configuration has expiration registered.
|
||||
|
||||
return store, expireDate
|
||||
}
|
||||
|
||||
func (p *provider) updateDatabases(sess *Session, store memstore.Store) {
|
||||
if l := store.Len(); l > 0 {
|
||||
mapValues := make(map[string]interface{}, l)
|
||||
|
||||
store.Visit(func(k string, v interface{}) {
|
||||
mapValues[k] = v
|
||||
})
|
||||
|
||||
for i, n := 0, len(p.databases); i < n; i++ {
|
||||
p.databases[i].Update(sess.sid, mapValues, sess.expireAt)
|
||||
}
|
||||
}
|
||||
/// TODO: bug on destroy doesn't being remove the file
|
||||
// we will have to see it, it's not db's problem it's here on provider destroy or lifetime onExpire.
|
||||
return store, lifetime
|
||||
}
|
||||
|
||||
// Init creates the session and returns it
|
||||
@@ -128,8 +143,10 @@ func (p *provider) Init(sid string, expires time.Duration) *Session {
|
||||
return newSession
|
||||
}
|
||||
|
||||
// UpdateExpiraton update expire date of a session, plus it updates destroy task
|
||||
func (p *provider) UpdateExpiraton(sid string, expires time.Duration) (done bool) {
|
||||
// UpdateExpiraton update expire date of a session.
|
||||
// if expires > 0 then it updates the destroy task.
|
||||
// if expires <=0 then it does nothing, to destroy a session call the `Destroy` func instead.
|
||||
func (p *provider) UpdateExpiraton(sid string, expires time.Duration) bool {
|
||||
if expires <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -137,26 +154,11 @@ func (p *provider) UpdateExpiraton(sid string, expires time.Duration) (done bool
|
||||
p.mu.Lock()
|
||||
sess, found := p.sessions[sid]
|
||||
p.mu.Unlock()
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
|
||||
if sess.timer == nil {
|
||||
return p.startAutoDestroy(sess, expires)
|
||||
} else {
|
||||
if expires <= 0 {
|
||||
sess.timer.Stop()
|
||||
sess.timer = nil
|
||||
sess.expireAt = nil
|
||||
} else {
|
||||
expireDate := time.Now().Add(expires)
|
||||
|
||||
sess.expireAt = &expireDate
|
||||
sess.timer.Reset(expires)
|
||||
}
|
||||
}
|
||||
|
||||
sess.lifetime.Shift(expires)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -180,17 +182,9 @@ func (p *provider) Read(sid string, expires time.Duration) *Session {
|
||||
func (p *provider) Destroy(sid string) {
|
||||
p.mu.Lock()
|
||||
if sess, found := p.sessions[sid]; found {
|
||||
sess.values = nil
|
||||
sess.flashes = nil
|
||||
if sess.timer != nil {
|
||||
sess.timer.Stop()
|
||||
}
|
||||
|
||||
delete(p.sessions, sid)
|
||||
p.updateDatabases(sess, nil)
|
||||
p.deleteSession(sess)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
// DestroyAll removes all sessions
|
||||
@@ -199,13 +193,12 @@ func (p *provider) Destroy(sid string) {
|
||||
func (p *provider) DestroyAll() {
|
||||
p.mu.Lock()
|
||||
for _, sess := range p.sessions {
|
||||
if sess.timer != nil {
|
||||
sess.timer.Stop()
|
||||
}
|
||||
|
||||
delete(p.sessions, sess.ID())
|
||||
p.updateDatabases(sess, nil)
|
||||
p.deleteSession(sess)
|
||||
}
|
||||
p.mu.Unlock()
|
||||
|
||||
}
|
||||
|
||||
func (p *provider) deleteSession(sess *Session) {
|
||||
delete(p.sessions, sess.sid)
|
||||
syncDatabases(p.databases, acquireSyncPayload(sess, ActionDestroy))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user