mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
add package gsession; mv gkvdb to new repo
This commit is contained in:
@ -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…
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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 <name> param is unnecessary, if <name> 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
|
||||
}
|
||||
@ -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 <key>-<value> pair data to current db with <ttl>.
|
||||
// The <ttl> 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 <key> 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 <key> 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 <f> starting from <seek>.
|
||||
// If <seek> is nil it iterates from the beginning of the db.
|
||||
// If <f> 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 <f> starting from <seek>.
|
||||
// If <prefix> is nil it iterates from the beginning of the db.
|
||||
// If <f> 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)
|
||||
}
|
||||
@ -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 <key>-<value> pair data to current db with <ttl> in this transaction.
|
||||
// The <ttl> 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 <key> 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 <key> 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 <f> starting from <prefix>.
|
||||
// If <seek> is nil it iterates from the beginning of the db.
|
||||
// If <f> 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 <f> starting from <prefix>.
|
||||
// If <seek> is nil it iterates from the beginning of the db.
|
||||
// If <f> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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")})
|
||||
|
||||
})
|
||||
}
|
||||
11
go.mod
11
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
|
||||
)
|
||||
|
||||
67
go.sum
67
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=
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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设置
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
)
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
// 查找静态文件的绝对路径
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 <flag>.
|
||||
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 <flag> and <perm>.
|
||||
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 <path> 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
|
||||
}
|
||||
|
||||
@ -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 <path> as string.
|
||||
@ -34,7 +34,7 @@ func GetBytes(path string) []byte {
|
||||
}
|
||||
|
||||
// putContents puts binary content to file of <path>.
|
||||
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 <path> recursively.
|
||||
dir := Dir(path)
|
||||
if !Exists(dir) {
|
||||
@ -64,30 +64,30 @@ func Truncate(path string, size int) error {
|
||||
// PutContents puts string <content> to file of <path>.
|
||||
// It creates file of <path> 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 <content> to file of <path>.
|
||||
// It creates file of <path> 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 <content> to file of <path>.
|
||||
// It creates file of <path> 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 <content> to file of <path>.
|
||||
// It creates file of <path> 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 <char> starting from <start>.
|
||||
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 <char> starting from <start>.
|
||||
// It opens file of <path> 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 <path> 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)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
21
os/gsession/gsession.go
Normal file
21
os/gsession/gsession.go
Normal file
@ -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))
|
||||
}
|
||||
65
os/gsession/gsession_manager.go
Normal file
65
os/gsession/gsession_manager.go
Normal file
@ -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)
|
||||
}
|
||||
285
os/gsession/gsession_session.go
Normal file
285
os/gsession/gsession_session.go
Normal file
@ -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...)
|
||||
}
|
||||
17
os/gsession/gsession_storage.go
Normal file
17
os/gsession/gsession_storage.go
Normal file
@ -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 <content> 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
|
||||
}
|
||||
147
os/gsession/gsession_storage_file.go
Normal file
147
os/gsession/gsession_storage_file.go
Normal file
@ -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 <content> 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()
|
||||
}
|
||||
Reference in New Issue
Block a user