forked from Shiloh/githaven
7f8e3192cd
* Allow common redis and leveldb connections Prevents multiple reopening of redis and leveldb connections to the same place by sharing connections. Further allows for more configurable redis connection type using the redisURI and a leveldbURI scheme. Signed-off-by: Andrew Thornton <art27@cantab.net> * add unit-test Signed-off-by: Andrew Thornton <art27@cantab.net> * as per @lunny Signed-off-by: Andrew Thornton <art27@cantab.net> * add test Signed-off-by: Andrew Thornton <art27@cantab.net> * Update modules/cache/cache_redis.go * Update modules/queue/queue_disk.go * Update modules/cache/cache_redis.go * Update modules/cache/cache_redis.go * Update modules/queue/unique_queue_disk.go * Update modules/queue/queue_disk.go * Update modules/queue/unique_queue_disk.go * Update modules/session/redis.go Co-authored-by: techknowlogick <techknowlogick@gitea.io> Co-authored-by: Lauris BH <lauris@nix.lv>
199 lines
5.0 KiB
Go
199 lines
5.0 KiB
Go
// Copyright 2019 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package session
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sync"
|
|
|
|
"gitea.com/macaron/session"
|
|
couchbase "gitea.com/macaron/session/couchbase"
|
|
memcache "gitea.com/macaron/session/memcache"
|
|
mysql "gitea.com/macaron/session/mysql"
|
|
nodb "gitea.com/macaron/session/nodb"
|
|
postgres "gitea.com/macaron/session/postgres"
|
|
)
|
|
|
|
// VirtualSessionProvider represents a shadowed session provider implementation.
|
|
type VirtualSessionProvider struct {
|
|
lock sync.RWMutex
|
|
provider session.Provider
|
|
}
|
|
|
|
// Init initializes the cookie session provider with given root path.
|
|
func (o *VirtualSessionProvider) Init(gclifetime int64, config string) error {
|
|
var opts session.Options
|
|
if err := json.Unmarshal([]byte(config), &opts); err != nil {
|
|
return err
|
|
}
|
|
// Note that these options are unprepared so we can't just use NewManager here.
|
|
// Nor can we access the provider map in session.
|
|
// So we will just have to do this by hand.
|
|
// This is only slightly more wrong than modules/setting/session.go:23
|
|
switch opts.Provider {
|
|
case "memory":
|
|
o.provider = &session.MemProvider{}
|
|
case "file":
|
|
o.provider = &session.FileProvider{}
|
|
case "redis":
|
|
o.provider = &RedisProvider{}
|
|
case "mysql":
|
|
o.provider = &mysql.MysqlProvider{}
|
|
case "postgres":
|
|
o.provider = &postgres.PostgresProvider{}
|
|
case "couchbase":
|
|
o.provider = &couchbase.CouchbaseProvider{}
|
|
case "memcache":
|
|
o.provider = &memcache.MemcacheProvider{}
|
|
case "nodb":
|
|
o.provider = &nodb.NodbProvider{}
|
|
default:
|
|
return fmt.Errorf("VirtualSessionProvider: Unknown Provider: %s", opts.Provider)
|
|
}
|
|
return o.provider.Init(gclifetime, opts.ProviderConfig)
|
|
}
|
|
|
|
// Read returns raw session store by session ID.
|
|
func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) {
|
|
o.lock.RLock()
|
|
defer o.lock.RUnlock()
|
|
if o.provider.Exist(sid) {
|
|
return o.provider.Read(sid)
|
|
}
|
|
kv := make(map[interface{}]interface{})
|
|
kv["_old_uid"] = "0"
|
|
return NewVirtualStore(o, sid, kv), nil
|
|
}
|
|
|
|
// Exist returns true if session with given ID exists.
|
|
func (o *VirtualSessionProvider) Exist(sid string) bool {
|
|
return true
|
|
}
|
|
|
|
// Destroy deletes a session by session ID.
|
|
func (o *VirtualSessionProvider) Destroy(sid string) error {
|
|
o.lock.Lock()
|
|
defer o.lock.Unlock()
|
|
return o.provider.Destroy(sid)
|
|
}
|
|
|
|
// Regenerate regenerates a session store from old session ID to new one.
|
|
func (o *VirtualSessionProvider) Regenerate(oldsid, sid string) (session.RawStore, error) {
|
|
o.lock.Lock()
|
|
defer o.lock.Unlock()
|
|
return o.provider.Regenerate(oldsid, sid)
|
|
}
|
|
|
|
// Count counts and returns number of sessions.
|
|
func (o *VirtualSessionProvider) Count() int {
|
|
o.lock.RLock()
|
|
defer o.lock.RUnlock()
|
|
return o.provider.Count()
|
|
}
|
|
|
|
// GC calls GC to clean expired sessions.
|
|
func (o *VirtualSessionProvider) GC() {
|
|
o.provider.GC()
|
|
}
|
|
|
|
func init() {
|
|
session.Register("VirtualSession", &VirtualSessionProvider{})
|
|
}
|
|
|
|
// VirtualStore represents a virtual session store implementation.
|
|
type VirtualStore struct {
|
|
p *VirtualSessionProvider
|
|
sid string
|
|
lock sync.RWMutex
|
|
data map[interface{}]interface{}
|
|
released bool
|
|
}
|
|
|
|
// NewVirtualStore creates and returns a virtual session store.
|
|
func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore {
|
|
return &VirtualStore{
|
|
p: p,
|
|
sid: sid,
|
|
data: kv,
|
|
}
|
|
}
|
|
|
|
// Set sets value to given key in session.
|
|
func (s *VirtualStore) Set(key, val interface{}) error {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
s.data[key] = val
|
|
return nil
|
|
}
|
|
|
|
// Get gets value by given key in session.
|
|
func (s *VirtualStore) Get(key interface{}) interface{} {
|
|
s.lock.RLock()
|
|
defer s.lock.RUnlock()
|
|
|
|
return s.data[key]
|
|
}
|
|
|
|
// Delete delete a key from session.
|
|
func (s *VirtualStore) Delete(key interface{}) error {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
delete(s.data, key)
|
|
return nil
|
|
}
|
|
|
|
// ID returns current session ID.
|
|
func (s *VirtualStore) ID() string {
|
|
return s.sid
|
|
}
|
|
|
|
// Release releases resource and save data to provider.
|
|
func (s *VirtualStore) Release() error {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
// Now need to lock the provider
|
|
s.p.lock.Lock()
|
|
defer s.p.lock.Unlock()
|
|
if oldUID, ok := s.data["_old_uid"]; (ok && (oldUID != "0" || len(s.data) > 1)) || (!ok && len(s.data) > 0) {
|
|
// Now ensure that we don't exist!
|
|
realProvider := s.p.provider
|
|
|
|
if !s.released && realProvider.Exist(s.sid) {
|
|
// This is an error!
|
|
return fmt.Errorf("new sid '%s' already exists", s.sid)
|
|
}
|
|
realStore, err := realProvider.Read(s.sid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := realStore.Flush(); err != nil {
|
|
return err
|
|
}
|
|
for key, value := range s.data {
|
|
if err := realStore.Set(key, value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
err = realStore.Release()
|
|
if err == nil {
|
|
s.released = true
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Flush deletes all session data.
|
|
func (s *VirtualStore) Flush() error {
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
s.data = make(map[interface{}]interface{})
|
|
return nil
|
|
}
|