github.com/gofiber/fiber/v2@v2.47.0/middleware/session/session.go (about) 1 package session 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 "fmt" 7 "sync" 8 "time" 9 10 "github.com/gofiber/fiber/v2" 11 "github.com/gofiber/fiber/v2/utils" 12 13 "github.com/valyala/fasthttp" 14 ) 15 16 type Session struct { 17 id string // session id 18 fresh bool // if new session 19 ctx *fiber.Ctx // fiber context 20 config *Store // store configuration 21 data *data // key value data 22 byteBuffer *bytes.Buffer // byte buffer for the en- and decode 23 exp time.Duration // expiration of this session 24 } 25 26 var sessionPool = sync.Pool{ 27 New: func() interface{} { 28 return new(Session) 29 }, 30 } 31 32 func acquireSession() *Session { 33 s := sessionPool.Get().(*Session) //nolint:forcetypeassert,errcheck // We store nothing else in the pool 34 if s.data == nil { 35 s.data = acquireData() 36 } 37 if s.byteBuffer == nil { 38 s.byteBuffer = new(bytes.Buffer) 39 } 40 s.fresh = true 41 return s 42 } 43 44 func releaseSession(s *Session) { 45 s.id = "" 46 s.exp = 0 47 s.ctx = nil 48 s.config = nil 49 if s.data != nil { 50 s.data.Reset() 51 } 52 if s.byteBuffer != nil { 53 s.byteBuffer.Reset() 54 } 55 sessionPool.Put(s) 56 } 57 58 // Fresh is true if the current session is new 59 func (s *Session) Fresh() bool { 60 return s.fresh 61 } 62 63 // ID returns the session id 64 func (s *Session) ID() string { 65 return s.id 66 } 67 68 // Get will return the value 69 func (s *Session) Get(key string) interface{} { 70 // Better safe than sorry 71 if s.data == nil { 72 return nil 73 } 74 return s.data.Get(key) 75 } 76 77 // Set will update or create a new key value 78 func (s *Session) Set(key string, val interface{}) { 79 // Better safe than sorry 80 if s.data == nil { 81 return 82 } 83 s.data.Set(key, val) 84 } 85 86 // Delete will delete the value 87 func (s *Session) Delete(key string) { 88 // Better safe than sorry 89 if s.data == nil { 90 return 91 } 92 s.data.Delete(key) 93 } 94 95 // Destroy will delete the session from Storage and expire session cookie 96 func (s *Session) Destroy() error { 97 // Better safe than sorry 98 if s.data == nil { 99 return nil 100 } 101 102 // Reset local data 103 s.data.Reset() 104 105 // Use external Storage if exist 106 if err := s.config.Storage.Delete(s.id); err != nil { 107 return err 108 } 109 110 // Expire session 111 s.delSession() 112 return nil 113 } 114 115 // Regenerate generates a new session id and delete the old one from Storage 116 func (s *Session) Regenerate() error { 117 // Delete old id from storage 118 if err := s.config.Storage.Delete(s.id); err != nil { 119 return err 120 } 121 122 // Generate a new session, and set session.fresh to true 123 s.refresh() 124 125 return nil 126 } 127 128 // refresh generates a new session, and set session.fresh to be true 129 func (s *Session) refresh() { 130 // Create a new id 131 s.id = s.config.KeyGenerator() 132 133 // We assign a new id to the session, so the session must be fresh 134 s.fresh = true 135 } 136 137 // Save will update the storage and client cookie 138 func (s *Session) Save() error { 139 // Better safe than sorry 140 if s.data == nil { 141 return nil 142 } 143 144 // Check if session has your own expiration, otherwise use default value 145 if s.exp <= 0 { 146 s.exp = s.config.Expiration 147 } 148 149 // Update client cookie 150 s.setSession() 151 152 // Convert data to bytes 153 mux.Lock() 154 defer mux.Unlock() 155 encCache := gob.NewEncoder(s.byteBuffer) 156 err := encCache.Encode(&s.data.Data) 157 if err != nil { 158 return fmt.Errorf("failed to encode data: %w", err) 159 } 160 161 // copy the data in buffer 162 encodedBytes := make([]byte, s.byteBuffer.Len()) 163 copy(encodedBytes, s.byteBuffer.Bytes()) 164 165 // pass copied bytes with session id to provider 166 if err := s.config.Storage.Set(s.id, encodedBytes, s.exp); err != nil { 167 return err 168 } 169 170 // Release session 171 // TODO: It's not safe to use the Session after called Save() 172 releaseSession(s) 173 174 return nil 175 } 176 177 // Keys will retrieve all keys in current session 178 func (s *Session) Keys() []string { 179 if s.data == nil { 180 return []string{} 181 } 182 return s.data.Keys() 183 } 184 185 // SetExpiry sets a specific expiration for this session 186 func (s *Session) SetExpiry(exp time.Duration) { 187 s.exp = exp 188 } 189 190 func (s *Session) setSession() { 191 if s.config.source == SourceHeader { 192 s.ctx.Request().Header.SetBytesV(s.config.sessionName, []byte(s.id)) 193 s.ctx.Response().Header.SetBytesV(s.config.sessionName, []byte(s.id)) 194 } else { 195 fcookie := fasthttp.AcquireCookie() 196 fcookie.SetKey(s.config.sessionName) 197 fcookie.SetValue(s.id) 198 fcookie.SetPath(s.config.CookiePath) 199 fcookie.SetDomain(s.config.CookieDomain) 200 // Cookies are also session cookies if they do not specify the Expires or Max-Age attribute. 201 // refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie 202 if !s.config.CookieSessionOnly { 203 fcookie.SetMaxAge(int(s.exp.Seconds())) 204 fcookie.SetExpire(time.Now().Add(s.exp)) 205 } 206 fcookie.SetSecure(s.config.CookieSecure) 207 fcookie.SetHTTPOnly(s.config.CookieHTTPOnly) 208 209 switch utils.ToLower(s.config.CookieSameSite) { 210 case "strict": 211 fcookie.SetSameSite(fasthttp.CookieSameSiteStrictMode) 212 case "none": 213 fcookie.SetSameSite(fasthttp.CookieSameSiteNoneMode) 214 default: 215 fcookie.SetSameSite(fasthttp.CookieSameSiteLaxMode) 216 } 217 s.ctx.Response().Header.SetCookie(fcookie) 218 fasthttp.ReleaseCookie(fcookie) 219 } 220 } 221 222 func (s *Session) delSession() { 223 if s.config.source == SourceHeader { 224 s.ctx.Request().Header.Del(s.config.sessionName) 225 s.ctx.Response().Header.Del(s.config.sessionName) 226 } else { 227 s.ctx.Request().Header.DelCookie(s.config.sessionName) 228 s.ctx.Response().Header.DelCookie(s.config.sessionName) 229 230 fcookie := fasthttp.AcquireCookie() 231 fcookie.SetKey(s.config.sessionName) 232 fcookie.SetPath(s.config.CookiePath) 233 fcookie.SetDomain(s.config.CookieDomain) 234 fcookie.SetMaxAge(-1) 235 fcookie.SetExpire(time.Now().Add(-1 * time.Minute)) 236 fcookie.SetSecure(s.config.CookieSecure) 237 fcookie.SetHTTPOnly(s.config.CookieHTTPOnly) 238 239 switch utils.ToLower(s.config.CookieSameSite) { 240 case "strict": 241 fcookie.SetSameSite(fasthttp.CookieSameSiteStrictMode) 242 case "none": 243 fcookie.SetSameSite(fasthttp.CookieSameSiteNoneMode) 244 default: 245 fcookie.SetSameSite(fasthttp.CookieSameSiteLaxMode) 246 } 247 248 s.ctx.Response().Header.SetCookie(fcookie) 249 fasthttp.ReleaseCookie(fcookie) 250 } 251 }