diff --git a/.example/database/gkvdb/badger/main.go b/.example/database/gkvdb/badger/main.go deleted file mode 100644 index 6bcbc3fdd..000000000 --- a/.example/database/gkvdb/badger/main.go +++ /dev/null @@ -1,18 +0,0 @@ -package main - -import ( - "log" - - "github.com/dgraph-io/badger" -) - -func main() { - // Open the Badger database located in the /tmp/badger directory. - // It will be created if it doesn't exist. - db, err := badger.Open(badger.DefaultOptions("/tmp/badger")) - if err != nil { - log.Fatal(err) - } - defer db.Close() - // Your code here… -} diff --git a/.example/database/gkvdb/main1.go b/.example/database/gkvdb/main1.go deleted file mode 100644 index 401a70a42..000000000 --- a/.example/database/gkvdb/main1.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "fmt" - "time" - - "github.com/gogf/gf/database/gkvdb" -) - -func main() { - key := []byte("key") - value := []byte("value") - - db := gkvdb.Instance() - db.SetPath("/tmp/gkvdb/test") - db.Set(key, value) - fmt.Println(db.Get(key)) - time.Sleep(time.Hour) -} diff --git a/DONATOR.MD b/DONATOR.MD index e8158e624..c087556ba 100644 --- a/DONATOR.MD +++ b/DONATOR.MD @@ -12,6 +12,7 @@ |[zhuhuan12](https://gitee.com/zhuhuan12)|gitee|¥50.00 |[zfan_codes](https://gitee.com/zfan_codes)|gitee|¥10.00 |[arden](https://github.com/arden)|alipay|¥10.00 +|[macnie](https://www.macnie.com)|wechat|¥100.00 |x*z|wechat|¥20.00 |潘兄|wechat|¥100.00 |Fly的狐狸|wechat|¥100.00 diff --git a/crypto/gaes/gaes.go b/crypto/gaes/gaes.go index 571f80c7c..b9428755d 100644 --- a/crypto/gaes/gaes.go +++ b/crypto/gaes/gaes.go @@ -14,8 +14,10 @@ import ( "errors" ) -const ( - ivDefValue = "I Love Go Frame!" +var ( + // IVDefaultValue is the default value for IV. + // This can be changed globally. + IVDefaultValue = "I Love Go Frame!" ) // Encrypt is alias of EncryptCBC. @@ -40,7 +42,7 @@ func EncryptCBC(plainText []byte, key []byte, iv ...[]byte) ([]byte, error) { if len(iv) > 0 { ivValue = iv[0] } else { - ivValue = []byte(ivDefValue) + ivValue = []byte(IVDefaultValue) } blockMode := cipher.NewCBCEncrypter(block, ivValue) cipherText := make([]byte, len(plainText)) @@ -63,7 +65,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) { if len(iv) > 0 { ivValue = iv[0] } else { - ivValue = []byte(ivDefValue) + ivValue = []byte(IVDefaultValue) } if len(cipherText)%blockSize != 0 { return nil, errors.New("cipherText is not a multiple of the block size") @@ -122,7 +124,7 @@ func EncryptCFB(plainText []byte, key []byte, padding *int, iv ...[]byte) ([]byt if len(iv) > 0 { ivValue = iv[0] } else { - ivValue = []byte(ivDefValue) + ivValue = []byte(IVDefaultValue) } stream := cipher.NewCFBEncrypter(block, ivValue) cipherText := make([]byte, len(plainText)) @@ -144,7 +146,7 @@ func DecryptCFB(cipherText []byte, key []byte, unpadding int, iv ...[]byte) ([]b if len(iv) > 0 { ivValue = iv[0] } else { - ivValue = []byte(ivDefValue) + ivValue = []byte(IVDefaultValue) } stream := cipher.NewCFBDecrypter(block, ivValue) plainText := make([]byte, len(cipherText)) diff --git a/database/gkvdb/gkvdb.go b/database/gkvdb/gkvdb.go deleted file mode 100644 index fed37002b..000000000 --- a/database/gkvdb/gkvdb.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -// Package gkvdb provides a lightweight, embeddable and persistent key-value database. -package gkvdb - -import ( - "github.com/dgraph-io/badger" - "github.com/gogf/gf/container/gmap" -) - -type Options = badger.Options - -const ( - // Default instance name. - DEFAULT_NAME = "default" -) - -var ( - // Instance map - instances = gmap.NewStrAnyMap(true) -) - -// DefaultOptions returns the default options for gkvdb. -func DefaultOptions(path string) Options { - options := badger.DefaultOptions(path) - options.Logger = nil - options.SyncWrites = false - return options -} - -// Instance returns an instance of DB with specified name. -// The param is unnecessary, if is not passed, -// it returns a instance with default name. -func Instance(name ...string) *DB { - instanceName := DEFAULT_NAME - if len(name) > 0 && name[0] != "" { - instanceName = name[0] - } - v := instances.GetOrSetFuncLock(instanceName, func() interface{} { - return New() - }) - if v != nil { - return v.(*DB) - } - return nil -} diff --git a/database/gkvdb/gkvdb_db.go b/database/gkvdb/gkvdb_db.go deleted file mode 100644 index 36d39eb96..000000000 --- a/database/gkvdb/gkvdb_db.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gkvdb - -import ( - "errors" - "time" - - "github.com/gogf/gf/os/gfile" - - "github.com/dgraph-io/badger" -) - -type DB struct { - options Options - badger *badger.DB -} - -// New creates and returns a new db object. -func New(options ...Options) *DB { - if len(options) > 0 { - return &DB{options: options[0]} - } - return &DB{options: DefaultOptions("")} -} - -// init does lazy initialization for db. -func (db *DB) init() (err error) { - if db.badger != nil { - return nil - } - if !gfile.Exists(db.options.Dir) { - err = gfile.Mkdir(db.options.Dir) - if err != nil { - return - } - } - db.badger, err = badger.Open(db.options) - return -} - -// Options return the options of current db object. -func (db *DB) Options() *Options { - return &db.options -} - -// SetOptions sets the options for db. -func (db *DB) SetOptions(options Options) error { - if db.badger != nil { - return errors.New("options cannot be changed after db is initialized") - } - db.options = options - return nil -} - -// SetPath sets the storage folder path for db. -func (db *DB) SetPath(path string) error { - if db.badger != nil { - return errors.New("options cannot be changed after db is initialized") - } - db.options.Dir = path - db.options.ValueDir = path - return nil -} - -// Size returns the data count of current db. -func (db *DB) Size() int64 { - if err := db.init(); err != nil { - return 0 - } - lsm, vlog := db.badger.Size() - return lsm + vlog -} - -// Set sets - pair data to current db with . -// The is optional, which is not expired in default. -func (db *DB) Set(key []byte, value []byte, ttl ...time.Duration) (err error) { - if err := db.init(); err != nil { - return err - } - tx := db.Begin(true) - defer tx.Commit() - return tx.Set(key, value, ttl...) -} - -// Get returns the value with given key. -// It returns nil if is not found in the db. -func (db *DB) Get(key []byte) (value []byte) { - if err := db.init(); err != nil { - return - } - tx := db.Begin(false) - defer tx.Rollback() - return tx.Get(key) -} - -// Delete removed data specified by from current db. -func (db *DB) Delete(key []byte) error { - if err := db.init(); err != nil { - return err - } - tx := db.Begin(true) - defer tx.Commit() - return tx.Delete(key) -} - -// Close closes the db. -func (db *DB) Close() error { - if db.badger == nil { - return nil - } - return db.badger.Close() -} - -// Iterate is alias of IterateAsc. -// See IterateAsc. -func (db *DB) Iterate(prefix []byte, f func(key, value []byte) bool) { - db.IterateAsc(prefix, f) -} - -// IteratorAsc iterates the db in ascending order -// with given callback function starting from . -// If is nil it iterates from the beginning of the db. -// If returns true, then it continues iterating; or false to stop. -func (db *DB) IterateAsc(prefix []byte, f func(key, value []byte) bool) { - if err := db.init(); err != nil { - return - } - tx := db.Begin(false) - defer tx.Rollback() - tx.IterateAsc(prefix, f) -} - -// IteratorDesc iterates the db in descending order -// with given callback function starting from . -// If is nil it iterates from the beginning of the db. -// If returns true, then it continues iterating; or false to stop. -func (db *DB) IterateDesc(prefix []byte, f func(key, value []byte) bool) { - if err := db.init(); err != nil { - return - } - tx := db.Begin(false) - defer tx.Rollback() - tx.IterateDesc(prefix, f) -} diff --git a/database/gkvdb/gkvdb_tx.go b/database/gkvdb/gkvdb_tx.go deleted file mode 100644 index 3cd54eb36..000000000 --- a/database/gkvdb/gkvdb_tx.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gkvdb - -import ( - "time" - - "github.com/dgraph-io/badger" -) - -// TX is the transaction object for db. -type TX struct { - db *DB - txn *badger.Txn -} - -// Begin starts the transaction for db. -func (db *DB) Begin(update bool) *TX { - if err := db.init(); err != nil { - return nil - } - return &TX{ - db: db, - txn: db.badger.NewTransaction(update), - } -} - -// Commit commits the changes and close the transaction. -func (tx *TX) Commit() error { - return tx.txn.Commit() -} - -// Rollback discards the changes and close the transaction. -func (tx *TX) Rollback() { - tx.txn.Discard() -} - -// Set sets - pair data to current db with in this transaction. -// The is optional, which is not expired in default. -func (tx *TX) Set(key []byte, value []byte, ttl ...time.Duration) error { - if len(ttl) > 0 && ttl[0] > 0 { - return tx.txn.SetEntry(badger.NewEntry(key, value).WithTTL(ttl[0])) - } - return tx.txn.Set(key, value) -} - -// Get returns the value with given key in this transaction. -// It returns nil if is not found in the db. -func (tx *TX) Get(key []byte) (value []byte) { - item, err := tx.txn.Get(key) - if err != nil { - return nil - } - if item.IsDeletedOrExpired() { - return nil - } - value, _ = item.ValueCopy(nil) - return -} - -// Delete removed data specified by from current db in this transaction. -func (tx *TX) Delete(key []byte) error { - return tx.txn.Delete(key) -} - -// Iterate is alias of IterateAsc. -// See IterateAsc. -func (tx *TX) Iterate(prefix []byte, f func(key, value []byte) bool) { - tx.IterateAsc(prefix, f) -} - -// IteratorAsc iterates the db in ascending order -// with given callback function starting from . -// If is nil it iterates from the beginning of the db. -// If returns true, then it continues iterating; or false to stop. -func (tx *TX) IterateAsc(prefix []byte, f func(key, value []byte) bool) { - tx.doIterate(false, prefix, f) -} - -// IteratorDesc iterates the db in descending order -// with given callback function starting from . -// If is nil it iterates from the beginning of the db. -// If returns true, then it continues iterating; or false to stop. -func (tx *TX) IterateDesc(prefix []byte, f func(key, value []byte) bool) { - tx.doIterate(true, prefix, f) -} - -func (tx *TX) doIterate(reverse bool, prefix []byte, f func(key, value []byte) bool) { - options := badger.DefaultIteratorOptions - if prefix != nil { - options.Prefix = prefix - } - options.Reverse = reverse - options.PrefetchSize = 10 - options.PrefetchValues = true - it := tx.txn.NewIterator(options) - defer it.Close() - var k, v []byte - var err error - var item *badger.Item - for it.Rewind(); it.Valid(); it.Next() { - item = it.Item() - k = item.Key() - v, err = item.ValueCopy(nil) - if err != nil { - return - } - if !f(k, v) { - return - } - } -} diff --git a/database/gkvdb/gkvdb_unit_test.go b/database/gkvdb/gkvdb_unit_test.go deleted file mode 100644 index b65d32a58..000000000 --- a/database/gkvdb/gkvdb_unit_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. -// -// This Source Code Form is subject to the terms of the MIT License. -// If a copy of the MIT was not distributed with this file, -// You can obtain one at https://github.com/gogf/gf. - -package gkvdb_test - -import ( - "strings" - "testing" - "time" - - "github.com/dgraph-io/badger/options" - - "github.com/gogf/gf/frame/g" - - "github.com/gogf/gf/container/garray" - - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/util/gconv" - - "github.com/gogf/gf/database/gkvdb" - - "github.com/gogf/gf/test/gtest" -) - -func init() { - -} - -func Test_New(t *testing.T) { - gtest.Case(t, func() { - name := gconv.String(gtime.Nanosecond()) - path := "/tmp/gkvdb/" + name - key := []byte("key") - value := []byte("value") - - db := gkvdb.Instance(name) - // https://github.com/dgraph-io/badger#memory-usage - db.Options().ValueLogLoadingMode = options.FileIO - err := db.SetPath(path) - gtest.Assert(err, nil) - - err = db.Set(key, value) - gtest.Assert(err, nil) - - gtest.Assert(db.Get(key), value) - gtest.Assert(db.Delete(key), nil) - gtest.Assert(db.Get(key), nil) - }) -} - -func Test_Set(t *testing.T) { - gtest.Case(t, func() { - name := gconv.String(gtime.Nanosecond()) - path := "/tmp/gkvdb/" + name - key := []byte("key") - value := []byte("value") - - db := gkvdb.Instance(name) - // https://github.com/dgraph-io/badger#memory-usage - db.Options().ValueLogLoadingMode = options.FileIO - err := db.SetPath(path) - gtest.Assert(err, nil) - - err = db.Set(key, value, 1000*time.Millisecond) - gtest.Assert(err, nil) - - gtest.Assert(db.Get(key), value) - time.Sleep(1500 * time.Millisecond) - gtest.Assert(db.Get(key), nil) - }) -} - -func Test_Iterate(t *testing.T) { - gtest.Case(t, func() { - name := gconv.String(gtime.Nanosecond()) - path := "/tmp/gkvdb/" + name - db := gkvdb.Instance(name) - // https://github.com/dgraph-io/badger#memory-usage - db.Options().ValueLogLoadingMode = options.FileIO - err := db.SetPath(path) - gtest.Assert(err, nil) - - strArray := garray.NewSortedStrArray() - strArrayReverse := garray.NewSortedStrArrayComparator(func(a, b string) int { - switch strings.Compare(a, b) { - case 0: - return 0 - case 1: - return -1 - case -1: - return 1 - } - return 0 - }) - for i := 1; i <= 10; i++ { - key := []byte("key_" + gconv.String(i)) - strArray.Add(string(key)) - strArrayReverse.Add(string(key)) - db.Set(key, key) - } - - array := garray.New() - db.Iterate(nil, func(key, value []byte) bool { - array.Append(string(key)) - return true - }) - gtest.Assert(array.Slice(), strArray.Slice()) - - array = garray.New() - db.IterateDesc(nil, func(key, value []byte) bool { - array.Append(string(key)) - return true - }) - gtest.Assert(array.Slice(), strArrayReverse.Slice()) - - array = garray.New() - db.Iterate([]byte("key_1"), func(key, value []byte) bool { - array.Append(key) - return true - }) - gtest.Assert(array.Slice(), g.Slice{[]byte("key_1"), []byte("key_10")}) - - array = garray.New() - db.IterateAsc([]byte("key_1"), func(key, value []byte) bool { - array.Append(key) - return true - }) - gtest.Assert(array.Slice(), g.Slice{[]byte("key_1"), []byte("key_10")}) - - }) -} diff --git a/go.mod b/go.mod index c8b12f44d..38228b5f5 100644 --- a/go.mod +++ b/go.mod @@ -3,21 +3,18 @@ module github.com/gogf/gf require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.4 - github.com/dgraph-io/badger v1.6.0 github.com/fatih/structs v1.1.0 github.com/fsnotify/fsnotify v1.4.7 github.com/gf-third/mysql v1.4.2 github.com/gofrs/flock v0.7.1 // indirect github.com/gomodule/redigo v2.0.0+incompatible - github.com/gorilla/websocket v1.4.0 + github.com/gorilla/websocket v1.4.1 github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 - github.com/kr/pretty v0.1.0 // indirect github.com/mattn/go-runewidth v0.0.4 // indirect github.com/olekukonko/tablewriter v0.0.1 github.com/theckman/go-flock v0.7.1 - golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 // indirect + golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b // indirect golang.org/x/text v0.3.2 - google.golang.org/appengine v1.6.1 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect - gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 + google.golang.org/appengine v1.6.2 // indirect + gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 ) diff --git a/go.sum b/go.sum index 5bc74472c..97b25b58c 100644 --- a/go.sum +++ b/go.sum @@ -1,22 +1,7 @@ -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9 h1:HD8gA2tkByhMAwYaFAX9w2l7vxvBQ5NMoxDrkhqhtn4= -github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/badger v1.6.0 h1:DshxFxZWXUcO0xX476VJC07Xsr6ZCBVRHKZ93Oh7Evo= -github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -25,72 +10,38 @@ github.com/gf-third/mysql v1.4.2 h1:f1M5CNFUG3WkE07UOomtu4o0n/KJKeuUUf5Nc9ZFXs4= github.com/gf-third/mysql v1.4.2/go.mod h1:+dd90V663ppI2fV5uQ6+rHk0u8KCyU6FkG8Um8Cx3ms= github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278 h1:DZo48DQFIDo/YWjUeFip1dfJztBhRuaxfUnPd+gAfcs= github.com/grokify/html-strip-tags-go v0.0.0-20190424092004-025bd760b278/go.mod h1:Xk7G0nwBiIloTMbLddk4WWJOqi4i/JLhadLd0HUXO30= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/theckman/go-flock v0.7.1 h1:YdJyIjDuQdEU7voZ9YaeXSO4OnrxdI+WejPUwyZ/Txs= github.com/theckman/go-flock v0.7.1/go.mod h1:kjuth3y9VJ2aNlkNEO99G/8lp9fMIKaGyBmh84IBheM= -github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI= +golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.2 h1:j8RI1yW0SkI+paT6uGwMlrMI/6zwYA6/CFil8rxOzGI= +google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22 h1:0efs3hwEZhFKsCoP8l6dDB1AZWMgnEl3yWXWRZTOaEA= -gopkg.in/yaml.v3 v3.0.0-20190709130402-674ba3eaed22/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966 h1:B0J02caTR6tpSJozBJyiAzT6CtBzjclw4pgm9gg8Ys0= +gopkg.in/yaml.v3 v3.0.0-20190905181640-827449938966/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index 1158a9934..627411bbb 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -12,6 +12,8 @@ import ( "net/http" "strings" + "github.com/gogf/gf/os/gsession" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/os/gtime" @@ -24,7 +26,7 @@ type Request struct { Id int // 请求ID(当前Server对象唯一) Server *Server // 请求关联的服务器对象 Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) - Session *Session // 与当前请求绑定的Session对象(并发安全) + Session *gsession.Session // 与当前请求绑定的Session对象(并发安全) Response *Response // 对应请求的返回数据操作对象 Router *Router // 匹配到的路由对象 EnterTime int64 // 请求进入时间(微秒) @@ -58,7 +60,7 @@ func newRequest(s *Server, r *http.Request, w http.ResponseWriter) *Request { } // 会话处理 request.Cookie = GetCookie(request) - request.Session = GetSession(request) + request.Session = s.sessionManager.New(request.GetSessionId()) request.Response.request = request request.Middleware = &Middleware{ request: request, @@ -239,7 +241,7 @@ func (r *Request) GetSessionId() string { // 生成随机的SESSIONID func (r *Request) MakeSessionId() string { - id := makeSessionId() + id := gsession.NewSessionId() r.Cookie.SetSessionId(id) r.Response.Header().Set(r.Server.GetSessionIdName(), id) return id diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index e252784d0..f2e6dbf1e 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -17,9 +17,7 @@ import ( "strings" "time" - "github.com/dgraph-io/badger/options" - - "github.com/gogf/gf/database/gkvdb" + "github.com/gogf/gf/os/gsession" "github.com/gogf/gf/container/garray" "github.com/gogf/gf/container/gmap" @@ -49,8 +47,7 @@ type ( serveCache *gcache.Cache // 服务注册路由内存缓存 routesMap map[string][]registeredRouteItem // 已经注册的路由及对应的注册方法文件地址(用以路由重复注册判断) statusHandlerMap map[string]HandlerFunc // 不同状态码下的注册处理方法(例如404状态时的处理方法) - sessions *gcache.Cache // Session内存存储 - sessionStorage *gkvdb.DB // Session物理存储 + sessionManager *gsession.Manager // Session管理器 logger *glog.Logger // 日志管理对象 } @@ -215,11 +212,9 @@ func GetServer(name ...interface{}) *Server { if s := serverMapping.Get(serverName); s != nil { return s.(*Server) } - storagePath := defaultServerConfig.SessionStoragePath + gfile.Separator + serverName - sessionStorage := gkvdb.Instance(storagePath) - sessionStorage.SetOptions(gkvdb.DefaultOptions(storagePath)) - if genv.Contains("UNDER_TEST") { - sessionStorage.Options().ValueLogLoadingMode = options.FileIO + config := defaultServerConfig + if config.SessionStorage == nil { + config.SessionStorage = gsession.NewStorageFile(config.SessionMaxAge) } s := &Server{ name: serverName, @@ -230,13 +225,12 @@ func GetServer(name ...interface{}) *Server { serveTree: make(map[string]interface{}), serveCache: gcache.New(), routesMap: make(map[string][]registeredRouteItem), - sessions: gcache.New(), - sessionStorage: sessionStorage, + sessionManager: gsession.New(config.SessionMaxAge, config.SessionStorage), servedCount: gtype.NewInt(), logger: glog.New(), } // 初始化时使用默认配置 - s.SetConfig(defaultServerConfig) + s.SetConfig(config) // 记录到全局ServerMap中 serverMapping.Set(serverName, s) return s diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 946af2148..e8595c193 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -13,6 +13,8 @@ import ( "strconv" "time" + "github.com/gogf/gf/os/gsession" + "github.com/gogf/gf/os/gview" "github.com/gogf/gf/os/gfile" @@ -20,16 +22,12 @@ import ( ) const ( - gDEFAULT_HTTP_ADDR = ":80" // 默认HTTP监听地址 - gDEFAULT_HTTPS_ADDR = ":443" // 默认HTTPS监听地址 - NAME_TO_URI_TYPE_DEFAULT = 0 // 服务注册时对象和方法名称转换为URI时,全部转为小写,单词以'-'连接符号连接 - NAME_TO_URI_TYPE_FULLNAME = 1 // 不处理名称,以原有名称构建成URI - NAME_TO_URI_TYPE_ALLLOWER = 2 // 仅转为小写,单词间不使用连接符号 - NAME_TO_URI_TYPE_CAMEL = 3 // 采用驼峰命名方式 - gDEFAULT_COOKIE_PATH = "/" // 默认path - gDEFAULT_COOKIE_MAX_AGE = 86400 * 365 // 默认cookie有效期(一年) - gDEFAULT_SESSION_MAX_AGE = 86400 // 默认session有效期(一天) - gDEFAULT_SESSION_ID_NAME = "gfsessionid" // 默认存放Cookie中的SessionId名称 + gDEFAULT_HTTP_ADDR = ":80" // 默认HTTP监听地址 + gDEFAULT_HTTPS_ADDR = ":443" // 默认HTTPS监听地址 + NAME_TO_URI_TYPE_DEFAULT = 0 // 服务注册时对象和方法名称转换为URI时,全部转为小写,单词以'-'连接符号连接 + NAME_TO_URI_TYPE_FULLNAME = 1 // 不处理名称,以原有名称构建成URI + NAME_TO_URI_TYPE_ALLLOWER = 2 // 仅转为小写,单词间不使用连接符号 + NAME_TO_URI_TYPE_CAMEL = 3 // 采用驼峰命名方式 gCHANGE_CONFIG_WHILE_RUNNING_ERROR = "server's configuration cannot be changed while running" ) @@ -38,74 +36,73 @@ type LogHandler func(r *Request, error ...interface{}) // HTTP Server 设置结构体,静态配置 type ServerConfig struct { - Addr string // 监听IP和端口,监听本地所有IP使用":端口"(支持多个地址,使用","号分隔) - HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔) - HTTPSCertPath string // HTTPS证书文件路径 - HTTPSKeyPath string // HTTPS签名文件路径 - Handler http.Handler // 默认的处理函数 - ReadTimeout time.Duration // 读取超时 - WriteTimeout time.Duration // 写入超时 - IdleTimeout time.Duration // 等待超时 - MaxHeaderBytes int // 最大的header长度 - TLSConfig tls.Config // HTTPS证书配置 - KeepAlive bool // 是否开启长连接 - ServerAgent string // Server Agent - View *gview.View // 模板引擎对象 - Rewrites map[string]string // URI Rewrite重写配置 - IndexFiles []string // Static: 默认访问的文件列表 - IndexFolder bool // Static: 如果访问目录是否显示目录列表 - ServerRoot string // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低) - SearchPaths []string // Static: 静态文件搜索目录(包含ServerRoot,按照优先级进行排序) - StaticPaths []staticPathItem // Static: 静态文件目录映射(按照优先级进行排序) - FileServerEnabled bool // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别) - CookieMaxAge int64 // Cookie: 有效期(秒) - CookiePath string // Cookie: 有效Path(注意同时也会影响SessionID) - CookieDomain string // Cookie: 有效Domain(注意同时也会影响SessionID) - SessionMaxAge int64 // Session: 有效期(秒) - SessionIdName string // Session: SessionId - SessionStoragePath string // Session: 存储路径 - DenyIps []string // Security: 不允许访问的ip列表,支持ip前缀过滤,如: 10 将不允许10开头的ip访问 - AllowIps []string // Security: 仅允许访问的ip列表,支持ip前缀过滤,如: 10 将仅允许10开头的ip访问 - DenyRoutes []string // Security: 不允许访问的路由规则列表 - LogPath string // Logging: 存放日志的目录路径(默认为空,表示不写文件) - LogHandler LogHandler // Logging: 日志配置: 自定义日志处理回调方法(默认为空) - LogStdout bool // Logging: 是否打印日志到终端(默认开启) - ErrorLogEnabled bool // Logging: 是否开启error log(默认开启) - AccessLogEnabled bool // Logging: 是否开启access log(默认关闭) - NameToUriType int // Mess: 服务注册时对象和方法名称转换为URI时的规则 - GzipContentTypes []string // Mess: 允许进行gzip压缩的文件类型 - DumpRouteMap bool // Mess: 是否在程序启动时默认打印路由表信息 - RouterCacheExpire int // Mess: 路由检索缓存过期时间(秒) + Addr string // 监听IP和端口,监听本地所有IP使用":端口"(支持多个地址,使用","号分隔) + HTTPSAddr string // HTTPS服务监听地址(支持多个地址,使用","号分隔) + HTTPSCertPath string // HTTPS证书文件路径 + HTTPSKeyPath string // HTTPS签名文件路径 + Handler http.Handler // 默认的处理函数 + ReadTimeout time.Duration // 读取超时 + WriteTimeout time.Duration // 写入超时 + IdleTimeout time.Duration // 等待超时 + MaxHeaderBytes int // 最大的header长度 + TLSConfig tls.Config // HTTPS证书配置 + KeepAlive bool // 是否开启长连接 + ServerAgent string // Server Agent + View *gview.View // 模板引擎对象 + Rewrites map[string]string // URI Rewrite重写配置 + IndexFiles []string // Static: 默认访问的文件列表 + IndexFolder bool // Static: 如果访问目录是否显示目录列表 + ServerRoot string // Static: 服务器服务的本地目录根路径(检索优先级比StaticPaths低) + SearchPaths []string // Static: 静态文件搜索目录(包含ServerRoot,按照优先级进行排序) + StaticPaths []staticPathItem // Static: 静态文件目录映射(按照优先级进行排序) + FileServerEnabled bool // Static: 是否允许静态文件服务(通过静态文件服务方法调用自动识别) + CookieMaxAge time.Duration // Cookie: 有效期 + CookiePath string // Cookie: 有效Path(注意同时也会影响SessionID) + CookieDomain string // Cookie: 有效Domain(注意同时也会影响SessionID) + SessionMaxAge time.Duration // Session: 有效期 + SessionIdName string // Session: SessionId + SessionStorage gsession.Storage // Session: 存储路径 + DenyIps []string // Security: 不允许访问的ip列表,支持ip前缀过滤,如: 10 将不允许10开头的ip访问 + AllowIps []string // Security: 仅允许访问的ip列表,支持ip前缀过滤,如: 10 将仅允许10开头的ip访问 + DenyRoutes []string // Security: 不允许访问的路由规则列表 + LogPath string // Logging: 存放日志的目录路径(默认为空,表示不写文件) + LogHandler LogHandler // Logging: 日志配置: 自定义日志处理回调方法(默认为空) + LogStdout bool // Logging: 是否打印日志到终端(默认开启) + ErrorLogEnabled bool // Logging: 是否开启error log(默认开启) + AccessLogEnabled bool // Logging: 是否开启access log(默认关闭) + NameToUriType int // Mess: 服务注册时对象和方法名称转换为URI时的规则 + GzipContentTypes []string // Mess: 允许进行gzip压缩的文件类型 + DumpRouteMap bool // Mess: 是否在程序启动时默认打印路由表信息 + RouterCacheExpire int // Mess: 路由检索缓存过期时间(秒) } // 默认HTTP Server配置 var defaultServerConfig = ServerConfig{ - Addr: "", - HTTPSAddr: "", - Handler: nil, - ReadTimeout: 60 * time.Second, - WriteTimeout: 60 * time.Second, - IdleTimeout: 60 * time.Second, - MaxHeaderBytes: 1024, - KeepAlive: true, - IndexFiles: []string{"index.html", "index.htm"}, - IndexFolder: false, - ServerAgent: "gf http server", - ServerRoot: "", - StaticPaths: make([]staticPathItem, 0), - FileServerEnabled: false, - CookieMaxAge: gDEFAULT_COOKIE_MAX_AGE, - CookiePath: gDEFAULT_COOKIE_PATH, - CookieDomain: "", - SessionMaxAge: gDEFAULT_SESSION_MAX_AGE, - SessionIdName: gDEFAULT_SESSION_ID_NAME, - SessionStoragePath: gfile.TempDir() + gfile.Separator + "gfsessions", - LogStdout: true, - ErrorLogEnabled: true, - AccessLogEnabled: false, - DumpRouteMap: true, - RouterCacheExpire: 60, - Rewrites: make(map[string]string), + Addr: "", + HTTPSAddr: "", + Handler: nil, + ReadTimeout: 60 * time.Second, + WriteTimeout: 60 * time.Second, + IdleTimeout: 60 * time.Second, + MaxHeaderBytes: 1024, + KeepAlive: true, + IndexFiles: []string{"index.html", "index.htm"}, + IndexFolder: false, + ServerAgent: "gf http server", + ServerRoot: "", + StaticPaths: make([]staticPathItem, 0), + FileServerEnabled: false, + CookieMaxAge: time.Hour * 24 * 365, + CookiePath: "/", + CookieDomain: "", + SessionMaxAge: time.Hour * 24, + SessionIdName: "gfsessionid", + LogStdout: true, + ErrorLogEnabled: true, + AccessLogEnabled: false, + DumpRouteMap: true, + RouterCacheExpire: 60, + Rewrites: make(map[string]string), } // 获取默认的http server设置 diff --git a/net/ghttp/ghttp_server_config_cookie.go b/net/ghttp/ghttp_server_config_cookie.go index 5c49962b7..f629f906a 100644 --- a/net/ghttp/ghttp_server_config_cookie.go +++ b/net/ghttp/ghttp_server_config_cookie.go @@ -7,16 +7,18 @@ package ghttp import ( + "time" + "github.com/gogf/gf/os/glog" ) // 设置http server参数 - CookieMaxAge -func (s *Server) SetCookieMaxAge(age int64) { +func (s *Server) SetCookieMaxAge(ttl time.Duration) { if s.Status() == SERVER_STATUS_RUNNING { glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR) return } - s.config.CookieMaxAge = age + s.config.CookieMaxAge = ttl } // 设置http server参数 - CookiePath @@ -38,7 +40,7 @@ func (s *Server) SetCookieDomain(domain string) { } // 获取http server参数 - CookieMaxAge -func (s *Server) GetCookieMaxAge() int64 { +func (s *Server) GetCookieMaxAge() time.Duration { return s.config.CookieMaxAge } diff --git a/net/ghttp/ghttp_server_config_session.go b/net/ghttp/ghttp_server_config_session.go index 823381392..a1c83c280 100644 --- a/net/ghttp/ghttp_server_config_session.go +++ b/net/ghttp/ghttp_server_config_session.go @@ -7,19 +7,20 @@ package ghttp import ( - "fmt" + "time" + + "github.com/gogf/gf/os/gsession" - "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/os/glog" ) // 设置http server参数 - SessionMaxAge -func (s *Server) SetSessionMaxAge(age int64) { +func (s *Server) SetSessionMaxAge(ttl time.Duration) { if s.Status() == SERVER_STATUS_RUNNING { glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR) return } - s.config.SessionMaxAge = age + s.config.SessionMaxAge = ttl } // 设置http server参数 - SessionIdName @@ -31,24 +32,17 @@ func (s *Server) SetSessionIdName(name string) { s.config.SessionIdName = name } -// 设置http server参数 - SessionStoragePath -func (s *Server) SetSessionStoragePath(path string) { +// 设置http server参数 - SessionStorage +func (s *Server) SetSessionStorage(storage gsession.Storage) { if s.Status() == SERVER_STATUS_RUNNING { glog.Error(gCHANGE_CONFIG_WHILE_RUNNING_ERROR) return } - realPath, _ := gfile.Search(path) - if realPath != "" { - glog.Fatal(fmt.Sprintf(`[ghttp] SetSessionStoragePath failed: '%s' does not exist`, path)) - } - s.config.SessionStoragePath = realPath - if err := s.sessionStorage.SetPath(realPath); err != nil { - glog.Fatal(fmt.Sprintf(`[ghttp] SetSessionStoragePath failed: %s`, err.Error())) - } + s.config.SessionStorage = storage } // 获取http server参数 - SessionMaxAge -func (s *Server) GetSessionMaxAge() int64 { +func (s *Server) GetSessionMaxAge() time.Duration { return s.config.SessionMaxAge } @@ -56,8 +50,3 @@ func (s *Server) GetSessionMaxAge() int64 { func (s *Server) GetSessionIdName() string { return s.config.SessionIdName } - -// 获取http server参数 - SessionStoragePath -func (s *Server) GetSessionStoragePath() string { - return s.config.SessionStoragePath -} diff --git a/net/ghttp/ghttp_server_cookie.go b/net/ghttp/ghttp_server_cookie.go index 4a9aff47c..4783acb49 100644 --- a/net/ghttp/ghttp_server_cookie.go +++ b/net/ghttp/ghttp_server_cookie.go @@ -21,7 +21,7 @@ type Cookie struct { data map[string]CookieItem // 数据项 path string // 默认的cookie path domain string // 默认的cookie domain - maxage int64 // 默认的cookie maxage + maxage time.Duration // 默认的cookie maxage server *Server // 所属Server request *Request // 所属HTTP请求对象 response *Response // 所属HTTP返回对象 @@ -32,7 +32,7 @@ type CookieItem struct { value string domain string // 有效域名 path string // 有效路径 - expire int64 // 过期时间 + expireAt int64 // 过期时间 httpOnly bool } @@ -81,7 +81,7 @@ func (c *Cookie) Map() map[string]string { func (c *Cookie) Contains(key string) bool { c.init() if r, ok := c.data[key]; ok { - if r.expire >= 0 { + if r.expireAt >= 0 { return true } } @@ -94,14 +94,14 @@ func (c *Cookie) Set(key, value string) { } // 设置cookie,带详细cookie参数 -func (c *Cookie) SetCookie(key, value, domain, path string, maxAge int64, httpOnly ...bool) { +func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, httpOnly ...bool) { c.init() isHttpOnly := false if len(httpOnly) > 0 { isHttpOnly = httpOnly[0] } c.data[key] = CookieItem{ - value, domain, path, gtime.Second() + maxAge, isHttpOnly, + value, domain, path, gtime.Second() + int64(maxAge.Seconds()), isHttpOnly, } } @@ -110,7 +110,7 @@ func (c *Cookie) GetSessionId() string { return c.Get(c.server.GetSessionIdName()) } -// 设置SessionId +// 设置SessionId到Cookie中 func (c *Cookie) SetSessionId(id string) { c.Set(c.server.GetSessionIdName(), id) } @@ -119,7 +119,7 @@ func (c *Cookie) SetSessionId(id string) { func (c *Cookie) Get(key string, def ...string) string { c.init() if r, ok := c.data[key]; ok { - if r.expire >= 0 { + if r.expireAt >= 0 { return r.value } } @@ -147,7 +147,7 @@ func (c *Cookie) Output() { } for k, v := range c.data { // 只有 expire != 0 的才是服务端在本次请求中设置的cookie - if v.expire == 0 { + if v.expireAt == 0 { continue } http.SetCookie( @@ -157,7 +157,7 @@ func (c *Cookie) Output() { Value: v.value, Domain: v.domain, Path: v.path, - Expires: time.Unix(int64(v.expire), 0), + Expires: time.Unix(v.expireAt, 0), HttpOnly: v.httpOnly, }, ) diff --git a/net/ghttp/ghttp_server_handler.go b/net/ghttp/ghttp_server_handler.go index ebf2fbd6f..8fd6f829a 100644 --- a/net/ghttp/ghttp_server_handler.go +++ b/net/ghttp/ghttp_server_handler.go @@ -139,6 +139,10 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { if !request.IsExited() { s.callHookHandler(HOOK_BEFORE_OUTPUT, request) } + // 设置Session Id到Cookie中 + if request.Session.Id() != "" && request.GetSessionId() != request.Session.Id() { + request.Cookie.SetSessionId(request.Session.Id()) + } // 输出Cookie request.Cookie.Output() // 输出缓冲区 @@ -148,7 +152,7 @@ func (s *Server) handleRequest(w http.ResponseWriter, r *http.Request) { s.callHookHandler(HOOK_AFTER_OUTPUT, request) } // 更新Session会话超时时间 - request.Session.UpdateExpire() + request.Session.UpdateTTL() } // 查找静态文件的绝对路径 diff --git a/net/ghttp/ghttp_server_session.go b/net/ghttp/ghttp_server_session.go index ceb3ef29e..3d7ec737a 100644 --- a/net/ghttp/ghttp_server_session.go +++ b/net/ghttp/ghttp_server_session.go @@ -6,329 +6,6 @@ package ghttp -import ( - "encoding/json" - "strconv" - "strings" - "time" +import "github.com/gogf/gf/os/gsession" - "github.com/gogf/gf/container/gmap" - "github.com/gogf/gf/container/gvar" - "github.com/gogf/gf/os/gtime" - "github.com/gogf/gf/util/gconv" - "github.com/gogf/gf/util/grand" -) - -// SESSION对象,并发安全 -type Session struct { - id string // SessionId - data *gmap.StrAnyMap // Session数据 - dirty bool // 数据是否被修改 - server *Server // 所属Server - request *Request // 关联的请求 -} - -// 生成一个唯一的SessionId字符串,长度18位。 -func makeSessionId() string { - return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.Str(6)) -} - -// 获取或者生成一个session对象(延迟初始化) -func GetSession(r *Request) *Session { - if r.Session != nil { - return r.Session - } - return &Session{ - request: r, - } -} - -// UpdateSession updates the session with custom map. -func (s *Server) UpdateSession(id string, data map[string]interface{}) { - v := s.sessions.GetOrSetFuncLock(id, func() interface{} { - return gmap.NewStrAnyMap(true) - }, s.GetSessionMaxAge()*1000) - v.(*gmap.StrAnyMap).Sets(data) -} - -// 延迟初始化 -func (s *Session) init() { - if len(s.id) == 0 { - s.server = s.request.Server - if id := s.request.GetSessionId(); id != "" { - if v := s.server.sessions.Get(id); v != nil { - // 纯内存查询 - s.id = id - s.data = v.(*gmap.StrAnyMap) - return - } else { - // 持久化恢复 - data := s.server.sessionStorage.Get([]byte(id)) - if data != nil { - s.id = id - s.data = gmap.NewStrAnyMap(true) - if err := s.Restore(data); err != nil { - panic(err) - } - return - } - } - } - // 否则执行初始化创建 - s.id = s.request.MakeSessionId() - s.data = gmap.NewStrAnyMap(true) - s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) - s.dirty = true - } -} - -// 获取/创建SessionId -func (s *Session) Id() string { - s.init() - return s.id -} - -// 获取当前session所有数据,注意是值拷贝 -func (s *Session) Map() map[string]interface{} { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - return s.data.Map() - } - return nil -} - -// 获得session map大小 -func (s *Session) Size() int { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - return s.data.Size() - } - return 0 -} - -// 设置session -func (s *Session) Set(key string, value interface{}) { - s.init() - s.data.Set(key, value) - s.dirty = true -} - -// 批量设置 -func (s *Session) Sets(m map[string]interface{}) { - s.init() - s.data.Sets(m) - s.dirty = true -} - -// 判断键名是否存在 -func (s *Session) Contains(key string) bool { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - return s.data.Contains(key) - } - return false -} - -// 判断session是否有修改(包括新创建) -func (s *Session) IsDirty() bool { - return s.dirty -} - -// 删除指定session键值对 -func (s *Session) Remove(key string) { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - s.data.Remove(key) - s.dirty = true - } -} - -// 将session数据导出为[]byte数据(目前使用json进行序列化) -func (s *Session) Export() (data []byte, err error) { - if s.Size() > 0 { - data, err = json.Marshal(s.data) - } - return -} - -// 从[]byte中恢复session数据(目前使用json进行序列化) -func (s *Session) Restore(data []byte) (err error) { - if len(data) == 0 { - return nil - } - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - s.data.LockFunc(func(m map[string]interface{}) { - err = json.Unmarshal(data, &m) - }) - } - return -} - -// 清空session -func (s *Session) Clear() { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - s.data.Clear() - s.dirty = true - } -} - -// 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) -func (s *Session) UpdateExpire() { - if len(s.id) > 0 && s.data.Size() > 0 { - // 优先持久化存储 - if s.dirty { - data, _ := s.Export() - err := s.server.sessionStorage.Set( - []byte(s.id), - data, - time.Duration(s.server.GetSessionMaxAge())*time.Second, - ) - if err != nil { - panic("saving session failed:" + err.Error()) - } - } - // 其次更新内存TTL - s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) - } -} - -// 获取SESSION变量 -func (s *Session) Get(key string, def ...interface{}) interface{} { - if len(s.id) > 0 || s.request.GetSessionId() != "" { - s.init() - if v := s.data.Get(key); v != nil { - return v - } - } - if len(def) > 0 { - return def[0] - } - return nil -} - -// 获取SESSION,建议都用该方法获取参数 -func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var { - return gvar.New(s.Get(key, def...), true) -} - -func (s *Session) GetString(key string, def ...interface{}) string { - return gconv.String(s.Get(key, def...)) -} - -func (s *Session) GetBool(key string, def ...interface{}) bool { - return gconv.Bool(s.Get(key, def...)) -} - -func (s *Session) GetInt(key string, def ...interface{}) int { - return gconv.Int(s.Get(key, def...)) -} - -func (s *Session) GetInt8(key string, def ...interface{}) int8 { - return gconv.Int8(s.Get(key, def...)) -} - -func (s *Session) GetInt16(key string, def ...interface{}) int16 { - return gconv.Int16(s.Get(key, def...)) -} - -func (s *Session) GetInt32(key string, def ...interface{}) int32 { - return gconv.Int32(s.Get(key, def...)) -} - -func (s *Session) GetInt64(key string, def ...interface{}) int64 { - return gconv.Int64(s.Get(key, def...)) -} - -func (s *Session) GetUint(key string, def ...interface{}) uint { - return gconv.Uint(s.Get(key, def...)) -} - -func (s *Session) GetUint8(key string, def ...interface{}) uint8 { - return gconv.Uint8(s.Get(key, def...)) -} - -func (s *Session) GetUint16(key string, def ...interface{}) uint16 { - return gconv.Uint16(s.Get(key, def...)) -} - -func (s *Session) GetUint32(key string, def ...interface{}) uint32 { - return gconv.Uint32(s.Get(key, def...)) -} - -func (s *Session) GetUint64(key string, def ...interface{}) uint64 { - return gconv.Uint64(s.Get(key, def...)) -} - -func (s *Session) GetFloat32(key string, def ...interface{}) float32 { - return gconv.Float32(s.Get(key, def...)) -} - -func (s *Session) GetFloat64(key string, def ...interface{}) float64 { - return gconv.Float64(s.Get(key, def...)) -} - -func (s *Session) GetBytes(key string, def ...interface{}) []byte { - return gconv.Bytes(s.Get(key, def...)) -} - -func (s *Session) GetInts(key string, def ...interface{}) []int { - return gconv.Ints(s.Get(key, def...)) -} - -func (s *Session) GetFloats(key string, def ...interface{}) []float64 { - return gconv.Floats(s.Get(key, def...)) -} - -func (s *Session) GetStrings(key string, def ...interface{}) []string { - return gconv.Strings(s.Get(key, def...)) -} - -func (s *Session) GetInterfaces(key string, def ...interface{}) []interface{} { - return gconv.Interfaces(s.Get(key, def...)) -} - -func (s *Session) GetTime(key string, format ...string) time.Time { - return gconv.Time(s.Get(key), format...) -} - -func (s *Session) GetGTime(key string, format ...string) *gtime.Time { - return gconv.GTime(s.Get(key), format...) -} - -func (s *Session) GetDuration(key string, def ...interface{}) time.Duration { - return gconv.Duration(s.Get(key, def...)) -} - -func (s *Session) GetMap(key string, tags ...string) map[string]interface{} { - return gconv.Map(s.Get(key), tags...) -} - -func (s *Session) GetMapDeep(key string, tags ...string) map[string]interface{} { - return gconv.MapDeep(s.Get(key), tags...) -} - -func (s *Session) GetMaps(key string, tags ...string) []map[string]interface{} { - return gconv.Maps(s.Get(key), tags...) -} - -func (s *Session) GetMapsDeep(key string, tags ...string) []map[string]interface{} { - return gconv.MapsDeep(s.Get(key), tags...) -} - -func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.Struct(s.Get(key), pointer, mapping...) -} - -func (s *Session) GetStructDeep(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructDeep(s.Get(key), pointer, mapping...) -} - -func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.Structs(s.Get(key), pointer, mapping...) -} - -func (s *Session) GetStructsDeep(key string, pointer interface{}, mapping ...map[string]string) error { - return gconv.StructsDeep(s.Get(key), pointer, mapping...) -} +type Session = gsession.Session diff --git a/os/gfile/gfile.go b/os/gfile/gfile.go index c32e297bb..8942797e4 100644 --- a/os/gfile/gfile.go +++ b/os/gfile/gfile.go @@ -26,11 +26,13 @@ import ( ) const ( - Separator = string(filepath.Separator) // Separator for file system. - gDEFAULT_PERM = 0666 // Default perm for file opening. + // Separator for file system. + Separator = string(filepath.Separator) ) var ( + // Default perm for file opening. + DefaultPerm = os.FileMode(0666) // The absolute file path for main package. // It can be only checked and set once. mainPkgPath = gtype.NewString() @@ -68,7 +70,7 @@ func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { // OpenWithFlag opens file/directory with default perm and given . func OpenWithFlag(path string, flag int) (*os.File, error) { - f, err := os.OpenFile(path, flag, gDEFAULT_PERM) + f, err := os.OpenFile(path, flag, DefaultPerm) if err != nil { return nil, err } @@ -76,7 +78,7 @@ func OpenWithFlag(path string, flag int) (*os.File, error) { } // OpenWithFlagPerm opens file/directory with given and . -func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) { +func OpenWithFlagPerm(path string, flag int, perm os.FileMode) (*os.File, error) { f, err := os.OpenFile(path, flag, os.FileMode(perm)) if err != nil { return nil, err @@ -295,7 +297,7 @@ func Remove(path string) error { // IsReadable checks whether given is readable. func IsReadable(path string) bool { result := true - file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM) + file, err := os.OpenFile(path, os.O_RDONLY, DefaultPerm) if err != nil { result = false } @@ -319,7 +321,7 @@ func IsWritable(path string) bool { } } else { // 如果是文件,那么判断文件是否可打开 - file, err := os.OpenFile(path, os.O_WRONLY, gDEFAULT_PERM) + file, err := os.OpenFile(path, os.O_WRONLY, DefaultPerm) if err != nil { result = false } diff --git a/os/gfile/gfile_contents.go b/os/gfile/gfile_contents.go index 96bdd7d66..4823e0f8b 100644 --- a/os/gfile/gfile_contents.go +++ b/os/gfile/gfile_contents.go @@ -12,9 +12,9 @@ import ( "os" ) -const ( +var ( // Buffer size for reading file content. - gREAD_BUFFER = 1024 + DefaultReadBuffer = 1024 ) // GetContents returns the file content of as string. @@ -34,7 +34,7 @@ func GetBytes(path string) []byte { } // putContents puts binary content to file of . -func putContents(path string, data []byte, flag int, perm int) error { +func putContents(path string, data []byte, flag int, perm os.FileMode) error { // It supports creating file of recursively. dir := Dir(path) if !Exists(dir) { @@ -64,30 +64,30 @@ func Truncate(path string, size int) error { // PutContents puts string to file of . // It creates file of recursively if it does not exist. func PutContents(path string, content string) error { - return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) + return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, DefaultPerm) } // PutContentsAppend appends string to file of . // It creates file of recursively if it does not exist. func PutContentsAppend(path string, content string) error { - return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) + return putContents(path, []byte(content), os.O_WRONLY|os.O_CREATE|os.O_APPEND, DefaultPerm) } // PutBytes puts binary to file of . // It creates file of recursively if it does not exist. func PutBytes(path string, content []byte) error { - return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, gDEFAULT_PERM) + return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, DefaultPerm) } // PutBytesAppend appends binary to file of . // It creates file of recursively if it does not exist. func PutBytesAppend(path string, content []byte) error { - return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_APPEND, gDEFAULT_PERM) + return putContents(path, content, os.O_WRONLY|os.O_CREATE|os.O_APPEND, DefaultPerm) } // GetNextCharOffset returns the file offset for given starting from . func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { - buffer := make([]byte, gREAD_BUFFER) + buffer := make([]byte, DefaultReadBuffer) offset := start for { if n, err := reader.ReadAt(buffer, offset); n > 0 { @@ -107,7 +107,7 @@ func GetNextCharOffset(reader io.ReaderAt, char byte, start int64) int64 { // GetNextCharOffsetByPath returns the file offset for given starting from . // It opens file of for reading with os.O_RDONLY flag and default perm. func GetNextCharOffsetByPath(path string, char byte, start int64) int64 { - if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { + if f, err := OpenWithFlagPerm(path, os.O_RDONLY, DefaultPerm); err == nil { defer f.Close() return GetNextCharOffset(f, char, start) } @@ -131,7 +131,7 @@ func GetBytesTilChar(reader io.ReaderAt, char byte, start int64) ([]byte, int64) // // Note: Returned value contains the character of the last position. func GetBytesTilCharByPath(path string, char byte, start int64) ([]byte, int64) { - if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { + if f, err := OpenWithFlagPerm(path, os.O_RDONLY, DefaultPerm); err == nil { defer f.Close() return GetBytesTilChar(f, char, start) } @@ -154,7 +154,7 @@ func GetBytesByTwoOffsets(reader io.ReaderAt, start int64, end int64) []byte { // it returns content range as [start, end). // It opens file of for reading with os.O_RDONLY flag and default perm. func GetBytesByTwoOffsetsByPath(path string, start int64, end int64) []byte { - if f, err := OpenWithFlagPerm(path, os.O_RDONLY, gDEFAULT_PERM); err == nil { + if f, err := OpenWithFlagPerm(path, os.O_RDONLY, DefaultPerm); err == nil { defer f.Close() return GetBytesByTwoOffsets(f, start, end) } diff --git a/os/gfile/gfile_z_contents_test.go b/os/gfile/gfile_z_contents_test.go index 93a4f28e1..3f1ad0d99 100644 --- a/os/gfile/gfile_z_contents_test.go +++ b/os/gfile/gfile_z_contents_test.go @@ -13,6 +13,8 @@ import ( "strings" "testing" + "github.com/gogf/gf/text/gstr" + "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/test/gtest" ) @@ -54,7 +56,7 @@ func formatpath(paths string) string { // 指定返回要测试的目录 func testpath() string { - return os.TempDir() + return gstr.TrimRight(os.TempDir(), "\\/") } func Test_GetContents(t *testing.T) { diff --git a/os/gsession/gsession.go b/os/gsession/gsession.go new file mode 100644 index 000000000..f2735feaa --- /dev/null +++ b/os/gsession/gsession.go @@ -0,0 +1,21 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package gsession implements manager and storage features for sessions. +package gsession + +import ( + "strconv" + "strings" + + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/grand" +) + +// NewSessionId creates and returns a new and unique session id string. +func NewSessionId() string { + return strings.ToUpper(strconv.FormatInt(gtime.Nanosecond(), 36) + grand.Str(6)) +} diff --git a/os/gsession/gsession_manager.go b/os/gsession/gsession_manager.go new file mode 100644 index 000000000..dc2490e52 --- /dev/null +++ b/os/gsession/gsession_manager.go @@ -0,0 +1,65 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gsession + +import ( + "time" + + "github.com/gogf/gf/os/gcache" +) + +// Manager for sessions. +type Manager struct { + ttl time.Duration // TTL for sessions. + storage Storage // Storage interface for session storage Set/Get. + sessions *gcache.Cache // Session cache for session TTL. +} + +// New creates and returns a new session manager. +func New(ttl time.Duration, storage ...Storage) *Manager { + m := &Manager{ + ttl: ttl, + storage: NewStorageFile(ttl), + sessions: gcache.New(), + } + if len(storage) > 0 && storage[0] != nil { + m.storage = storage[0] + } + return m +} + +// New creates or fetches the session for given session id. +func (m *Manager) New(sessionId ...string) *Session { + var id string + if len(sessionId) > 0 && sessionId[0] != "" { + id = sessionId[0] + } + // NOTE: + // We CANNOT creates and stores it directory to manager + // as it might be a fake and invalid session id + // which would consumes your memory as much as possible. + return &Session{ + id: id, + manager: m, + } +} + +// SetStorage sets the session storage for manager. +func (m *Manager) SetStorage(storage Storage) { + m.storage = storage +} + +// TTL returns the TTL of the session manager. +func (m *Manager) TTL() time.Duration { + return m.ttl +} + +// UpdateTTL updates the ttl for given session. +// If this session is dirty, it also exports it to storage. +func (m *Manager) UpdateTTL(id string, session *Session) { + m.sessions.Set(id, session, m.ttl) +} diff --git a/os/gsession/gsession_session.go b/os/gsession/gsession_session.go new file mode 100644 index 000000000..5a753ce7f --- /dev/null +++ b/os/gsession/gsession_session.go @@ -0,0 +1,285 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gsession + +import ( + "encoding/json" + "time" + + "github.com/gogf/gf/container/gtype" + + "github.com/gogf/gf/container/gmap" + "github.com/gogf/gf/container/gvar" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/gconv" +) + +// Session struct for storing single session data. +type Session struct { + id string // Session id. + data *gmap.StrAnyMap // Session data. + dirty *gtype.Bool // Used to mark session is modified. + manager *Manager // Parent manager. +} + +// init does the delay initialization for session. +// It here to initialization real session if necessary. +func (s *Session) init() { + s.dirty = gtype.NewBool(false) + if len(s.id) > 0 && s.data == nil { + if data := s.manager.storage.Get(s.id); data != nil { + if s.data = gmap.NewStrAnyMapFrom(data, true); s.data == nil { + panic("session restoring failed for id:" + s.id) + } + return + } else { + // Invalid or expired session id, + // it should create a new one. + s.id = "" + } + } + if len(s.id) == 0 { + s.id = NewSessionId() + s.data = gmap.NewStrAnyMap(true) + } +} + +// Id returns the session id for this session. +// It might be empty if session is not actually used. +func (s *Session) Id() string { + return s.id +} + +// Map returns all data as map. +// Note that it's using value copy internally for concurrent-safe purpose. +func (s *Session) Map() map[string]interface{} { + if len(s.id) > 0 { + s.init() + return s.data.Map() + } + return nil +} + +// Size returns the size of the session. +func (s *Session) Size() int { + if len(s.id) > 0 { + s.init() + return s.data.Size() + } + return 0 +} + +// Set sets key-value pair to this session. +func (s *Session) Set(key string, value interface{}) { + s.init() + s.data.Set(key, value) + s.dirty.Set(true) +} + +// Sets batch sets the session using map. +func (s *Session) Sets(m map[string]interface{}) { + s.init() + s.data.Sets(m) + s.dirty.Set(true) +} + +// Contains checks whether key exist in the session. +func (s *Session) Contains(key string) bool { + s.init() + return s.data.Contains(key) +} + +// IsDirty checks whether there's any data changes in the session. +func (s *Session) IsDirty() bool { + return s.dirty.Val() +} + +// Remove removes key along with its value from this session. +func (s *Session) Remove(key string) { + s.init() + s.data.Remove(key) + s.dirty.Set(true) +} + +// Restore un-serializes the data and restore the session from it. +func (s *Session) Restore(data []byte) (err error) { + if len(data) == 0 { + return nil + } + s.init() + s.data.LockFunc(func(m map[string]interface{}) { + err = json.Unmarshal(data, &m) + }) + return +} + +// Clear deletes all key-value pairs from this session. +func (s *Session) Clear() { + if len(s.id) > 0 { + s.init() + s.data.Clear() + s.dirty.Set(true) + } +} + +// UpdateTTL updates the ttl of the session. +// If this session is dirty, it also exports it to storage. +func (s *Session) UpdateTTL() { + if len(s.id) > 0 && s.data != nil && s.data.Size() > 0 { + if s.manager.storage != nil { + if s.dirty.Cas(true, false) { + s.data.RLockFunc(func(m map[string]interface{}) { + if err := s.manager.storage.Set(s.id, m); err != nil { + panic(err) + } + }) + } else { + if err := s.manager.storage.UpdateTTL(s.id); err != nil { + panic(err) + } + } + } + s.manager.UpdateTTL(s.id, s) + } +} + +func (s *Session) Get(key string, def ...interface{}) interface{} { + if len(s.id) > 0 { + s.init() + if v := s.data.Get(key); v != nil { + return v + } + } + if len(def) > 0 { + return def[0] + } + return nil +} + +func (s *Session) GetVar(key string, def ...interface{}) *gvar.Var { + return gvar.New(s.Get(key, def...), true) +} + +func (s *Session) GetString(key string, def ...interface{}) string { + return gconv.String(s.Get(key, def...)) +} + +func (s *Session) GetBool(key string, def ...interface{}) bool { + return gconv.Bool(s.Get(key, def...)) +} + +func (s *Session) GetInt(key string, def ...interface{}) int { + return gconv.Int(s.Get(key, def...)) +} + +func (s *Session) GetInt8(key string, def ...interface{}) int8 { + return gconv.Int8(s.Get(key, def...)) +} + +func (s *Session) GetInt16(key string, def ...interface{}) int16 { + return gconv.Int16(s.Get(key, def...)) +} + +func (s *Session) GetInt32(key string, def ...interface{}) int32 { + return gconv.Int32(s.Get(key, def...)) +} + +func (s *Session) GetInt64(key string, def ...interface{}) int64 { + return gconv.Int64(s.Get(key, def...)) +} + +func (s *Session) GetUint(key string, def ...interface{}) uint { + return gconv.Uint(s.Get(key, def...)) +} + +func (s *Session) GetUint8(key string, def ...interface{}) uint8 { + return gconv.Uint8(s.Get(key, def...)) +} + +func (s *Session) GetUint16(key string, def ...interface{}) uint16 { + return gconv.Uint16(s.Get(key, def...)) +} + +func (s *Session) GetUint32(key string, def ...interface{}) uint32 { + return gconv.Uint32(s.Get(key, def...)) +} + +func (s *Session) GetUint64(key string, def ...interface{}) uint64 { + return gconv.Uint64(s.Get(key, def...)) +} + +func (s *Session) GetFloat32(key string, def ...interface{}) float32 { + return gconv.Float32(s.Get(key, def...)) +} + +func (s *Session) GetFloat64(key string, def ...interface{}) float64 { + return gconv.Float64(s.Get(key, def...)) +} + +func (s *Session) GetBytes(key string, def ...interface{}) []byte { + return gconv.Bytes(s.Get(key, def...)) +} + +func (s *Session) GetInts(key string, def ...interface{}) []int { + return gconv.Ints(s.Get(key, def...)) +} + +func (s *Session) GetFloats(key string, def ...interface{}) []float64 { + return gconv.Floats(s.Get(key, def...)) +} + +func (s *Session) GetStrings(key string, def ...interface{}) []string { + return gconv.Strings(s.Get(key, def...)) +} + +func (s *Session) GetInterfaces(key string, def ...interface{}) []interface{} { + return gconv.Interfaces(s.Get(key, def...)) +} + +func (s *Session) GetTime(key string, format ...string) time.Time { + return gconv.Time(s.Get(key), format...) +} + +func (s *Session) GetGTime(key string, format ...string) *gtime.Time { + return gconv.GTime(s.Get(key), format...) +} + +func (s *Session) GetDuration(key string, def ...interface{}) time.Duration { + return gconv.Duration(s.Get(key, def...)) +} + +func (s *Session) GetMap(key string, tags ...string) map[string]interface{} { + return gconv.Map(s.Get(key), tags...) +} + +func (s *Session) GetMapDeep(key string, tags ...string) map[string]interface{} { + return gconv.MapDeep(s.Get(key), tags...) +} + +func (s *Session) GetMaps(key string, tags ...string) []map[string]interface{} { + return gconv.Maps(s.Get(key), tags...) +} + +func (s *Session) GetMapsDeep(key string, tags ...string) []map[string]interface{} { + return gconv.MapsDeep(s.Get(key), tags...) +} + +func (s *Session) GetStruct(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Struct(s.Get(key), pointer, mapping...) +} + +func (s *Session) GetStructDeep(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructDeep(s.Get(key), pointer, mapping...) +} + +func (s *Session) GetStructs(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.Structs(s.Get(key), pointer, mapping...) +} + +func (s *Session) GetStructsDeep(key string, pointer interface{}, mapping ...map[string]string) error { + return gconv.StructsDeep(s.Get(key), pointer, mapping...) +} diff --git a/os/gsession/gsession_storage.go b/os/gsession/gsession_storage.go new file mode 100644 index 000000000..12a0b8ea5 --- /dev/null +++ b/os/gsession/gsession_storage.go @@ -0,0 +1,17 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gsession + +type Storage interface { + // Get returns the session data bytes for given session id. + Get(id string) map[string]interface{} + // Set updates the content for session id. + // Note that the parameter is the serialized bytes for session map. + Set(id string, data map[string]interface{}) error + // UpdateTTL updates the TTL for session id. + UpdateTTL(id string) error +} diff --git a/os/gsession/gsession_storage_file.go b/os/gsession/gsession_storage_file.go new file mode 100644 index 000000000..c65b228d3 --- /dev/null +++ b/os/gsession/gsession_storage_file.go @@ -0,0 +1,147 @@ +// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gsession + +import ( + "encoding/json" + "os" + "time" + + "github.com/gogf/gf/crypto/gaes" + + "github.com/gogf/gf/os/gtimer" + + "github.com/gogf/gf/container/gset" + "github.com/gogf/gf/encoding/gbinary" + + "github.com/gogf/gf/os/gtime" + + "github.com/gogf/gf/os/glog" + + "github.com/gogf/gf/os/gfile" +) + +// StorageFile implements the Session Storage interface with file system. +type StorageFile struct { + ttl time.Duration + path string + updatingIdSet *gset.StrSet +} + +var ( + DefaultStorageFilePath = gfile.Join(gfile.TempDir(), "gsessions") + DefaultStorageFileCryptoKey = []byte("Session storage file crypto key!") + DefaultStorageFileLoopInterval = 5 * time.Second +) + +func init() { + tmpPath := "/tmp" + if gfile.Exists(tmpPath) && gfile.IsWritable(tmpPath) { + DefaultStorageFilePath = gfile.Join(tmpPath, "gsessions") + } +} + +func NewStorageFile(ttl time.Duration, path ...string) *StorageFile { + storagePath := DefaultStorageFilePath + if len(path) > 0 && path[0] != "" { + storagePath, _ = gfile.Search(path[0]) + if storagePath == "" { + glog.Panicf("'%s' does not exist", path[0]) + } + if !gfile.IsWritable(storagePath) { + glog.Panicf("'%s' is not writable", path[0]) + } + } + s := &StorageFile{ + ttl: ttl, + path: storagePath, + updatingIdSet: gset.NewStrSet(true), + } + gtimer.AddSingleton(DefaultStorageFileLoopInterval, func() { + var err error + s.updatingIdSet.Iterator(func(v string) bool { + if err = s.doUpdateTTL(v); err != nil { + panic(err) + } + return true + }) + }) + return s +} + +func (s *StorageFile) sessionFilePath(id string) string { + return gfile.Join(s.path, id) +} + +// Get return the session data for given session id. +func (s *StorageFile) Get(id string) map[string]interface{} { + path := s.sessionFilePath(id) + data := gfile.GetBytes(path) + if len(data) > 8 { + timestamp := gbinary.DecodeToInt64(data[:8]) + if timestamp+int64(s.ttl.Seconds()) < gtime.Second() { + return nil + } + // Decrypt with AES. + content, err := gaes.Decrypt(data[8:], DefaultStorageFileCryptoKey) + if err != nil { + return nil + } + var m map[string]interface{} + if err := json.Unmarshal(content, &m); err != nil { + return nil + } + return m + } + return nil +} + +// Set updates the content for session id. +// Note that the parameter is the serialized bytes for session map. +func (s *StorageFile) Set(id string, data map[string]interface{}) error { + path := s.sessionFilePath(id) + content, err := json.Marshal(data) + if err != nil { + return err + } + // Encrypt with AES. + content, err = gaes.Encrypt(content, DefaultStorageFileCryptoKey) + if err != nil { + return err + } + file, err := gfile.OpenWithFlag(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC) + if err != nil { + return err + } + if _, err = file.Write(gbinary.EncodeInt64(gtime.Second())); err != nil { + return err + } + if _, err = file.Write(content); err != nil { + return err + } + return file.Close() +} + +// UpdateTTL updates the TTL for session id. +// It just adds the session id to the async handling queue. +func (s *StorageFile) UpdateTTL(id string) error { + s.updatingIdSet.Add(id) + return nil +} + +// doUpdateTTL updates the TTL for session id. +func (s *StorageFile) doUpdateTTL(id string) error { + path := s.sessionFilePath(id) + file, err := gfile.OpenWithFlag(path, os.O_WRONLY) + if err != nil { + return err + } + if _, err = file.Write(gbinary.EncodeInt64(gtime.Second())); err != nil { + return err + } + return file.Close() +}