github.com/cloudwego/localsession@v0.0.2/session.go (about)

     1  // Copyright 2023 CloudWeGo Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package localsession
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"sync/atomic"
    21  	"time"
    22  )
    23  
    24  // Session represents a local storage for one session
    25  type Session interface {
    26  	// IsValid tells if the session is valid at present
    27  	IsValid() bool
    28  
    29  	// Get returns value for specific key
    30  	Get(key interface{}) interface{}
    31  
    32  	// WithValue sets value for specific key,and return newly effective session
    33  	WithValue(key interface{}, val interface{}) Session
    34  }
    35  
    36  // SessionCtx implements Session with context,
    37  // which means children session WON'T affect parent and sibling sessions
    38  type SessionCtx struct {
    39  	enabled *atomic.Value
    40  	storage context.Context
    41  }
    42  
    43  // NewSessionCtx creates and enables a SessionCtx
    44  func NewSessionCtx(ctx context.Context) SessionCtx {
    45  	var enabled atomic.Value
    46  	enabled.Store(true)
    47  	return SessionCtx{
    48  		enabled: &enabled,
    49  		storage: ctx,
    50  	}
    51  }
    52  
    53  // NewSessionCtx creates and enables a SessionCtx,
    54  // and disable the session after timeout
    55  func NewSessionCtxWithTimeout(ctx context.Context, timeout time.Duration) SessionCtx {
    56  	ret := NewSessionCtx(ctx)
    57  	go func() {
    58  		<-time.NewTimer(timeout).C
    59  		ret.Disable()
    60  	}()
    61  	return ret
    62  }
    63  
    64  // Disable ends the session
    65  func (self SessionCtx) Disable() {
    66  	self.enabled.Store(false)
    67  }
    68  
    69  // Export exports underlying context
    70  func (self SessionCtx) Export() context.Context {
    71  	return self.storage
    72  }
    73  
    74  // IsValid tells if the session is valid at present
    75  func (self SessionCtx) IsValid() bool {
    76  	return self.enabled.Load().(bool)
    77  }
    78  
    79  // Get value for specific key
    80  func (self SessionCtx) Get(key interface{}) interface{} {
    81  	return self.storage.Value(key)
    82  }
    83  
    84  // Set value for specific key,and return newly effective session
    85  func (self SessionCtx) WithValue(key interface{}, val interface{}) Session {
    86  	ctx := context.WithValue(self.storage, key, val)
    87  	return SessionCtx{
    88  		enabled: self.enabled,
    89  		storage: ctx,
    90  	}
    91  }
    92  
    93  // NewSessionMap implements Session with map,
    94  // which means children session WILL affect parent session and sibling sessions
    95  type SessionMap struct {
    96  	enabled *atomic.Value
    97  	storage map[interface{}]interface{}
    98  	lock    sync.RWMutex
    99  }
   100  
   101  // NewSessionMap creates and enables a SessionMap
   102  func NewSessionMap(m map[interface{}]interface{}) *SessionMap {
   103  	var enabled atomic.Value
   104  	enabled.Store(true)
   105  	return &SessionMap{
   106  		enabled: &enabled,
   107  		storage: m,
   108  	}
   109  }
   110  
   111  // NewSessionCtx creates and enables a SessionCtx,
   112  // and disable the session after timeout
   113  func NewSessionMapWithTimeout(m map[interface{}]interface{}, timeout time.Duration) *SessionMap {
   114  	ret := NewSessionMap(m)
   115  	go func() {
   116  		<-time.NewTimer(timeout).C
   117  		ret.Disable()
   118  	}()
   119  	return ret
   120  }
   121  
   122  // IsValid tells if the session is valid at present
   123  func (self *SessionMap) IsValid() bool {
   124  	if self == nil {
   125  		return false
   126  	}
   127  	return self.enabled.Load().(bool)
   128  }
   129  
   130  // Disable ends the session
   131  func (self *SessionMap) Disable() {
   132  	if self == nil {
   133  		return
   134  	}
   135  	self.enabled.Store(false)
   136  }
   137  
   138  // Export COPIES and exports underlying map
   139  func (self *SessionMap) Export() map[interface{}]interface{} {
   140  	if self == nil {
   141  		return nil
   142  	}
   143  	m := make(map[interface{}]interface{}, len(self.storage))
   144  	self.lock.RLock()
   145  	for k, v := range self.storage {
   146  		m[k] = v
   147  	}
   148  	self.lock.RUnlock()
   149  	return m
   150  }
   151  
   152  // Get value for specific key
   153  func (self *SessionMap) Get(key interface{}) interface{} {
   154  	if self == nil {
   155  		return nil
   156  	}
   157  	self.lock.RLock()
   158  	val := self.storage[key]
   159  	self.lock.RUnlock()
   160  	return val
   161  }
   162  
   163  // Set value for specific key,and return itself
   164  func (self *SessionMap) WithValue(key interface{}, val interface{}) Session {
   165  	if self == nil {
   166  		return nil
   167  	}
   168  	self.lock.Lock()
   169  	self.storage[key] = val
   170  	self.lock.Unlock()
   171  	return self
   172  }