Skip to content

Instantly share code, notes, and snippets.

@Teddy-Schmitz
Created December 12, 2019 17:15
Show Gist options
  • Save Teddy-Schmitz/adfc51c808a163c03ac1c0df1957b9c2 to your computer and use it in GitHub Desktop.
Save Teddy-Schmitz/adfc51c808a163c03ac1c0df1957b9c2 to your computer and use it in GitHub Desktop.
radix_store
package radix_store
import (
"bytes"
"crypto/rand"
"encoding/base32"
"encoding/gob"
"errors"
"github.com/gorilla/sessions"
"github.com/mediocregopher/radix/v3"
"io"
"net/http"
"strings"
"time"
)
type RadixStore struct {
client radix.Client
options sessions.Options
keyGen KeyGenFunc
}
type KeyGenFunc func() (string, error)
func NewRadixStore(client radix.Client) (*RadixStore, error) {
rs := &RadixStore{
client: client,
keyGen: generateRandomKey,
options: sessions.Options{
Path: "/",
MaxAge: 86400 * 30,
},
}
//TODO: Test client connection first
return rs, nil
}
func (s *RadixStore) Get(r *http.Request, name string) (*sessions.Session, error) {
return sessions.GetRegistry(r).Get(s, name)
}
func (s *RadixStore) New(r *http.Request, name string) (*sessions.Session, error) {
session := sessions.NewSession(s, name)
opts := s.options
session.Options = &opts
session.IsNew = true
c, err := r.Cookie(name)
if err != nil {
return session, nil
}
session.ID = c.Value
err = s.load(session)
if err == nil {
session.IsNew = false
}
return session, err
}
func (s *RadixStore) Save(r *http.Request, w http.ResponseWriter, session *sessions.Session) error {
if session.Options.MaxAge <= 0 {
if err := s.delete(session); err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), "", session.Options))
return nil
}
if session.ID == "" {
id, err := s.keyGen()
if err != nil {
return errors.New("radixstore: unable to generate session id")
}
session.ID = id
}
if err := s.save(session); err != nil {
return err
}
http.SetCookie(w, sessions.NewCookie(session.Name(), session.ID, session.Options))
return nil
}
func (s *RadixStore) load(session *sessions.Session) error {
var val []byte
if err := s.client.Do(radix.Cmd(&val, "GET", session.ID)); err != nil {
return err
}
return deserialize(val, session)
}
func (s *RadixStore) save(session *sessions.Session) error {
b, err := serialize(session)
if err != nil {
return err
}
return s.client.Do(radix.Cmd(nil, "SET", session.ID, string(b)))
}
func (s *RadixStore) delete(session *sessions.Session) error {
return s.client.Do(radix.Cmd(nil, "DEL", session.ID))
}
func serialize(s *sessions.Session) ([]byte, error) {
buf := new(bytes.Buffer)
enc := gob.NewEncoder(buf)
err := enc.Encode(s.Values)
if err == nil {
return buf.Bytes(), nil
}
return nil, err
}
func deserialize(d []byte, s *sessions.Session) error {
dec := gob.NewDecoder(bytes.NewBuffer(d))
return dec.Decode(&s.Values)
}
// generateRandomKey returns a new random key
func generateRandomKey() (string, error) {
k := make([]byte, 64)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return "", err
}
return strings.TrimRight(base32.StdEncoding.EncodeToString(k), "="), nil
}
func init() {
gob.Register(time.Time{})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment