* When reading expired sessions - expire them Update to latest macaron/session following merge of https://gitea.com/macaron/session/pulls/11 Also remove old memory provider as 11 updates the memory provider to make it unnecessary. Signed-off-by: Andrew Thornton <art27@cantab.net> * and macaron/session/pulls/12 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: zeripath <art27@cantab.net>tags/v1.12.4
gitea.com/macaron/i18n v0.0.0-20190822004228-474e714e2223 | gitea.com/macaron/i18n v0.0.0-20190822004228-474e714e2223 | ||||
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a | gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a | ||||
gitea.com/macaron/macaron v1.4.0 | gitea.com/macaron/macaron v1.4.0 | ||||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | |||||
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e | |||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 | ||||
github.com/BurntSushi/toml v0.3.1 | github.com/BurntSushi/toml v0.3.1 | ||||
github.com/PuerkitoBio/goquery v1.5.0 | github.com/PuerkitoBio/goquery v1.5.0 |
gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= | gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY= | ||||
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0= | gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0= | ||||
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= | gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA= | ||||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d h1:XLww3CvnFZkXVwauN67fniDaIpIqsE+9KVcxlZKlvLU= | |||||
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY= | |||||
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e h1:BHoJ/xWNt6FrVsL54JennM9HPIQlnbmRvmaC5DO65pU= | |||||
gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY= | |||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14mefLzGluqV7w2mGU3u+iZU+jCeWk= | gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7 h1:N9QFoeNsUXLhl14mefLzGluqV7w2mGU3u+iZU+jCeWk= | ||||
gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks= | gitea.com/macaron/toolbox v0.0.0-20190822013122-05ff0fc766b7/go.mod h1:kgsbFPPS4P+acDYDOPDa3N4IWWOuDJt5/INKRUz7aks= | ||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= | gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s= |
// Copyright 2013 Beego Authors | |||||
// Copyright 2014 The Macaron Authors | |||||
// Copyright 2019 The Gitea Authors | |||||
// | |||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may | |||||
// not use this file except in compliance with the License. You may obtain | |||||
// a copy of the License at | |||||
// | |||||
// http://www.apache.org/licenses/LICENSE-2.0 | |||||
// | |||||
// Unless required by applicable law or agreed to in writing, software | |||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |||||
// License for the specific language governing permissions and limitations | |||||
// under the License. | |||||
package session | |||||
import ( | |||||
"container/list" | |||||
"fmt" | |||||
"sync" | |||||
"time" | |||||
"gitea.com/macaron/session" | |||||
) | |||||
// MemStore represents a in-memory session store implementation. | |||||
type MemStore struct { | |||||
sid string | |||||
lock sync.RWMutex | |||||
data map[interface{}]interface{} | |||||
lastAccess time.Time | |||||
} | |||||
// NewMemStore creates and returns a memory session store. | |||||
func NewMemStore(sid string) *MemStore { | |||||
return &MemStore{ | |||||
sid: sid, | |||||
data: make(map[interface{}]interface{}), | |||||
lastAccess: time.Now(), | |||||
} | |||||
} | |||||
// Set sets value to given key in session. | |||||
func (s *MemStore) Set(key, val interface{}) error { | |||||
s.lock.Lock() | |||||
defer s.lock.Unlock() | |||||
s.data[key] = val | |||||
return nil | |||||
} | |||||
// Get gets value by given key in session. | |||||
func (s *MemStore) Get(key interface{}) interface{} { | |||||
s.lock.RLock() | |||||
defer s.lock.RUnlock() | |||||
return s.data[key] | |||||
} | |||||
// Delete deletes a key from session. | |||||
func (s *MemStore) Delete(key interface{}) error { | |||||
s.lock.Lock() | |||||
defer s.lock.Unlock() | |||||
delete(s.data, key) | |||||
return nil | |||||
} | |||||
// ID returns current session ID. | |||||
func (s *MemStore) ID() string { | |||||
return s.sid | |||||
} | |||||
// Release releases resource and save data to provider. | |||||
func (*MemStore) Release() error { | |||||
return nil | |||||
} | |||||
// Flush deletes all session data. | |||||
func (s *MemStore) Flush() error { | |||||
s.lock.Lock() | |||||
defer s.lock.Unlock() | |||||
s.data = make(map[interface{}]interface{}) | |||||
return nil | |||||
} | |||||
// MemProvider represents a in-memory session provider implementation. | |||||
type MemProvider struct { | |||||
lock sync.RWMutex | |||||
maxLifetime int64 | |||||
data map[string]*list.Element | |||||
// A priority list whose lastAccess newer gets higher priority. | |||||
list *list.List | |||||
} | |||||
// Init initializes memory session provider. | |||||
func (p *MemProvider) Init(maxLifetime int64, _ string) error { | |||||
p.lock.Lock() | |||||
p.maxLifetime = maxLifetime | |||||
p.lock.Unlock() | |||||
return nil | |||||
} | |||||
// update expands time of session store by given ID. | |||||
func (p *MemProvider) update(sid string) error { | |||||
p.lock.Lock() | |||||
defer p.lock.Unlock() | |||||
if e, ok := p.data[sid]; ok { | |||||
e.Value.(*MemStore).lastAccess = time.Now() | |||||
p.list.MoveToFront(e) | |||||
return nil | |||||
} | |||||
return nil | |||||
} | |||||
// Read returns raw session store by session ID. | |||||
func (p *MemProvider) Read(sid string) (_ session.RawStore, err error) { | |||||
p.lock.RLock() | |||||
e, ok := p.data[sid] | |||||
p.lock.RUnlock() | |||||
if ok { | |||||
if err = p.update(sid); err != nil { | |||||
return nil, err | |||||
} | |||||
return e.Value.(*MemStore), nil | |||||
} | |||||
// Create a new session. | |||||
p.lock.Lock() | |||||
defer p.lock.Unlock() | |||||
s := NewMemStore(sid) | |||||
p.data[sid] = p.list.PushBack(s) | |||||
return s, nil | |||||
} | |||||
// Exist returns true if session with given ID exists. | |||||
func (p *MemProvider) Exist(sid string) bool { | |||||
p.lock.RLock() | |||||
defer p.lock.RUnlock() | |||||
_, ok := p.data[sid] | |||||
return ok | |||||
} | |||||
// Destroy deletes a session by session ID. | |||||
func (p *MemProvider) Destroy(sid string) error { | |||||
p.lock.Lock() | |||||
defer p.lock.Unlock() | |||||
e, ok := p.data[sid] | |||||
if !ok { | |||||
return nil | |||||
} | |||||
p.list.Remove(e) | |||||
delete(p.data, sid) | |||||
return nil | |||||
} | |||||
// Regenerate regenerates a session store from old session ID to new one. | |||||
func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) { | |||||
if p.Exist(sid) { | |||||
return nil, fmt.Errorf("new sid '%s' already exists", sid) | |||||
} | |||||
s, err := p.Read(oldsid) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
if err = p.Destroy(oldsid); err != nil { | |||||
return nil, err | |||||
} | |||||
s.(*MemStore).sid = sid | |||||
p.lock.Lock() | |||||
defer p.lock.Unlock() | |||||
p.data[sid] = p.list.PushBack(s) | |||||
return s, nil | |||||
} | |||||
// Count counts and returns number of sessions. | |||||
func (p *MemProvider) Count() int { | |||||
return p.list.Len() | |||||
} | |||||
// GC calls GC to clean expired sessions. | |||||
func (p *MemProvider) GC() { | |||||
p.lock.RLock() | |||||
for { | |||||
// No session in the list. | |||||
e := p.list.Back() | |||||
if e == nil { | |||||
break | |||||
} | |||||
if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { | |||||
p.lock.RUnlock() | |||||
p.lock.Lock() | |||||
p.list.Remove(e) | |||||
delete(p.data, e.Value.(*MemStore).sid) | |||||
p.lock.Unlock() | |||||
p.lock.RLock() | |||||
} else { | |||||
break | |||||
} | |||||
} | |||||
p.lock.RUnlock() | |||||
} |
package session | package session | ||||
import ( | import ( | ||||
"container/list" | |||||
"encoding/json" | "encoding/json" | ||||
"fmt" | "fmt" | ||||
"sync" | "sync" | ||||
// This is only slightly more wrong than modules/setting/session.go:23 | // This is only slightly more wrong than modules/setting/session.go:23 | ||||
switch opts.Provider { | switch opts.Provider { | ||||
case "memory": | case "memory": | ||||
o.provider = &MemProvider{list: list.New(), data: make(map[string]*list.Element)} | |||||
o.provider = &session.MemProvider{} | |||||
case "file": | case "file": | ||||
o.provider = &session.FileProvider{} | o.provider = &session.FileProvider{} | ||||
case "redis": | case "redis": |
defer p.lock.RUnlock() | defer p.lock.RUnlock() | ||||
var f *os.File | var f *os.File | ||||
ok := false | |||||
if com.IsFile(filename) { | if com.IsFile(filename) { | ||||
modTime, err := com.FileMTime(filename) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
ok = (modTime + p.maxlifetime) >= time.Now().Unix() | |||||
} | |||||
if ok { | |||||
f, err = os.OpenFile(filename, os.O_RDONLY, 0600) | f, err = os.OpenFile(filename, os.O_RDONLY, 0600) | ||||
} else { | } else { | ||||
f, err = os.Create(filename) | f, err = os.Create(filename) |
// Init initializes memory session provider. | // Init initializes memory session provider. | ||||
func (p *MemProvider) Init(maxLifetime int64, _ string) error { | func (p *MemProvider) Init(maxLifetime int64, _ string) error { | ||||
p.lock.Lock() | p.lock.Lock() | ||||
p.list = list.New() | |||||
p.data = make(map[string]*list.Element) | |||||
p.maxLifetime = maxLifetime | p.maxLifetime = maxLifetime | ||||
p.lock.Unlock() | p.lock.Unlock() | ||||
return nil | return nil | ||||
e, ok := p.data[sid] | e, ok := p.data[sid] | ||||
p.lock.RUnlock() | p.lock.RUnlock() | ||||
if ok { | |||||
// Only restore if the session is still alive. | |||||
if ok && (e.Value.(*MemStore).lastAccess.Unix()+p.maxLifetime) >= time.Now().Unix() { | |||||
if err = p.update(sid); err != nil { | if err = p.update(sid); err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
// Create a new session. | // Create a new session. | ||||
p.lock.Lock() | p.lock.Lock() | ||||
defer p.lock.Unlock() | defer p.lock.Unlock() | ||||
if ok { | |||||
p.list.Remove(e) | |||||
delete(p.data, sid) | |||||
} | |||||
s := NewMemStore(sid) | s := NewMemStore(sid) | ||||
p.data[sid] = p.list.PushBack(s) | p.data[sid] = p.list.PushBack(s) | ||||
return s, nil | return s, nil | ||||
} | } | ||||
func init() { | func init() { | ||||
Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) | |||||
Register("memory", &MemProvider{}) | |||||
} | } |
// Read returns raw session store by session ID. | // Read returns raw session store by session ID. | ||||
func (p *MysqlProvider) Read(sid string) (session.RawStore, error) { | func (p *MysqlProvider) Read(sid string) (session.RawStore, error) { | ||||
now := time.Now().Unix() | |||||
var data []byte | var data []byte | ||||
err := p.c.QueryRow("SELECT data FROM session WHERE `key`=?", sid).Scan(&data) | |||||
expiry := now | |||||
err := p.c.QueryRow("SELECT data, expiry FROM session WHERE `key`=?", sid).Scan(&data, &expiry) | |||||
if err == sql.ErrNoRows { | if err == sql.ErrNoRows { | ||||
_, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", | _, err = p.c.Exec("INSERT INTO session(`key`,data,expiry) VALUES(?,?,?)", | ||||
sid, "", time.Now().Unix()) | |||||
sid, "", now) | |||||
} | } | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
var kv map[interface{}]interface{} | var kv map[interface{}]interface{} | ||||
if len(data) == 0 { | |||||
if len(data) == 0 || expiry+p.expire <= now { | |||||
kv = make(map[interface{}]interface{}) | kv = make(map[interface{}]interface{}) | ||||
} else { | } else { | ||||
kv, err = session.DecodeGob(data) | kv, err = session.DecodeGob(data) |
// Read returns raw session store by session ID. | // Read returns raw session store by session ID. | ||||
func (p *PostgresProvider) Read(sid string) (session.RawStore, error) { | func (p *PostgresProvider) Read(sid string) (session.RawStore, error) { | ||||
now := time.Now().Unix() | |||||
var data []byte | var data []byte | ||||
err := p.c.QueryRow("SELECT data FROM session WHERE key=$1", sid).Scan(&data) | |||||
expiry := now | |||||
err := p.c.QueryRow("SELECT data, expiry FROM session WHERE key=$1", sid).Scan(&data, &expiry) | |||||
if err == sql.ErrNoRows { | if err == sql.ErrNoRows { | ||||
_, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", | _, err = p.c.Exec("INSERT INTO session(key,data,expiry) VALUES($1,$2,$3)", | ||||
sid, "", time.Now().Unix()) | |||||
sid, "", now) | |||||
} | } | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
var kv map[interface{}]interface{} | var kv map[interface{}]interface{} | ||||
if len(data) == 0 { | |||||
if len(data) == 0 || expiry+p.maxlifetime <= now { | |||||
kv = make(map[interface{}]interface{}) | kv = make(map[interface{}]interface{}) | ||||
} else { | } else { | ||||
kv, err = session.DecodeGob(data) | kv, err = session.DecodeGob(data) |
# gitea.com/macaron/macaron v1.4.0 | # gitea.com/macaron/macaron v1.4.0 | ||||
## explicit | ## explicit | ||||
gitea.com/macaron/macaron | gitea.com/macaron/macaron | ||||
# gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d | |||||
# gitea.com/macaron/session v0.0.0-20200902202411-e3a87877db6e | |||||
## explicit | ## explicit | ||||
gitea.com/macaron/session | gitea.com/macaron/session | ||||
gitea.com/macaron/session/couchbase | gitea.com/macaron/session/couchbase |