added test / naming

This commit is contained in:
Lars Hoogestraat 2021-05-24 18:26:10 +02:00
parent f0149ef2d3
commit b859560fb9
5 changed files with 100 additions and 38 deletions

View File

@ -1,6 +1,8 @@
// Copyright 2018 Lars Hoogestraat // Copyright 2018 Lars Hoogestraat
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// Package session
package session package session
import ( import (
@ -8,7 +10,7 @@ import (
"time" "time"
) )
//Session contains the session // Session contains the session
type Session struct { type Session struct {
sid string sid string
lastTouched time.Time lastTouched time.Time
@ -17,32 +19,32 @@ type Session struct {
mutex sync.RWMutex mutex sync.RWMutex
} }
//GetLastTouchTime recveives the date when the session was touched // GetLastTouchTime receives the date when the session was touched last time
func (s *Session) GetLastTouchTime() time.Time { func (s *Session) GetLastTouchTime() time.Time {
return s.lastTouched return s.lastTouched
} }
//SessionID gets the sessionID // SessionID gets the sessionID
func (s *Session) SessionID() string { func (s *Session) SessionID() string {
return s.sid return s.sid
} }
//SetValue sets a value into the session // SetValue sets a value into the session
func (s *Session) SetValue(key string, value interface{}) { func (s *Session) SetValue(key string, value interface{}) {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
s.values[key] = value s.values[key] = value
} }
//GetValue receives a value from the session // GetValue receives a value from the session
func (s *Session) GetValue(key string) interface{} { func (s *Session) GetValue(key string) interface{} {
s.mutex.RLock() s.mutex.RLock()
defer s.mutex.RUnlock() defer s.mutex.RUnlock()
return s.values[key] return s.values[key]
} }
//RemoveValue removes a previously set value from the session // RemoveKey removes a key from the session
func (s *Session) RemoveValue(key string) { func (s *Session) RemoveKey(key string) {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
delete(s.values, key) delete(s.values, key)

View File

@ -10,29 +10,29 @@ import (
"time" "time"
) )
//SessionProvider an interface for storing and accessing sessions // Provider an interface for storing and accessing sessions
type SessionProvider interface { type Provider interface {
Create(sid string) *Session Create(sid string) *Session
Get(sid string) (*Session, error) Get(sid string) (*Session, error)
FindSessionsByValue(key string, value interface{}) []Session FindByValue(key string, value interface{}) []*Session
Remove(sid string) Remove(sid string)
Clean(ticker *time.Ticker, timeoutAfter time.Duration) Clean(ticker *time.Ticker, timeoutAfter time.Duration)
} }
//InMemoryProvider implements a in memory storage for sessions // InMemoryProvider implements a in memory storage for sessions
type InMemoryProvider struct { type InMemoryProvider struct {
sessions map[string]*Session sessions map[string]*Session
mutex sync.RWMutex mutex sync.RWMutex
} }
//NewInMemoryProvider creates a new in memory provider // NewInMemoryProvider creates a new in memory provider
func NewInMemoryProvider() *InMemoryProvider { func NewInMemoryProvider() *InMemoryProvider {
return &InMemoryProvider{ return &InMemoryProvider{
sessions: make(map[string]*Session), sessions: make(map[string]*Session),
} }
} }
//Create stores a session in the map // Create stores a session in the map
func (imp *InMemoryProvider) Create(sid string) *Session { func (imp *InMemoryProvider) Create(sid string) *Session {
imp.mutex.Lock() imp.mutex.Lock()
@ -47,7 +47,7 @@ func (imp *InMemoryProvider) Create(sid string) *Session {
return imp.sessions[sid] return imp.sessions[sid]
} }
//Get receives the session from the map by the session identifier // Get receives the session from the map by the session identifier
func (imp *InMemoryProvider) Get(sid string) (*Session, error) { func (imp *InMemoryProvider) Get(sid string) (*Session, error) {
imp.mutex.RLock() imp.mutex.RLock()
defer imp.mutex.RUnlock() defer imp.mutex.RUnlock()
@ -59,24 +59,24 @@ func (imp *InMemoryProvider) Get(sid string) (*Session, error) {
return nil, fmt.Errorf("no session with id %s found", sid) return nil, fmt.Errorf("no session with id %s found", sid)
} }
//FindSessionsByValues finds all sessions from the map found by the key and value // FindByValue finds all sessions from the map found by the key and value
func (imp *InMemoryProvider) FindSessionsByValue(key string, value interface{}) []Session { func (imp *InMemoryProvider) FindByValue(key string, value interface{}) []*Session {
imp.mutex.RLock() imp.mutex.RLock()
defer imp.mutex.RUnlock() defer imp.mutex.RUnlock()
var sessions []Session var sessions []*Session
for _, s := range imp.sessions { for _, s := range imp.sessions {
if s.values[key] == value { if s.values[key] == value {
sessions = append(sessions, *s) sessions = append(sessions, s)
} }
} }
return sessions return sessions
} }
//Remove removes a session by the session identifier from the map // Remove removes a session by the session identifier from the map
func (imp *InMemoryProvider) Remove(sid string) { func (imp *InMemoryProvider) Remove(sid string) {
imp.mutex.Lock() imp.mutex.Lock()
defer imp.mutex.Unlock() defer imp.mutex.Unlock()
@ -84,7 +84,7 @@ func (imp *InMemoryProvider) Remove(sid string) {
delete(imp.sessions, sid) delete(imp.sessions, sid)
} }
//Clean clean sessions after the specified timeout // Clean clean sessions after the specified timeout
func (imp *InMemoryProvider) Clean(ticker *time.Ticker, timeoutAfter time.Duration) { func (imp *InMemoryProvider) Clean(ticker *time.Ticker, timeoutAfter time.Duration) {
go func() { go func() {
for range ticker.C { for range ticker.C {

60
session_provider_test.go Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2018 Lars Hoogestraat
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package session_test
import (
"git.hoogi.eu/snafu/session"
"testing"
)
func TestInMemoryStore(t *testing.T) {
prov := session.NewInMemoryProvider()
s1 := prov.Create("1234567890")
s1.SetValue("userid", 8)
s2 := prov.Create("1234567891")
s2.SetValue("userid", 8)
s3 := prov.Create("1234567892")
s3.SetValue("userid", 8)
s4 := prov.Create("1234567893")
s4.SetValue("userid", 7)
sessions := prov.FindByValue("userid", 8)
if len(sessions) != 3 {
t.Errorf("invalid length of sessions expected '3' bot got '%d'", len(sessions))
}
prov.Remove("1234567892")
sessions = prov.FindByValue("userid", 8)
if len(sessions) != 2 {
t.Errorf("invalid length of sessions expected '2' bot got '%d'", len(sessions))
}
sess, err := prov.Get("1234567893")
if err != nil {
t.Error(err)
}
if sess.SessionID() != "1234567893" {
t.Errorf("invalid session id returned expected '1234567893' bot got '%s'", sess.SessionID())
}
if uid := sess.GetValue("userid"); uid != 7 {
t.Errorf("invalid user id returned expected '7' bot got '%d'", uid)
}
sess.RemoveKey("userid")
if uid := sess.GetValue("userid"); uid != nil {
t.Errorf("user id should be removed but got '%d'", uid)
}
}

View File

@ -12,19 +12,19 @@ import (
"time" "time"
) )
//SessionService contains settings for the session // Service contains settings for the session
type SessionService struct { type Service struct {
Path string Path string
HTTPOnly bool HTTPOnly bool
Name string Name string
Secure bool Secure bool
IdleSessionTTL int64 IdleSessionTTL int64
SessionProvider SessionProvider SessionProvider Provider
} }
//Create creates the session for the request // Create creates the session for the request
func (sc SessionService) Create(rw http.ResponseWriter, r *http.Request) *Session { func (sc Service) Create(rw http.ResponseWriter, r *http.Request) *Session {
sid := base64.StdEncoding.EncodeToString(randomSecureKey(64)) sid := base64.StdEncoding.EncodeToString(randomSecureKey(64))
s := sc.SessionProvider.Create(sid) s := sc.SessionProvider.Create(sid)
@ -42,8 +42,8 @@ func (sc SessionService) Create(rw http.ResponseWriter, r *http.Request) *Sessio
return s return s
} }
//Get receives the session from the cookie // Get receives the session from the cookie
func (sc SessionService) Get(rw http.ResponseWriter, r *http.Request) (*Session, error) { func (sc Service) Get(rw http.ResponseWriter, r *http.Request) (*Session, error) {
cookie, err := r.Cookie(sc.Name) cookie, err := r.Cookie(sc.Name)
if err != nil { if err != nil {
@ -69,8 +69,8 @@ func (sc SessionService) Get(rw http.ResponseWriter, r *http.Request) (*Session,
return sess, nil return sess, nil
} }
//Renew renews the session // Renew renews the session
func (sc SessionService) Renew(rw http.ResponseWriter, r *http.Request) (*Session, error) { func (sc Service) Renew(rw http.ResponseWriter, r *http.Request) (*Session, error) {
cookie, err := r.Cookie(sc.Name) cookie, err := r.Cookie(sc.Name)
if err != nil { if err != nil {
@ -111,8 +111,8 @@ func (sc SessionService) Renew(rw http.ResponseWriter, r *http.Request) (*Sessio
return s, nil return s, nil
} }
//Remove removes the session from the session map and the cookie // Remove removes the session from the session map and the cookie
func (sc SessionService) Remove(rw http.ResponseWriter, r *http.Request) error { func (sc Service) Remove(rw http.ResponseWriter, r *http.Request) error {
cookie, err := r.Cookie(sc.Name) cookie, err := r.Cookie(sc.Name)
if err != nil { if err != nil {
@ -133,8 +133,8 @@ func (sc SessionService) Remove(rw http.ResponseWriter, r *http.Request) error {
return nil return nil
} }
//InitGC initialized the garbage collection for removing the session after the TTL has reached // InitGC initializes the garbage collection for removing the session after the TTL has reached
func (sc SessionService) InitGC(ticker *time.Ticker, timeoutAfter time.Duration) { func (sc Service) InitGC(ticker *time.Ticker, timeoutAfter time.Duration) {
sc.SessionProvider.Clean(ticker, timeoutAfter) sc.SessionProvider.Clean(ticker, timeoutAfter)
} }

View File

@ -13,8 +13,8 @@ import (
"git.hoogi.eu/snafu/session" "git.hoogi.eu/snafu/session"
) )
func sampleSessionService() session.SessionService { func sampleSessionService() session.Service {
sessionService := session.SessionService{ sessionService := session.Service{
Secure: true, Secure: true,
Path: "/test", Path: "/test",
HTTPOnly: true, HTTPOnly: true,
@ -102,7 +102,7 @@ func TestSessionGarbageCollection(t *testing.T) {
} }
} }
func createSession(t *testing.T, sc session.SessionService) (*session.Session, *http.Cookie) { func createSession(t *testing.T, sc session.Service) (*session.Session, *http.Cookie) {
req, err := http.NewRequest("GET", "/", nil) req, err := http.NewRequest("GET", "/", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -117,7 +117,7 @@ func createSession(t *testing.T, sc session.SessionService) (*session.Session, *
return createdSession, cookies[0] return createdSession, cookies[0]
} }
func getSession(t *testing.T, rawCookieValue string, sc session.SessionService) (*session.Session, error) { func getSession(t *testing.T, rawCookieValue string, sc session.Service) (*session.Session, error) {
req, err := http.NewRequest("GET", "/", nil) req, err := http.NewRequest("GET", "/", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -128,7 +128,7 @@ func getSession(t *testing.T, rawCookieValue string, sc session.SessionService)
return getSession, err return getSession, err
} }
func removeSession(t *testing.T, rawCookieValue string, cs session.SessionService) { func removeSession(t *testing.T, rawCookieValue string, cs session.Service) {
req, err := http.NewRequest("GET", "/", nil) req, err := http.NewRequest("GET", "/", nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)