boltstore/store/store.go

148 lines
4 KiB
Go
Raw Normal View History

2014-06-14 20:15:10 +04:00
package store
2014-06-13 22:36:36 +04:00
import (
"bytes"
"encoding/base32"
"encoding/gob"
"net/http"
"strings"
"code.google.com/p/gogoprotobuf/proto"
"github.com/boltdb/bolt"
"github.com/gorilla/securecookie"
"github.com/gorilla/sessions"
2014-06-14 20:15:10 +04:00
"github.com/yosssi/boltstore/shared"
2014-06-13 22:36:36 +04:00
)
2014-06-14 20:29:02 +04:00
// Store represents a session store.
type Store struct {
2014-06-13 22:36:36 +04:00
codecs []securecookie.Codec
config Config
db *bolt.DB
}
// Get returns a session for the given name after adding it to the registry.
//
// See gorilla/sessions FilesystemStore.Get().
2014-06-14 20:29:02 +04:00
func (s *Store) Get(r *http.Request, name string) (*sessions.Session, error) {
2014-06-13 22:36:36 +04:00
return sessions.GetRegistry(r).Get(s, name)
}
// New returns a session for the given name without adding it to the registry.
//
// See gorilla/sessions FilesystemStore.New().
2014-06-14 20:29:02 +04:00
func (s *Store) New(r *http.Request, name string) (*sessions.Session, error) {
2014-06-13 22:36:36 +04:00
var err error
session := sessions.NewSession(s, name)
session.Options = &s.config.SessionOptions
session.IsNew = true
if c, errCookie := r.Cookie(name); errCookie == nil {
err = securecookie.DecodeMulti(name, c.Value, &session.ID, s.codecs...)
if err == nil {
ok, err := s.load(session)
session.IsNew = !(err == nil && ok) // not new if no error and data available
}
}
return session, err
}
// Save adds a single session to the response.
2014-06-14 20:29:02 +04:00
func (s *Store) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
2014-06-13 22:36:36 +04:00
if session.Options.MaxAge < 0 {
s.delete(session)
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
} else {
// Build an alphanumeric ID.
if session.ID == "" {
session.ID = strings.TrimRight(base32.StdEncoding.EncodeToString(securecookie.GenerateRandomKey(32)), "=")
}
if err := s.save(session); err != nil {
return err
}
encoded, err := securecookie.EncodeMulti(session.Name(), session.ID, s.codecs...)
if err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), encoded, session.Options))
}
return nil
}
// load loads a session data from the database.
// True is returned if there is a session data in the database.
2014-06-14 20:29:02 +04:00
func (s *Store) load(session *sessions.Session) (bool, error) {
2014-06-13 22:36:36 +04:00
// exists represents whether a session data exists or not.
var exists bool
2014-06-14 20:15:10 +04:00
err := s.db.View(func(tx *bolt.Tx) error {
2014-06-13 22:36:36 +04:00
id := []byte(session.ID)
bucket := tx.Bucket(s.config.DBOptions.BucketName)
// Get the session data.
data := bucket.Get(id)
if data == nil {
return nil
}
2014-06-14 20:15:10 +04:00
sessionData, err := shared.Session(data)
if err != nil {
2014-06-13 22:36:36 +04:00
return err
}
// Check the expiration of the session data.
2014-06-14 20:15:10 +04:00
if shared.Expired(sessionData) {
err := s.db.Update(func(txu *bolt.Tx) error {
return txu.Bucket(s.config.DBOptions.BucketName).Delete(id)
})
return err
2014-06-13 22:36:36 +04:00
}
exists = true
dec := gob.NewDecoder(bytes.NewBuffer(sessionData.Values))
return dec.Decode(&session.Values)
})
return exists, err
}
// delete removes the key-value from the database.
2014-06-14 20:29:02 +04:00
func (s *Store) delete(session *sessions.Session) error {
2014-06-13 22:36:36 +04:00
err := s.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(s.config.DBOptions.BucketName).Delete([]byte(session.ID))
})
if err != nil {
return err
}
return nil
}
// save stores the session data in the database.
2014-06-14 20:29:02 +04:00
func (s *Store) save(session *sessions.Session) error {
2014-06-13 22:36:36 +04:00
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
err := enc.Encode(session.Values)
if err != nil {
return err
}
2014-06-17 12:53:24 +04:00
data, err := proto.Marshal(shared.NewSession(buf.Bytes(), session.Options.MaxAge))
2014-06-13 22:36:36 +04:00
if err != nil {
return nil
}
err = s.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(s.config.DBOptions.BucketName).Put([]byte(session.ID), data)
})
return err
}
// New creates and returns a session store.
2014-06-14 20:29:02 +04:00
func New(db *bolt.DB, config Config, keyPairs ...[]byte) (*Store, error) {
2014-06-13 22:36:36 +04:00
config.setDefault()
2014-06-14 20:29:02 +04:00
store := &Store{
2014-06-13 22:36:36 +04:00
codecs: securecookie.CodecsFromPairs(keyPairs...),
config: config,
2014-06-14 20:15:10 +04:00
db: db,
2014-06-13 22:36:36 +04:00
}
2014-06-14 20:15:10 +04:00
err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists(config.DBOptions.BucketName)
return err
})
if err != nil {
2014-06-13 22:36:36 +04:00
return nil, err
}
return store, nil
}