move to stores
This commit is contained in:
		| @@ -1,12 +1,13 @@ | |||||||
| package sessions | package cookiestore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	ginsessions "github.com/dalu/sessions" | ||||||
| 	"github.com/gorilla/sessions" | 	"github.com/gorilla/sessions" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // CookieStore interface | // CookieStore interface | ||||||
| type CookieStore interface { | type CookieStore interface { | ||||||
| 	Store | 	ginsessions.Store | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewCookieStore creates a new cookieStore. | // NewCookieStore creates a new cookieStore. | ||||||
| @@ -27,7 +28,7 @@ type cookieStore struct { | |||||||
| 	*sessions.CookieStore | 	*sessions.CookieStore | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *cookieStore) Options(options Options) { | func (c *cookieStore) Options(options ginsessions.Options) { | ||||||
| 	c.CookieStore.Options = &sessions.Options{ | 	c.CookieStore.Options = &sessions.Options{ | ||||||
| 		Path:     options.Path, | 		Path:     options.Path, | ||||||
| 		Domain:   options.Domain, | 		Domain:   options.Domain, | ||||||
| @@ -1,15 +1,18 @@ | |||||||
| package sessions | package redisstore | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/boj/redistore" | 	"github.com/boj/redistore" | ||||||
|  | 	ginsessions "github.com/dalu/sessions" | ||||||
| 	"github.com/garyburd/redigo/redis" | 	"github.com/garyburd/redigo/redis" | ||||||
| 	"github.com/gorilla/sessions" | 	"github.com/gorilla/sessions" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // RedisStore interface | ||||||
| type RedisStore interface { | type RedisStore interface { | ||||||
| 	Store | 	ginsessions.Store | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // NewRedisStore creates a new Redis Store | ||||||
| // size: maximum number of idle connections. | // size: maximum number of idle connections. | ||||||
| // network: tcp or udp | // network: tcp or udp | ||||||
| // address: host:port | // address: host:port | ||||||
| @@ -58,12 +61,12 @@ type redisStore struct { | |||||||
| 	*redistore.RediStore | 	*redistore.RediStore | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (c *redisStore) Options(options Options) { | func (c *redisStore) Options(options ginsessions.Options) { | ||||||
| 	c.RediStore.Options = &sessions.Options{ | 	c.RediStore.Options = &sessions.Options{ | ||||||
| 		Path:     options.Path, | 		Path:     options.Path, | ||||||
| 		Domain:   options.Domain, | 		Domain:   options.Domain, | ||||||
| 		MaxAge:   options.MaxAge, | 		MaxAge:   options.MaxAge, | ||||||
| 		Secure:   options.Secure, | 		Secure:   options.Secure, | ||||||
| 		HttpOnly: options.HttpOnly, | 		HttpOnly: options.HTTPOnly, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -1,30 +0,0 @@ | |||||||
| package sessions |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var newCookieStore = func(_ *testing.T) Store { |  | ||||||
| 	store := NewCookieStore([]byte("secret")) |  | ||||||
| 	return store |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCookie_SessionGetSet(t *testing.T) { |  | ||||||
| 	sessionGetSet(t, newCookieStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCookie_SessionDeleteKey(t *testing.T) { |  | ||||||
| 	sessionDeleteKey(t, newCookieStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCookie_SessionFlashes(t *testing.T) { |  | ||||||
| 	sessionFlashes(t, newCookieStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCookie_SessionClear(t *testing.T) { |  | ||||||
| 	sessionClear(t, newCookieStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestCookie_SessionOptions(t *testing.T) { |  | ||||||
| 	sessionOptions(t, newCookieStore) |  | ||||||
| } |  | ||||||
| @@ -1,35 +0,0 @@ | |||||||
| package sessions |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const redisTestServer = "localhost:6379" |  | ||||||
|  |  | ||||||
| var newRedisStore = func(_ *testing.T) Store { |  | ||||||
| 	store, err := NewRedisStore(10, "tcp", redisTestServer, "", []byte("secret")) |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	return store |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRedis_SessionGetSet(t *testing.T) { |  | ||||||
| 	sessionGetSet(t, newRedisStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRedis_SessionDeleteKey(t *testing.T) { |  | ||||||
| 	sessionDeleteKey(t, newRedisStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRedis_SessionFlashes(t *testing.T) { |  | ||||||
| 	sessionFlashes(t, newRedisStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRedis_SessionClear(t *testing.T) { |  | ||||||
| 	sessionClear(t, newRedisStore) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestRedis_SessionOptions(t *testing.T) { |  | ||||||
| 	sessionOptions(t, newRedisStore) |  | ||||||
| } |  | ||||||
| @@ -1,154 +0,0 @@ | |||||||
| package sessions |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"log" |  | ||||||
| 	"net/http" |  | ||||||
|  |  | ||||||
| 	"github.com/gin-gonic/gin" |  | ||||||
| 	"github.com/gorilla/context" |  | ||||||
| 	"github.com/gorilla/sessions" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	DefaultKey  = "github.com/gin-contrib/sessions" |  | ||||||
| 	errorFormat = "[sessions] ERROR! %s\n" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type Store interface { |  | ||||||
| 	sessions.Store |  | ||||||
| 	Options(Options) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Options stores configuration for a session or session store. |  | ||||||
| // Fields are a subset of http.Cookie fields. |  | ||||||
| type Options struct { |  | ||||||
| 	Path   string |  | ||||||
| 	Domain string |  | ||||||
| 	// MaxAge=0 means no 'Max-Age' attribute specified. |  | ||||||
| 	// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'. |  | ||||||
| 	// MaxAge>0 means Max-Age attribute present and given in seconds. |  | ||||||
| 	MaxAge   int |  | ||||||
| 	Secure   bool |  | ||||||
| 	HttpOnly bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Wraps thinly gorilla-session methods. |  | ||||||
| // Session stores the values and optional configuration for a session. |  | ||||||
| type Session interface { |  | ||||||
| 	// Get returns the session value associated to the given key. |  | ||||||
| 	Get(key interface{}) interface{} |  | ||||||
| 	// Set sets the session value associated to the given key. |  | ||||||
| 	Set(key interface{}, val interface{}) |  | ||||||
| 	// Delete removes the session value associated to the given key. |  | ||||||
| 	Delete(key interface{}) |  | ||||||
| 	// Clear deletes all values in the session. |  | ||||||
| 	Clear() |  | ||||||
| 	// AddFlash adds a flash message to the session. |  | ||||||
| 	// A single variadic argument is accepted, and it is optional: it defines the flash key. |  | ||||||
| 	// If not defined "_flash" is used by default. |  | ||||||
| 	AddFlash(value interface{}, vars ...string) |  | ||||||
| 	// Flashes returns a slice of flash messages from the session. |  | ||||||
| 	// A single variadic argument is accepted, and it is optional: it defines the flash key. |  | ||||||
| 	// If not defined "_flash" is used by default. |  | ||||||
| 	Flashes(vars ...string) []interface{} |  | ||||||
| 	// Options sets confuguration for a session. |  | ||||||
| 	Options(Options) |  | ||||||
| 	// Save saves all sessions used during the current request. |  | ||||||
| 	Save() error |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Sessions(name string, store Store) gin.HandlerFunc { |  | ||||||
| 	return func(c *gin.Context) { |  | ||||||
| 		s := &session{ |  | ||||||
| 			name:    name, |  | ||||||
| 			request: c.Request, |  | ||||||
| 			store:   store, |  | ||||||
| 			session: nil, |  | ||||||
| 			written: false, |  | ||||||
| 			writer:  c.Writer, |  | ||||||
| 		} |  | ||||||
| 		c.Set(DefaultKey, s) |  | ||||||
| 		defer context.Clear(c.Request) |  | ||||||
| 		c.Next() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type session struct { |  | ||||||
| 	name    string |  | ||||||
| 	request *http.Request |  | ||||||
| 	store   Store |  | ||||||
| 	session *sessions.Session |  | ||||||
| 	written bool |  | ||||||
| 	writer  http.ResponseWriter |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Get(key interface{}) interface{} { |  | ||||||
| 	return s.Session().Values[key] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Set(key interface{}, val interface{}) { |  | ||||||
| 	s.Session().Values[key] = val |  | ||||||
| 	s.written = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Delete(key interface{}) { |  | ||||||
| 	delete(s.Session().Values, key) |  | ||||||
| 	s.written = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Clear() { |  | ||||||
| 	for key := range s.Session().Values { |  | ||||||
| 		s.Delete(key) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) AddFlash(value interface{}, vars ...string) { |  | ||||||
| 	s.Session().AddFlash(value, vars...) |  | ||||||
| 	s.written = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Flashes(vars ...string) []interface{} { |  | ||||||
| 	s.written = true |  | ||||||
| 	return s.Session().Flashes(vars...) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Options(options Options) { |  | ||||||
| 	s.Session().Options = &sessions.Options{ |  | ||||||
| 		Path:     options.Path, |  | ||||||
| 		Domain:   options.Domain, |  | ||||||
| 		MaxAge:   options.MaxAge, |  | ||||||
| 		Secure:   options.Secure, |  | ||||||
| 		HttpOnly: options.HttpOnly, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Save() error { |  | ||||||
| 	if s.Written() { |  | ||||||
| 		e := s.Session().Save(s.request, s.writer) |  | ||||||
| 		if e == nil { |  | ||||||
| 			s.written = false |  | ||||||
| 		} |  | ||||||
| 		return e |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Session() *sessions.Session { |  | ||||||
| 	if s.session == nil { |  | ||||||
| 		var err error |  | ||||||
| 		s.session, err = s.store.Get(s.request, s.name) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Printf(errorFormat, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return s.session |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *session) Written() bool { |  | ||||||
| 	return s.written |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // shortcut to get session |  | ||||||
| func Default(c *gin.Context) Session { |  | ||||||
| 	return c.MustGet(DefaultKey).(Session) |  | ||||||
| } |  | ||||||
| @@ -1,220 +0,0 @@ | |||||||
| package sessions |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"net/http" |  | ||||||
| 	"net/http/httptest" |  | ||||||
| 	"strings" |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/gin-gonic/gin" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type storeFactory func(*testing.T) Store |  | ||||||
|  |  | ||||||
| const sessionName = "mysession" |  | ||||||
|  |  | ||||||
| const ok = "ok" |  | ||||||
|  |  | ||||||
| func sessionGetSet(t *testing.T, newStore storeFactory) { |  | ||||||
| 	r := gin.Default() |  | ||||||
| 	r.Use(Sessions(sessionName, newStore(t))) |  | ||||||
|  |  | ||||||
| 	r.GET("/set", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.Set("key", ok) |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/get", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		if session.Get("key") != ok { |  | ||||||
| 			t.Error("Session writing failed") |  | ||||||
| 		} |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	res1 := httptest.NewRecorder() |  | ||||||
| 	req1, _ := http.NewRequest("GET", "/set", nil) |  | ||||||
| 	r.ServeHTTP(res1, req1) |  | ||||||
|  |  | ||||||
| 	res2 := httptest.NewRecorder() |  | ||||||
| 	req2, _ := http.NewRequest("GET", "/get", nil) |  | ||||||
| 	req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res2, req2) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sessionDeleteKey(t *testing.T, newStore storeFactory) { |  | ||||||
| 	r := gin.Default() |  | ||||||
| 	r.Use(Sessions(sessionName, newStore(t))) |  | ||||||
|  |  | ||||||
| 	r.GET("/set", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.Set("key", ok) |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/delete", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.Delete("key") |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/get", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		if session.Get("key") != nil { |  | ||||||
| 			t.Error("Session deleting failed") |  | ||||||
| 		} |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	res1 := httptest.NewRecorder() |  | ||||||
| 	req1, _ := http.NewRequest("GET", "/set", nil) |  | ||||||
| 	r.ServeHTTP(res1, req1) |  | ||||||
|  |  | ||||||
| 	res2 := httptest.NewRecorder() |  | ||||||
| 	req2, _ := http.NewRequest("GET", "/delete", nil) |  | ||||||
| 	req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res2, req2) |  | ||||||
|  |  | ||||||
| 	res3 := httptest.NewRecorder() |  | ||||||
| 	req3, _ := http.NewRequest("GET", "/get", nil) |  | ||||||
| 	req3.Header.Set("Cookie", res2.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res3, req3) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sessionFlashes(t *testing.T, newStore storeFactory) { |  | ||||||
| 	r := gin.Default() |  | ||||||
| 	store := newStore(t) |  | ||||||
| 	store.Options(Options{ |  | ||||||
| 		Domain: "localhost", |  | ||||||
| 	}) |  | ||||||
| 	r.Use(Sessions(sessionName, store)) |  | ||||||
|  |  | ||||||
| 	r.GET("/set", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.AddFlash(ok) |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/flash", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		l := len(session.Flashes()) |  | ||||||
| 		if l != 1 { |  | ||||||
| 			t.Error("Flashes count does not equal 1. Equals ", l) |  | ||||||
| 		} |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/check", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		l := len(session.Flashes()) |  | ||||||
| 		if l != 0 { |  | ||||||
| 			t.Error("flashes count is not 0 after reading. Equals ", l) |  | ||||||
| 		} |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	res1 := httptest.NewRecorder() |  | ||||||
| 	req1, _ := http.NewRequest("GET", "/set", nil) |  | ||||||
| 	r.ServeHTTP(res1, req1) |  | ||||||
|  |  | ||||||
| 	res2 := httptest.NewRecorder() |  | ||||||
| 	req2, _ := http.NewRequest("GET", "/flash", nil) |  | ||||||
| 	req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res2, req2) |  | ||||||
|  |  | ||||||
| 	res3 := httptest.NewRecorder() |  | ||||||
| 	req3, _ := http.NewRequest("GET", "/check", nil) |  | ||||||
| 	req3.Header.Set("Cookie", res2.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res3, req3) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sessionClear(t *testing.T, newStore storeFactory) { |  | ||||||
| 	data := map[string]string{ |  | ||||||
| 		"key": "val", |  | ||||||
| 		"foo": "bar", |  | ||||||
| 	} |  | ||||||
| 	r := gin.Default() |  | ||||||
| 	store := newStore(t) |  | ||||||
| 	r.Use(Sessions(sessionName, store)) |  | ||||||
|  |  | ||||||
| 	r.GET("/set", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		for k, v := range data { |  | ||||||
| 			session.Set(k, v) |  | ||||||
| 		} |  | ||||||
| 		session.Clear() |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	r.GET("/check", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		for k, v := range data { |  | ||||||
| 			if session.Get(k) == v { |  | ||||||
| 				t.Fatal("Session clear failed") |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	res1 := httptest.NewRecorder() |  | ||||||
| 	req1, _ := http.NewRequest("GET", "/set", nil) |  | ||||||
| 	r.ServeHTTP(res1, req1) |  | ||||||
|  |  | ||||||
| 	res2 := httptest.NewRecorder() |  | ||||||
| 	req2, _ := http.NewRequest("GET", "/check", nil) |  | ||||||
| 	req2.Header.Set("Cookie", res1.Header().Get("Set-Cookie")) |  | ||||||
| 	r.ServeHTTP(res2, req2) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func sessionOptions(t *testing.T, newStore storeFactory) { |  | ||||||
| 	r := gin.Default() |  | ||||||
| 	store := newStore(t) |  | ||||||
| 	store.Options(Options{ |  | ||||||
| 		Domain: "localhost", |  | ||||||
| 	}) |  | ||||||
| 	r.Use(Sessions(sessionName, store)) |  | ||||||
|  |  | ||||||
| 	r.GET("/domain", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.Set("key", ok) |  | ||||||
| 		session.Options(Options{ |  | ||||||
| 			Path: "/foo/bar/bat", |  | ||||||
| 		}) |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
| 	r.GET("/path", func(c *gin.Context) { |  | ||||||
| 		session := Default(c) |  | ||||||
| 		session.Set("key", ok) |  | ||||||
| 		session.Save() |  | ||||||
| 		c.String(200, ok) |  | ||||||
| 	}) |  | ||||||
| 	res1 := httptest.NewRecorder() |  | ||||||
| 	req1, _ := http.NewRequest("GET", "/domain", nil) |  | ||||||
| 	r.ServeHTTP(res1, req1) |  | ||||||
|  |  | ||||||
| 	res2 := httptest.NewRecorder() |  | ||||||
| 	req2, _ := http.NewRequest("GET", "/path", nil) |  | ||||||
| 	r.ServeHTTP(res2, req2) |  | ||||||
|  |  | ||||||
| 	s := strings.Split(res1.Header().Get("Set-Cookie"), ";") |  | ||||||
| 	if s[1] != " Path=/foo/bar/bat" { |  | ||||||
| 		t.Error("Error writing path with options:", s[1]) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	s = strings.Split(res2.Header().Get("Set-Cookie"), ";") |  | ||||||
| 	if s[1] != " Domain=localhost" { |  | ||||||
| 		t.Error("Error writing domain with options:", s[1]) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user
	 Darko Luketic
					Darko Luketic