hlfw.ca

webbing

Download patch

ref: 39a5a1455de79e2ca046d2b486f86dc7cc8f31d6
parent: acc962a0381815da8dd9f4c42e0ca244084be432
author: Moses Olson, MD <52055478+nemo-olmax@users.noreply.github.com>
date: Mon Aug 12 06:52:36 PDT 2019

Add files via upload

--- /dev/null
+++ b/session/data.go
@@ -1,0 +1,103 @@
+package session
+
+import (
+	"container/list"
+	"time"
+	"sync"
+)
+
+var pder = &Default {
+	list: list.New(),
+}
+
+type SessionStore struct {
+	sid string
+	atime time.Time
+	value map[interface{}]interface{}
+}
+
+func init() {
+	pder.sessions = make(map[string]*list.Element, 0)
+	Register("default", pder)
+}
+
+func (st *SessionStore) Set(key, value interface{}) error {
+	st.value[key] = value
+	pder.SessionUpdate(st.sid)
+	return nil
+}
+
+func (st *SessionStore) Get(key interface{}) interface{} {
+	pder.SessionUpdate(st.sid)
+	if v, ok := st.value[key]; ok {
+		return v
+	}
+	return nil
+}
+
+func (st *SessionStore) Delete(key interface{}) error {
+	delete(st.value, key)
+	pder.SessionUpdate(st.sid)
+	return nil
+}
+
+func (st *SessionStore) SessionID() string {
+	return st.sid
+}
+
+type Default struct {
+	lock sync.Mutex
+	sessions map[string]*list.Element
+	list *list.List
+}
+
+func (pder *Default) SessionInit(sid string) (Session, error) {
+        pder.lock.Lock()
+        defer pder.lock.Unlock()
+        v := make(map[interface{}]interface{}, 0)
+        newsess := &SessionStore{sid: sid, atime: time.Now(), value: v}
+        element := pder.list.PushBack(newsess)
+        pder.sessions[sid] = element
+        return newsess, nil
+}
+
+func (pder *Default) SessionRead(sid string) (Session, error) {
+	if element, ok := pder.sessions[sid]; ok {
+		return element.Value.(*SessionStore), nil
+	} 
+	return pder.SessionInit(sid)
+}
+
+func (pder *Default) SessionDestroy(sid string) error {
+	if element, ok := pder.sessions[sid]; ok {
+		delete(pder.sessions, sid)
+		pder.list.Remove(element)
+    }
+	return nil
+}
+
+func (pder *Default) SessionGC(maxlifetime int64) {
+	pder.lock.Lock()
+	defer pder.lock.Unlock()
+	for {
+		element := pder.list.Back()
+		if element == nil {
+			break
+		}
+		if (element.Value.(*SessionStore).atime.Unix() + maxlifetime) >= time.Now().Unix() {
+			break
+		}
+		pder.list.Remove(element)
+		delete(pder.sessions, element.Value.(*SessionStore).sid)
+	}
+}
+
+func (pder *Default) SessionUpdate(sid string) error {
+	pder.lock.Lock()
+	defer pder.lock.Unlock()
+	if element, ok := pder.sessions[sid]; ok {
+		element.Value.(*SessionStore).atime = time.Now()
+		pder.list.MoveToFront(element)
+	}
+	return nil
+}
--- /dev/null
+++ b/session/manager.go
@@ -1,0 +1,114 @@
+package session
+
+import (
+	"fmt"
+	"log"
+	"net/http"
+	"net/url"
+	"time"
+	"sync"
+
+	"github.com/google/uuid"
+)
+
+var provides = make(map[string]Provider)
+
+type Manager struct {
+	cookieName string
+	lock sync.Mutex
+	provider Provider
+	maxlifetime int64
+}
+
+func NewManager(provideName, cookieName string, maxlifetime int64) (*Manager, error) {
+	if provider, ok := provides[provideName]; ok {
+		m := &Manager{
+			provider: provider,
+			cookieName: cookieName,
+			maxlifetime: maxlifetime,
+		}
+		return m, nil
+	}
+	return nil, fmt.Errorf("session: unknown provide %q", provideName)
+}
+
+type Provider interface {
+	SessionInit(sid string) (Session, error)
+	SessionRead(sid string) (Session, error)
+	SessionDestroy(sid string) error
+	SessionGC(maxLifeTime int64)
+}
+
+type Session interface {
+	Set(key, value interface{}) error
+	Get(key interface{}) interface{}
+	Delete(key interface{}) error
+	SessionID() string
+}
+
+func Register(name string, provider Provider) {
+	if provider == nil {
+		log.Fatal("session: Register provider is nil")
+	}
+	if _, ok := provides[name]; !ok {
+		provides[name] = provider
+		return
+	}
+	log.Fatal("session: Register called twice for provider " + name)
+}
+
+func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
+	cookie, err := r.Cookie(manager.cookieName)
+	if err != nil || cookie.Value == "" {
+		return
+	}
+	manager.lock.Lock()
+	defer manager.lock.Unlock()
+	manager.provider.SessionDestroy(cookie.Value)
+	expiration := time.Now()
+	c := http.Cookie{
+		Name: manager.cookieName,
+		Path: "/",
+		HttpOnly: true,
+		Expires: expiration,
+		MaxAge: -1,
+	}
+	http.SetCookie(w, &c)
+}
+
+func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
+	manager.lock.Lock()
+	defer manager.lock.Unlock()
+	cookie, err := r.Cookie(manager.cookieName)
+	if err != nil || cookie.Value == "" {
+		sid := manager.sessionId()
+		session, _ = manager.provider.SessionInit(sid)
+		cookie := http.Cookie{
+			Name: manager.cookieName,
+			Value: url.QueryEscape(sid),
+			Path: "/",
+			HttpOnly: true,
+			MaxAge: int(manager.maxlifetime),
+		}
+		http.SetCookie(w, &cookie)
+		return
+	}
+	sid, _ := url.QueryUnescape(cookie.Value)
+	session, _ = manager.provider.SessionRead(sid)
+	return
+}
+
+func (manager *Manager) GC() {
+	manager.lock.Lock()
+	defer manager.lock.Unlock()
+	manager.provider.SessionGC(manager.maxlifetime)
+	time.AfterFunc(time.Duration(manager.maxlifetime), func() { manager.GC() })
+}
+
+func (manager *Manager) sessionId() string {
+	u, err := uuid.NewRandom()
+	if err != nil {
+		log.Fatal("Unable to generate UUID %q", err)
+	}
+	return u.String()
+}