func (s *Encoder) timestamp() int64 {
	if s.TimeFunc == nil {
		return time.UTC().Seconds()
	}
	return s.TimeFunc()
}
timestamp returns the current timestamp, in seconds.

For testing purposes, the function that generates the timestamp can be
overridden. If TimeFunc is not set, it will return time.UTC().Seconds().
package sessions
import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/subtle"
	"encoding/base64"
	"errors"
	"fmt"
	"gob"
	"hash"
	"http"
	"strconv"
	"time"
	"gorilla.googlecode.com/hg/gorilla/context"
)
func init() {
	gob.Register(SessionData{})
	gob.Register([]interface{}{})
	DefaultSessionFactory.SetStore("cookie", new(CookieSessionStore))
}
var (
	DefaultSessionKey = "s"
	DefaultStoreKey   = "cookie"
	DefaultFlashesKey = "_flash"
	ErrEncoding       = errors.New("The value could not be encoded.")
	ErrDecoding       = errors.New("The value could not be decoded.")
	ErrAuthentication = errors.New("The value could not be verified using HMAC.")
	ErrDecryption     = errors.New("The value could not be decrypted.")
	ErrMaxLength      = errors.New("The value exceeds the maximum allowed length.")
	ErrBadTimestamp   = errors.New("Invalid timestamp.")
	ErrNewTimestamp   = errors.New("The value has a newer timestamp than expected.")
	ErrOldTimestamp   = errors.New("The value has an expired timestamp.")
	ErrMissingHash    = errors.New("A hash is required to create and verify values using HMAC.")
	ErrMissingHashKey = errors.New("Authentication secret can't be nil.")
	ErrNoSession      = errors.New("No session found for the given key.")
	ErrNoFlashes      = errors.New("No flashes found for the given key.")
	ErrNoStore        = errors.New("No store found for the given key.")
	ErrStoreMismatch  = errors.New("A session with the given key already exists using a different store.")
	ErrBadIdLength    = errors.New("Session id length must be greater than zero.")
)
type SessionData map[string]interface{}
type SessionConfig struct {
	Path   string
	Domain string
	MaxAge   int
	Secure   bool
	HttpOnly bool
}
type SessionInfo struct {
	Data   SessionData
	Store  SessionStore
	Config SessionConfig
}
func Session(r *http.Request, vars ...string) (SessionData, error) {
	return DefaultSessionFactory.Session(r, vars...)
}
func Flashes(r *http.Request, vars ...string) ([]interface{}, error) {
	return DefaultSessionFactory.Flashes(r, vars...)
}
func AddFlash(r *http.Request, value interface{},
	vars ...string) (bool, error) {
	return DefaultSessionFactory.AddFlash(r, value, vars...)
}
func Config(r *http.Request, key ...string) (*SessionConfig, error) {
	return DefaultSessionFactory.Config(r, key...)
}
func Save(r *http.Request, w http.ResponseWriter) []error {
	return DefaultSessionFactory.Save(r, w)
}
func Store(key string) (SessionStore, error) {
	return DefaultSessionFactory.Store(key)
}
func SetStore(key string, store SessionStore) {
	DefaultSessionFactory.SetStore(key, store)
}
func SetStoreKeys(key string, pairs ...[]byte) (bool, error) {
	return DefaultSessionFactory.SetStoreKeys(key, pairs...)
}
func DefaultConfig() *SessionConfig {
	return DefaultSessionFactory.DefaultConfig()
}
func SetDefaultConfig(config *SessionConfig) {
	DefaultSessionFactory.SetDefaultConfig(config)
}
var DefaultSessionFactory = new(SessionFactory)
var DefaultSessionConfig = &SessionConfig{
	Path:     "/",
	Domain:   "",
	MaxAge:   86400 * 30,
	Secure:   false,
	HttpOnly: false,
}
type SessionFactory struct {
	stores        map[string]SessionStore
	defaultConfig *SessionConfig
}
func (f *SessionFactory) Store(key string) (SessionStore, error) {
	store, ok := f.stores[key]
	if !ok {
		return nil, ErrNoStore
	}
	return store, nil
}
func (f *SessionFactory) SetStore(key string, store SessionStore) {
	if f.stores == nil {
		f.stores = make(map[string]SessionStore)
	}
	f.stores[key] = store
}
func (f *SessionFactory) SetStoreKeys(key string,
	pairs ...[]byte) (bool, error) {
	store, err := f.Store(key)
	if err != nil {
		return false, err
	}
	var b cipher.Block
	size := len(pairs)
	encoders := make([]SessionEncoder, size/2+size%2)
	for i := 0; i < size; i += 2 {
		if pairs[i] == nil || len(pairs[i]) == 0 {
			return false, ErrMissingHashKey
		}
		if size <= i+1 || pairs[i+1] == nil {
			b = nil
		} else {
			b, err = aes.NewCipher(pairs[i+1])
			if err != nil {
				return false, err
			}
		}
		encoders[i/2] = &Encoder{
			Hash:      hmac.NewSHA256(pairs[i]),
			Block:     b,
			MaxAge:    86400 * 30,
			MaxLength: 4096,
		}
	}
	store.SetEncoders(encoders...)
	return true, nil
}
func (f *SessionFactory) Session(r *http.Request,
	vars ...string) (SessionData, error) {
	return getRequestSessions(f, r).Session(vars...)
}
func (f *SessionFactory) Flashes(r *http.Request,
	vars ...string) ([]interface{}, error) {
	key, newvars := flashKey(vars...)
	session, err := f.Session(r, newvars...)
	if err != nil {
		return nil, err
	}
	if flashes, ok := session[key]; ok {
		delete(session, key)
		return flashes.([]interface{}), nil
	}
	return nil, ErrNoFlashes
}
func (f *SessionFactory) AddFlash(r *http.Request, value interface{},
	vars ...string) (bool, error) {
	key, newvars := flashKey(vars...)
	session, err := f.Session(r, newvars...)
	if err != nil {
		return false, err
	}
	var flashes []interface{}
	if v, ok := session[key]; ok {
		flashes = v.([]interface{})
	} else {
		flashes = make([]interface{}, 0)
	}
	session[key] = append(flashes, value)
	return true, nil
}
func (f *SessionFactory) Config(r *http.Request,
	key ...string) (*SessionConfig, error) {
	return getRequestSessions(f, r).Config(key...)
}
func (f *SessionFactory) Save(r *http.Request,
	w http.ResponseWriter) []error {
	return getRequestSessions(f, r).Save(w)
}
func (f *SessionFactory) DefaultConfig() *SessionConfig {
	if f.defaultConfig == nil {
		f.defaultConfig = DefaultSessionConfig
	}
	return f.defaultConfig
}
func (f *SessionFactory) SetDefaultConfig(config *SessionConfig) {
	f.defaultConfig = config
}
func (f *SessionFactory) defaultConfigValue() SessionConfig {
	d := f.DefaultConfig()
	return SessionConfig{
		Path:     d.Path,
		Domain:   d.Domain,
		MaxAge:   d.MaxAge,
		Secure:   d.Secure,
		HttpOnly: d.HttpOnly,
	}
}
func flashKey(vars ...string) (string, []string) {
	key := DefaultFlashesKey
	if len(vars) > 0 {
		if vars[0] != "" {
			key = vars[0]
		}
		vars = vars[1:]
	}
	return key, vars
}
var ns = new(context.Namespace)
func getRequestSessions(f *SessionFactory,
	r *http.Request) *requestSessions {
	var s *requestSessions
	rv := ns.Get(r)
	if rv != nil {
		s = rv.(*requestSessions)
	} else {
		s = &requestSessions{factory: f, request: r}
		ns.Set(r, s)
	}
	return s
}
type requestSessions struct {
	factory  *SessionFactory
	request  *http.Request
	sessions map[string]SessionInfo
}
func (s *requestSessions) Session(vars ...string) (SessionData, error) {
	sessionKey, storeKey := sessionKeys(vars...)
	store, err := s.factory.Store(storeKey)
	if err != nil {
		return nil, err
	}
	if s.sessions == nil {
		s.sessions = make(map[string]SessionInfo)
	}
	info, ok := s.sessions[sessionKey]
	if ok {
		if store != info.Store {
			return nil, ErrStoreMismatch
		}
		return info.Data, nil
	}
	info = SessionInfo{
		Store:  store,
		Config: s.factory.defaultConfigValue(),
	}
	store.Load(s.request, sessionKey, &info)
	s.sessions[sessionKey] = info
	return info.Data, nil
}
func (s *requestSessions) Config(key ...string) (*SessionConfig, error) {
	sessionKey, _ := sessionKeys(key...)
	if info, ok := s.sessions[sessionKey]; ok {
		return &info.Config, nil
	}
	return nil, ErrNoSession
}
func (s *requestSessions) Save(w http.ResponseWriter) []error {
	var err error
	var ok bool
	var errors []error
	for key, info := range s.sessions {
		if ok, err = info.Store.Save(s.request, w, key, &info); !ok {
			if errors == nil {
				errors = []error{err}
			} else {
				errors = append(errors, err)
			}
		}
	}
	return errors
}
func sessionKeys(vars ...string) (string, string) {
	sessionKey := DefaultSessionKey
	storeKey := DefaultStoreKey
	if len(vars) > 0 && vars[0] != "" {
		sessionKey = vars[0]
	}
	if len(vars) > 1 && vars[1] != "" {
		storeKey = vars[1]
	}
	return sessionKey, storeKey
}
type SessionStore interface {
	Load(r *http.Request, key string, info *SessionInfo)
	Save(r *http.Request, w http.ResponseWriter, key string, info *SessionInfo) (bool, error)
	Encoders() []SessionEncoder
	SetEncoders(encoders ...SessionEncoder)
}
type CookieSessionStore struct {
	encoders []SessionEncoder
}
func (s *CookieSessionStore) Load(r *http.Request, key string,
	info *SessionInfo) {
	info.Data = GetCookie(s, r, key)
}
func (s *CookieSessionStore) Save(r *http.Request, w http.ResponseWriter,
	key string, info *SessionInfo) (bool, error) {
	return SetCookie(s, w, key, info)
}
func (s *CookieSessionStore) Encoders() []SessionEncoder {
	return s.encoders
}
func (s *CookieSessionStore) SetEncoders(encoders ...SessionEncoder) {
	s.encoders = encoders
}
func GenerateSessionId(length int) (string, error) {
	if length <= 0 {
		return "", ErrBadIdLength
	}
	id := make([]byte, length)
	if _, err := rand.Read(id); err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", id), nil
}
func GetCookie(s SessionStore, r *http.Request, key string) SessionData {
	if cookie, err := r.Cookie(key); err == nil {
		if data, err2 := Decode(s, key, cookie.Value); err2 == nil {
			return data
		}
	}
	return SessionData{}
}
func SetCookie(s SessionStore, w http.ResponseWriter, key string,
	info *SessionInfo) (bool, error) {
	encoded, err := Encode(s, key, info.Data)
	if err != nil {
		return false, err
	}
	cookie := &http.Cookie{
		Name:     key,
		Value:    encoded,
		Path:     info.Config.Path,
		Domain:   info.Config.Domain,
		MaxAge:   info.Config.MaxAge,
		Secure:   info.Config.Secure,
		HttpOnly: info.Config.HttpOnly,
	}
	http.SetCookie(w, cookie)
	return true, nil
}
func Encode(s SessionStore, key string, value SessionData) (string, error) {
	encoders := s.Encoders()
	if encoders != nil {
		var encoded string
		var err error
		for _, encoder := range encoders {
			encoded, err = encoder.Encode(key, value)
			if err == nil {
				return encoded, nil
			}
		}
	}
	return "", ErrEncoding
}
func Decode(s SessionStore, key, value string) (SessionData, error) {
	encoders := s.Encoders()
	if encoders != nil {
		var decoded SessionData
		var err error
		for _, encoder := range encoders {
			decoded, err = encoder.Decode(key, value)
			if err == nil {
				return decoded, nil
			}
		}
	}
	return nil, ErrDecoding
}
func SerializeSessionData(session SessionData) ([]byte, error) {
	return serialize(session)
}
func DeserializeSessionData(value []byte) (data SessionData, err error) {
	return deserialize(value)
}
type SessionEncoder interface {
	Encode(key string, value SessionData) (string, error)
	Decode(key, value string) (SessionData, error)
}
type Encoder struct {
	Hash hash.Hash
	Block cipher.Block
	MinAge int64
	MaxAge int64
	MaxLength int
	TimeFunc func() int64
}
func (s *Encoder) Encode(key string, value SessionData) (rv string, err error) {
	if s.Hash == nil {
		err = ErrMissingHash
		return
	}
	var b []byte
	b, err = serialize(value)
	if err != nil {
		return
	}
	if s.Block != nil {
		b, err = encrypt(s.Block, b)
		if err != nil {
			return
		}
		b = encode(b)
	}
	b = createHmac(s.Hash, key, b, s.timestamp())
	rv = string(encode(b))
	return
}
func (s *Encoder) Decode(key, value string) (SessionData, error) {
	if s.Hash == nil {
		return nil, ErrMissingHash
	}
	if s.MaxLength != 0 && len(value) > s.MaxLength {
		return nil, ErrMaxLength
	}
	rv, err := decode([]byte(value))
	if err != nil {
		return nil, err
	}
	rv, err = verifyHmac(s.Hash, key, rv, s.timestamp(), s.MinAge, s.MaxAge)
	if err != nil {
		return nil, err
	}
	if s.Block != nil {
		rv, err = decode(rv)
		if err != nil {
			return nil, err
		}
		rv, err = decrypt(s.Block, rv)
		if err != nil {
			return nil, err
		}
	}
	var data SessionData
	data, err = deserialize(rv)
	if err != nil {
		return nil, err
	}
	return data, nil
}