1
2
3
4
5 package sessions
6
7 import (
8 "encoding/base32"
9 "io/ioutil"
10 "net/http"
11 "os"
12 "path/filepath"
13 "strings"
14 "sync"
15
16 "github.com/gorilla/securecookie"
17 )
18
19
20
21
22 type Store interface {
23
24 Get(r *http.Request, name string) (*Session, error)
25
26
27
28
29
30 New(r *http.Request, name string) (*Session, error)
31
32
33 Save(r *http.Request, w http.ResponseWriter, s *Session) error
34 }
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 func NewCookieStore(keyPairs ...[]byte) *CookieStore {
51 cs := &CookieStore{
52 Codecs: securecookie.CodecsFromPairs(keyPairs...),
53 Options: &Options{
54 Path: "/",
55 MaxAge: 86400 * 30,
56 },
57 }
58
59 cs.MaxAge(cs.Options.MaxAge)
60 return cs
61 }
62
63
64 type CookieStore struct {
65 Codecs []securecookie.Codec
66 Options *Options
67 }
68
69
70
71
72
73
74
75
76 func (s *CookieStore) Get(r *http.Request, name string) (*Session, error) {
77 return GetRegistry(r).Get(s, name)
78 }
79
80
81
82
83
84
85 func (s *CookieStore) New(r *http.Request, name string) (*Session, error) {
86 session := NewSession(s, name)
87 opts := *s.Options
88 session.Options = &opts
89 session.IsNew = true
90 var err error
91 if c, errCookie := r.Cookie(name); errCookie == nil {
92 err = securecookie.DecodeMulti(name, c.Value, &session.Values,
93 s.Codecs...)
94 if err == nil {
95 session.IsNew = false
96 }
97 }
98 return session, err
99 }
100
101
102 func (s *CookieStore) Save(r *http.Request, w http.ResponseWriter,
103 session *Session) error {
104 encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
105 s.Codecs...)
106 if err != nil {
107 return err
108 }
109 http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
110 return nil
111 }
112
113
114
115
116 func (s *CookieStore) MaxAge(age int) {
117 s.Options.MaxAge = age
118
119
120 for _, codec := range s.Codecs {
121 if sc, ok := codec.(*securecookie.SecureCookie); ok {
122 sc.MaxAge(age)
123 }
124 }
125 }
126
127
128
129 var fileMutex sync.RWMutex
130
131
132
133
134
135
136
137 func NewFilesystemStore(path string, keyPairs ...[]byte) *FilesystemStore {
138 if path == "" {
139 path = os.TempDir()
140 }
141 fs := &FilesystemStore{
142 Codecs: securecookie.CodecsFromPairs(keyPairs...),
143 Options: &Options{
144 Path: "/",
145 MaxAge: 86400 * 30,
146 },
147 path: path,
148 }
149
150 fs.MaxAge(fs.Options.MaxAge)
151 return fs
152 }
153
154
155
156
157
158
159 type FilesystemStore struct {
160 Codecs []securecookie.Codec
161 Options *Options
162 path string
163 }
164
165
166
167
168 func (s *FilesystemStore) MaxLength(l int) {
169 for _, c := range s.Codecs {
170 if codec, ok := c.(*securecookie.SecureCookie); ok {
171 codec.MaxLength(l)
172 }
173 }
174 }
175
176
177
178
179 func (s *FilesystemStore) Get(r *http.Request, name string) (*Session, error) {
180 return GetRegistry(r).Get(s, name)
181 }
182
183
184
185
186 func (s *FilesystemStore) New(r *http.Request, name string) (*Session, error) {
187 session := NewSession(s, name)
188 opts := *s.Options
189 session.Options = &opts
190 session.IsNew = true
191 var err error
192 if c, errCookie := r.Cookie(name); errCookie == nil {
193 err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.Codecs...)
194 if err == nil {
195 err = s.load(session)
196 if err == nil {
197 session.IsNew = false
198 }
199 }
200 }
201 return session, err
202 }
203
204
205
206
207
208
209
210 func (s *FilesystemStore) Save(r *http.Request, w http.ResponseWriter,
211 session *Session) error {
212
213 if session.Options.MaxAge <= 0 {
214 if err := s.erase(session); err != nil {
215 return err
216 }
217 http.SetCookie(w, NewCookie(session.Name(), "", session.Options))
218 return nil
219 }
220
221 if session.ID == "" {
222
223
224 session.ID = strings.TrimRight(
225 base32.StdEncoding.EncodeToString(
226 securecookie.GenerateRandomKey(32)), "=")
227 }
228 if err := s.save(session); err != nil {
229 return err
230 }
231 encoded, err := securecookie.EncodeMulti(session.Name(), session.ID,
232 s.Codecs...)
233 if err != nil {
234 return err
235 }
236 http.SetCookie(w, NewCookie(session.Name(), encoded, session.Options))
237 return nil
238 }
239
240
241
242
243 func (s *FilesystemStore) MaxAge(age int) {
244 s.Options.MaxAge = age
245
246
247 for _, codec := range s.Codecs {
248 if sc, ok := codec.(*securecookie.SecureCookie); ok {
249 sc.MaxAge(age)
250 }
251 }
252 }
253
254
255 func (s *FilesystemStore) save(session *Session) error {
256 encoded, err := securecookie.EncodeMulti(session.Name(), session.Values,
257 s.Codecs...)
258 if err != nil {
259 return err
260 }
261 filename := filepath.Join(s.path, "session_"+session.ID)
262 fileMutex.Lock()
263 defer fileMutex.Unlock()
264 return ioutil.WriteFile(filename, []byte(encoded), 0600)
265 }
266
267
268 func (s *FilesystemStore) load(session *Session) error {
269 filename := filepath.Join(s.path, "session_"+session.ID)
270 fileMutex.RLock()
271 defer fileMutex.RUnlock()
272 fdata, err := ioutil.ReadFile(filename)
273 if err != nil {
274 return err
275 }
276 if err = securecookie.DecodeMulti(session.Name(), string(fdata),
277 &session.Values, s.Codecs...); err != nil {
278 return err
279 }
280 return nil
281 }
282
283
284 func (s *FilesystemStore) erase(session *Session) error {
285 filename := filepath.Join(s.path, "session_"+session.ID)
286
287 fileMutex.RLock()
288 defer fileMutex.RUnlock()
289
290 err := os.Remove(filename)
291 return err
292 }
293
View as plain text