Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
5.7 KiB
218 lines
5.7 KiB
// Copyright 2012 The Gorilla Authors. All rights reserved. |
|
// Use of this source code is governed by a BSD-style |
|
// license that can be found in the LICENSE file. |
|
|
|
package sessions |
|
|
|
import ( |
|
"context" |
|
"encoding/gob" |
|
"fmt" |
|
"net/http" |
|
"time" |
|
) |
|
|
|
// Default flashes key. |
|
const flashesKey = "_flash" |
|
|
|
// Session -------------------------------------------------------------------- |
|
|
|
// NewSession is called by session stores to create a new session instance. |
|
func NewSession(store Store, name string) *Session { |
|
return &Session{ |
|
Values: make(map[interface{}]interface{}), |
|
store: store, |
|
name: name, |
|
Options: new(Options), |
|
} |
|
} |
|
|
|
// Session stores the values and optional configuration for a session. |
|
type Session struct { |
|
// The ID of the session, generated by stores. It should not be used for |
|
// user data. |
|
ID string |
|
// Values contains the user-data for the session. |
|
Values map[interface{}]interface{} |
|
Options *Options |
|
IsNew bool |
|
store Store |
|
name string |
|
} |
|
|
|
// Flashes returns a slice of flash messages from the session. |
|
// |
|
// A single variadic argument is accepted, and it is optional: it defines |
|
// the flash key. If not defined "_flash" is used by default. |
|
func (s *Session) Flashes(vars ...string) []interface{} { |
|
var flashes []interface{} |
|
key := flashesKey |
|
if len(vars) > 0 { |
|
key = vars[0] |
|
} |
|
if v, ok := s.Values[key]; ok { |
|
// Drop the flashes and return it. |
|
delete(s.Values, key) |
|
flashes = v.([]interface{}) |
|
} |
|
return flashes |
|
} |
|
|
|
// AddFlash adds a flash message to the session. |
|
// |
|
// A single variadic argument is accepted, and it is optional: it defines |
|
// the flash key. If not defined "_flash" is used by default. |
|
func (s *Session) AddFlash(value interface{}, vars ...string) { |
|
key := flashesKey |
|
if len(vars) > 0 { |
|
key = vars[0] |
|
} |
|
var flashes []interface{} |
|
if v, ok := s.Values[key]; ok { |
|
flashes = v.([]interface{}) |
|
} |
|
s.Values[key] = append(flashes, value) |
|
} |
|
|
|
// Save is a convenience method to save this session. It is the same as calling |
|
// store.Save(request, response, session). You should call Save before writing to |
|
// the response or returning from the handler. |
|
func (s *Session) Save(r *http.Request, w http.ResponseWriter) error { |
|
return s.store.Save(r, w, s) |
|
} |
|
|
|
// Name returns the name used to register the session. |
|
func (s *Session) Name() string { |
|
return s.name |
|
} |
|
|
|
// Store returns the session store used to register the session. |
|
func (s *Session) Store() Store { |
|
return s.store |
|
} |
|
|
|
// Registry ------------------------------------------------------------------- |
|
|
|
// sessionInfo stores a session tracked by the registry. |
|
type sessionInfo struct { |
|
s *Session |
|
e error |
|
} |
|
|
|
// contextKey is the type used to store the registry in the context. |
|
type contextKey int |
|
|
|
// registryKey is the key used to store the registry in the context. |
|
const registryKey contextKey = 0 |
|
|
|
// GetRegistry returns a registry instance for the current request. |
|
func GetRegistry(r *http.Request) *Registry { |
|
var ctx = r.Context() |
|
registry := ctx.Value(registryKey) |
|
if registry != nil { |
|
return registry.(*Registry) |
|
} |
|
newRegistry := &Registry{ |
|
request: r, |
|
sessions: make(map[string]sessionInfo), |
|
} |
|
*r = *r.WithContext(context.WithValue(ctx, registryKey, newRegistry)) |
|
return newRegistry |
|
} |
|
|
|
// Registry stores sessions used during a request. |
|
type Registry struct { |
|
request *http.Request |
|
sessions map[string]sessionInfo |
|
} |
|
|
|
// Get registers and returns a session for the given name and session store. |
|
// |
|
// It returns a new session if there are no sessions registered for the name. |
|
func (s *Registry) Get(store Store, name string) (session *Session, err error) { |
|
if !isCookieNameValid(name) { |
|
return nil, fmt.Errorf("sessions: invalid character in cookie name: %s", name) |
|
} |
|
if info, ok := s.sessions[name]; ok { |
|
session, err = info.s, info.e |
|
} else { |
|
session, err = store.New(s.request, name) |
|
session.name = name |
|
s.sessions[name] = sessionInfo{s: session, e: err} |
|
} |
|
session.store = store |
|
return |
|
} |
|
|
|
// Save saves all sessions registered for the current request. |
|
func (s *Registry) Save(w http.ResponseWriter) error { |
|
var errMulti MultiError |
|
for name, info := range s.sessions { |
|
session := info.s |
|
if session.store == nil { |
|
errMulti = append(errMulti, fmt.Errorf( |
|
"sessions: missing store for session %q", name)) |
|
} else if err := session.store.Save(s.request, w, session); err != nil { |
|
errMulti = append(errMulti, fmt.Errorf( |
|
"sessions: error saving session %q -- %v", name, err)) |
|
} |
|
} |
|
if errMulti != nil { |
|
return errMulti |
|
} |
|
return nil |
|
} |
|
|
|
// Helpers -------------------------------------------------------------------- |
|
|
|
func init() { |
|
gob.Register([]interface{}{}) |
|
} |
|
|
|
// Save saves all sessions used during the current request. |
|
func Save(r *http.Request, w http.ResponseWriter) error { |
|
return GetRegistry(r).Save(w) |
|
} |
|
|
|
// NewCookie returns an http.Cookie with the options set. It also sets |
|
// the Expires field calculated based on the MaxAge value, for Internet |
|
// Explorer compatibility. |
|
func NewCookie(name, value string, options *Options) *http.Cookie { |
|
cookie := newCookieFromOptions(name, value, options) |
|
if options.MaxAge > 0 { |
|
d := time.Duration(options.MaxAge) * time.Second |
|
cookie.Expires = time.Now().Add(d) |
|
} else if options.MaxAge < 0 { |
|
// Set it to the past to expire now. |
|
cookie.Expires = time.Unix(1, 0) |
|
} |
|
return cookie |
|
} |
|
|
|
// Error ---------------------------------------------------------------------- |
|
|
|
// MultiError stores multiple errors. |
|
// |
|
// Borrowed from the App Engine SDK. |
|
type MultiError []error |
|
|
|
func (m MultiError) Error() string { |
|
s, n := "", 0 |
|
for _, e := range m { |
|
if e != nil { |
|
if n == 0 { |
|
s = e.Error() |
|
} |
|
n++ |
|
} |
|
} |
|
switch n { |
|
case 0: |
|
return "(0 errors)" |
|
case 1: |
|
return s |
|
case 2: |
|
return s + " (and 1 other error)" |
|
} |
|
return fmt.Sprintf("%s (and %d other errors)", s, n-1) |
|
}
|
|
|