浏览代码

Updated session lib

Torkel Ödegaard 11 年之前
父节点
当前提交
aafe2c5b98

+ 1 - 1
Godeps/Godeps.json

@@ -30,7 +30,7 @@
 		},
 		{
 			"ImportPath": "github.com/macaron-contrib/session",
-			"Rev": "f00d48fd4f85088603c1493b0a99fdfe95d0658c"
+			"Rev": "65b8817c40cb5bdce08673a15fd2a648c2ba0e16"
 		},
 		{
 			"ImportPath": "github.com/mattn/go-sqlite3",

+ 5 - 166
Godeps/_workspace/src/github.com/macaron-contrib/session/README.md

@@ -1,177 +1,16 @@
-session
+session [![Build Status](https://drone.io/github.com/macaron-contrib/session/status.png)](https://drone.io/github.com/macaron-contrib/session/latest) [![](http://gocover.io/_badge/github.com/macaron-contrib/session)](http://gocover.io/github.com/macaron-contrib/session)
 =======
 
-Middleware session is the session manager of [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including cookie, memory, file, redis, memcache, PostgreSQL, MySQL, and couchbase.
-
-[API Reference](https://gowalker.org/github.com/macaron-contrib/session)
+Middleware session provides session management for [Macaron](https://github.com/Unknwon/macaron). It can use many session providers, including memory, file, Redis, Memcache, PostgreSQL, MySQL, Couchbase and Ledis.
 
 ### Installation
 
 	go get github.com/macaron-contrib/session
-
-## Usage
-
-```go
-import (
-	"github.com/Unknwon/macaron"
-	"github.com/macaron-contrib/session"
-)
-
-func main() {
-  	m := macaron.Classic()
-  	m.Use(session.Sessioner())
-	
-	m.Get("/", func(sess session.Store) string {
-		sess.Set("session", "session middleware")
-		return sess.Get("session").(string)
-	})
-	
-	m.Get("/signup", func(ctx *macaron.Context, f *session.Flash) {
-		f.Success("yes!!!")
-		f.Error("opps...")
-		// Use following fields in template
-		// - {{.Flash.SuccessMsg}}
-		// - {{.Flash.ErrorMsg}}
-		ctx.HTML(200, "signup", ctx.Data)
-	})
-
-	m.Run()
-}
-```
-
-To use redis, memcache, PostgreSQL, MySQL, or couchbase as adapter, you should import their init functions:
-
-```go
-import (
-	_ "github.com/macaron-contrib/session/redis"
-	_ "github.com/macaron-contrib/session/memcache"
-	_ "github.com/macaron-contrib/session/postgres"
-	_ "github.com/macaron-contrib/session/mysql"
-	_ "github.com/macaron-contrib/session/couchbase"
-)
-```
-
-## Options
-
-`session.Sessioner` comes with a variety of configuration options:
-
-```go
-// ...
-m.Use(session.Sessioner(session.Options{
-	Provider:		"memory", // Name of provider.
-	Config: Config{
-		CookieName:		"MacaronSession", // Key name store in cookie.
-		Gclifetime:		3600, // GC interval for memory adapter.
-		ProviderConfig:	"./tmp", // Provider configuration string.
-	},
-}))
-// ...
-```
-
-### Example Options
-
-- memory:
-
-	```go
-	// ...
-	m.Use(session.Sessioner(session.Options{
-		Provider:		"memory", // Name of provider.
-		Config: Config{
-			CookieName:		"MacaronSession", // Key name store in cookie.
-			Gclifetime:		3600, // GC interval for memory adapter.
-			ProviderConfig:	"./tmp", // Provider configuration string.
-		},
-	}))
-	// ...
-	```
-
-- file:
-
-	```go
-	// ...
-	m.Use(session.Sessioner(session.Options{
-		Provider:		"file", // Name of provider.
-		Config: Config{
-			CookieName:		"MacaronSession", // Key name store in cookie.
-			Gclifetime:		3600, // GC interval for memory adapter.
-			ProviderConfig:	"./tmp", // Provider configuration string.
-		},
-	}))
-	// ...
-	```
-
-- Redis:
-
-	```go
-	// ...
-	m.Use(session.Sessioner(session.Options{
-		Provider:		"redis", // Name of provider.
-		Config: Config{
-			CookieName:		"MacaronSession", // Key name store in cookie.
-			Gclifetime:		3600, // GC interval for memory adapter.
-			ProviderConfig:	"127.0.0.1:6379,100,macaron", // Provider configuration string.
-		},
-	}))
-	// ...
-	```
-
-- MySQL:
-
-	```go
-	// ...
-	m.Use(session.Sessioner(session.Options{
-		Provider:		"mysql", // Name of provider.
-		Config: Config{
-			CookieName:		"MacaronSession", // Key name store in cookie.
-			Gclifetime:		3600, // GC interval for memory adapter.
-			ProviderConfig:	"username:password@protocol(address)/dbname?param=value", // Provider configuration string.
-		},
-	}))
-	// ...
-	```
-
-- Cookie:
-
-	```go
-	// ...
-	m.Use(session.Sessioner(session.Options{
-		Provider:		"cookie", // Name of provider.
-		Config: Config{
-			CookieName:		"MacaronSession", // Key name store in cookie.
-			Gclifetime:		3600, // GC interval for memory adapter.
-			ProviderConfig:	"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}", // Provider configuration string.
-		},
-	}))
-	// ...
-	```
-
-## How to write own provider?
-
-When you develop a web app, maybe you want to write own provider because you must meet the requirements.
-
-Writing a provider is easy. You only need to define two struct types 
-(Session and Provider), which satisfy the interface definition. 
-Maybe you will find the **memory** provider is a good example.
-
-	type Store interface {
-		Set(key, value interface{}) error     //set session value
-		Get(key interface{}) interface{}      //get session value
-		Delete(key interface{}) error         //delete session value
-		SessionID() string                    //back current sessionID
-		SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data
-		Flush() error                         //delete all data
-	}
 	
-	type Provider interface {
-		SessionInit(gclifetime int64, config string) error
-		SessionRead(sid string) (SessionStore, error)
-		SessionExist(sid string) bool
-		SessionRegenerate(oldsid, sid string) (Store, error)
-		SessionDestroy(sid string) error
-		SessionAll() int //get all active session
-		SessionGC()
-	}
+## Getting Help
 
+- [API Reference](https://gowalker.org/github.com/macaron-contrib/session)
+- [Documentation](http://macaron.gogs.io/docs/middlewares/session)
 
 ## License
 

+ 89 - 80
Godeps/_workspace/src/github.com/macaron-contrib/session/couchbase/couchbase.go

@@ -16,7 +16,6 @@
 package session
 
 import (
-	"net/http"
 	"strings"
 	"sync"
 
@@ -25,72 +24,78 @@ import (
 	"github.com/macaron-contrib/session"
 )
 
-var couchbpder = &CouchbaseProvider{}
-
+// CouchbaseSessionStore represents a couchbase session store implementation.
 type CouchbaseSessionStore struct {
 	b           *couchbase.Bucket
 	sid         string
 	lock        sync.RWMutex
-	values      map[interface{}]interface{}
+	data        map[interface{}]interface{}
 	maxlifetime int64
 }
 
-type CouchbaseProvider struct {
-	maxlifetime int64
-	savePath    string
-	pool        string
-	bucket      string
-	b           *couchbase.Bucket
-}
+// Set sets value to given key in session.
+func (s *CouchbaseSessionStore) Set(key, val interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
 
-func (cs *CouchbaseSessionStore) Set(key, value interface{}) error {
-	cs.lock.Lock()
-	defer cs.lock.Unlock()
-	cs.values[key] = value
+	s.data[key] = val
 	return nil
 }
 
-func (cs *CouchbaseSessionStore) Get(key interface{}) interface{} {
-	cs.lock.RLock()
-	defer cs.lock.RUnlock()
-	if v, ok := cs.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Get gets value by given key in session.
+func (s *CouchbaseSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
 
-func (cs *CouchbaseSessionStore) Delete(key interface{}) error {
-	cs.lock.Lock()
-	defer cs.lock.Unlock()
-	delete(cs.values, key)
-	return nil
+	return s.data[key]
 }
 
-func (cs *CouchbaseSessionStore) Flush() error {
-	cs.lock.Lock()
-	defer cs.lock.Unlock()
-	cs.values = make(map[interface{}]interface{})
+// Delete delete a key from session.
+func (s *CouchbaseSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-func (cs *CouchbaseSessionStore) SessionID() string {
-	return cs.sid
+// ID returns current session ID.
+func (s *CouchbaseSessionStore) ID() string {
+	return s.sid
 }
 
-func (cs *CouchbaseSessionStore) SessionRelease(w http.ResponseWriter) {
-	defer cs.b.Close()
+// Release releases resource and save data to provider.
+func (s *CouchbaseSessionStore) Release() error {
+	defer s.b.Close()
 
-	bo, err := session.EncodeGob(cs.values)
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
 	}
 
-	cs.b.Set(cs.sid, int(cs.maxlifetime), bo)
+	return s.b.Set(s.sid, int(s.maxlifetime), data)
+}
+
+// Flush deletes all session data.
+func (s *CouchbaseSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data = make(map[interface{}]interface{})
+	return nil
+}
+
+// CouchbaseProvider represents a couchbase session provider implementation.
+type CouchbaseProvider struct {
+	maxlifetime int64
+	connStr     string
+	pool        string
+	bucket      string
+	b           *couchbase.Bucket
 }
 
 func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket {
-	c, err := couchbase.Connect(cp.savePath)
+	c, err := couchbase.Connect(cp.connStr)
 	if err != nil {
 		return nil
 	}
@@ -108,32 +113,32 @@ func (cp *CouchbaseProvider) getBucket() *couchbase.Bucket {
 	return bucket
 }
 
-// init couchbase session
-// savepath like couchbase server REST/JSON URL
+// Init initializes memory session provider.
+// connStr is couchbase server REST/JSON URL
 // e.g. http://host:port/, Pool, Bucket
-func (cp *CouchbaseProvider) SessionInit(maxlifetime int64, savePath string) error {
-	cp.maxlifetime = maxlifetime
-	configs := strings.Split(savePath, ",")
+func (p *CouchbaseProvider) Init(maxlifetime int64, connStr string) error {
+	p.maxlifetime = maxlifetime
+	configs := strings.Split(connStr, ",")
 	if len(configs) > 0 {
-		cp.savePath = configs[0]
+		p.connStr = configs[0]
 	}
 	if len(configs) > 1 {
-		cp.pool = configs[1]
+		p.pool = configs[1]
 	}
 	if len(configs) > 2 {
-		cp.bucket = configs[2]
+		p.bucket = configs[2]
 	}
 
 	return nil
 }
 
-// read couchbase session by sid
-func (cp *CouchbaseProvider) SessionRead(sid string) (session.RawStore, error) {
-	cp.b = cp.getBucket()
+// Read returns raw session store by session ID.
+func (p *CouchbaseProvider) Read(sid string) (session.RawStore, error) {
+	p.b = p.getBucket()
 
 	var doc []byte
 
-	err := cp.b.Get(sid, &doc)
+	err := p.b.Get(sid, &doc)
 	var kv map[interface{}]interface{}
 	if doc == nil {
 		kv = make(map[interface{}]interface{})
@@ -144,38 +149,49 @@ func (cp *CouchbaseProvider) SessionRead(sid string) (session.RawStore, error) {
 		}
 	}
 
-	cs := &CouchbaseSessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+	cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return cs, nil
 }
 
-func (cp *CouchbaseProvider) SessionExist(sid string) bool {
-	cp.b = cp.getBucket()
-	defer cp.b.Close()
+// Exist returns true if session with given ID exists.
+func (p *CouchbaseProvider) Exist(sid string) bool {
+	p.b = p.getBucket()
+	defer p.b.Close()
 
 	var doc []byte
 
-	if err := cp.b.Get(sid, &doc); err != nil || doc == nil {
+	if err := p.b.Get(sid, &doc); err != nil || doc == nil {
 		return false
 	} else {
 		return true
 	}
 }
 
-func (cp *CouchbaseProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
-	cp.b = cp.getBucket()
+// Destory deletes a session by session ID.
+func (p *CouchbaseProvider) Destory(sid string) error {
+	p.b = p.getBucket()
+	defer p.b.Close()
+
+	p.b.Delete(sid)
+	return nil
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *CouchbaseProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
+	p.b = p.getBucket()
 
 	var doc []byte
-	if err := cp.b.Get(oldsid, &doc); err != nil || doc == nil {
-		cp.b.Set(sid, int(cp.maxlifetime), "")
+	if err := p.b.Get(oldsid, &doc); err != nil || doc == nil {
+		p.b.Set(sid, int(p.maxlifetime), "")
 	} else {
-		err := cp.b.Delete(oldsid)
+		err := p.b.Delete(oldsid)
 		if err != nil {
 			return nil, err
 		}
-		_, _ = cp.b.Add(sid, int(cp.maxlifetime), doc)
+		_, _ = p.b.Add(sid, int(p.maxlifetime), doc)
 	}
 
-	err := cp.b.Get(sid, &doc)
+	err := p.b.Get(sid, &doc)
 	if err != nil {
 		return nil, err
 	}
@@ -189,26 +205,19 @@ func (cp *CouchbaseProvider) SessionRegenerate(oldsid, sid string) (session.RawS
 		}
 	}
 
-	cs := &CouchbaseSessionStore{b: cp.b, sid: sid, values: kv, maxlifetime: cp.maxlifetime}
+	cs := &CouchbaseSessionStore{b: p.b, sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return cs, nil
 }
 
-func (cp *CouchbaseProvider) SessionDestroy(sid string) error {
-	cp.b = cp.getBucket()
-	defer cp.b.Close()
-
-	cp.b.Delete(sid)
-	return nil
-}
-
-func (cp *CouchbaseProvider) SessionGC() {
-	return
-}
-
-func (cp *CouchbaseProvider) SessionAll() int {
+// Count counts and returns number of sessions.
+func (p *CouchbaseProvider) Count() int {
+	// FIXME
 	return 0
 }
 
+// GC calls GC to clean expired sessions.
+func (p *CouchbaseProvider) GC() {}
+
 func init() {
-	session.Register("couchbase", couchbpder)
+	session.Register("couchbase", &CouchbaseProvider{})
 }

+ 71 - 72
Godeps/_workspace/src/github.com/macaron-contrib/session/ledis/ledis.go

@@ -16,89 +16,89 @@
 package session
 
 import (
-	"net/http"
 	"sync"
 
-	"github.com/astaxie/beego/session"
 	"github.com/siddontang/ledisdb/config"
 	"github.com/siddontang/ledisdb/ledis"
+
+	"github.com/macaron-contrib/session"
 )
 
-var ledispder = &LedisProvider{}
 var c *ledis.DB
 
-// ledis session store
+// LedisSessionStore represents a ledis session store implementation.
 type LedisSessionStore struct {
 	sid         string
 	lock        sync.RWMutex
-	values      map[interface{}]interface{}
+	data        map[interface{}]interface{}
 	maxlifetime int64
 }
 
-// set value in ledis session
-func (ls *LedisSessionStore) Set(key, value interface{}) error {
-	ls.lock.Lock()
-	defer ls.lock.Unlock()
-	ls.values[key] = value
+// Set sets value to given key in session.
+func (s *LedisSessionStore) Set(key, val interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data[key] = val
 	return nil
 }
 
-// get value in ledis session
-func (ls *LedisSessionStore) Get(key interface{}) interface{} {
-	ls.lock.RLock()
-	defer ls.lock.RUnlock()
-	if v, ok := ls.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Get gets value by given key in session.
+func (s *LedisSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
 
-// delete value in ledis session
-func (ls *LedisSessionStore) Delete(key interface{}) error {
-	ls.lock.Lock()
-	defer ls.lock.Unlock()
-	delete(ls.values, key)
-	return nil
+	return s.data[key]
 }
 
-// clear all values in ledis session
-func (ls *LedisSessionStore) Flush() error {
-	ls.lock.Lock()
-	defer ls.lock.Unlock()
-	ls.values = make(map[interface{}]interface{})
+// Delete delete a key from session.
+func (s *LedisSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-// get ledis session id
-func (ls *LedisSessionStore) SessionID() string {
-	return ls.sid
+// ID returns current session ID.
+func (s *LedisSessionStore) ID() string {
+	return s.sid
 }
 
-// save session values to ledis
-func (ls *LedisSessionStore) SessionRelease(w http.ResponseWriter) {
-	b, err := session.EncodeGob(ls.values)
+// Release releases resource and save data to provider.
+func (s *LedisSessionStore) Release() error {
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
+	}
+	if err = c.Set([]byte(s.sid), data); err != nil {
+		return err
 	}
-	c.Set([]byte(ls.sid), b)
-	c.Expire([]byte(ls.sid), ls.maxlifetime)
+	_, err = c.Expire([]byte(s.sid), s.maxlifetime)
+	return err
 }
 
-// ledis session provider
+// Flush deletes all session data.
+func (s *LedisSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data = make(map[interface{}]interface{})
+	return nil
+}
+
+// LedisProvider represents a ledis session provider implementation.
 type LedisProvider struct {
 	maxlifetime int64
 	savePath    string
 }
 
-// init ledis session
-// savepath like ledis server saveDataPath,pool size
-// e.g. 127.0.0.1:6379,100,astaxie
-func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
-	lp.maxlifetime = maxlifetime
-	lp.savePath = savePath
+// Init initializes memory session provider.
+func (p *LedisProvider) Init(maxlifetime int64, savePath string) error {
+	p.maxlifetime = maxlifetime
+	p.savePath = savePath
 	cfg := new(config.Config)
-	cfg.DataDir = lp.savePath
+	cfg.DataDir = p.savePath
 	var err error
 	nowLedis, err := ledis.Open(cfg)
 	c, err = nowLedis.Select(0)
@@ -109,8 +109,8 @@ func (lp *LedisProvider) SessionInit(maxlifetime int64, savePath string) error {
 	return nil
 }
 
-// read ledis session by sid
-func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
+// Read returns raw session store by session ID.
+func (p *LedisProvider) Read(sid string) (session.RawStore, error) {
 	kvs, err := c.Get([]byte(sid))
 	var kv map[interface{}]interface{}
 	if len(kvs) == 0 {
@@ -121,12 +121,12 @@ func (lp *LedisProvider) SessionRead(sid string) (session.SessionStore, error) {
 			return nil, err
 		}
 	}
-	ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+	ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return ls, nil
 }
 
-// check ledis session exist by sid
-func (lp *LedisProvider) SessionExist(sid string) bool {
+// Exist returns true if session with given ID exists.
+func (p *LedisProvider) Exist(sid string) bool {
 	count, _ := c.Exists([]byte(sid))
 	if count == 0 {
 		return false
@@ -135,19 +135,25 @@ func (lp *LedisProvider) SessionExist(sid string) bool {
 	}
 }
 
-// generate new sid for ledis session
-func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) {
+// Destory deletes a session by session ID.
+func (p *LedisProvider) Destory(sid string) error {
+	_, err := c.Del([]byte(sid))
+	return err
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *LedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
 	count, _ := c.Exists([]byte(sid))
 	if count == 0 {
 		// oldsid doesn't exists, set the new sid directly
 		// ignore error here, since if it return error
 		// the existed value will be 0
 		c.Set([]byte(sid), []byte(""))
-		c.Expire([]byte(sid), lp.maxlifetime)
+		c.Expire([]byte(sid), p.maxlifetime)
 	} else {
 		data, _ := c.Get([]byte(oldsid))
 		c.Set([]byte(sid), data)
-		c.Expire([]byte(sid), lp.maxlifetime)
+		c.Expire([]byte(sid), p.maxlifetime)
 	}
 	kvs, err := c.Get([]byte(sid))
 	var kv map[interface{}]interface{}
@@ -159,26 +165,19 @@ func (lp *LedisProvider) SessionRegenerate(oldsid, sid string) (session.SessionS
 			return nil, err
 		}
 	}
-	ls := &LedisSessionStore{sid: sid, values: kv, maxlifetime: lp.maxlifetime}
+	ls := &LedisSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return ls, nil
 }
 
-// delete ledis session by id
-func (lp *LedisProvider) SessionDestroy(sid string) error {
-	c.Del([]byte(sid))
-	return nil
-}
-
-// Impelment method, no used.
-func (lp *LedisProvider) SessionGC() {
-	return
-}
-
-// @todo
-func (lp *LedisProvider) SessionAll() int {
+// Count counts and returns number of sessions.
+func (p *LedisProvider) Count() int {
+	// FIXME
 	return 0
 }
 
+// GC calls GC to clean expired sessions.
+func (p *LedisProvider) GC() {}
+
 func init() {
-	session.Register("ledis", ledispder)
+	session.Register("ledis", &LedisProvider{})
 }

+ 80 - 83
Godeps/_workspace/src/github.com/macaron-contrib/session/memcache/memcache.go

@@ -16,7 +16,6 @@
 package session
 
 import (
-	"net/http"
 	"strings"
 	"sync"
 
@@ -26,73 +25,72 @@ import (
 )
 
 var (
-	mempder = &MemProvider{}
-	client  *memcache.Client
+	client *memcache.Client
 )
 
-// memcache session store
+// MemcacheSessionStore represents a memcache session store implementation.
 type MemcacheSessionStore struct {
 	sid         string
 	lock        sync.RWMutex
-	values      map[interface{}]interface{}
+	data        map[interface{}]interface{}
 	maxlifetime int64
 }
 
-// set value in memcache session
-func (rs *MemcacheSessionStore) Set(key, value interface{}) error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	rs.values[key] = value
+// Set sets value to given key in session.
+func (s *MemcacheSessionStore) Set(key, val interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data[key] = val
 	return nil
 }
 
-// get value in memcache session
-func (rs *MemcacheSessionStore) Get(key interface{}) interface{} {
-	rs.lock.RLock()
-	defer rs.lock.RUnlock()
-	if v, ok := rs.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Get gets value by given key in session.
+func (s *MemcacheSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
 
-// delete value in memcache session
-func (rs *MemcacheSessionStore) Delete(key interface{}) error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	delete(rs.values, key)
-	return nil
+	return s.data[key]
 }
 
-// clear all values in memcache session
-func (rs *MemcacheSessionStore) Flush() error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	rs.values = make(map[interface{}]interface{})
+// Delete delete a key from session.
+func (s *MemcacheSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-// get redis session id
-func (rs *MemcacheSessionStore) SessionID() string {
-	return rs.sid
+// ID returns current session ID.
+func (s *MemcacheSessionStore) ID() string {
+	return s.sid
 }
 
-// save session values to redis
-func (rs *MemcacheSessionStore) SessionRelease(w http.ResponseWriter) {
-	b, err := session.EncodeGob(rs.values)
+// Release releases resource and save data to provider.
+func (s *MemcacheSessionStore) Release() error {
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
 	}
 
-	client.Set(&memcache.Item{
-		Key:        rs.sid,
-		Value:      b,
-		Expiration: int32(rs.maxlifetime),
+	return client.Set(&memcache.Item{
+		Key:        s.sid,
+		Value:      data,
+		Expiration: int32(s.maxlifetime),
 	})
 }
 
-// redis session provider
+// Flush deletes all session data.
+func (s *MemcacheSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data = make(map[interface{}]interface{})
+	return nil
+}
+
+// MemProvider represents a memcache session provider implementation.
 type MemProvider struct {
 	maxlifetime int64
 	conninfo    []string
@@ -100,25 +98,25 @@ type MemProvider struct {
 	password    string
 }
 
-// init redis session
-// savepath like
+// Init initializes memory session provider.
+// connStrs can be multiple connection strings separate by ;
 // e.g. 127.0.0.1:9090
-func (rp *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
-	rp.maxlifetime = maxlifetime
-	rp.conninfo = strings.Split(savePath, ";")
-	client = memcache.New(rp.conninfo...)
+func (p *MemProvider) Init(maxlifetime int64, connStrs string) error {
+	p.maxlifetime = maxlifetime
+	p.conninfo = strings.Split(connStrs, ";")
+	client = memcache.New(p.conninfo...)
 	return nil
 }
 
-func (rp *MemProvider) connectInit() error {
-	client = memcache.New(rp.conninfo...)
+func (p *MemProvider) connectInit() error {
+	client = memcache.New(p.conninfo...)
 	return nil
 }
 
-// read redis session by sid
-func (rp *MemProvider) SessionRead(sid string) (session.RawStore, error) {
+// Read returns raw session store by session ID.
+func (p *MemProvider) Read(sid string) (session.RawStore, error) {
 	if client == nil {
-		if err := rp.connectInit(); err != nil {
+		if err := p.connectInit(); err != nil {
 			return nil, err
 		}
 	}
@@ -138,14 +136,14 @@ func (rp *MemProvider) SessionRead(sid string) (session.RawStore, error) {
 		}
 	}
 
-	rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return rs, nil
 }
 
-// check redis session exist by sid
-func (rp *MemProvider) SessionExist(sid string) bool {
+// Exist returns true if session with given ID exists.
+func (p *MemProvider) Exist(sid string) bool {
 	if client == nil {
-		if err := rp.connectInit(); err != nil {
+		if err := p.connectInit(); err != nil {
 			return false
 		}
 	}
@@ -157,10 +155,21 @@ func (rp *MemProvider) SessionExist(sid string) bool {
 	}
 }
 
-// generate new sid for redis session
-func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
+// Destory deletes a session by session ID.
+func (p *MemProvider) Destory(sid string) error {
 	if client == nil {
-		if err := rp.connectInit(); err != nil {
+		if err := p.connectInit(); err != nil {
+			return err
+		}
+	}
+
+	return client.Delete(sid)
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *MemProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
+	if client == nil {
+		if err := p.connectInit(); err != nil {
 			return nil, err
 		}
 	}
@@ -172,13 +181,13 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore,
 		// the existed value will be 0
 		item.Key = sid
 		item.Value = []byte("")
-		item.Expiration = int32(rp.maxlifetime)
+		item.Expiration = int32(p.maxlifetime)
 		client.Set(item)
 	} else {
 		client.Delete(oldsid)
 		item.Key = sid
 		item.Value = item.Value
-		item.Expiration = int32(rp.maxlifetime)
+		item.Expiration = int32(p.maxlifetime)
 		client.Set(item)
 		contain = item.Value
 	}
@@ -194,31 +203,19 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.RawStore,
 		}
 	}
 
-	rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	rs := &MemcacheSessionStore{sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return rs, nil
 }
 
-// delete redis session by id
-func (rp *MemProvider) SessionDestroy(sid string) error {
-	if client == nil {
-		if err := rp.connectInit(); err != nil {
-			return err
-		}
-	}
-
-	return client.Delete(sid)
-}
-
-// Impelment method, no used.
-func (rp *MemProvider) SessionGC() {
-	return
-}
-
-// @todo
-func (rp *MemProvider) SessionAll() int {
+// Count counts and returns number of sessions.
+func (p *MemProvider) Count() int {
+	// FIXME
 	return 0
 }
 
+// GC calls GC to clean expired sessions.
+func (p *MemProvider) GC() {}
+
 func init() {
-	session.Register("memcache", mempder)
+	session.Register("memcache", &MemProvider{})
 }

+ 84 - 97
Godeps/_workspace/src/github.com/macaron-contrib/session/mysql/mysql.go

@@ -15,17 +15,8 @@
 
 package session
 
-// mysql session support need create table as sql:
-//	CREATE TABLE `session` (
-//	`session_key` char(64) NOT NULL,
-//	session_data` blob,
-//	`session_expiry` int(11) unsigned NOT NULL,
-//	PRIMARY KEY (`session_key`)
-//	) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-
 import (
 	"database/sql"
-	"net/http"
 	"sync"
 	"time"
 
@@ -34,96 +25,90 @@ import (
 	"github.com/macaron-contrib/session"
 )
 
-var mysqlpder = &MysqlProvider{}
-
-// mysql session store
+// MysqlSessionStore represents a mysql session store implementation.
 type MysqlSessionStore struct {
-	c      *sql.DB
-	sid    string
-	lock   sync.RWMutex
-	values map[interface{}]interface{}
-}
-
-// set value in mysql session.
-// it is temp value in map.
-func (st *MysqlSessionStore) Set(key, value interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values[key] = value
-	return nil
+	c    *sql.DB
+	sid  string
+	lock sync.RWMutex
+	data map[interface{}]interface{}
 }
 
-// get value from mysql session
-func (st *MysqlSessionStore) Get(key interface{}) interface{} {
-	st.lock.RLock()
-	defer st.lock.RUnlock()
-	if v, ok := st.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Set sets value to given key in session.
+func (s *MysqlSessionStore) Set(key, val interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
 
-// delete value in mysql session
-func (st *MysqlSessionStore) Delete(key interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	delete(st.values, key)
+	s.data[key] = val
 	return nil
 }
 
-// clear all values in mysql session
-func (st *MysqlSessionStore) Flush() error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values = make(map[interface{}]interface{})
+// Get gets value by given key in session.
+func (s *MysqlSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
+
+	return s.data[key]
+}
+
+// Delete delete a key from session.
+func (s *MysqlSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-// get session id of this mysql session store
-func (st *MysqlSessionStore) SessionID() string {
-	return st.sid
+// ID returns current session ID.
+func (s *MysqlSessionStore) ID() string {
+	return s.sid
 }
 
-// save mysql session values to database.
-// must call this method to save values to database.
-func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
-	defer st.c.Close()
-	b, err := session.EncodeGob(st.values)
+// Release releases resource and save data to provider.
+func (s *MysqlSessionStore) Release() error {
+	defer s.c.Close()
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
 	}
-	st.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?",
-		b, time.Now().Unix(), st.sid)
+	_, err = s.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?",
+		data, time.Now().Unix(), s.sid)
+	return err
+}
+
+// Flush deletes all session data.
+func (s *MysqlSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
 
+	s.data = make(map[interface{}]interface{})
+	return nil
 }
 
-// mysql session provider
+// MysqlProvider represents a mysql session provider implementation.
 type MysqlProvider struct {
 	maxlifetime int64
-	savePath    string
+	connStr     string
 }
 
-// connect to mysql
-func (mp *MysqlProvider) connectInit() *sql.DB {
-	db, e := sql.Open("mysql", mp.savePath)
+func (p *MysqlProvider) connectInit() *sql.DB {
+	db, e := sql.Open("mysql", p.connStr)
 	if e != nil {
 		return nil
 	}
 	return db
 }
 
-// init mysql session.
-// savepath is the connection string of mysql.
-func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error {
-	mp.maxlifetime = maxlifetime
-	mp.savePath = savePath
+// Init initializes memory session provider.
+func (p *MysqlProvider) Init(maxlifetime int64, connStr string) error {
+	p.maxlifetime = maxlifetime
+	p.connStr = connStr
 	return nil
 }
 
-// get mysql session by sid
-func (mp *MysqlProvider) SessionRead(sid string) (session.RawStore, error) {
-	c := mp.connectInit()
+// Read returns raw session store by session ID.
+func (p *MysqlProvider) Read(sid string) (session.RawStore, error) {
+	c := p.connectInit()
 	row := c.QueryRow("select session_data from session where session_key=?", sid)
 	var sessiondata []byte
 	err := row.Scan(&sessiondata)
@@ -140,14 +125,15 @@ func (mp *MysqlProvider) SessionRead(sid string) (session.RawStore, error) {
 			return nil, err
 		}
 	}
-	rs := &MysqlSessionStore{c: c, sid: sid, values: kv}
+	rs := &MysqlSessionStore{c: c, sid: sid, data: kv}
 	return rs, nil
 }
 
-// check mysql session exist
-func (mp *MysqlProvider) SessionExist(sid string) bool {
-	c := mp.connectInit()
+// Exist returns true if session with given ID exists.
+func (p *MysqlProvider) Exist(sid string) bool {
+	c := p.connectInit()
 	defer c.Close()
+
 	row := c.QueryRow("select session_data from session where session_key=?", sid)
 	var sessiondata []byte
 	err := row.Scan(&sessiondata)
@@ -158,9 +144,18 @@ func (mp *MysqlProvider) SessionExist(sid string) bool {
 	}
 }
 
-// generate new sid for mysql session
-func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
-	c := mp.connectInit()
+// Destory deletes a session by session ID.
+func (p *MysqlProvider) Destory(sid string) (err error) {
+	c := p.connectInit()
+	if _, err = c.Exec("DELETE FROM session where session_key=?", sid); err != nil {
+		return err
+	}
+	return c.Close()
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *MysqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
+	c := p.connectInit()
 	row := c.QueryRow("select session_data from session where session_key=?", oldsid)
 	var sessiondata []byte
 	err := row.Scan(&sessiondata)
@@ -177,30 +172,15 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore
 			return nil, err
 		}
 	}
-	rs := &MysqlSessionStore{c: c, sid: sid, values: kv}
+	rs := &MysqlSessionStore{c: c, sid: sid, data: kv}
 	return rs, nil
 }
 
-// delete mysql session by sid
-func (mp *MysqlProvider) SessionDestroy(sid string) error {
-	c := mp.connectInit()
-	c.Exec("DELETE FROM session where session_key=?", sid)
-	c.Close()
-	return nil
-}
-
-// delete expired values in mysql session
-func (mp *MysqlProvider) SessionGC() {
-	c := mp.connectInit()
-	c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
-	c.Close()
-	return
-}
-
-// count values in mysql session
-func (mp *MysqlProvider) SessionAll() int {
-	c := mp.connectInit()
+// Count counts and returns number of sessions.
+func (p *MysqlProvider) Count() int {
+	c := p.connectInit()
 	defer c.Close()
+
 	var total int
 	err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
 	if err != nil {
@@ -209,6 +189,13 @@ func (mp *MysqlProvider) SessionAll() int {
 	return total
 }
 
+// GC calls GC to clean expired sessions.
+func (mp *MysqlProvider) GC() {
+	c := mp.connectInit()
+	c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
+	c.Close()
+}
+
 func init() {
-	session.Register("mysql", mysqlpder)
+	session.Register("mysql", &MysqlProvider{})
 }

+ 83 - 116
Godeps/_workspace/src/github.com/macaron-contrib/session/postgres/postgresql.go

@@ -15,38 +15,8 @@
 
 package session
 
-/*
-
-beego session provider for postgresql
--------------------------------------
-
-depends on github.com/lib/pq:
-
-go install github.com/lib/pq
-
-
-needs this table in your database:
-
-CREATE TABLE session (
-session_key	char(64) NOT NULL,
-session_data	bytea,
-session_expiry	timestamp NOT NULL,
-CONSTRAINT session_key PRIMARY KEY(session_key)
-);
-
-
-will be activated with these settings in app.conf:
-
-SessionOn = true
-SessionProvider = postgresql
-SessionSavePath = "user=a password=b dbname=c sslmode=disable"
-SessionName = session
-
-*/
-
 import (
 	"database/sql"
-	"net/http"
 	"sync"
 	"time"
 
@@ -55,96 +25,93 @@ import (
 	"github.com/macaron-contrib/session"
 )
 
-var postgresqlpder = &PostgresqlProvider{}
-
-// postgresql session store
+// PostgresqlSessionStore represents a postgresql session store implementation.
 type PostgresqlSessionStore struct {
-	c      *sql.DB
-	sid    string
-	lock   sync.RWMutex
-	values map[interface{}]interface{}
-}
-
-// set value in postgresql session.
-// it is temp value in map.
-func (st *PostgresqlSessionStore) Set(key, value interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values[key] = value
-	return nil
+	c    *sql.DB
+	sid  string
+	lock sync.RWMutex
+	data map[interface{}]interface{}
 }
 
-// get value from postgresql session
-func (st *PostgresqlSessionStore) Get(key interface{}) interface{} {
-	st.lock.RLock()
-	defer st.lock.RUnlock()
-	if v, ok := st.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Set sets value to given key in session.
+func (s *PostgresqlSessionStore) Set(key, value interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
 
-// delete value in postgresql session
-func (st *PostgresqlSessionStore) Delete(key interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	delete(st.values, key)
+	s.data[key] = value
 	return nil
 }
 
-// clear all values in postgresql session
-func (st *PostgresqlSessionStore) Flush() error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values = make(map[interface{}]interface{})
+// Get gets value by given key in session.
+func (s *PostgresqlSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
+
+	return s.data[key]
+}
+
+// Delete delete a key from session.
+func (s *PostgresqlSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-// get session id of this postgresql session store
-func (st *PostgresqlSessionStore) SessionID() string {
-	return st.sid
+// ID returns current session ID.
+func (s *PostgresqlSessionStore) ID() string {
+	return s.sid
 }
 
 // save postgresql session values to database.
 // must call this method to save values to database.
-func (st *PostgresqlSessionStore) SessionRelease(w http.ResponseWriter) {
-	defer st.c.Close()
-	b, err := session.EncodeGob(st.values)
+func (s *PostgresqlSessionStore) Release() error {
+	defer s.c.Close()
+
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
 	}
-	st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
-		b, time.Now().Format(time.RFC3339), st.sid)
 
+	_, err = s.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3",
+		data, time.Now().Format(time.RFC3339), s.sid)
+	return err
+}
+
+// Flush deletes all session data.
+func (s *PostgresqlSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data = make(map[interface{}]interface{})
+	return nil
 }
 
-// postgresql session provider
+// PostgresqlProvider represents a postgresql session provider implementation.
 type PostgresqlProvider struct {
 	maxlifetime int64
-	savePath    string
+	connStr     string
 }
 
-// connect to postgresql
-func (mp *PostgresqlProvider) connectInit() *sql.DB {
-	db, e := sql.Open("postgres", mp.savePath)
+func (p *PostgresqlProvider) connectInit() *sql.DB {
+	db, e := sql.Open("postgres", p.connStr)
 	if e != nil {
 		return nil
 	}
 	return db
 }
 
-// init postgresql session.
-// savepath is the connection string of postgresql.
-func (mp *PostgresqlProvider) SessionInit(maxlifetime int64, savePath string) error {
-	mp.maxlifetime = maxlifetime
-	mp.savePath = savePath
+// Init initializes memory session provider.
+func (p *PostgresqlProvider) Init(maxlifetime int64, connStr string) error {
+	p.maxlifetime = maxlifetime
+	p.connStr = connStr
 	return nil
 }
 
-// get postgresql session by sid
-func (mp *PostgresqlProvider) SessionRead(sid string) (session.RawStore, error) {
-	c := mp.connectInit()
+// Read returns raw session store by session ID.
+func (p *PostgresqlProvider) Read(sid string) (session.RawStore, error) {
+	c := p.connectInit()
 	row := c.QueryRow("select session_data from session where session_key=$1", sid)
 	var sessiondata []byte
 	err := row.Scan(&sessiondata)
@@ -168,13 +135,13 @@ func (mp *PostgresqlProvider) SessionRead(sid string) (session.RawStore, error)
 			return nil, err
 		}
 	}
-	rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
+	rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv}
 	return rs, nil
 }
 
-// check postgresql session exist
-func (mp *PostgresqlProvider) SessionExist(sid string) bool {
-	c := mp.connectInit()
+// Exist returns true if session with given ID exists.
+func (p *PostgresqlProvider) Exist(sid string) bool {
+	c := p.connectInit()
 	defer c.Close()
 	row := c.QueryRow("select session_data from session where session_key=$1", sid)
 	var sessiondata []byte
@@ -187,9 +154,18 @@ func (mp *PostgresqlProvider) SessionExist(sid string) bool {
 	}
 }
 
-// generate new sid for postgresql session
-func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
-	c := mp.connectInit()
+// Destory deletes a session by session ID.
+func (p *PostgresqlProvider) Destory(sid string) (err error) {
+	c := p.connectInit()
+	if _, err = c.Exec("DELETE FROM session where session_key=$1", sid); err != nil {
+		return err
+	}
+	return c.Close()
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *PostgresqlProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
+	c := p.connectInit()
 	row := c.QueryRow("select session_data from session where session_key=$1", oldsid)
 	var sessiondata []byte
 	err := row.Scan(&sessiondata)
@@ -207,29 +183,13 @@ func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (session.Raw
 			return nil, err
 		}
 	}
-	rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv}
+	rs := &PostgresqlSessionStore{c: c, sid: sid, data: kv}
 	return rs, nil
 }
 
-// delete postgresql session by sid
-func (mp *PostgresqlProvider) SessionDestroy(sid string) error {
-	c := mp.connectInit()
-	c.Exec("DELETE FROM session where session_key=$1", sid)
-	c.Close()
-	return nil
-}
-
-// delete expired values in postgresql session
-func (mp *PostgresqlProvider) SessionGC() {
-	c := mp.connectInit()
-	c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
-	c.Close()
-	return
-}
-
-// count values in postgresql session
-func (mp *PostgresqlProvider) SessionAll() int {
-	c := mp.connectInit()
+// Count counts and returns number of sessions.
+func (p *PostgresqlProvider) Count() int {
+	c := p.connectInit()
 	defer c.Close()
 	var total int
 	err := c.QueryRow("SELECT count(*) as num from session").Scan(&total)
@@ -239,6 +199,13 @@ func (mp *PostgresqlProvider) SessionAll() int {
 	return total
 }
 
+// GC calls GC to clean expired sessions.
+func (mp *PostgresqlProvider) GC() {
+	c := mp.connectInit()
+	c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime)
+	c.Close()
+}
+
 func init() {
-	session.Register("postgresql", postgresqlpder)
+	session.Register("postgresql", &PostgresqlProvider{})
 }

+ 86 - 89
Godeps/_workspace/src/github.com/macaron-contrib/session/redis/redis.go

@@ -16,7 +16,6 @@
 package session
 
 import (
-	"net/http"
 	"strconv"
 	"strings"
 	"sync"
@@ -26,126 +25,125 @@ import (
 	"github.com/macaron-contrib/session"
 )
 
-var redispder = &RedisProvider{}
-
 // redis max pool size
 var MAX_POOL_SIZE = 100
 
 var redisPool chan redis.Conn
 
-// redis session store
+// RedisSessionStore represents a redis session store implementation.
 type RedisSessionStore struct {
 	p           *redis.Pool
 	sid         string
 	lock        sync.RWMutex
-	values      map[interface{}]interface{}
+	data        map[interface{}]interface{}
 	maxlifetime int64
 }
 
-// set value in redis session
-func (rs *RedisSessionStore) Set(key, value interface{}) error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	rs.values[key] = value
+// Set sets value to given key in session.
+func (s *RedisSessionStore) Set(key, val interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data[key] = val
 	return nil
 }
 
-// get value in redis session
-func (rs *RedisSessionStore) Get(key interface{}) interface{} {
-	rs.lock.RLock()
-	defer rs.lock.RUnlock()
-	if v, ok := rs.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
+// Get gets value by given key in session.
+func (s *RedisSessionStore) Get(key interface{}) interface{} {
+	s.lock.RLock()
+	defer s.lock.RUnlock()
 
-// delete value in redis session
-func (rs *RedisSessionStore) Delete(key interface{}) error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	delete(rs.values, key)
-	return nil
+	return s.data[key]
 }
 
-// clear all values in redis session
-func (rs *RedisSessionStore) Flush() error {
-	rs.lock.Lock()
-	defer rs.lock.Unlock()
-	rs.values = make(map[interface{}]interface{})
+// Delete delete a key from session.
+func (s *RedisSessionStore) Delete(key interface{}) error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	delete(s.data, key)
 	return nil
 }
 
-// get redis session id
-func (rs *RedisSessionStore) SessionID() string {
-	return rs.sid
+// ID returns current session ID.
+func (s *RedisSessionStore) ID() string {
+	return s.sid
 }
 
-// save session values to redis
-func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
-	c := rs.p.Get()
+// Release releases resource and save data to provider.
+func (s *RedisSessionStore) Release() error {
+	c := s.p.Get()
 	defer c.Close()
 
-	b, err := session.EncodeGob(rs.values)
+	data, err := session.EncodeGob(s.data)
 	if err != nil {
-		return
+		return err
 	}
 
-	c.Do("SETEX", rs.sid, rs.maxlifetime, string(b))
+	_, err = c.Do("SETEX", s.sid, s.maxlifetime, string(data))
+	return err
+}
+
+// Flush deletes all session data.
+func (s *RedisSessionStore) Flush() error {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+
+	s.data = make(map[interface{}]interface{})
+	return nil
 }
 
-// redis session provider
+// RedisProvider represents a redis session provider implementation.
 type RedisProvider struct {
 	maxlifetime int64
-	savePath    string
+	connAddr    string
 	poolsize    int
 	password    string
 	poollist    *redis.Pool
 }
 
-// init redis session
-// savepath like redis server addr,pool size,password
-// e.g. 127.0.0.1:6379,100,astaxie
-func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
-	rp.maxlifetime = maxlifetime
-	configs := strings.Split(savePath, ",")
+// Init initializes memory session provider.
+// connStr: <redis server addr>,<pool size>,<password>
+// e.g. 127.0.0.1:6379,100,macaron
+func (p *RedisProvider) Init(maxlifetime int64, connStr string) error {
+	p.maxlifetime = maxlifetime
+	configs := strings.Split(connStr, ",")
 	if len(configs) > 0 {
-		rp.savePath = configs[0]
+		p.connAddr = configs[0]
 	}
 	if len(configs) > 1 {
 		poolsize, err := strconv.Atoi(configs[1])
 		if err != nil || poolsize <= 0 {
-			rp.poolsize = MAX_POOL_SIZE
+			p.poolsize = MAX_POOL_SIZE
 		} else {
-			rp.poolsize = poolsize
+			p.poolsize = poolsize
 		}
 	} else {
-		rp.poolsize = MAX_POOL_SIZE
+		p.poolsize = MAX_POOL_SIZE
 	}
 	if len(configs) > 2 {
-		rp.password = configs[2]
+		p.password = configs[2]
 	}
-	rp.poollist = redis.NewPool(func() (redis.Conn, error) {
-		c, err := redis.Dial("tcp", rp.savePath)
+	p.poollist = redis.NewPool(func() (redis.Conn, error) {
+		c, err := redis.Dial("tcp", p.connAddr)
 		if err != nil {
 			return nil, err
 		}
-		if rp.password != "" {
-			if _, err := c.Do("AUTH", rp.password); err != nil {
+		if p.password != "" {
+			if _, err := c.Do("AUTH", p.password); err != nil {
 				c.Close()
 				return nil, err
 			}
 		}
 		return c, err
-	}, rp.poolsize)
+	}, p.poolsize)
 
-	return rp.poollist.Get().Err()
+	return p.poollist.Get().Err()
 }
 
-// read redis session by sid
-func (rp *RedisProvider) SessionRead(sid string) (session.RawStore, error) {
-	c := rp.poollist.Get()
+// Read returns raw session store by session ID.
+func (p *RedisProvider) Read(sid string) (session.RawStore, error) {
+	c := p.poollist.Get()
 	defer c.Close()
 
 	kvs, err := redis.String(c.Do("GET", sid))
@@ -159,13 +157,13 @@ func (rp *RedisProvider) SessionRead(sid string) (session.RawStore, error) {
 		}
 	}
 
-	rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return rs, nil
 }
 
-// check redis session exist by sid
-func (rp *RedisProvider) SessionExist(sid string) bool {
-	c := rp.poollist.Get()
+// Exist returns true if session with given ID exists.
+func (p *RedisProvider) Exist(sid string) bool {
+	c := p.poollist.Get()
 	defer c.Close()
 
 	if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
@@ -175,19 +173,28 @@ func (rp *RedisProvider) SessionExist(sid string) bool {
 	}
 }
 
-// generate new sid for redis session
-func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (session.RawStore, error) {
-	c := rp.poollist.Get()
+// Destory deletes a session by session ID.
+func (p *RedisProvider) Destory(sid string) error {
+	c := p.poollist.Get()
+	defer c.Close()
+
+	_, err := c.Do("DEL", sid)
+	return err
+}
+
+// Regenerate regenerates a session store from old session ID to new one.
+func (p *RedisProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
+	c := p.poollist.Get()
 	defer c.Close()
 
 	if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 {
 		// oldsid doesn't exists, set the new sid directly
 		// ignore error here, since if it return error
 		// the existed value will be 0
-		c.Do("SET", sid, "", "EX", rp.maxlifetime)
+		c.Do("SET", sid, "", "EX", p.maxlifetime)
 	} else {
 		c.Do("RENAME", oldsid, sid)
-		c.Do("EXPIRE", sid, rp.maxlifetime)
+		c.Do("EXPIRE", sid, p.maxlifetime)
 	}
 
 	kvs, err := redis.String(c.Do("GET", sid))
@@ -201,29 +208,19 @@ func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (session.RawStore
 		}
 	}
 
-	rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
+	rs := &RedisSessionStore{p: p.poollist, sid: sid, data: kv, maxlifetime: p.maxlifetime}
 	return rs, nil
 }
 
-// delete redis session by id
-func (rp *RedisProvider) SessionDestroy(sid string) error {
-	c := rp.poollist.Get()
-	defer c.Close()
-
-	c.Do("DEL", sid)
-	return nil
-}
-
-// Impelment method, no used.
-func (rp *RedisProvider) SessionGC() {
-	return
-}
-
-// @todo
-func (rp *RedisProvider) SessionAll() int {
+// Count counts and returns number of sessions.
+func (p *RedisProvider) Count() int {
+	// FIXME
 	return 0
 }
 
+// GC calls GC to clean expired sessions.
+func (_ *RedisProvider) GC() {}
+
 func init() {
-	session.Register("redis", redispder)
+	session.Register("redis", &RedisProvider{})
 }

+ 0 - 186
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_cookie.go

@@ -1,186 +0,0 @@
-// Copyright 2013 Beego Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"crypto/aes"
-	"crypto/cipher"
-	"encoding/json"
-	"net/http"
-	"net/url"
-	"sync"
-)
-
-var cookiepder = &CookieProvider{}
-
-// Cookie SessionStore
-type CookieSessionStore struct {
-	sid    string
-	values map[interface{}]interface{} // session data
-	lock   sync.RWMutex
-}
-
-// Set value to cookie session.
-// the value are encoded as gob with hash block string.
-func (st *CookieSessionStore) Set(key, value interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values[key] = value
-	return nil
-}
-
-// Get value from cookie session
-func (st *CookieSessionStore) Get(key interface{}) interface{} {
-	st.lock.RLock()
-	defer st.lock.RUnlock()
-	if v, ok := st.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
-
-// Delete value in cookie session
-func (st *CookieSessionStore) Delete(key interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	delete(st.values, key)
-	return nil
-}
-
-// Clean all values in cookie session
-func (st *CookieSessionStore) Flush() error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.values = make(map[interface{}]interface{})
-	return nil
-}
-
-// Return id of this cookie session
-func (st *CookieSessionStore) SessionID() string {
-	return st.sid
-}
-
-// Write cookie session to http response cookie
-func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
-	str, err := encodeCookie(cookiepder.block,
-		cookiepder.config.SecurityKey,
-		cookiepder.config.SecurityName,
-		st.values)
-	if err != nil {
-		return
-	}
-	cookie := &http.Cookie{Name: cookiepder.config.CookieName,
-		Value:    url.QueryEscape(str),
-		Path:     cookiepder.config.CookiePath,
-		HttpOnly: true,
-		Secure:   cookiepder.config.Secure,
-		MaxAge:   cookiepder.config.Maxage}
-	http.SetCookie(w, cookie)
-	return
-}
-
-type cookieConfig struct {
-	SecurityKey  string `json:"securityKey"`
-	BlockKey     string `json:"blockKey"`
-	SecurityName string `json:"securityName"`
-	CookieName   string `json:"cookieName"`
-	CookiePath   string `json:"cookiePath"`
-	Secure       bool   `json:"secure"`
-	Maxage       int    `json:"maxage"`
-}
-
-// Cookie session provider
-type CookieProvider struct {
-	maxlifetime int64
-	config      *cookieConfig
-	block       cipher.Block
-}
-
-// Init cookie session provider with max lifetime and config json.
-// maxlifetime is ignored.
-// json config:
-// 	securityKey - hash string
-// 	blockKey - gob encode hash string. it's saved as aes crypto.
-// 	securityName - recognized name in encoded cookie string
-// 	cookieName - cookie name
-// 	maxage - cookie max life time.
-func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error {
-	pder.config = &cookieConfig{}
-	err := json.Unmarshal([]byte(config), pder.config)
-	if err != nil {
-		return err
-	}
-	if pder.config.BlockKey == "" {
-		pder.config.BlockKey = string(generateRandomKey(16))
-	}
-	if pder.config.SecurityName == "" {
-		pder.config.SecurityName = string(generateRandomKey(20))
-	}
-	pder.block, err = aes.NewCipher([]byte(pder.config.BlockKey))
-	if err != nil {
-		return err
-	}
-	pder.maxlifetime = maxlifetime
-	return nil
-}
-
-// Get SessionStore in cooke.
-// decode cooke string to map and put into SessionStore with sid.
-func (pder *CookieProvider) SessionRead(sid string) (RawStore, error) {
-	maps, _ := decodeCookie(pder.block,
-		pder.config.SecurityKey,
-		pder.config.SecurityName,
-		sid, pder.maxlifetime)
-	if maps == nil {
-		maps = make(map[interface{}]interface{})
-	}
-	rs := &CookieSessionStore{sid: sid, values: maps}
-	return rs, nil
-}
-
-// Cookie session is always existed
-func (pder *CookieProvider) SessionExist(sid string) bool {
-	return true
-}
-
-// Implement method, no used.
-func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
-	return nil, nil
-}
-
-// Implement method, no used.
-func (pder *CookieProvider) SessionDestroy(sid string) error {
-	return nil
-}
-
-// Implement method, no used.
-func (pder *CookieProvider) SessionGC() {
-	return
-}
-
-// Implement method, return 0.
-func (pder *CookieProvider) SessionAll() int {
-	return 0
-}
-
-// Implement method, no used.
-func (pder *CookieProvider) SessionUpdate(sid string) error {
-	return nil
-}
-
-func init() {
-	Register("cookie", cookiepder)
-}

+ 0 - 60
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_cookie_test.go

@@ -1,60 +0,0 @@
-// Copyright 2013 Beego Authors
-// Copyright 2014 Unknwon
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"net/http"
-	"net/http/httptest"
-	"strings"
-	"testing"
-)
-
-func TestCookie(t *testing.T) {
-	config := &Config{
-		CookieName:     "gosessionid",
-		Gclifetime:     3600,
-		ProviderConfig: "{\"cookieName\":\"gosessionid\",\"securityKey\":\"macaroncookiehashkey\"}",
-	}
-	globalSessions, err := NewManager("cookie", config)
-	if err != nil {
-		t.Fatal("init cookie session err", err)
-	}
-	r, _ := http.NewRequest("GET", "/", nil)
-	w := httptest.NewRecorder()
-	sess, err := globalSessions.SessionStart(w, r)
-	if err != nil {
-		t.Fatal("start session,", err)
-	}
-	err = sess.Set("username", "astaxie")
-	if err != nil {
-		t.Fatal("set error,", err)
-	}
-	if username := sess.Get("username"); username != "astaxie" {
-		t.Fatal("get username error")
-	}
-	sess.SessionRelease(w)
-	if cookiestr := w.Header().Get("Set-Cookie"); cookiestr == "" {
-		t.Fatal("setcookie error")
-	} else {
-		parts := strings.Split(strings.TrimSpace(cookiestr), ";")
-		for k, v := range parts {
-			nameval := strings.Split(v, "=")
-			if k == 0 && nameval[0] != "gosessionid" {
-				t.Fatal("error")
-			}
-		}
-	}
-}

+ 0 - 288
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_file.go

@@ -1,288 +0,0 @@
-// Copyright 2013 Beego Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"errors"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net/http"
-	"os"
-	"path"
-	"path/filepath"
-	"sync"
-	"time"
-)
-
-var (
-	filepder      = &FileProvider{}
-	gcmaxlifetime int64
-)
-
-// File session store
-type FileSessionStore struct {
-	f      *os.File
-	sid    string
-	lock   sync.RWMutex
-	values map[interface{}]interface{}
-}
-
-// Set value to file session
-func (fs *FileSessionStore) Set(key, value interface{}) error {
-	fs.lock.Lock()
-	defer fs.lock.Unlock()
-	fs.values[key] = value
-	return nil
-}
-
-// Get value from file session
-func (fs *FileSessionStore) Get(key interface{}) interface{} {
-	fs.lock.RLock()
-	defer fs.lock.RUnlock()
-	if v, ok := fs.values[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
-
-// Delete value in file session by given key
-func (fs *FileSessionStore) Delete(key interface{}) error {
-	fs.lock.Lock()
-	defer fs.lock.Unlock()
-	delete(fs.values, key)
-	return nil
-}
-
-// Clean all values in file session
-func (fs *FileSessionStore) Flush() error {
-	fs.lock.Lock()
-	defer fs.lock.Unlock()
-	fs.values = make(map[interface{}]interface{})
-	return nil
-}
-
-// Get file session store id
-func (fs *FileSessionStore) SessionID() string {
-	return fs.sid
-}
-
-// Write file session to local file with Gob string
-func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
-	b, err := EncodeGob(fs.values)
-	if err != nil {
-		return
-	}
-
-	_, err = os.Stat(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid))
-	var f *os.File
-	if err == nil {
-		f, err = os.OpenFile(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid), os.O_RDWR, 0777)
-	} else if os.IsNotExist(err) {
-		f, err = os.Create(path.Join(filepder.savePath, string(fs.sid[0]), string(fs.sid[1]), fs.sid))
-	} else {
-		return
-	}
-	f.Truncate(0)
-	f.Seek(0, 0)
-	f.Write(b)
-	f.Close()
-}
-
-// File session provider
-type FileProvider struct {
-	lock        sync.RWMutex
-	maxlifetime int64
-	savePath    string
-}
-
-// Init file session provider.
-// savePath sets the session files path.
-func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
-	fp.maxlifetime = maxlifetime
-	fp.savePath = savePath
-	return nil
-}
-
-// Read file session by sid.
-// if file is not exist, create it.
-// the file path is generated from sid string.
-func (fp *FileProvider) SessionRead(sid string) (RawStore, error) {
-	filepder.lock.Lock()
-	defer filepder.lock.Unlock()
-
-	err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
-	if err != nil {
-		println(err.Error())
-	}
-	_, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	var f *os.File
-	if err == nil {
-		f, err = os.OpenFile(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), os.O_RDWR, 0777)
-	} else if os.IsNotExist(err) {
-		f, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	} else {
-		return nil, err
-	}
-	os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
-	var kv map[interface{}]interface{}
-	b, err := ioutil.ReadAll(f)
-	if err != nil {
-		return nil, err
-	}
-	if len(b) == 0 {
-		kv = make(map[interface{}]interface{})
-	} else {
-		kv, err = DecodeGob(b)
-		if err != nil {
-			return nil, err
-		}
-	}
-	f.Close()
-	ss := &FileSessionStore{sid: sid, values: kv}
-	return ss, nil
-}
-
-// Check file session exist.
-// it checkes the file named from sid exist or not.
-func (fp *FileProvider) SessionExist(sid string) bool {
-	filepder.lock.Lock()
-	defer filepder.lock.Unlock()
-
-	_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	if err == nil {
-		return true
-	} else {
-		return false
-	}
-}
-
-// Remove all files in this save path
-func (fp *FileProvider) SessionDestroy(sid string) error {
-	filepder.lock.Lock()
-	defer filepder.lock.Unlock()
-	os.Remove(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	return nil
-}
-
-// Recycle files in save path
-func (fp *FileProvider) SessionGC() {
-	filepder.lock.Lock()
-	defer filepder.lock.Unlock()
-
-	gcmaxlifetime = fp.maxlifetime
-	filepath.Walk(fp.savePath, gcpath)
-}
-
-// Get active file session number.
-// it walks save path to count files.
-func (fp *FileProvider) SessionAll() int {
-	a := &activeSession{}
-	err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error {
-		return a.visit(path, f, err)
-	})
-	if err != nil {
-		fmt.Printf("filepath.Walk() returned %v\n", err)
-		return 0
-	}
-	return a.total
-}
-
-// Generate new sid for file session.
-// it delete old file and create new file named from new sid.
-func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
-	filepder.lock.Lock()
-	defer filepder.lock.Unlock()
-
-	err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777)
-	if err != nil {
-		println(err.Error())
-	}
-	err = os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
-	if err != nil {
-		println(err.Error())
-	}
-	_, err = os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	var newf *os.File
-	if err == nil {
-		return nil, errors.New("newsid exist")
-	} else if os.IsNotExist(err) {
-		newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	}
-
-	_, err = os.Stat(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid))
-	var f *os.File
-	if err == nil {
-		f, err = os.OpenFile(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1]), oldsid), os.O_RDWR, 0777)
-		io.Copy(newf, f)
-	} else if os.IsNotExist(err) {
-		newf, err = os.Create(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
-	} else {
-		return nil, err
-	}
-	f.Close()
-	os.Remove(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])))
-	os.Chtimes(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid), time.Now(), time.Now())
-	var kv map[interface{}]interface{}
-	b, err := ioutil.ReadAll(newf)
-	if err != nil {
-		return nil, err
-	}
-	if len(b) == 0 {
-		kv = make(map[interface{}]interface{})
-	} else {
-		kv, err = DecodeGob(b)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	ss := &FileSessionStore{sid: sid, values: kv}
-	return ss, nil
-}
-
-// remove file in save path if expired
-func gcpath(path string, info os.FileInfo, err error) error {
-	if err != nil {
-		return err
-	}
-	if info.IsDir() {
-		return nil
-	}
-	if (info.ModTime().Unix() + gcmaxlifetime) < time.Now().Unix() {
-		os.Remove(path)
-	}
-	return nil
-}
-
-type activeSession struct {
-	total int
-}
-
-func (self *activeSession) visit(paths string, f os.FileInfo, err error) error {
-	if err != nil {
-		return err
-	}
-	if f.IsDir() {
-		return nil
-	}
-	self.total = self.total + 1
-	return nil
-}
-
-func init() {
-	Register("file", filepder)
-}

+ 0 - 199
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_mem.go

@@ -1,199 +0,0 @@
-// Copyright 2013 Beego Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"container/list"
-	"net/http"
-	"sync"
-	"time"
-)
-
-var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)}
-
-// memory session store.
-// it saved sessions in a map in memory.
-type MemSessionStore struct {
-	sid          string                      //session id
-	timeAccessed time.Time                   //last access time
-	value        map[interface{}]interface{} //session store
-	lock         sync.RWMutex
-}
-
-// set value to memory session
-func (st *MemSessionStore) Set(key, value interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.value[key] = value
-	return nil
-}
-
-// get value from memory session by key
-func (st *MemSessionStore) Get(key interface{}) interface{} {
-	st.lock.RLock()
-	defer st.lock.RUnlock()
-	if v, ok := st.value[key]; ok {
-		return v
-	} else {
-		return nil
-	}
-}
-
-// delete in memory session by key
-func (st *MemSessionStore) Delete(key interface{}) error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	delete(st.value, key)
-	return nil
-}
-
-// clear all values in memory session
-func (st *MemSessionStore) Flush() error {
-	st.lock.Lock()
-	defer st.lock.Unlock()
-	st.value = make(map[interface{}]interface{})
-	return nil
-}
-
-// get this id of memory session store
-func (st *MemSessionStore) SessionID() string {
-	return st.sid
-}
-
-// Implement method, no used.
-func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) {
-}
-
-type MemProvider struct {
-	lock        sync.RWMutex             // locker
-	sessions    map[string]*list.Element // map in memory
-	list        *list.List               // for gc
-	maxlifetime int64
-	savePath    string
-}
-
-// init memory session
-func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
-	pder.maxlifetime = maxlifetime
-	pder.savePath = savePath
-	return nil
-}
-
-// get memory session store by sid
-func (pder *MemProvider) SessionRead(sid string) (RawStore, error) {
-	pder.lock.RLock()
-	if element, ok := pder.sessions[sid]; ok {
-		go pder.SessionUpdate(sid)
-		pder.lock.RUnlock()
-		return element.Value.(*MemSessionStore), nil
-	} else {
-		pder.lock.RUnlock()
-		pder.lock.Lock()
-		newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
-		element := pder.list.PushBack(newsess)
-		pder.sessions[sid] = element
-		pder.lock.Unlock()
-		return newsess, nil
-	}
-}
-
-// check session store exist in memory session by sid
-func (pder *MemProvider) SessionExist(sid string) bool {
-	pder.lock.RLock()
-	defer pder.lock.RUnlock()
-	if _, ok := pder.sessions[sid]; ok {
-		return true
-	} else {
-		return false
-	}
-}
-
-// generate new sid for session store in memory session
-func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (RawStore, error) {
-	pder.lock.RLock()
-	if element, ok := pder.sessions[oldsid]; ok {
-		go pder.SessionUpdate(oldsid)
-		pder.lock.RUnlock()
-		pder.lock.Lock()
-		element.Value.(*MemSessionStore).sid = sid
-		pder.sessions[sid] = element
-		delete(pder.sessions, oldsid)
-		pder.lock.Unlock()
-		return element.Value.(*MemSessionStore), nil
-	} else {
-		pder.lock.RUnlock()
-		pder.lock.Lock()
-		newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})}
-		element := pder.list.PushBack(newsess)
-		pder.sessions[sid] = element
-		pder.lock.Unlock()
-		return newsess, nil
-	}
-}
-
-// delete session store in memory session by id
-func (pder *MemProvider) SessionDestroy(sid string) error {
-	pder.lock.Lock()
-	defer pder.lock.Unlock()
-	if element, ok := pder.sessions[sid]; ok {
-		delete(pder.sessions, sid)
-		pder.list.Remove(element)
-		return nil
-	}
-	return nil
-}
-
-// clean expired session stores in memory session
-func (pder *MemProvider) SessionGC() {
-	pder.lock.RLock()
-	for {
-		element := pder.list.Back()
-		if element == nil {
-			break
-		}
-		if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() {
-			pder.lock.RUnlock()
-			pder.lock.Lock()
-			pder.list.Remove(element)
-			delete(pder.sessions, element.Value.(*MemSessionStore).sid)
-			pder.lock.Unlock()
-			pder.lock.RLock()
-		} else {
-			break
-		}
-	}
-	pder.lock.RUnlock()
-}
-
-// get count number of memory session
-func (pder *MemProvider) SessionAll() int {
-	return pder.list.Len()
-}
-
-// expand time of session store by id in memory session
-func (pder *MemProvider) SessionUpdate(sid string) error {
-	pder.lock.Lock()
-	defer pder.lock.Unlock()
-	if element, ok := pder.sessions[sid]; ok {
-		element.Value.(*MemSessionStore).timeAccessed = time.Now()
-		pder.list.MoveToFront(element)
-		return nil
-	}
-	return nil
-}
-
-func init() {
-	Register("memory", mempder)
-}

+ 0 - 59
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_mem_test.go

@@ -1,59 +0,0 @@
-// Copyright 2013 Beego Authors
-// Copyright 2014 Unknwon
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"net/http"
-	"net/http/httptest"
-	"strings"
-	"testing"
-
-	. "github.com/smartystreets/goconvey/convey"
-)
-
-func TestMem(t *testing.T) {
-	Convey("Memory provider", t, func() {
-		config := &Config{
-			CookieName: "gosessionid",
-			Gclifetime: 10,
-		}
-		globalSessions, err := NewManager("memory", config)
-		So(err, ShouldBeNil)
-		go globalSessions.GC()
-
-		resp := httptest.NewRecorder()
-		req, err := http.NewRequest("GET", "/", nil)
-		So(err, ShouldBeNil)
-
-		sess, err := globalSessions.SessionStart(resp, req)
-		if err != nil {
-			t.Fatal("start session,", err)
-		}
-		defer sess.SessionRelease(resp)
-
-		So(sess.Set("username", "Unknwon"), ShouldBeNil)
-		So(sess.Get("username"), ShouldEqual, "Unknwon")
-
-		cookiestr := resp.Header().Get("Set-Cookie")
-		So(cookiestr, ShouldNotBeEmpty)
-		parts := strings.Split(strings.TrimSpace(cookiestr), ";")
-		for _, v := range parts {
-			nameval := strings.Split(v, "=")
-			So(nameval[0], ShouldEqual, "gosessionid")
-			break
-		}
-	})
-}

+ 0 - 132
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_test.go

@@ -1,132 +0,0 @@
-// Copyright 2013 Beego Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"crypto/aes"
-	"encoding/json"
-	"testing"
-)
-
-func Test_gob(t *testing.T) {
-	a := make(map[interface{}]interface{})
-	a["username"] = "astaxie"
-	a[12] = 234
-	a["user"] = User{"asta", "xie"}
-	b, err := EncodeGob(a)
-	if err != nil {
-		t.Error(err)
-	}
-	c, err := DecodeGob(b)
-	if err != nil {
-		t.Error(err)
-	}
-	if len(c) == 0 {
-		t.Error("decodeGob empty")
-	}
-	if c["username"] != "astaxie" {
-		t.Error("decode string error")
-	}
-	if c[12] != 234 {
-		t.Error("decode int error")
-	}
-	if c["user"].(User).Username != "asta" {
-		t.Error("decode struct error")
-	}
-}
-
-type User struct {
-	Username string
-	NickName string
-}
-
-func TestGenerate(t *testing.T) {
-	str := generateRandomKey(20)
-	if len(str) != 20 {
-		t.Fatal("generate length is not equal to 20")
-	}
-}
-
-func TestCookieEncodeDecode(t *testing.T) {
-	hashKey := "testhashKey"
-	blockkey := generateRandomKey(16)
-	block, err := aes.NewCipher(blockkey)
-	if err != nil {
-		t.Fatal("NewCipher:", err)
-	}
-	securityName := string(generateRandomKey(20))
-	val := make(map[interface{}]interface{})
-	val["name"] = "astaxie"
-	val["gender"] = "male"
-	str, err := encodeCookie(block, hashKey, securityName, val)
-	if err != nil {
-		t.Fatal("encodeCookie:", err)
-	}
-	dst := make(map[interface{}]interface{})
-	dst, err = decodeCookie(block, hashKey, securityName, str, 3600)
-	if err != nil {
-		t.Fatal("decodeCookie", err)
-	}
-	if dst["name"] != "astaxie" {
-		t.Fatal("dst get map error")
-	}
-	if dst["gender"] != "male" {
-		t.Fatal("dst get map error")
-	}
-}
-
-func TestParseConfig(t *testing.T) {
-	s := `{"cookieName":"gosessionid","gclifetime":3600}`
-	cf := new(Config)
-	cf.EnableSetCookie = true
-	err := json.Unmarshal([]byte(s), cf)
-	if err != nil {
-		t.Fatal("parse json error,", err)
-	}
-	if cf.CookieName != "gosessionid" {
-		t.Fatal("parseconfig get cookiename error")
-	}
-	if cf.Gclifetime != 3600 {
-		t.Fatal("parseconfig get gclifetime error")
-	}
-
-	cc := `{"cookieName":"gosessionid","enableSetCookie":false,"gclifetime":3600,"ProviderConfig":"{\"cookieName\":\"gosessionid\",\"securityKey\":\"beegocookiehashkey\"}"}`
-	cf2 := new(Config)
-	cf2.EnableSetCookie = true
-	err = json.Unmarshal([]byte(cc), cf2)
-	if err != nil {
-		t.Fatal("parse json error,", err)
-	}
-	if cf2.CookieName != "gosessionid" {
-		t.Fatal("parseconfig get cookiename error")
-	}
-	if cf2.Gclifetime != 3600 {
-		t.Fatal("parseconfig get gclifetime error")
-	}
-	if cf2.EnableSetCookie != false {
-		t.Fatal("parseconfig get enableSetCookie error")
-	}
-	cconfig := new(cookieConfig)
-	err = json.Unmarshal([]byte(cf2.ProviderConfig), cconfig)
-	if err != nil {
-		t.Fatal("parse ProviderConfig err,", err)
-	}
-	if cconfig.CookieName != "gosessionid" {
-		t.Fatal("ProviderConfig get cookieName error")
-	}
-	if cconfig.SecurityKey != "beegocookiehashkey" {
-		t.Fatal("ProviderConfig get securityKey error")
-	}
-}

+ 0 - 231
Godeps/_workspace/src/github.com/macaron-contrib/session/sess_utils.go

@@ -1,231 +0,0 @@
-// Copyright 2013 Beego Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License"): you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations
-// under the License.
-
-package session
-
-import (
-	"bytes"
-	"crypto/cipher"
-	"crypto/hmac"
-	"crypto/rand"
-	"crypto/sha1"
-	"crypto/subtle"
-	"encoding/base64"
-	"encoding/gob"
-	"errors"
-	"fmt"
-	"io"
-	r "math/rand"
-	"strconv"
-	"time"
-)
-
-func init() {
-	gob.Register([]interface{}{})
-	gob.Register(map[int]interface{}{})
-	gob.Register(map[string]interface{}{})
-	gob.Register(map[interface{}]interface{}{})
-	gob.Register(map[string]string{})
-	gob.Register(map[int]string{})
-	gob.Register(map[int]int{})
-	gob.Register(map[int]int64{})
-}
-
-// RandomCreateBytes generate random []byte by specify chars.
-func RandomCreateBytes(n int, alphabets ...byte) []byte {
-	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
-	var bytes = make([]byte, n)
-	var randby bool
-	if num, err := rand.Read(bytes); num != n || err != nil {
-		r.Seed(time.Now().UnixNano())
-		randby = true
-	}
-	for i, b := range bytes {
-		if len(alphabets) == 0 {
-			if randby {
-				bytes[i] = alphanum[r.Intn(len(alphanum))]
-			} else {
-				bytes[i] = alphanum[b%byte(len(alphanum))]
-			}
-		} else {
-			if randby {
-				bytes[i] = alphabets[r.Intn(len(alphabets))]
-			} else {
-				bytes[i] = alphabets[b%byte(len(alphabets))]
-			}
-		}
-	}
-	return bytes
-}
-
-func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) {
-	for _, v := range obj {
-		gob.Register(v)
-	}
-	buf := bytes.NewBuffer(nil)
-	enc := gob.NewEncoder(buf)
-	err := enc.Encode(obj)
-	if err != nil {
-		return []byte(""), err
-	}
-	return buf.Bytes(), nil
-}
-
-func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) {
-	buf := bytes.NewBuffer(encoded)
-	dec := gob.NewDecoder(buf)
-	var out map[interface{}]interface{}
-	err := dec.Decode(&out)
-	if err != nil {
-		return nil, err
-	}
-	return out, nil
-}
-
-// generateRandomKey creates a random key with the given strength.
-func generateRandomKey(strength int) []byte {
-	k := make([]byte, strength)
-	if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil {
-		return RandomCreateBytes(strength)
-	}
-	return k
-}
-
-// Encryption -----------------------------------------------------------------
-
-// encrypt encrypts a value using the given block in counter mode.
-//
-// A random initialization vector (http://goo.gl/zF67k) with the length of the
-// block size is prepended to the resulting ciphertext.
-func encrypt(block cipher.Block, value []byte) ([]byte, error) {
-	iv := generateRandomKey(block.BlockSize())
-	if iv == nil {
-		return nil, errors.New("encrypt: failed to generate random iv")
-	}
-	// Encrypt it.
-	stream := cipher.NewCTR(block, iv)
-	stream.XORKeyStream(value, value)
-	// Return iv + ciphertext.
-	return append(iv, value...), nil
-}
-
-// decrypt decrypts a value using the given block in counter mode.
-//
-// The value to be decrypted must be prepended by a initialization vector
-// (http://goo.gl/zF67k) with the length of the block size.
-func decrypt(block cipher.Block, value []byte) ([]byte, error) {
-	size := block.BlockSize()
-	if len(value) > size {
-		// Extract iv.
-		iv := value[:size]
-		// Extract ciphertext.
-		value = value[size:]
-		// Decrypt it.
-		stream := cipher.NewCTR(block, iv)
-		stream.XORKeyStream(value, value)
-		return value, nil
-	}
-	return nil, errors.New("decrypt: the value could not be decrypted")
-}
-
-func encodeCookie(block cipher.Block, hashKey, name string, value map[interface{}]interface{}) (string, error) {
-	var err error
-	var b []byte
-	// 1. EncodeGob.
-	if b, err = EncodeGob(value); err != nil {
-		return "", err
-	}
-	// 2. Encrypt (optional).
-	if b, err = encrypt(block, b); err != nil {
-		return "", err
-	}
-	b = encode(b)
-	// 3. Create MAC for "name|date|value". Extra pipe to be used later.
-	b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b))
-	h := hmac.New(sha1.New, []byte(hashKey))
-	h.Write(b)
-	sig := h.Sum(nil)
-	// Append mac, remove name.
-	b = append(b, sig...)[len(name)+1:]
-	// 4. Encode to base64.
-	b = encode(b)
-	// Done.
-	return string(b), nil
-}
-
-func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime int64) (map[interface{}]interface{}, error) {
-	// 1. Decode from base64.
-	b, err := decode([]byte(value))
-	if err != nil {
-		return nil, err
-	}
-	// 2. Verify MAC. Value is "date|value|mac".
-	parts := bytes.SplitN(b, []byte("|"), 3)
-	if len(parts) != 3 {
-		return nil, errors.New("Decode: invalid value %v")
-	}
-
-	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...)
-	h := hmac.New(sha1.New, []byte(hashKey))
-	h.Write(b)
-	sig := h.Sum(nil)
-	if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 {
-		return nil, errors.New("Decode: the value is not valid")
-	}
-	// 3. Verify date ranges.
-	var t1 int64
-	if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil {
-		return nil, errors.New("Decode: invalid timestamp")
-	}
-	t2 := time.Now().UTC().Unix()
-	if t1 > t2 {
-		return nil, errors.New("Decode: timestamp is too new")
-	}
-	if t1 < t2-gcmaxlifetime {
-		return nil, errors.New("Decode: expired timestamp")
-	}
-	// 4. Decrypt (optional).
-	b, err = decode(parts[1])
-	if err != nil {
-		return nil, err
-	}
-	if b, err = decrypt(block, b); err != nil {
-		return nil, err
-	}
-	// 5. DecodeGob.
-	if dst, err := DecodeGob(b); err != nil {
-		return nil, err
-	} else {
-		return dst, nil
-	}
-}
-
-// Encoding -------------------------------------------------------------------
-
-// encode encodes a value using base64.
-func encode(value []byte) []byte {
-	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
-	base64.URLEncoding.Encode(encoded, value)
-	return encoded
-}
-
-// decode decodes a cookie using base64.
-func decode(value []byte) ([]byte, error) {
-	decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value)))
-	b, err := base64.URLEncoding.Decode(decoded, value)
-	if err != nil {
-		return nil, err
-	}
-	return decoded[:b], nil
-}

+ 259 - 268
Godeps/_workspace/src/github.com/macaron-contrib/session/session.go

@@ -16,10 +16,9 @@
 // Package session a middleware that provides the session manager of Macaron.
 package session
 
-// NOTE: last sync fc6b9ce on Nov 4, 2014.
+// NOTE: last sync 000033e on Nov 4, 2014.
 
 import (
-	"crypto/rand"
 	"encoding/hex"
 	"fmt"
 	"net/http"
@@ -29,30 +28,74 @@ import (
 	"github.com/Unknwon/macaron"
 )
 
+const _VERSION = "0.1.1"
+
 func Version() string {
-	return "0.0.5"
+	return _VERSION
 }
 
+// RawStore is the interface that operates the session data.
 type RawStore interface {
-	Set(key, value interface{}) error     //set session value
-	Get(key interface{}) interface{}      //get session value
-	Delete(key interface{}) error         //delete session value
-	SessionID() string                    //back current sessionID
-	SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data
-	Flush() error                         //delete all data
+	// Set sets value to given key in session.
+	Set(key, value interface{}) error
+	// Get gets value by given key in session.
+	Get(key interface{}) interface{}
+	// Delete delete a key from session.
+	Delete(key interface{}) error
+	// ID returns current session ID.
+	ID() string
+	// Release releases session resource and save data to provider.
+	Release() error
+	// Flush deletes all session data.
+	Flush() error
 }
 
-// Store contains all data for one session process with specific id.
+// Store is the interface that contains all data for one session process with specific ID.
 type Store interface {
 	RawStore
-	GetActiveSession() int
+	// Read returns raw session store by session ID.
+	Read(sid string) (RawStore, error)
+	// Destory deletes a session.
+	Destory(*macaron.Context) error
+	// RegenerateId regenerates a session store from old session ID to new one.
+	RegenerateId(*macaron.Context) (RawStore, error)
+	// Count counts and returns number of sessions.
+	Count() int
+	// GC calls GC to clean expired sessions.
+	GC()
+}
+
+type store struct {
+	RawStore
+	*Manager
 }
 
+var _ Store = &store{}
+
+// Options represents a struct for specifying configuration options for the session middleware.
 type Options struct {
-	// Name of provider. Default is memory.
+	// Name of provider. Default is "memory".
 	Provider string
-	// Provider configuration string.
-	Config
+	// Provider configuration, it's corresponding to provider.
+	ProviderConfig string
+	// Cookie name to save session ID. Default is "MacaronSession".
+	CookieName string
+	// Cookie path to store. Default is "/".
+	CookiePath string
+	// GC interval time in seconds. Default is 3600.
+	Gclifetime int64
+	// Max life time in seconds. Default is whatever GC interval time is.
+	Maxlifetime int64
+	// Use HTTPS only. Default is false.
+	Secure bool
+	// Cookie life time. Default is 0.
+	CookieLifeTime int
+	// Cookie domain name. Default is empty.
+	Domain string
+	// Session ID length. Default is 16.
+	IDLength int
+	// Configuration section name. Default is "session".
+	Section string
 }
 
 func prepareOptions(options []Options) Options {
@@ -60,97 +103,60 @@ func prepareOptions(options []Options) Options {
 	if len(options) > 0 {
 		opt = options[0]
 	}
+	if len(opt.Section) == 0 {
+		opt.Section = "session"
+	}
+	sec := macaron.Config().Section(opt.Section)
 
-	// Defaults
 	if len(opt.Provider) == 0 {
-		opt.Provider = "memory"
+		opt.Provider = sec.Key("PROVIDER").MustString("memory")
+	}
+	if len(opt.ProviderConfig) == 0 && opt.Provider == "file" {
+		opt.ProviderConfig = sec.Key("PROVIDER_CONFIG").MustString("data/sessions")
 	}
-	opt.EnableSetCookie = true
 	if len(opt.CookieName) == 0 {
-		opt.CookieName = "MacaronSession"
+		opt.CookieName = sec.Key("COOKIE_NAME").MustString("MacaronSession")
 	}
 	if len(opt.CookiePath) == 0 {
-		opt.CookiePath = "/"
+		opt.CookiePath = sec.Key("COOKIE_PATH").MustString("/")
 	}
 	if opt.Gclifetime == 0 {
-		opt.Gclifetime = 3600
+		opt.Gclifetime = sec.Key("GC_INTERVAL_TIME").MustInt64(3600)
 	}
 	if opt.Maxlifetime == 0 {
-		opt.Maxlifetime = opt.Gclifetime
+		opt.Maxlifetime = sec.Key("MAX_LIFE_TIME").MustInt64(opt.Gclifetime)
 	}
-
-	if opt.SessionIdLength == 0 {
-		opt.SessionIdLength = 16
+	if !opt.Secure {
+		opt.Secure = sec.Key("SECURE").MustBool()
 	}
-
-	return opt
-}
-
-// ___________.____       _____    _________ ___ ___
-// \_   _____/|    |     /  _  \  /   _____//   |   \
-//  |    __)  |    |    /  /_\  \ \_____  \/    ~    \
-//  |     \   |    |___/    |    \/        \    Y    /
-//  \___  /   |_______ \____|__  /_______  /\___|_  /
-//      \/            \/       \/        \/       \/
-
-type Flash struct {
-	ctx *macaron.Context
-	url.Values
-	ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
-}
-
-func (f *Flash) set(name, msg string, current ...bool) {
-	isShow := false
-	if (len(current) == 0 && macaron.FlashNow) ||
-		(len(current) > 0 && current[0]) {
-		isShow = true
+	if opt.CookieLifeTime == 0 {
+		opt.CookieLifeTime = sec.Key("COOKIE_LIFE_TIME").MustInt()
 	}
-
-	if isShow {
-		f.ctx.Data["Flash"] = f
-	} else {
-		f.Set(name, msg)
+	if len(opt.Domain) == 0 {
+		opt.Domain = sec.Key("DOMAIN").String()
+	}
+	if opt.IDLength == 0 {
+		opt.IDLength = sec.Key("ID_LENGTH").MustInt(16)
 	}
-}
-
-func (f *Flash) Error(msg string, current ...bool) {
-	f.ErrorMsg = msg
-	f.set("error", msg, current...)
-}
-
-func (f *Flash) Warning(msg string, current ...bool) {
-	f.WarningMsg = msg
-	f.set("warning", msg, current...)
-}
-
-func (f *Flash) Info(msg string, current ...bool) {
-	f.InfoMsg = msg
-	f.set("info", msg, current...)
-}
-
-func (f *Flash) Success(msg string, current ...bool) {
-	f.SuccessMsg = msg
-	f.set("success", msg, current...)
-}
 
-type store struct {
-	RawStore
-	*Manager
+	return opt
 }
 
 // Sessioner is a middleware that maps a session.SessionStore service into the Macaron handler chain.
 // An single variadic session.Options struct can be optionally provided to configure.
 func Sessioner(options ...Options) macaron.Handler {
 	opt := prepareOptions(options)
-	manager, err := NewManager(opt.Provider, &opt.Config)
+	manager, err := NewManager(opt.Provider, opt)
 	if err != nil {
 		panic(err)
 	}
-	go manager.GC()
+	go manager.startGC()
 
 	return func(ctx *macaron.Context) {
-		// FIXME: should I panic for error?
-		sess, _ := manager.SessionStart(ctx.Resp, ctx.Req.Request)
+		sess, err := manager.Start(ctx)
+		if err != nil {
+			panic("session: " + err.Error())
+		}
 
 		// Get flash.
 		vals, _ := url.ParseQuery(ctx.GetCookie("macaron_flash"))
@@ -166,8 +172,6 @@ func Sessioner(options ...Options) macaron.Handler {
 
 		f := &Flash{ctx, url.Values{}, "", "", "", ""}
 		ctx.Resp.Before(func(macaron.ResponseWriter) {
-			sess.SessionRelease(ctx.Resp)
-
 			if flash := f.Encode(); len(flash) > 0 {
 				ctx.SetCookie("macaron_flash", flash, 0, opt.CookiePath)
 			}
@@ -178,243 +182,230 @@ func Sessioner(options ...Options) macaron.Handler {
 			RawStore: sess,
 			Manager:  manager,
 		}
+
 		ctx.MapTo(s, (*Store)(nil))
+
+		ctx.Next()
+
+		if sess.Release() != nil {
+			panic("session: " + err.Error())
+		}
 	}
 }
 
-// Provider contains global session methods and saved SessionStores.
-// it can operate a SessionStore by its id.
+// Provider is the interface that provides session manipulations.
 type Provider interface {
-	SessionInit(gclifetime int64, config string) error
-	SessionRead(sid string) (RawStore, error)
-	SessionExist(sid string) bool
-	SessionRegenerate(oldsid, sid string) (RawStore, error)
-	SessionDestroy(sid string) error
-	SessionAll() int //get all active session
-	SessionGC()
+	// Init initializes session provider.
+	Init(gclifetime int64, config string) error
+	// Read returns raw session store by session ID.
+	Read(sid string) (RawStore, error)
+	// Exist returns true if session with given ID exists.
+	Exist(sid string) bool
+	// Destory deletes a session by session ID.
+	Destory(sid string) error
+	// Regenerate regenerates a session store from old session ID to new one.
+	Regenerate(oldsid, sid string) (RawStore, error)
+	// Count counts and returns number of sessions.
+	Count() int
+	// GC calls GC to clean expired sessions.
+	GC()
 }
 
-var provides = make(map[string]Provider)
+var providers = make(map[string]Provider)
 
-// Register makes a session provide available by the provided name.
-// If Register is called twice with the same name or if driver is nil,
-// it panics.
-func Register(name string, provide Provider) {
-	if provide == nil {
-		panic("session: Register provide is nil")
+// Register registers a provider.
+func Register(name string, provider Provider) {
+	if provider == nil {
+		panic("session: cannot register provider with nil value")
 	}
-	if _, dup := provides[name]; dup {
-		panic("session: Register called twice for provider " + name)
+	if _, dup := providers[name]; dup {
+		panic(fmt.Errorf("session: cannot register provider '%s' twice", name))
 	}
-	provides[name] = provide
+	providers[name] = provider
 }
 
-type Config struct {
-	CookieName      string `json:"cookieName"`
-	CookiePath      string `json:"cookiePath"`
-	EnableSetCookie bool   `json:"enableSetCookie,omitempty"`
-	Gclifetime      int64  `json:"gclifetime"`
-	Maxlifetime     int64  `json:"maxLifetime"`
-	Secure          bool   `json:"secure"`
-	CookieLifeTime  int    `json:"cookieLifeTime"`
-	ProviderConfig  string `json:"providerConfig"`
-	Domain          string `json:"domain"`
-	SessionIdLength int64  `json:"sessionIdLength"`
-}
+//    _____
+//   /     \ _____    ____ _____     ____   ___________
+//  /  \ /  \\__  \  /    \\__  \   / ___\_/ __ \_  __ \
+// /    Y    \/ __ \|   |  \/ __ \_/ /_/  >  ___/|  | \/
+// \____|__  (____  /___|  (____  /\___  / \___  >__|
+//         \/     \/     \/     \//_____/      \/
 
-// Manager contains Provider and its configuration.
+// Manager represents a struct that contains session provider and its configuration.
 type Manager struct {
 	provider Provider
-	config   *Config
+	opt      Options
 }
 
-// Create new Manager with provider name and json config string.
-// provider name:
-// 1. cookie
-// 2. file
-// 3. memory
-// 4. redis
-// 5. mysql
-// json config:
-// 1. is https  default false
-// 2. hashfunc  default sha1
-// 3. hashkey default beegosessionkey
-// 4. maxage default is none
-func NewManager(provideName string, config *Config) (*Manager, error) {
-	provider, ok := provides[provideName]
+// NewManager creates and returns a new session manager by given provider name and configuration.
+// It panics when given provider isn't registered.
+func NewManager(name string, opt Options) (*Manager, error) {
+	p, ok := providers[name]
 	if !ok {
-		return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
+		return nil, fmt.Errorf("session: unknown provider ‘%q’(forgotten import?)", name)
 	}
+	if err := p.Init(opt.Maxlifetime, opt.ProviderConfig); err != nil {
+		return nil, err
+	}
+	return &Manager{p, opt}, nil
+}
 
-	config.EnableSetCookie = true
-	if config.Maxlifetime == 0 {
-		config.Maxlifetime = config.Gclifetime
+// sessionId generates a new session ID with rand string, unix nano time, remote addr by hash function.
+func (m *Manager) sessionId() string {
+	return hex.EncodeToString(generateRandomKey(m.opt.IDLength))
+}
+
+// Start starts a session by generating new one
+// or retrieve existence one by reading session ID from HTTP request if it's valid.
+func (m *Manager) Start(ctx *macaron.Context) (RawStore, error) {
+	sid := ctx.GetCookie(m.opt.CookieName)
+	if len(sid) > 0 && m.provider.Exist(sid) {
+		return m.provider.Read(sid)
 	}
-	if err := provider.SessionInit(config.Maxlifetime, config.ProviderConfig); err != nil {
+
+	sid = m.sessionId()
+	sess, err := m.provider.Read(sid)
+	if err != nil {
 		return nil, err
 	}
-	return &Manager{
-		provider: provider,
-		config:   config,
-	}, nil
+
+	cookie := &http.Cookie{
+		Name:     m.opt.CookieName,
+		Value:    sid,
+		Path:     m.opt.CookiePath,
+		HttpOnly: true,
+		Secure:   m.opt.Secure,
+		Domain:   m.opt.Domain,
+	}
+	if m.opt.CookieLifeTime >= 0 {
+		cookie.MaxAge = m.opt.CookieLifeTime
+	}
+	http.SetCookie(ctx.Resp, cookie)
+	ctx.Req.AddCookie(cookie)
+	return sess, nil
 }
 
-// Start session. generate or read the session id from http request.
-// if session id exists, return SessionStore with this id.
-func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session RawStore, _ error) {
-	cookie, err := r.Cookie(manager.config.CookieName)
-	if err != nil || len(cookie.Value) == 0 {
-		sid, err := manager.sessionId(r)
-		if err != nil {
-			return nil, err
-		}
-		session, err = manager.provider.SessionRead(sid)
+// Read returns raw session store by session ID.
+func (m *Manager) Read(sid string) (RawStore, error) {
+	return m.provider.Read(sid)
+}
+
+// Destory deletes a session by given ID.
+func (m *Manager) Destory(ctx *macaron.Context) error {
+	sid := ctx.GetCookie(m.opt.CookieName)
+	if len(sid) == 0 {
+		return nil
+	}
+
+	if err := m.provider.Destory(sid); err != nil {
+		return err
+	}
+	cookie := &http.Cookie{
+		Name:     m.opt.CookieName,
+		Path:     m.opt.CookiePath,
+		HttpOnly: true,
+		Expires:  time.Now(),
+		MaxAge:   -1,
+	}
+	http.SetCookie(ctx.Resp, cookie)
+	return nil
+}
+
+// RegenerateId regenerates a session store from old session ID to new one.
+func (m *Manager) RegenerateId(ctx *macaron.Context) (sess RawStore, err error) {
+	sid := m.sessionId()
+	oldsid := ctx.GetCookie(m.opt.CookieName)
+	if len(oldsid) == 0 {
+		sess, err = m.provider.Read(oldsid)
 		if err != nil {
 			return nil, err
 		}
-
-		cookie = &http.Cookie{Name: manager.config.CookieName,
-			Value:    url.QueryEscape(sid),
-			Path:     manager.config.CookiePath,
-			HttpOnly: true,
-			Secure:   manager.config.Secure,
-			Domain:   manager.config.Domain,
-		}
-		if manager.config.CookieLifeTime >= 0 {
-			cookie.MaxAge = manager.config.CookieLifeTime
-		}
-		if manager.config.EnableSetCookie {
-			http.SetCookie(w, cookie)
-		}
-		r.AddCookie(cookie)
 	} else {
-		sid, err := url.QueryUnescape(cookie.Value)
+		sess, err = m.provider.Regenerate(oldsid, sid)
 		if err != nil {
 			return nil, err
 		}
+	}
+	ck := &http.Cookie{
+		Name:     m.opt.CookieName,
+		Value:    sid,
+		Path:     m.opt.CookiePath,
+		HttpOnly: true,
+		Secure:   m.opt.Secure,
+		Domain:   m.opt.Domain,
+	}
+	if m.opt.CookieLifeTime >= 0 {
+		ck.MaxAge = m.opt.CookieLifeTime
+	}
+	http.SetCookie(ctx.Resp, ck)
+	ctx.Req.AddCookie(ck)
+	return sess, nil
+}
 
-		if manager.provider.SessionExist(sid) {
-			session, err = manager.provider.SessionRead(sid)
-			if err != nil {
-				return nil, err
-			}
-		} else {
-			sid, err = manager.sessionId(r)
-			if err != nil {
-				return nil, err
-			}
-			session, err = manager.provider.SessionRead(sid)
-			if err != nil {
-				return nil, err
-			}
+// Count counts and returns number of sessions.
+func (m *Manager) Count() int {
+	return m.provider.Count()
+}
 
-			cookie = &http.Cookie{Name: manager.config.CookieName,
-				Value:    url.QueryEscape(sid),
-				Path:     manager.config.CookiePath,
-				HttpOnly: true,
-				Secure:   manager.config.Secure,
-				Domain:   manager.config.Domain,
-			}
-			if manager.config.CookieLifeTime >= 0 {
-				cookie.MaxAge = manager.config.CookieLifeTime
-			}
-			if manager.config.EnableSetCookie {
-				http.SetCookie(w, cookie)
-			}
-			r.AddCookie(cookie)
-		}
-	}
-	return session, nil
+// GC starts GC job in a certain period.
+func (m *Manager) GC() {
+	m.provider.GC()
 }
 
-// Destroy session by its id in http request cookie.
-func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
-	cookie, err := r.Cookie(manager.config.CookieName)
-	if err != nil || cookie.Value == "" {
-		return
-	} else {
-		manager.provider.SessionDestroy(cookie.Value)
-		expiration := time.Now()
-		cookie := http.Cookie{Name: manager.config.CookieName,
-			Path:     manager.config.CookiePath,
-			HttpOnly: true,
-			Expires:  expiration,
-			MaxAge:   -1}
-		http.SetCookie(w, &cookie)
-	}
+// startGC starts GC job in a certain period.
+func (m *Manager) startGC() {
+	m.GC()
+	time.AfterFunc(time.Duration(m.opt.Gclifetime)*time.Second, func() { m.startGC() })
 }
 
-// Get SessionStore by its id.
-func (manager *Manager) GetSessionStore(sid string) (sessions RawStore, err error) {
-	sessions, err = manager.provider.SessionRead(sid)
-	return
+// SetSecure indicates whether to set cookie with HTTPS or not.
+func (m *Manager) SetSecure(secure bool) {
+	m.opt.Secure = secure
 }
 
-// Start session gc process.
-// it can do gc in times after gc lifetime.
-func (manager *Manager) GC() {
-	manager.provider.SessionGC()
-	time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() })
+// ___________.____       _____    _________ ___ ___
+// \_   _____/|    |     /  _  \  /   _____//   |   \
+//  |    __)  |    |    /  /_\  \ \_____  \/    ~    \
+//  |     \   |    |___/    |    \/        \    Y    /
+//  \___  /   |_______ \____|__  /_______  /\___|_  /
+//      \/            \/       \/        \/       \/
+
+type Flash struct {
+	ctx *macaron.Context
+	url.Values
+	ErrorMsg, WarningMsg, InfoMsg, SuccessMsg string
 }
 
-// Regenerate a session id for this SessionStore who's id is saving in http request.
-func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session RawStore) {
-	sid, err := manager.sessionId(r)
-	if err != nil {
-		return nil
+func (f *Flash) set(name, msg string, current ...bool) {
+	isShow := false
+	if (len(current) == 0 && macaron.FlashNow) ||
+		(len(current) > 0 && current[0]) {
+		isShow = true
 	}
 
-	cookie, err := r.Cookie(manager.config.CookieName)
-	if err != nil && cookie.Value == "" {
-		// delete old cookie
-		session, err = manager.provider.SessionRead(sid)
-		if err != nil {
-			return nil
-		}
-		cookie = &http.Cookie{Name: manager.config.CookieName,
-			Value:    url.QueryEscape(sid),
-			Path:     manager.config.CookiePath,
-			HttpOnly: true,
-			Secure:   manager.config.Secure,
-			Domain:   manager.config.Domain,
-		}
+	if isShow {
+		f.ctx.Data["Flash"] = f
 	} else {
-		oldsid, err := url.QueryUnescape(cookie.Value)
-		if err != nil {
-			return nil
-		}
-		session, err = manager.provider.SessionRegenerate(oldsid, sid)
-		if err != nil {
-			return nil
-		}
-		cookie.Value = url.QueryEscape(sid)
-		cookie.HttpOnly = true
-		cookie.Path = "/"
-	}
-	if manager.config.CookieLifeTime >= 0 {
-		cookie.MaxAge = manager.config.CookieLifeTime
+		f.Set(name, msg)
 	}
-	http.SetCookie(w, cookie)
-	r.AddCookie(cookie)
-	return session
 }
 
-// Get all active sessions count number.
-func (manager *Manager) GetActiveSession() int {
-	return manager.provider.SessionAll()
+func (f *Flash) Error(msg string, current ...bool) {
+	f.ErrorMsg = msg
+	f.set("error", msg, current...)
 }
 
-// Set cookie with https.
-func (manager *Manager) SetSecure(secure bool) {
-	manager.config.Secure = secure
+func (f *Flash) Warning(msg string, current ...bool) {
+	f.WarningMsg = msg
+	f.set("warning", msg, current...)
 }
 
-// generate session id with rand string, unix nano time, remote addr by hash function.
-func (manager *Manager) sessionId(r *http.Request) (string, error) {
-	b := make([]byte, manager.config.SessionIdLength)
-	n, err := rand.Read(b)
-	if n != len(b) || err != nil {
-		return "", fmt.Errorf("fail to read from the system CSPRNG.")
-	}
-	return hex.EncodeToString(b), nil
+func (f *Flash) Info(msg string, current ...bool) {
+	f.InfoMsg = msg
+	f.set("info", msg, current...)
+}
+
+func (f *Flash) Success(msg string, current ...bool) {
+	f.SuccessMsg = msg
+	f.set("success", msg, current...)
 }

二进制
grafana-pro


+ 1 - 4
pkg/cmd/web.go

@@ -37,10 +37,7 @@ func newMacaron() *macaron.Macaron {
 	mapStatic(m, "public/app", "app")
 	mapStatic(m, "public/img", "img")
 
-	m.Use(session.Sessioner(session.Options{
-		Provider: setting.SessionProvider,
-		Config:   *setting.SessionConfig,
-	}))
+	m.Use(session.Sessioner(setting.SessionOptions))
 
 	m.Use(macaron.Renderer(macaron.RenderOptions{
 		Directory:  path.Join(setting.StaticRootPath, "views"),

+ 13 - 15
pkg/setting/setting.go

@@ -54,8 +54,7 @@ var (
 	StaticRootPath     string
 
 	// Session settings.
-	SessionProvider string
-	SessionConfig   *session.Config
+	SessionOptions session.Options
 
 	// Global setting objects.
 	Cfg          *goconfig.ConfigFile
@@ -149,19 +148,18 @@ func NewConfigContext() {
 }
 
 func initSessionService() {
-	SessionProvider = Cfg.MustValueRange("session", "provider", "memory", []string{"memory", "file"})
-
-	SessionConfig = new(session.Config)
-	SessionConfig.ProviderConfig = strings.Trim(Cfg.MustValue("session", "provider_config"), "\" ")
-	SessionConfig.CookieName = Cfg.MustValue("session", "cookie_name", "grafana_pro_sess")
-	SessionConfig.CookiePath = AppSubUrl
-	SessionConfig.Secure = Cfg.MustBool("session", "cookie_secure")
-	SessionConfig.EnableSetCookie = Cfg.MustBool("session", "enable_set_cookie", true)
-	SessionConfig.Gclifetime = Cfg.MustInt64("session", "gc_interval_time", 86400)
-	SessionConfig.Maxlifetime = Cfg.MustInt64("session", "session_life_time", 86400)
-
-	if SessionProvider == "file" {
-		os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm)
+
+	SessionOptions = session.Options{}
+	SessionOptions.Provider = Cfg.MustValueRange("session", "provider", "memory", []string{"memory", "file"})
+	SessionOptions.ProviderConfig = strings.Trim(Cfg.MustValue("session", "provider_config"), "\" ")
+	SessionOptions.CookieName = Cfg.MustValue("session", "cookie_name", "grafana_pro_sess")
+	SessionOptions.CookiePath = AppSubUrl
+	SessionOptions.Secure = Cfg.MustBool("session", "cookie_secure")
+	SessionOptions.Gclifetime = Cfg.MustInt64("session", "gc_interval_time", 86400)
+	SessionOptions.Maxlifetime = Cfg.MustInt64("session", "session_life_time", 86400)
+
+	if SessionOptions.Provider == "file" {
+		os.MkdirAll(path.Dir(SessionOptions.ProviderConfig), os.ModePerm)
 	}
 
 	log.Info("Session Service Enabled")