github.com/gogf/gf/v2@v2.7.4/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/gogf/gf.
     6  
     7  package gsession
     8  
     9  import (
    10  	"context"
    11  	"time"
    12  
    13  	"github.com/gogf/gf/v2/container/gmap"
    14  	"github.com/gogf/gf/v2/container/gvar"
    15  	"github.com/gogf/gf/v2/errors/gcode"
    16  	"github.com/gogf/gf/v2/errors/gerror"
    17  	"github.com/gogf/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  }