1
2
3
4
5 package sessions
6
7 import (
8 "context"
9 "encoding/gob"
10 "fmt"
11 "net/http"
12 "time"
13 )
14
15
16 const flashesKey = "_flash"
17
18
19
20
21 func NewSession(store Store, name string) *Session {
22 return &Session{
23 Values: make(map[interface{}]interface{}),
24 store: store,
25 name: name,
26 Options: new(Options),
27 }
28 }
29
30
31 type Session struct {
32
33
34 ID string
35
36 Values map[interface{}]interface{}
37 Options *Options
38 IsNew bool
39 store Store
40 name string
41 }
42
43
44
45
46
47 func (s *Session) Flashes(vars ...string) []interface{} {
48 var flashes []interface{}
49 key := flashesKey
50 if len(vars) > 0 {
51 key = vars[0]
52 }
53 if v, ok := s.Values[key]; ok {
54
55 delete(s.Values, key)
56 flashes = v.([]interface{})
57 }
58 return flashes
59 }
60
61
62
63
64
65 func (s *Session) AddFlash(value interface{}, vars ...string) {
66 key := flashesKey
67 if len(vars) > 0 {
68 key = vars[0]
69 }
70 var flashes []interface{}
71 if v, ok := s.Values[key]; ok {
72 flashes = v.([]interface{})
73 }
74 s.Values[key] = append(flashes, value)
75 }
76
77
78
79
80 func (s *Session) Save(r *http.Request, w http.ResponseWriter) error {
81 return s.store.Save(r, w, s)
82 }
83
84
85 func (s *Session) Name() string {
86 return s.name
87 }
88
89
90 func (s *Session) Store() Store {
91 return s.store
92 }
93
94
95
96
97 type sessionInfo struct {
98 s *Session
99 e error
100 }
101
102
103 type contextKey int
104
105
106 const registryKey contextKey = 0
107
108
109 func GetRegistry(r *http.Request) *Registry {
110 var ctx = r.Context()
111 registry := ctx.Value(registryKey)
112 if registry != nil {
113 return registry.(*Registry)
114 }
115 newRegistry := &Registry{
116 request: r,
117 sessions: make(map[string]sessionInfo),
118 }
119 *r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry))
120 return newRegistry
121 }
122
123
124 type Registry struct {
125 request *http.Request
126 sessions map[string]sessionInfo
127 }
128
129
130
131
132 func (s *Registry) Get(store Store, name string) (session *Session, err error) {
133 if !isCookieNameValid(name) {
134 return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name)
135 }
136 if info, ok := s.sessions[name]; ok {
137 session, err = info.s, info.e
138 } else {
139 session, err = store.New(s.request, name)
140 session.name = name
141 s.sessions[name] = sessionInfo{s: session, e: err}
142 }
143 session.store = store
144 return
145 }
146
147
148 func (s *Registry) Save(w http.ResponseWriter) error {
149 var errMulti MultiError
150 for name, info := range s.sessions {
151 session := info.s
152 if session.store == nil {
153 errMulti = append(errMulti, fmt.Errorf(
154 "sessions: missing store for session %q", name))
155 } else if err := session.store.Save(s.request, w, session); err != nil {
156 errMulti = append(errMulti, fmt.Errorf(
157 "sessions: error saving session %q -- %v", name, err))
158 }
159 }
160 if errMulti != nil {
161 return errMulti
162 }
163 return nil
164 }
165
166
167
168 func init() {
169 gob.Register([]interface{}{})
170 }
171
172
173 func Save(r *http.Request, w http.ResponseWriter) error {
174 return GetRegistry(r).Save(w)
175 }
176
177
178
179
180 func NewCookie(name, value string, options *Options) *http.Cookie {
181 cookie := newCookieFromOptions(name, value, options)
182 if options.MaxAge > 0 {
183 d := time.Duration(options.MaxAge) * time.Second
184 cookie.Expires = time.Now().Add(d)
185 } else if options.MaxAge < 0 {
186
187 cookie.Expires = time.Unix(1, 0)
188 }
189 return cookie
190 }
191
192
193
194
195
196
197 type MultiError []error
198
199 func (m MultiError) Error() string {
200 s, n := "", 0
201 for _, e := range m {
202 if e != nil {
203 if n == 0 {
204 s = e.Error()
205 }
206 n++
207 }
208 }
209 switch n {
210 case 0:
211 return "(0 errors)"
212 case 1:
213 return s
214 case 2:
215 return s + " (and 1 other error)"
216 }
217 return fmt.Sprintf("%s (and %d other errors)", s, n-1)
218 }
219
View as plain text