forked from gofiber/fiber
-
Notifications
You must be signed in to change notification settings - Fork 0
/
store.go
154 lines (128 loc) · 3.02 KB
/
store.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package session
import (
"encoding/gob"
"errors"
"fmt"
"sync"
"github.com/hakantaskin/fiber"
"github.com/hakantaskin/fiber/internal/storage/memory"
"github.com/hakantaskin/fiber/utils"
"github.com/valyala/fasthttp"
)
// ErrEmptySessionID is an error that occurs when the session ID is empty.
var ErrEmptySessionID = errors.New("session id cannot be empty")
type Store struct {
Config
}
var mux sync.Mutex
func New(config ...Config) *Store {
// Set default config
cfg := configDefault(config...)
if cfg.Storage == nil {
cfg.Storage = memory.New()
}
return &Store{
cfg,
}
}
// RegisterType will allow you to encode/decode custom types
// into any Storage provider
func (*Store) RegisterType(i interface{}) {
gob.Register(i)
}
// Get will get/create a session
func (s *Store) Get(c *fiber.Ctx) (*Session, error) {
var fresh bool
loadData := true
id := s.getSessionID(c)
if len(id) == 0 {
fresh = true
var err error
if id, err = s.responseCookies(c); err != nil {
return nil, err
}
}
// If no key exist, create new one
if len(id) == 0 {
loadData = false
id = s.KeyGenerator()
}
// Create session object
sess := acquireSession()
sess.ctx = c
sess.config = s
sess.id = id
sess.fresh = fresh
// Fetch existing data
if loadData {
raw, err := s.Storage.Get(id)
// Unmarshal if we found data
if raw != nil && err == nil {
mux.Lock()
defer mux.Unlock()
_, _ = sess.byteBuffer.Write(raw) //nolint:errcheck // This will never fail
encCache := gob.NewDecoder(sess.byteBuffer)
err := encCache.Decode(&sess.data.Data)
if err != nil {
return nil, fmt.Errorf("failed to decode session data: %w", err)
}
} else if err != nil {
return nil, err
} else {
// both raw and err is nil, which means id is not in the storage
sess.fresh = true
}
}
return sess, nil
}
// getSessionID will return the session id from:
// 1. cookie
// 2. http headers
// 3. query string
func (s *Store) getSessionID(c *fiber.Ctx) string {
id := c.Cookies(s.sessionName)
if len(id) > 0 {
return utils.CopyString(id)
}
if s.source == SourceHeader {
id = string(c.Request().Header.Peek(s.sessionName))
if len(id) > 0 {
return id
}
}
if s.source == SourceURLQuery {
id = c.Query(s.sessionName)
if len(id) > 0 {
return utils.CopyString(id)
}
}
return ""
}
func (s *Store) responseCookies(c *fiber.Ctx) (string, error) {
// Get key from response cookie
cookieValue := c.Response().Header.PeekCookie(s.sessionName)
if len(cookieValue) == 0 {
return "", nil
}
cookie := fasthttp.AcquireCookie()
defer fasthttp.ReleaseCookie(cookie)
err := cookie.ParseBytes(cookieValue)
if err != nil {
return "", err
}
value := make([]byte, len(cookie.Value()))
copy(value, cookie.Value())
id := string(value)
return id, nil
}
// Reset will delete all session from the storage
func (s *Store) Reset() error {
return s.Storage.Reset()
}
// Delete deletes a session by its id.
func (s *Store) Delete(id string) error {
if id == "" {
return ErrEmptySessionID
}
return s.Storage.Delete(id)
}