github.com/wangyougui/gf/v2@v2.6.5/os/gsession/gsession_session.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gsession 8 9 import ( 10 "context" 11 "time" 12 13 "github.com/wangyougui/gf/v2/container/gmap" 14 "github.com/wangyougui/gf/v2/container/gvar" 15 "github.com/wangyougui/gf/v2/errors/gcode" 16 "github.com/wangyougui/gf/v2/errors/gerror" 17 "github.com/wangyougui/gf/v2/internal/intlog" 18 ) 19 20 // Session struct for storing single session data, which is bound to a single request. 21 // The Session struct is the interface with user, but the Storage is the underlying adapter designed interface 22 // for functionality implements. 23 type Session struct { 24 id string // Session id. It retrieves the session if id is custom specified. 25 ctx context.Context // Context for current session. Please note that, session lives along with context. 26 data *gmap.StrAnyMap // Current Session data, which is retrieved from Storage. 27 dirty bool // Used to mark session is modified. 28 start bool // Used to mark session is started. 29 manager *Manager // Parent session Manager. 30 31 // idFunc is a callback function used for creating custom session id. 32 // This is called if session id is empty ever when session starts. 33 idFunc func(ttl time.Duration) (id string) 34 } 35 36 // init does the lazy initialization for session, which retrieves the session if session id is specified, 37 // or else it creates a new empty session. 38 func (s *Session) init() error { 39 if s.start { 40 return nil 41 } 42 var err error 43 // Session retrieving. 44 if s.id != "" { 45 // Retrieve stored session data from storage. 46 if s.manager.storage != nil { 47 s.data, err = s.manager.storage.GetSession(s.ctx, s.id, s.manager.GetTTL()) 48 if err != nil && err != ErrorDisabled { 49 intlog.Errorf(s.ctx, `session restoring failed for id "%s": %+v`, s.id, err) 50 return err 51 } 52 } 53 } 54 // Session id creation. 55 if s.id == "" { 56 if s.idFunc != nil { 57 // Use custom session id creating function. 58 s.id = s.idFunc(s.manager.ttl) 59 } else { 60 // Use default session id creating function of storage. 61 s.id, err = s.manager.storage.New(s.ctx, s.manager.ttl) 62 if err != nil && err != ErrorDisabled { 63 intlog.Errorf(s.ctx, "create session id failed: %+v", err) 64 return err 65 } 66 // If session storage does not implements id generating functionality, 67 // it then uses default session id creating function. 68 if s.id == "" { 69 s.id = NewSessionId() 70 } 71 } 72 } 73 if s.data == nil { 74 s.data = gmap.NewStrAnyMap(true) 75 } 76 s.start = true 77 return nil 78 } 79 80 // Close closes current session and updates its ttl in the session manager. 81 // If this session is dirty, it also exports it to storage. 82 // 83 // NOTE that this function must be called ever after a session request done. 84 func (s *Session) Close() error { 85 if s.manager.storage == nil { 86 return nil 87 } 88 if s.start && s.id != "" { 89 size := s.data.Size() 90 if s.dirty { 91 err := s.manager.storage.SetSession(s.ctx, s.id, s.data, s.manager.ttl) 92 if err != nil && err != ErrorDisabled { 93 return err 94 } 95 } else if size > 0 { 96 err := s.manager.storage.UpdateTTL(s.ctx, s.id, s.manager.ttl) 97 if err != nil && err != ErrorDisabled { 98 return err 99 } 100 } 101 } 102 return nil 103 } 104 105 // Set sets key-value pair to this session. 106 func (s *Session) Set(key string, value interface{}) (err error) { 107 if err = s.init(); err != nil { 108 return err 109 } 110 if err = s.manager.storage.Set(s.ctx, s.id, key, value, s.manager.ttl); err != nil { 111 if err == ErrorDisabled { 112 s.data.Set(key, value) 113 } else { 114 return err 115 } 116 } 117 s.dirty = true 118 return nil 119 } 120 121 // SetMap batch sets the session using map. 122 func (s *Session) SetMap(data map[string]interface{}) (err error) { 123 if err = s.init(); err != nil { 124 return err 125 } 126 if err = s.manager.storage.SetMap(s.ctx, s.id, data, s.manager.ttl); err != nil { 127 if err == ErrorDisabled { 128 s.data.Sets(data) 129 } else { 130 return err 131 } 132 } 133 s.dirty = true 134 return nil 135 } 136 137 // Remove removes key along with its value from this session. 138 func (s *Session) Remove(keys ...string) (err error) { 139 if s.id == "" { 140 return nil 141 } 142 if err = s.init(); err != nil { 143 return err 144 } 145 for _, key := range keys { 146 if err = s.manager.storage.Remove(s.ctx, s.id, key); err != nil { 147 if err == ErrorDisabled { 148 s.data.Remove(key) 149 } else { 150 return err 151 } 152 } 153 } 154 s.dirty = true 155 return nil 156 } 157 158 // RemoveAll deletes all key-value pairs from this session. 159 func (s *Session) RemoveAll() (err error) { 160 if s.id == "" { 161 return nil 162 } 163 if err = s.init(); err != nil { 164 return err 165 } 166 if err = s.manager.storage.RemoveAll(s.ctx, s.id); err != nil { 167 if err != ErrorDisabled { 168 return err 169 } 170 } 171 // Remove data from memory. 172 if s.data != nil { 173 s.data.Clear() 174 } 175 s.dirty = true 176 return nil 177 } 178 179 // Id returns the session id for this session. 180 // It creates and returns a new session id if the session id is not passed in initialization. 181 func (s *Session) Id() (id string, err error) { 182 if err = s.init(); err != nil { 183 return "", err 184 } 185 return s.id, nil 186 } 187 188 // SetId sets custom session before session starts. 189 // It returns error if it is called after session starts. 190 func (s *Session) SetId(id string) error { 191 if s.start { 192 return gerror.NewCode(gcode.CodeInvalidOperation, "session already started") 193 } 194 s.id = id 195 return nil 196 } 197 198 // SetIdFunc sets custom session id creating function before session starts. 199 // It returns error if it is called after session starts. 200 func (s *Session) SetIdFunc(f func(ttl time.Duration) string) error { 201 if s.start { 202 return gerror.NewCode(gcode.CodeInvalidOperation, "session already started") 203 } 204 s.idFunc = f 205 return nil 206 } 207 208 // Data returns all data as map. 209 // Note that it's using value copy internally for concurrent-safe purpose. 210 func (s *Session) Data() (sessionData map[string]interface{}, err error) { 211 if s.id == "" { 212 return map[string]interface{}{}, nil 213 } 214 if err = s.init(); err != nil { 215 return nil, err 216 } 217 sessionData, err = s.manager.storage.Data(s.ctx, s.id) 218 if err != nil && err != ErrorDisabled { 219 intlog.Errorf(s.ctx, `%+v`, err) 220 } 221 if sessionData != nil { 222 return sessionData, nil 223 } 224 return s.data.Map(), nil 225 } 226 227 // Size returns the size of the session. 228 func (s *Session) Size() (size int, err error) { 229 if s.id == "" { 230 return 0, nil 231 } 232 if err = s.init(); err != nil { 233 return 0, err 234 } 235 size, err = s.manager.storage.GetSize(s.ctx, s.id) 236 if err != nil && err != ErrorDisabled { 237 intlog.Errorf(s.ctx, `%+v`, err) 238 } 239 if size > 0 { 240 return size, nil 241 } 242 return s.data.Size(), nil 243 } 244 245 // Contains checks whether key exist in the session. 246 func (s *Session) Contains(key string) (ok bool, err error) { 247 if s.id == "" { 248 return false, nil 249 } 250 if err = s.init(); err != nil { 251 return false, err 252 } 253 v, err := s.Get(key) 254 if err != nil { 255 return false, err 256 } 257 return !v.IsNil(), nil 258 } 259 260 // IsDirty checks whether there's any data changes in the session. 261 func (s *Session) IsDirty() bool { 262 return s.dirty 263 } 264 265 // Get retrieves session value with given key. 266 // It returns `def` if the key does not exist in the session if `def` is given, 267 // or else it returns nil. 268 func (s *Session) Get(key string, def ...interface{}) (value *gvar.Var, err error) { 269 if s.id == "" { 270 return nil, nil 271 } 272 if err = s.init(); err != nil { 273 return nil, err 274 } 275 v, err := s.manager.storage.Get(s.ctx, s.id, key) 276 if err != nil && err != ErrorDisabled { 277 intlog.Errorf(s.ctx, `%+v`, err) 278 return nil, err 279 } 280 if v != nil { 281 return gvar.New(v), nil 282 } 283 if v = s.data.Get(key); v != nil { 284 return gvar.New(v), nil 285 } 286 if len(def) > 0 { 287 return gvar.New(def[0]), nil 288 } 289 return nil, nil 290 } 291 292 // MustId performs as function Id, but it panics if any error occurs. 293 func (s *Session) MustId() string { 294 id, err := s.Id() 295 if err != nil { 296 panic(err) 297 } 298 return id 299 } 300 301 // MustGet performs as function Get, but it panics if any error occurs. 302 func (s *Session) MustGet(key string, def ...interface{}) *gvar.Var { 303 v, err := s.Get(key, def...) 304 if err != nil { 305 panic(err) 306 } 307 return v 308 } 309 310 // MustSet performs as function Set, but it panics if any error occurs. 311 func (s *Session) MustSet(key string, value interface{}) { 312 err := s.Set(key, value) 313 if err != nil { 314 panic(err) 315 } 316 } 317 318 // MustSetMap performs as function SetMap, but it panics if any error occurs. 319 func (s *Session) MustSetMap(data map[string]interface{}) { 320 err := s.SetMap(data) 321 if err != nil { 322 panic(err) 323 } 324 } 325 326 // MustContains performs as function Contains, but it panics if any error occurs. 327 func (s *Session) MustContains(key string) bool { 328 b, err := s.Contains(key) 329 if err != nil { 330 panic(err) 331 } 332 return b 333 } 334 335 // MustData performs as function Data, but it panics if any error occurs. 336 func (s *Session) MustData() map[string]interface{} { 337 m, err := s.Data() 338 if err != nil { 339 panic(err) 340 } 341 return m 342 } 343 344 // MustSize performs as function Size, but it panics if any error occurs. 345 func (s *Session) MustSize() int { 346 size, err := s.Size() 347 if err != nil { 348 panic(err) 349 } 350 return size 351 } 352 353 // MustRemove performs as function Remove, but it panics if any error occurs. 354 func (s *Session) MustRemove(keys ...string) { 355 err := s.Remove(keys...) 356 if err != nil { 357 panic(err) 358 } 359 }