ref: dc410ec80e1295d40a83e6826cdcf2095ef75ed1
parent: 910a508a5ca9c521acbb7c9fab2ee06d174b38e4
author: halfwit <michaelmisch1985@gmail.com>
date: Thu Aug 6 05:46:15 PDT 2020
Add session
--- a/.github/workflows/go.yml
+++ /dev/null
@@ -1,35 +1,0 @@
-name: Testing
-
-on:
- pull_request:
- branches: [ master ]
-
-jobs:
-
- build:
- name: Build
- runs-on: ubuntu-latest
- steps:
- - name: Configure git for private modules
- env:
- TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- run: git config --global url."https://halfwit:${TOKEN}@github.com".insteadOf "https://github.com"
- - name: Set up Go 1.13
- uses: actions/setup-go@v1
- with:
- go-version: 1.13
- id: go
-
- - name: Check out code into the Go module directory
- uses: actions/checkout@v2
-
- - name: Get dependencies
- run: |
- go get -v -t -d ./...
- if [ -f Gopkg.toml ]; then
- curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
- dep ensure
- fi
-
- - name: Run Tests
- run: go test -v ./...
--- a/data.go
+++ /dev/null
@@ -1,116 +1,0 @@
-package session
-
-import (
- "container/list"
- "log"
- "sync"
- "time"
-)
-
-var pder = &Default{
- list: list.New(),
-}
-
-// Store - map of clients and their sessions
-type Store struct {
- sid string
- atime time.Time
- value map[interface{}]interface{}
-}
-
-func init() {
- pder.sessions = make(map[string]*list.Element, 0)
- Register("default", pder)
-}
-
-// Set - register kv into manager
-func (st *Store) Set(key, value interface{}) error {
- st.value[key] = value
- pder.Update(st.sid)
- return nil
-}
-
-// Get - lookup value by key
-func (st *Store) Get(key interface{}) interface{} {
- pder.Update(st.sid)
- if v, ok := st.value[key]; ok {
- return v
- }
- return nil
-}
-
-// Delete - remove value pointed to by key
-func (st *Store) Delete(key interface{}) error {
- delete(st.value, key)
- pder.Update(st.sid)
- return nil
-}
-
-// ID - return session ID
-func (st *Store) ID() string {
- return st.sid
-}
-
-// Default - The main session used for clients on the site
-type Default struct {
- lock sync.Mutex
- sessions map[string]*list.Element
- list *list.List
-}
-
-// Init - create session
-func (pder *Default) Init(sid string) (Session, error) {
- pder.lock.Lock()
- defer pder.lock.Unlock()
- v := make(map[interface{}]interface{}, 0)
- newsess := &Store{sid: sid, atime: time.Now(), value: v}
- element := pder.list.PushBack(newsess)
- pder.sessions[sid] = element
- return newsess, nil
-}
-
-// Read - Request session by id
-func (pder *Default) Read(sid string) (Session, error) {
- if element, ok := pder.sessions[sid]; ok {
- return element.Value.(*Store), nil
- }
- return pder.Init(sid)
-}
-
-// Destroy - Remove session by id
-func (pder *Default) Destroy(sid string) error {
- if element, ok := pder.sessions[sid]; ok {
- delete(pder.sessions, sid)
- pder.list.Remove(element)
- }
- return nil
-}
-
-// GC - Clean up all expired sessions
-func (pder *Default) GC(maxlifetime int64) {
- log.Println("Starting GC sweep")
- pder.lock.Lock()
- defer pder.lock.Unlock()
- for {
- element := pder.list.Back()
- if element == nil {
- return
- }
- if (element.Value.(*Store).atime.Unix() + maxlifetime) >= time.Now().Unix() {
- return
- }
- pder.list.Remove(element)
- delete(pder.sessions, element.Value.(*Store).sid)
- }
-}
-
-// Update - move session labelled with ID to top of list
-func (pder *Default) Update(sid string) error {
- pder.lock.Lock()
- defer pder.lock.Unlock()
- if element, ok := pder.sessions[sid]; ok {
- element.Value.(*Store).atime = time.Now()
- pder.list.MoveToFront(element)
- }
- return nil
-}
--- a/go.mod
+++ /dev/null
@@ -1,5 +1,0 @@
-module github.com/olmaxmedical/session
-
-go 1.14
-
-require github.com/google/uuid v1.1.1
--- a/go.sum
+++ /dev/null
@@ -1,2 +1,0 @@
-github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
--- a/manager.go
+++ /dev/null
@@ -1,122 +1,0 @@
-package session
-
-import (
- "fmt"
- "log"
- "net/http"
- "net/url"
- "sync"
- "time"
-
- "github.com/google/uuid"
-)
-
-var provides = make(map[string]Provider)
-
-// Manager - Represents a particular cookie to a client
-type Manager struct {
- cookieName string
- lock sync.Mutex
- provider Provider
- maxlifetime int64
-}
-
-// NewManager - returns a Cookie manager, ready to use or an error
-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)
-}
-
-// Provider - interface our cookie manager calls to manage lifetimes
-type Provider interface {
- Init(sid string) (Session, error)
- Read(sid string) (Session, error)
- Destroy(sid string) error
- GC(maxLifeTime int64)
-}
-
-// Session - a client connection
-type Session interface {
- Set(key, value interface{}) error
- Get(key interface{}) interface{}
- Delete(key interface{}) error
- ID() string
-}
-
-// Register - Add a provider to our stack (e.g., Default)
-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)
-}
-
-// Destroy - Call provider Destroy
-func (manager *Manager) Destroy(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.Destroy(cookie.Value)
- expiration := time.Now()
- c := http.Cookie{
- Name: manager.cookieName,
- Path: "/",
- HttpOnly: true,
- Expires: expiration,
- MaxAge: -1,
- }
- http.SetCookie(w, &c)
-}
-
-// Start - Call provider Start
-func (manager *Manager) Start(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.Init(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.Read(sid)
- return
-}
-
-// GC - call all providers GC
-func (manager *Manager) GC() {
- manager.lock.Lock()
- defer manager.lock.Unlock()
- manager.provider.GC(manager.maxlifetime)
- time.AfterFunc(30*time.Second, func() { manager.GC() })
-}
-
-func (manager *Manager) sessionID() string {
- u, err := uuid.NewRandom()
- if err != nil {
- log.Fatalf("Unable to generate UUID %q", err)
- }
- return u.String()
-}
--- /dev/null
+++ b/session/data.go
@@ -1,0 +1,116 @@
+package session
+
+import (
+ "container/list"
+ "log"
+ "sync"
+ "time"
+)
+
+var pder = &Default{
+ list: list.New(),
+}
+
+// Store - map of clients and their sessions
+type Store struct {
+ sid string
+ atime time.Time
+ value map[interface{}]interface{}
+}
+
+func init() {
+ pder.sessions = make(map[string]*list.Element, 0)
+ Register("default", pder)
+}
+
+// Set - register kv into manager
+func (st *Store) Set(key, value interface{}) error {
+ st.value[key] = value
+ pder.Update(st.sid)
+ return nil
+}
+
+// Get - lookup value by key
+func (st *Store) Get(key interface{}) interface{} {
+ pder.Update(st.sid)
+ if v, ok := st.value[key]; ok {
+ return v
+ }
+ return nil
+}
+
+// Delete - remove value pointed to by key
+func (st *Store) Delete(key interface{}) error {
+ delete(st.value, key)
+ pder.Update(st.sid)
+ return nil
+}
+
+// ID - return session ID
+func (st *Store) ID() string {
+ return st.sid
+}
+
+// Default - The main session used for clients on the site
+type Default struct {
+ lock sync.Mutex
+ sessions map[string]*list.Element
+ list *list.List
+}
+
+// Init - create session
+func (pder *Default) Init(sid string) (Session, error) {
+ pder.lock.Lock()
+ defer pder.lock.Unlock()
+ v := make(map[interface{}]interface{}, 0)
+ newsess := &Store{sid: sid, atime: time.Now(), value: v}
+ element := pder.list.PushBack(newsess)
+ pder.sessions[sid] = element
+ return newsess, nil
+}
+
+// Read - Request session by id
+func (pder *Default) Read(sid string) (Session, error) {
+ if element, ok := pder.sessions[sid]; ok {
+ return element.Value.(*Store), nil
+ }
+ return pder.Init(sid)
+}
+
+// Destroy - Remove session by id
+func (pder *Default) Destroy(sid string) error {
+ if element, ok := pder.sessions[sid]; ok {
+ delete(pder.sessions, sid)
+ pder.list.Remove(element)
+ }
+ return nil
+}
+
+// GC - Clean up all expired sessions
+func (pder *Default) GC(maxlifetime int64) {
+ log.Println("Starting GC sweep")
+ pder.lock.Lock()
+ defer pder.lock.Unlock()
+ for {
+ element := pder.list.Back()
+ if element == nil {
+ return
+ }
+ if (element.Value.(*Store).atime.Unix() + maxlifetime) >= time.Now().Unix() {
+ return
+ }
+ pder.list.Remove(element)
+ delete(pder.sessions, element.Value.(*Store).sid)
+ }
+}
+
+// Update - move session labelled with ID to top of list
+func (pder *Default) Update(sid string) error {
+ pder.lock.Lock()
+ defer pder.lock.Unlock()
+ if element, ok := pder.sessions[sid]; ok {
+ element.Value.(*Store).atime = time.Now()
+ pder.list.MoveToFront(element)
+ }
+ return nil
+}
--- /dev/null
+++ b/session/manager.go
@@ -1,0 +1,122 @@
+package session
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+ "net/url"
+ "sync"
+ "time"
+
+ "github.com/google/uuid"
+)
+
+var provides = make(map[string]Provider)
+
+// Manager - Represents a particular cookie to a client
+type Manager struct {
+ cookieName string
+ lock sync.Mutex
+ provider Provider
+ maxlifetime int64
+}
+
+// NewManager - returns a Cookie manager, ready to use or an error
+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)
+}
+
+// Provider - interface our cookie manager calls to manage lifetimes
+type Provider interface {
+ Init(sid string) (Session, error)
+ Read(sid string) (Session, error)
+ Destroy(sid string) error
+ GC(maxLifeTime int64)
+}
+
+// Session - a client connection
+type Session interface {
+ Set(key, value interface{}) error
+ Get(key interface{}) interface{}
+ Delete(key interface{}) error
+ ID() string
+}
+
+// Register - Add a provider to our stack (e.g., Default)
+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)
+}
+
+// Destroy - Call provider Destroy
+func (manager *Manager) Destroy(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.Destroy(cookie.Value)
+ expiration := time.Now()
+ c := http.Cookie{
+ Name: manager.cookieName,
+ Path: "/",
+ HttpOnly: true,
+ Expires: expiration,
+ MaxAge: -1,
+ }
+ http.SetCookie(w, &c)
+}
+
+// Start - Call provider Start
+func (manager *Manager) Start(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.Init(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.Read(sid)
+ return
+}
+
+// GC - call all providers GC
+func (manager *Manager) GC() {
+ manager.lock.Lock()
+ defer manager.lock.Unlock()
+ manager.provider.GC(manager.maxlifetime)
+ time.AfterFunc(30*time.Second, func() { manager.GC() })
+}
+
+func (manager *Manager) sessionID() string {
+ u, err := uuid.NewRandom()
+ if err != nil {
+ log.Fatalf("Unable to generate UUID %q", err)
+ }
+ return u.String()
+}