mirror of
https://gitee.com/johng/gf
synced 2026-07-09 06:50:30 +08:00
improve gsession/gmap/gtree
This commit is contained in:
28
.example/os/gsession/storage-file/file.go
Normal file
28
.example/os/gsession/storage-file/file.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetConfigWithMap(g.Map{
|
||||
"SessionMaxAge": time.Minute,
|
||||
})
|
||||
s.BindHandler("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.BindHandler("/get", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Session.Map())
|
||||
})
|
||||
s.BindHandler("/del", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
4
.example/os/gsession/storage-redis-hashtable/config.toml
Normal file
4
.example/os/gsession/storage-redis-hashtable/config.toml
Normal file
@ -0,0 +1,4 @@
|
||||
# Redis数据库配置
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetConfigWithMap(g.Map{
|
||||
"SessionMaxAge": 60 * time.Minute,
|
||||
"SessionStorage": gsession.NewStorageRedisHashTable(g.Redis()),
|
||||
})
|
||||
s.BindHandler("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.BindHandler("/get", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Session.Map())
|
||||
})
|
||||
s.BindHandler("/del", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
4
.example/os/gsession/storage-redis/config.toml
Normal file
4
.example/os/gsession/storage-redis/config.toml
Normal file
@ -0,0 +1,4 @@
|
||||
# Redis数据库配置
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
30
.example/os/gsession/storage-redis/redis.go
Normal file
30
.example/os/gsession/storage-redis/redis.go
Normal file
@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetConfigWithMap(g.Map{
|
||||
"SessionMaxAge": time.Minute,
|
||||
"SessionStorage": gsession.NewStorageRedis(g.Redis()),
|
||||
})
|
||||
s.BindHandler("/set", func(r *ghttp.Request) {
|
||||
r.Session.Set("time", gtime.Second())
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.BindHandler("/get", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.Session.Map())
|
||||
})
|
||||
s.BindHandler("/del", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
@ -1,22 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
|
||||
})
|
||||
s.BindHandler("/user", func(r *ghttp.Request) {
|
||||
|
||||
})
|
||||
s.BindHandler("/user/:id", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetRouterString("id"))
|
||||
})
|
||||
s.EnablePprof()
|
||||
s.SetPort(3000)
|
||||
s.Run()
|
||||
func Test(data *gmap.Map) {
|
||||
data = gmap.New()
|
||||
fmt.Println(data)
|
||||
}
|
||||
func main() {
|
||||
var m *gmap.Map
|
||||
fmt.Println(m)
|
||||
Test(m)
|
||||
fmt.Println(m)
|
||||
}
|
||||
|
||||
@ -368,6 +368,13 @@ func (m *AnyAnyMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *AnyAnyMap) Replace(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *AnyAnyMap) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -366,6 +366,13 @@ func (m *IntAnyMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *IntAnyMap) Replace(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntAnyMap) LockFunc(f func(m map[int]interface{})) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -343,6 +343,13 @@ func (m *IntIntMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *IntIntMap) Replace(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -344,6 +344,13 @@ func (m *IntStrMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *IntStrMap) Replace(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *IntStrMap) LockFunc(f func(m map[int]string)) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -362,6 +362,13 @@ func (m *StrAnyMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *StrAnyMap) Replace(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrAnyMap) LockFunc(f func(m map[string]interface{})) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -345,6 +345,13 @@ func (m *StrIntMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *StrIntMap) Replace(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrIntMap) LockFunc(f func(m map[string]int)) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -346,6 +346,13 @@ func (m *StrStrMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *StrStrMap) Replace(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
m.data = data
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (m *StrStrMap) LockFunc(f func(m map[string]string)) {
|
||||
m.mu.Lock()
|
||||
|
||||
@ -92,6 +92,21 @@ func (m *ListMap) Clear() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Replace the data of the map with given <data>.
|
||||
func (m *ListMap) Replace(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
e.Value = &gListMapNode{key, value}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Map returns a copy of the data of the map.
|
||||
func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
|
||||
@ -386,6 +386,17 @@ func (tree *AVLTree) Clear() {
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// Replace the data of the tree with given <data>.
|
||||
func (tree *AVLTree) Replace(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
for key, value := range data {
|
||||
tree.put(key, value, nil, &tree.root)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of container
|
||||
func (tree *AVLTree) String() string {
|
||||
tree.mu.RLock()
|
||||
|
||||
@ -324,6 +324,17 @@ func (tree *BTree) Clear() {
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// Replace the data of the tree with given <data>.
|
||||
func (tree *BTree) Replace(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// Height returns the height of the tree.
|
||||
func (tree *BTree) Height() int {
|
||||
tree.mu.RLock()
|
||||
|
||||
@ -605,6 +605,17 @@ func (tree *RedBlackTree) Clear() {
|
||||
tree.size = 0
|
||||
}
|
||||
|
||||
// Replace the data of the tree with given <data>.
|
||||
func (tree *RedBlackTree) Replace(data map[interface{}]interface{}) {
|
||||
tree.mu.Lock()
|
||||
defer tree.mu.Unlock()
|
||||
tree.root = nil
|
||||
tree.size = 0
|
||||
for k, v := range data {
|
||||
tree.doSet(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of container.
|
||||
func (tree *RedBlackTree) String() string {
|
||||
tree.mu.RLock()
|
||||
|
||||
@ -77,6 +77,8 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
// access log
|
||||
s.handleAccessLog(request)
|
||||
// 关闭当前Session,并更新会话超时时间
|
||||
request.Session.Close()
|
||||
}()
|
||||
|
||||
// ============================================================
|
||||
@ -168,8 +170,6 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if !request.IsExited() {
|
||||
s.callHookHandler(HOOK_AFTER_OUTPUT, request)
|
||||
}
|
||||
// 关闭当前Session,并更新会话超时时间
|
||||
request.Session.Close()
|
||||
}
|
||||
|
||||
// 查找静态文件的绝对路径
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gsession
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
@ -14,16 +15,16 @@ import (
|
||||
|
||||
// Manager for sessions.
|
||||
type Manager struct {
|
||||
ttl time.Duration // TTL for sessions.
|
||||
storage Storage // Storage interface for session storage Set/Get.
|
||||
sessions *gcache.Cache // Session cache for session TTL.
|
||||
ttl time.Duration // TTL for sessions.
|
||||
storage Storage // Storage interface for session storage Set/Get.
|
||||
sessionData *gcache.Cache // Session data cache for session TTL.
|
||||
}
|
||||
|
||||
// New creates and returns a new session manager.
|
||||
func New(ttl time.Duration, storage ...Storage) *Manager {
|
||||
m := &Manager{
|
||||
ttl: ttl,
|
||||
sessions: gcache.New(),
|
||||
ttl: ttl,
|
||||
sessionData: gcache.New(),
|
||||
}
|
||||
if len(storage) > 0 && storage[0] != nil {
|
||||
m.storage = storage[0]
|
||||
@ -39,12 +40,6 @@ func (m *Manager) New(sessionId ...string) *Session {
|
||||
if len(sessionId) > 0 && sessionId[0] != "" {
|
||||
id = sessionId[0]
|
||||
}
|
||||
// NOTE:
|
||||
// We CANNOT creates and stores session directly to manager
|
||||
// as it might be a fake and invalid session id
|
||||
// which would consumes your memory as much as possible.
|
||||
//
|
||||
// We here create a temporary tiny session struct.
|
||||
return &Session{
|
||||
id: id,
|
||||
manager: m,
|
||||
@ -67,7 +62,6 @@ func (m *Manager) TTL() time.Duration {
|
||||
}
|
||||
|
||||
// UpdateSessionTTL updates the ttl for given session.
|
||||
// If this session is dirty, it also exports it to storage.
|
||||
func (m *Manager) UpdateSessionTTL(id string, session *Session) {
|
||||
m.sessions.Set(id, session, m.ttl)
|
||||
func (m *Manager) UpdateSessionTTL(id string, data *gmap.StrAnyMap) {
|
||||
m.sessionData.Set(id, data, m.ttl)
|
||||
}
|
||||
|
||||
@ -7,10 +7,9 @@
|
||||
package gsession
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
@ -21,25 +20,33 @@ import (
|
||||
type Session struct {
|
||||
id string // Session id.
|
||||
data *gmap.StrAnyMap // Session data.
|
||||
dirty *gtype.Bool // Used to mark session is modified.
|
||||
dirty bool // Used to mark session is modified.
|
||||
start bool // Used to mark session is started.
|
||||
manager *Manager // Parent manager.
|
||||
}
|
||||
|
||||
// init does the delay initialization for session.
|
||||
// It here to initialization real session if necessary.
|
||||
// It here initializes real session if necessary.
|
||||
func (s *Session) init() {
|
||||
if s.dirty == nil {
|
||||
s.dirty = gtype.NewBool(false)
|
||||
if s.start {
|
||||
return
|
||||
}
|
||||
if len(s.id) > 0 && s.data == nil {
|
||||
if data := s.manager.storage.GetSession(s.id, s.manager.ttl); data != nil {
|
||||
if s.data = gmap.NewStrAnyMapFrom(data, true); s.data == nil {
|
||||
panic("session restoring failed for id:" + s.id)
|
||||
if len(s.id) > 0 {
|
||||
var err error
|
||||
// Retrieve memory session data from manager.
|
||||
if r := s.manager.sessionData.Get(s.id); r != nil {
|
||||
s.data = r.(*gmap.StrAnyMap)
|
||||
intlog.Print("session init data:", s.data)
|
||||
}
|
||||
// Retrieve stored session data from storage.
|
||||
if s.manager.storage != nil {
|
||||
if s.data, err = s.manager.storage.GetSession(s.id, s.manager.ttl, s.data); err != nil {
|
||||
intlog.Errorf("session restoring failed for id '%s': %v", s.id, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
// Invalid or expired session id,
|
||||
// it should create a new one.
|
||||
}
|
||||
// If it's an invalid or expired session id,
|
||||
// it should create a new session id.
|
||||
if s.data == nil {
|
||||
s.id = ""
|
||||
}
|
||||
}
|
||||
@ -52,6 +59,31 @@ func (s *Session) init() {
|
||||
if s.data == nil {
|
||||
s.data = gmap.NewStrAnyMap(true)
|
||||
}
|
||||
s.start = true
|
||||
}
|
||||
|
||||
// Close closes current session and updates its ttl in the session manager.
|
||||
// If this session is dirty, it also exports it to storage.
|
||||
//
|
||||
// NOTE that this function must be called ever after a session request done.
|
||||
func (s *Session) Close() {
|
||||
if s.start && len(s.id) > 0 {
|
||||
size := s.data.Size()
|
||||
if s.manager.storage != nil {
|
||||
if s.dirty {
|
||||
if err := s.manager.storage.SetSession(s.id, s.data, s.manager.ttl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else if size > 0 {
|
||||
if err := s.manager.storage.UpdateTTL(s.id, s.manager.ttl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if s.dirty || size > 0 {
|
||||
s.manager.UpdateSessionTTL(s.id, s.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value pair to this session.
|
||||
@ -64,7 +96,7 @@ func (s *Session) Set(key string, value interface{}) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.dirty.Set(true)
|
||||
s.dirty = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -78,7 +110,7 @@ func (s *Session) Sets(data map[string]interface{}) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.dirty.Set(true)
|
||||
s.dirty = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -95,7 +127,7 @@ func (s *Session) Remove(key string) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.dirty.Set(true)
|
||||
s.dirty = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -117,7 +149,7 @@ func (s *Session) RemoveAll() error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s.dirty.Set(true)
|
||||
s.dirty = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -161,33 +193,7 @@ func (s *Session) Contains(key string) bool {
|
||||
|
||||
// IsDirty checks whether there's any data changes in the session.
|
||||
func (s *Session) IsDirty() bool {
|
||||
if s.dirty == nil {
|
||||
return false
|
||||
}
|
||||
return s.dirty.Val()
|
||||
}
|
||||
|
||||
// Close closes current session and updates its ttl in the session manager.
|
||||
// If this session is dirty, it also exports it to storage.
|
||||
//
|
||||
// NOTE that this function must be called ever after a session request done.
|
||||
func (s *Session) Close() {
|
||||
if len(s.id) > 0 && s.data != nil {
|
||||
if s.manager.storage != nil {
|
||||
if s.dirty.Cas(true, false) {
|
||||
s.data.RLockFunc(func(m map[string]interface{}) {
|
||||
if err := s.manager.storage.SetSession(s.id, m, s.manager.ttl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if err := s.manager.storage.UpdateTTL(s.id, s.manager.ttl); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
s.manager.UpdateSessionTTL(s.id, s)
|
||||
}
|
||||
return s.dirty
|
||||
}
|
||||
|
||||
// Get retrieves session value with given key.
|
||||
|
||||
@ -6,7 +6,11 @@
|
||||
|
||||
package gsession
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Storage interface {
|
||||
// New creates a custom session id.
|
||||
@ -37,17 +41,25 @@ type Storage interface {
|
||||
// RemoveAll deletes all key-value pairs from storage.
|
||||
RemoveAll(id string) error
|
||||
|
||||
// GetSession returns the session data as map for given session id.
|
||||
// The parameter <ttl> specifies the TTL for this session.
|
||||
// It returns nil if the TTL is exceeded.
|
||||
GetSession(id string, ttl time.Duration) map[string]interface{}
|
||||
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
|
||||
//
|
||||
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
|
||||
// The parameter <data> is the current old session data stored in memory,
|
||||
// and for some storage it might be nil if memory storage is disabled.
|
||||
//
|
||||
// This function is called ever when session starts.
|
||||
GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error)
|
||||
|
||||
// SetSession updates the data map for specified session id.
|
||||
// SetSession updates the data for specified session id.
|
||||
// This function is called ever after session, which is changed dirty, is closed.
|
||||
// This copy all session data map from memory to storage.
|
||||
SetSession(id string, data map[string]interface{}, ttl time.Duration) error
|
||||
SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error
|
||||
|
||||
// UpdateTTL updates the TTL for specified session id.
|
||||
// This function is called ever after session, which is not dirty, is closed.
|
||||
UpdateTTL(id string, ttl time.Duration) error
|
||||
}
|
||||
|
||||
var (
|
||||
ErrorDisabled = errors.New("this feature is disabled in this storage")
|
||||
)
|
||||
|
||||
@ -8,7 +8,7 @@ package gsession
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"os"
|
||||
"time"
|
||||
@ -40,7 +40,6 @@ var (
|
||||
DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!")
|
||||
DefaultStorageFileCryptoEnabled = false
|
||||
DefaultStorageFileLoopInterval = time.Minute
|
||||
ErrorDisabled = errors.New("this feature is disabled in this storage")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -151,39 +150,51 @@ func (s *StorageFile) RemoveAll(id string) error {
|
||||
return ErrorDisabled
|
||||
}
|
||||
|
||||
// GetSession returns the session data as map for given session id.
|
||||
// The parameter <ttl> specifies the TTL for this session.
|
||||
// It returns nil if the TTL is exceeded.
|
||||
func (s *StorageFile) GetSession(id string, ttl time.Duration) map[string]interface{} {
|
||||
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
|
||||
//
|
||||
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
|
||||
// The parameter <data> is the current old session data stored in memory,
|
||||
// and for some storage it might be nil if memory storage is disabled.
|
||||
//
|
||||
// This function is called ever when session starts.
|
||||
func (s *StorageFile) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
|
||||
if data != nil {
|
||||
return data, nil
|
||||
}
|
||||
intlog.Printf("StorageFile.GetSession: %s, %v", id, ttl)
|
||||
path := s.sessionFilePath(id)
|
||||
data := gfile.GetBytes(path)
|
||||
if len(data) > 8 {
|
||||
timestampMilli := gbinary.DecodeToInt64(data[:8])
|
||||
content := gfile.GetBytes(path)
|
||||
if len(content) > 8 {
|
||||
timestampMilli := gbinary.DecodeToInt64(content[:8])
|
||||
if timestampMilli+ttl.Nanoseconds()/1e6 < gtime.Millisecond() {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
var err error
|
||||
content := data[8:]
|
||||
content = content[8:]
|
||||
// Decrypt with AES.
|
||||
if s.cryptoEnabled {
|
||||
content, err = gaes.Decrypt(data[8:], DefaultStorageFileCryptoKey)
|
||||
content, err = gaes.Decrypt(content, DefaultStorageFileCryptoKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if err = json.Unmarshal(content, &m); err != nil {
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
return m
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return gmap.NewStrAnyMapFrom(m, true), nil
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SetSession updates the data map for specified session id.
|
||||
// This function is called ever after session, which is changed dirty, is closed.
|
||||
// This copy all session data map from memory to storage.
|
||||
func (s *StorageFile) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
|
||||
func (s *StorageFile) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
|
||||
intlog.Printf("StorageFile.SetSession: %s, %v, %v", id, data, ttl)
|
||||
path := s.sessionFilePath(id)
|
||||
content, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
@ -214,6 +225,7 @@ func (s *StorageFile) SetSession(id string, data map[string]interface{}, ttl tim
|
||||
// This function is called ever after session, which is not dirty, is closed.
|
||||
// It just adds the session id to the async handling queue.
|
||||
func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
|
||||
intlog.Printf("StorageFile.UpdateTTL: %s, %v", id, ttl)
|
||||
if ttl >= DefaultStorageRedisLoopInterval {
|
||||
s.updatingIdSet.Add(id)
|
||||
}
|
||||
@ -222,7 +234,7 @@ func (s *StorageFile) UpdateTTL(id string, ttl time.Duration) error {
|
||||
|
||||
// doUpdateTTL updates the TTL for session id.
|
||||
func (s *StorageFile) doUpdateTTL(id string) error {
|
||||
intlog.Printf("update StorageFile TTL for session id: %s", id)
|
||||
intlog.Printf("StorageFile.doUpdateTTL: %s", id)
|
||||
path := s.sessionFilePath(id)
|
||||
file, err := gfile.OpenWithFlag(path, os.O_WRONLY)
|
||||
if err != nil {
|
||||
|
||||
@ -32,6 +32,7 @@ var (
|
||||
// NewStorageRedis creates and returns a redis storage object for session.
|
||||
func NewStorageRedis(redis *gredis.Redis, prefix ...string) *StorageRedis {
|
||||
if redis == nil {
|
||||
panic("redis instance for storage cannot be empty")
|
||||
return nil
|
||||
}
|
||||
s := &StorageRedis{
|
||||
@ -105,26 +106,43 @@ func (s *StorageRedis) RemoveAll(id string) error {
|
||||
return ErrorDisabled
|
||||
}
|
||||
|
||||
// GetSession returns the session data as map for given session id.
|
||||
// The parameter <ttl> specifies the TTL for this session.
|
||||
// It returns nil if the TTL is exceeded.
|
||||
func (s *StorageRedis) GetSession(id string, ttl time.Duration) map[string]interface{} {
|
||||
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
|
||||
//
|
||||
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
|
||||
// The parameter <data> is the current old session data stored in memory,
|
||||
// and for some storage it might be nil if memory storage is disabled.
|
||||
//
|
||||
// This function is called ever when session starts.
|
||||
func (s *StorageRedis) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
|
||||
intlog.Printf("StorageRedis.GetSession: %s, %v", id, ttl)
|
||||
r, err := s.redis.DoVar("GET", s.key(id))
|
||||
if err != nil {
|
||||
// If it does not exist, it returns an empty map to replace the session memory data.
|
||||
return map[string]interface{}{}
|
||||
return nil, err
|
||||
}
|
||||
content := r.Bytes()
|
||||
if len(content) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var m map[string]interface{}
|
||||
if err = json.Unmarshal(r.Bytes(), &m); err != nil {
|
||||
return nil
|
||||
if err = json.Unmarshal(content, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m
|
||||
if m == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if data == nil {
|
||||
return gmap.NewStrAnyMapFrom(m, true), nil
|
||||
} else {
|
||||
data.Replace(m)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SetSession updates the data map for specified session id.
|
||||
// This function is called ever after session, which is changed dirty, is closed.
|
||||
// This copy all session data map from memory to storage.
|
||||
func (s *StorageRedis) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
|
||||
func (s *StorageRedis) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
|
||||
intlog.Printf("StorageRedis.SetSession: %s, %v, %v", id, data, ttl)
|
||||
content, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -137,6 +155,7 @@ func (s *StorageRedis) SetSession(id string, data map[string]interface{}, ttl ti
|
||||
// This function is called ever after session, which is not dirty, is closed.
|
||||
// It just adds the session id to the async handling queue.
|
||||
func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error {
|
||||
intlog.Printf("StorageRedis.UpdateTTL: %s, %v", id, ttl)
|
||||
if ttl >= DefaultStorageRedisLoopInterval {
|
||||
s.updatingIdMap.Set(id, int(ttl.Seconds()))
|
||||
}
|
||||
@ -145,7 +164,7 @@ func (s *StorageRedis) UpdateTTL(id string, ttl time.Duration) error {
|
||||
|
||||
// doUpdateTTL updates the TTL for session id.
|
||||
func (s *StorageRedis) doUpdateTTL(id string, ttlSeconds int) error {
|
||||
intlog.Printf("update StorageRedis TTL for session id: %s, %d", id, ttlSeconds)
|
||||
intlog.Printf("StorageRedis.doUpdateTTL: %s, %d", id, ttlSeconds)
|
||||
_, err := s.redis.DoVar("EXPIRE", s.key(id), ttlSeconds)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gsession
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/database/gredis"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -22,6 +23,7 @@ type StorageRedisHashTable struct {
|
||||
// NewStorageRedisHashTable creates and returns a redis hash table storage object for session.
|
||||
func NewStorageRedisHashTable(redis *gredis.Redis, prefix ...string) *StorageRedisHashTable {
|
||||
if redis == nil {
|
||||
panic("redis instance for storage cannot be empty")
|
||||
return nil
|
||||
}
|
||||
s := &StorageRedisHashTable{
|
||||
@ -42,8 +44,8 @@ func (s *StorageRedisHashTable) New(ttl time.Duration) (id string) {
|
||||
// Get retrieves session value with given key.
|
||||
// It returns nil if the key does not exist in the session.
|
||||
func (s *StorageRedisHashTable) Get(id string, key string) interface{} {
|
||||
r, _ := s.redis.Do("HGET", s.key(id), key)
|
||||
return r
|
||||
r, _ := s.redis.DoVar("HGET", s.key(id), key)
|
||||
return r.String()
|
||||
}
|
||||
|
||||
// GetMap retrieves all key-value pairs as map from storage.
|
||||
@ -55,7 +57,7 @@ func (s *StorageRedisHashTable) GetMap(id string) map[string]interface{} {
|
||||
array := r.Interfaces()
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < len(array); i += 2 {
|
||||
m[gconv.String(array[i])] = array[i+1]
|
||||
m[gconv.String(array[i])] = gconv.String(array[i+1])
|
||||
}
|
||||
return m
|
||||
}
|
||||
@ -101,21 +103,29 @@ func (s *StorageRedisHashTable) RemoveAll(id string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// GetSession returns the session data as map for given session id.
|
||||
// The parameter <ttl> specifies the TTL for this session.
|
||||
// It returns nil if the TTL is exceeded.
|
||||
func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration) map[string]interface{} {
|
||||
r, _ := s.redis.DoVar("EXISTS", s.key(id))
|
||||
if r.Bool() {
|
||||
return map[string]interface{}{}
|
||||
// GetSession returns the session data as *gmap.StrAnyMap for given session id from storage.
|
||||
//
|
||||
// The parameter <ttl> specifies the TTL for this session, and it returns nil if the TTL is exceeded.
|
||||
// The parameter <data> is the current old session data stored in memory,
|
||||
// and for some storage it might be nil if memory storage is disabled.
|
||||
//
|
||||
// This function is called ever when session starts.
|
||||
func (s *StorageRedisHashTable) GetSession(id string, ttl time.Duration, data *gmap.StrAnyMap) (*gmap.StrAnyMap, error) {
|
||||
intlog.Printf("StorageRedisHashTable.GetSession: %s, %v", id, ttl)
|
||||
r, err := s.redis.DoVar("EXISTS", s.key(id))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
if r.Bool() {
|
||||
return gmap.NewStrAnyMap(true), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SetSession updates the data map for specified session id.
|
||||
// This function is called ever after session, which is changed dirty, is closed.
|
||||
// This copy all session data map from memory to storage.
|
||||
func (s *StorageRedisHashTable) SetSession(id string, data map[string]interface{}, ttl time.Duration) error {
|
||||
func (s *StorageRedisHashTable) SetSession(id string, data *gmap.StrAnyMap, ttl time.Duration) error {
|
||||
intlog.Printf("StorageRedisHashTable.SetSession: %s, %v", id, ttl)
|
||||
_, err := s.redis.Do("EXPIRE", s.key(id), ttl.Seconds())
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user