mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 13:20:55 +00:00
Merge branch 'develop' of git://github.com/astaxie/beego
This commit is contained in:
commit
846d766499
@ -45,6 +45,7 @@ type Controller struct {
|
|||||||
CruSession session.SessionStore
|
CruSession session.SessionStore
|
||||||
XSRFExpire int
|
XSRFExpire int
|
||||||
AppController interface{}
|
AppController interface{}
|
||||||
|
EnableReander bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ControllerInterface is an interface to uniform all controller handler.
|
// ControllerInterface is an interface to uniform all controller handler.
|
||||||
@ -74,6 +75,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin
|
|||||||
c.Ctx = ctx
|
c.Ctx = ctx
|
||||||
c.TplExt = "tpl"
|
c.TplExt = "tpl"
|
||||||
c.AppController = app
|
c.AppController = app
|
||||||
|
c.EnableReander = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare runs after Init before request function execution.
|
// Prepare runs after Init before request function execution.
|
||||||
@ -123,6 +125,9 @@ func (c *Controller) Options() {
|
|||||||
|
|
||||||
// Render sends the response with rendered template bytes as text/html type.
|
// Render sends the response with rendered template bytes as text/html type.
|
||||||
func (c *Controller) Render() error {
|
func (c *Controller) Render() error {
|
||||||
|
if !c.EnableReander {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
rb, err := c.RenderBytes()
|
rb, err := c.RenderBytes()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -398,6 +403,7 @@ func (c *Controller) SessionRegenerateID() {
|
|||||||
|
|
||||||
// DestroySession cleans session data and session cookie.
|
// DestroySession cleans session data and session cookie.
|
||||||
func (c *Controller) DestroySession() {
|
func (c *Controller) DestroySession() {
|
||||||
|
c.Ctx.Input.CruSession.Flush()
|
||||||
GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
|
GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,28 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Brush func(string) string
|
||||||
|
|
||||||
|
func NewBrush(color string) Brush {
|
||||||
|
pre := "\033["
|
||||||
|
reset := "\033[0m"
|
||||||
|
return func(text string) string {
|
||||||
|
return pre + color + "m" + text + reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var colors = []Brush{
|
||||||
|
NewBrush("1;36"), // Trace cyan
|
||||||
|
NewBrush("1;34"), // Debug blue
|
||||||
|
NewBrush("1;32"), // Info green
|
||||||
|
NewBrush("1;33"), // Warn yellow
|
||||||
|
NewBrush("1;31"), // Error red
|
||||||
|
NewBrush("1;35"), // Critical purple
|
||||||
|
}
|
||||||
|
|
||||||
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
// ConsoleWriter implements LoggerInterface and writes messages to terminal.
|
||||||
type ConsoleWriter struct {
|
type ConsoleWriter struct {
|
||||||
lg *log.Logger
|
lg *log.Logger
|
||||||
@ -35,7 +55,11 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error {
|
|||||||
if level < c.Level {
|
if level < c.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if goos := runtime.GOOS; goos == "windows" {
|
||||||
c.lg.Println(msg)
|
c.lg.Println(msg)
|
||||||
|
} else {
|
||||||
|
c.lg.Println(colors[level](msg))
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1350,6 +1350,10 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond
|
|||||||
return cnt, nil
|
return cnt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *dbBase) RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// flag of update joined record.
|
// flag of update joined record.
|
||||||
func (d *dbBase) SupportUpdateJoin() bool {
|
func (d *dbBase) SupportUpdateJoin() bool {
|
||||||
return true
|
return true
|
||||||
|
116
orm/db_alias.go
116
orm/db_alias.go
@ -3,7 +3,6 @@ package orm
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -96,40 +95,15 @@ type alias struct {
|
|||||||
Engine string
|
Engine string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting the database connect params. Use the database driver self dataSource args.
|
func detectTZ(al *alias) {
|
||||||
func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) {
|
|
||||||
al := new(alias)
|
|
||||||
al.Name = aliasName
|
|
||||||
al.DriverName = driverName
|
|
||||||
al.DataSource = dataSource
|
|
||||||
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if dr, ok := drivers[driverName]; ok {
|
|
||||||
al.DbBaser = dbBasers[dr]
|
|
||||||
al.Driver = dr
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("driver name `%s` have not registered", driverName)
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
|
|
||||||
if dataBaseCache.add(aliasName, al) == false {
|
|
||||||
err = fmt.Errorf("db name `%s` already registered, cannot reuse", aliasName)
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
|
|
||||||
al.DB, err = sql.Open(driverName, dataSource)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error())
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
|
|
||||||
// orm timezone system match database
|
// orm timezone system match database
|
||||||
// default use Local
|
// default use Local
|
||||||
al.TZ = time.Local
|
al.TZ = time.Local
|
||||||
|
|
||||||
|
if al.DriverName == "sphinx" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch al.Driver {
|
switch al.Driver {
|
||||||
case DR_MySQL:
|
case DR_MySQL:
|
||||||
row := al.DB.QueryRow("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP)")
|
row := al.DB.QueryRow("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP)")
|
||||||
@ -173,6 +147,60 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) {
|
|||||||
DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error())
|
DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) {
|
||||||
|
al := new(alias)
|
||||||
|
al.Name = aliasName
|
||||||
|
al.DriverName = driverName
|
||||||
|
al.DB = db
|
||||||
|
|
||||||
|
if dr, ok := drivers[driverName]; ok {
|
||||||
|
al.DbBaser = dbBasers[dr]
|
||||||
|
al.Driver = dr
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("driver name `%s` have not registered", driverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := db.Ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if dataBaseCache.add(aliasName, al) == false {
|
||||||
|
return nil, fmt.Errorf("db name `%s` already registered, cannot reuse", aliasName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return al, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddAliasWthDB(aliasName, driverName string, db *sql.DB) error {
|
||||||
|
_, err := addAliasWthDB(aliasName, driverName, db)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting the database connect params. Use the database driver self dataSource args.
|
||||||
|
func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) error {
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
db *sql.DB
|
||||||
|
al *alias
|
||||||
|
)
|
||||||
|
|
||||||
|
db, err = sql.Open(driverName, dataSource)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error())
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
|
||||||
|
al, err = addAliasWthDB(aliasName, driverName, db)
|
||||||
|
if err != nil {
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
|
||||||
|
al.DataSource = dataSource
|
||||||
|
|
||||||
|
detectTZ(al)
|
||||||
|
|
||||||
for i, v := range params {
|
for i, v := range params {
|
||||||
switch i {
|
switch i {
|
||||||
@ -183,39 +211,37 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = al.DB.Ping()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error())
|
|
||||||
goto end
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
if db != nil {
|
||||||
os.Exit(2)
|
db.Close()
|
||||||
}
|
}
|
||||||
|
DebugLog.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register a database driver use specify driver name, this can be definition the driver is which database type.
|
// Register a database driver use specify driver name, this can be definition the driver is which database type.
|
||||||
func RegisterDriver(driverName string, typ DriverType) {
|
func RegisterDriver(driverName string, typ DriverType) error {
|
||||||
if t, ok := drivers[driverName]; ok == false {
|
if t, ok := drivers[driverName]; ok == false {
|
||||||
drivers[driverName] = typ
|
drivers[driverName] = typ
|
||||||
} else {
|
} else {
|
||||||
if t != typ {
|
if t != typ {
|
||||||
fmt.Sprintf("driverName `%s` db driver already registered and is other type\n", driverName)
|
return fmt.Errorf("driverName `%s` db driver already registered and is other type\n", driverName)
|
||||||
os.Exit(2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the database default used timezone
|
// Change the database default used timezone
|
||||||
func SetDataBaseTZ(aliasName string, tz *time.Location) {
|
func SetDataBaseTZ(aliasName string, tz *time.Location) error {
|
||||||
if al, ok := dataBaseCache.get(aliasName); ok {
|
if al, ok := dataBaseCache.get(aliasName); ok {
|
||||||
al.TZ = tz
|
al.TZ = tz
|
||||||
} else {
|
} else {
|
||||||
fmt.Sprintf("DataBase name `%s` not registered\n", aliasName)
|
return fmt.Errorf("DataBase name `%s` not registered\n", aliasName)
|
||||||
os.Exit(2)
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Change the max idle conns for *sql.DB, use specify database alias name
|
// Change the max idle conns for *sql.DB, use specify database alias name
|
||||||
|
47
orm/orm.go
47
orm/orm.go
@ -74,6 +74,20 @@ func (o *orm) Read(md interface{}, cols ...string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to read a row from the database, or insert one if it doesn't exist
|
||||||
|
func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) {
|
||||||
|
cols = append([]string{col1}, cols...)
|
||||||
|
mi, ind := o.getMiInd(md, true)
|
||||||
|
err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols)
|
||||||
|
if err == ErrNoRows {
|
||||||
|
// Create
|
||||||
|
id, err := o.Insert(md)
|
||||||
|
return (err == nil), id, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, ind.Field(mi.fields.pk.fieldIndex).Int(), err
|
||||||
|
}
|
||||||
|
|
||||||
// insert model data to database
|
// insert model data to database
|
||||||
func (o *orm) Insert(md interface{}) (int64, error) {
|
func (o *orm) Insert(md interface{}) (int64, error) {
|
||||||
mi, ind := o.getMiInd(md, true)
|
mi, ind := o.getMiInd(md, true)
|
||||||
@ -425,6 +439,12 @@ func (o *orm) Driver() Driver {
|
|||||||
return driver(o.alias.Name)
|
return driver(o.alias.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *orm) GetDB() dbQuerier {
|
||||||
|
panic(ErrNotImplement)
|
||||||
|
// not enough
|
||||||
|
return o.db
|
||||||
|
}
|
||||||
|
|
||||||
// create new orm
|
// create new orm
|
||||||
func NewOrm() Ormer {
|
func NewOrm() Ormer {
|
||||||
BootStrap() // execute only once
|
BootStrap() // execute only once
|
||||||
@ -436,3 +456,30 @@ func NewOrm() Ormer {
|
|||||||
}
|
}
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create a new ormer object with specify *sql.DB for query
|
||||||
|
func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) {
|
||||||
|
var al *alias
|
||||||
|
|
||||||
|
if dr, ok := drivers[driverName]; ok {
|
||||||
|
al = new(alias)
|
||||||
|
al.DbBaser = dbBasers[dr]
|
||||||
|
al.Driver = dr
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("driver name `%s` have not registered", driverName)
|
||||||
|
}
|
||||||
|
|
||||||
|
al.Name = aliasName
|
||||||
|
al.DriverName = driverName
|
||||||
|
|
||||||
|
o := new(orm)
|
||||||
|
o.alias = al
|
||||||
|
|
||||||
|
if Debug {
|
||||||
|
o.db = newDbQueryLog(o.alias, db)
|
||||||
|
} else {
|
||||||
|
o.db = db
|
||||||
|
}
|
||||||
|
|
||||||
|
return o, nil
|
||||||
|
}
|
||||||
|
@ -197,6 +197,36 @@ func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) {
|
|||||||
return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ)
|
return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query all rows into map[string]interface with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to map[string]interface{}{
|
||||||
|
// "total": 100,
|
||||||
|
// "found": 200,
|
||||||
|
// }
|
||||||
|
func (o *querySet) RowsToMap(result *Params, keyCol, valueCol string) (int64, error) {
|
||||||
|
panic(ErrNotImplement)
|
||||||
|
return o.orm.alias.DbBaser.RowsTo(o.orm.db, o, o.mi, o.cond, result, keyCol, valueCol, o.orm.alias.TZ)
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all rows into struct with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to struct {
|
||||||
|
// Total int
|
||||||
|
// Found int
|
||||||
|
// }
|
||||||
|
func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) {
|
||||||
|
panic(ErrNotImplement)
|
||||||
|
return o.orm.alias.DbBaser.RowsTo(o.orm.db, o, o.mi, o.cond, ptrStruct, keyCol, valueCol, o.orm.alias.TZ)
|
||||||
|
}
|
||||||
|
|
||||||
// create new QuerySeter.
|
// create new QuerySeter.
|
||||||
func newQuerySet(orm *orm, mi *modelInfo) QuerySeter {
|
func newQuerySet(orm *orm, mi *modelInfo) QuerySeter {
|
||||||
o := new(querySet)
|
o := new(querySet)
|
||||||
|
185
orm/orm_raw.go
185
orm/orm_raw.go
@ -518,7 +518,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) {
|
|||||||
return cnt, nil
|
return cnt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (o *rawSet) readValues(container interface{}) (int64, error) {
|
func (o *rawSet) readValues(container interface{}, needCols []string) (int64, error) {
|
||||||
var (
|
var (
|
||||||
maps []Params
|
maps []Params
|
||||||
lists []ParamsList
|
lists []ParamsList
|
||||||
@ -555,17 +555,35 @@ func (o *rawSet) readValues(container interface{}) (int64, error) {
|
|||||||
refs []interface{}
|
refs []interface{}
|
||||||
cnt int64
|
cnt int64
|
||||||
cols []string
|
cols []string
|
||||||
|
indexs []int
|
||||||
)
|
)
|
||||||
|
|
||||||
for rs.Next() {
|
for rs.Next() {
|
||||||
if cnt == 0 {
|
if cnt == 0 {
|
||||||
if columns, err := rs.Columns(); err != nil {
|
if columns, err := rs.Columns(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
} else {
|
} else {
|
||||||
|
if len(needCols) > 0 {
|
||||||
|
indexs = make([]int, 0, len(needCols))
|
||||||
|
} else {
|
||||||
|
indexs = make([]int, 0, len(columns))
|
||||||
|
}
|
||||||
|
|
||||||
cols = columns
|
cols = columns
|
||||||
refs = make([]interface{}, len(cols))
|
refs = make([]interface{}, len(cols))
|
||||||
for i, _ := range refs {
|
for i, _ := range refs {
|
||||||
var ref sql.NullString
|
var ref sql.NullString
|
||||||
refs[i] = &ref
|
refs[i] = &ref
|
||||||
|
|
||||||
|
if len(needCols) > 0 {
|
||||||
|
for _, c := range needCols {
|
||||||
|
if c == cols[i] {
|
||||||
|
indexs = append(indexs, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
indexs = append(indexs, i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -577,7 +595,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) {
|
|||||||
switch typ {
|
switch typ {
|
||||||
case 1:
|
case 1:
|
||||||
params := make(Params, len(cols))
|
params := make(Params, len(cols))
|
||||||
for i, ref := range refs {
|
for _, i := range indexs {
|
||||||
|
ref := refs[i]
|
||||||
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
||||||
if value.Valid {
|
if value.Valid {
|
||||||
params[cols[i]] = value.String
|
params[cols[i]] = value.String
|
||||||
@ -588,7 +607,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) {
|
|||||||
maps = append(maps, params)
|
maps = append(maps, params)
|
||||||
case 2:
|
case 2:
|
||||||
params := make(ParamsList, 0, len(cols))
|
params := make(ParamsList, 0, len(cols))
|
||||||
for _, ref := range refs {
|
for _, i := range indexs {
|
||||||
|
ref := refs[i]
|
||||||
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
||||||
if value.Valid {
|
if value.Valid {
|
||||||
params = append(params, value.String)
|
params = append(params, value.String)
|
||||||
@ -598,7 +618,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) {
|
|||||||
}
|
}
|
||||||
lists = append(lists, params)
|
lists = append(lists, params)
|
||||||
case 3:
|
case 3:
|
||||||
for _, ref := range refs {
|
for _, i := range indexs {
|
||||||
|
ref := refs[i]
|
||||||
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString)
|
||||||
if value.Valid {
|
if value.Valid {
|
||||||
list = append(list, value.String)
|
list = append(list, value.String)
|
||||||
@ -623,19 +644,163 @@ func (o *rawSet) readValues(container interface{}) (int64, error) {
|
|||||||
return cnt, nil
|
return cnt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (int64, error) {
|
||||||
|
var (
|
||||||
|
maps Params
|
||||||
|
ind *reflect.Value
|
||||||
|
)
|
||||||
|
|
||||||
|
typ := 0
|
||||||
|
switch container.(type) {
|
||||||
|
case *Params:
|
||||||
|
typ = 1
|
||||||
|
default:
|
||||||
|
typ = 2
|
||||||
|
vl := reflect.ValueOf(container)
|
||||||
|
id := reflect.Indirect(vl)
|
||||||
|
if vl.Kind() != reflect.Ptr || id.Kind() != reflect.Struct {
|
||||||
|
panic(fmt.Errorf("<RawSeter> RowsTo unsupport type `%T` need ptr struct", container))
|
||||||
|
}
|
||||||
|
|
||||||
|
ind = &id
|
||||||
|
}
|
||||||
|
|
||||||
|
query := o.query
|
||||||
|
o.orm.alias.DbBaser.ReplaceMarks(&query)
|
||||||
|
|
||||||
|
args := getFlatParams(nil, o.args, o.orm.alias.TZ)
|
||||||
|
|
||||||
|
var rs *sql.Rows
|
||||||
|
if r, err := o.orm.db.Query(query, args...); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
rs = r
|
||||||
|
}
|
||||||
|
|
||||||
|
defer rs.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
refs []interface{}
|
||||||
|
cnt int64
|
||||||
|
cols []string
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
keyIndex = -1
|
||||||
|
valueIndex = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
for rs.Next() {
|
||||||
|
if cnt == 0 {
|
||||||
|
if columns, err := rs.Columns(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
cols = columns
|
||||||
|
refs = make([]interface{}, len(cols))
|
||||||
|
for i, _ := range refs {
|
||||||
|
if keyCol == cols[i] {
|
||||||
|
keyIndex = i
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ == 1 || keyIndex == i {
|
||||||
|
var ref sql.NullString
|
||||||
|
refs[i] = &ref
|
||||||
|
} else {
|
||||||
|
var ref interface{}
|
||||||
|
refs[i] = &ref
|
||||||
|
}
|
||||||
|
|
||||||
|
if valueCol == cols[i] {
|
||||||
|
valueIndex = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyIndex == -1 || valueIndex == -1 {
|
||||||
|
panic(fmt.Errorf("<RawSeter> RowsTo unknown key, value column name `%s: %s`", keyCol, valueCol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rs.Scan(refs...); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cnt == 0 {
|
||||||
|
switch typ {
|
||||||
|
case 1:
|
||||||
|
maps = make(Params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
key := reflect.Indirect(reflect.ValueOf(refs[keyIndex])).Interface().(sql.NullString).String
|
||||||
|
|
||||||
|
switch typ {
|
||||||
|
case 1:
|
||||||
|
value := reflect.Indirect(reflect.ValueOf(refs[valueIndex])).Interface().(sql.NullString)
|
||||||
|
if value.Valid {
|
||||||
|
maps[key] = value.String
|
||||||
|
} else {
|
||||||
|
maps[key] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
if id := ind.FieldByName(camelString(key)); id.IsValid() {
|
||||||
|
o.setFieldValue(id, reflect.ValueOf(refs[valueIndex]).Elem().Interface())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ == 1 {
|
||||||
|
v, _ := container.(*Params)
|
||||||
|
*v = maps
|
||||||
|
}
|
||||||
|
|
||||||
|
return cnt, nil
|
||||||
|
}
|
||||||
|
|
||||||
// query data to []map[string]interface
|
// query data to []map[string]interface
|
||||||
func (o *rawSet) Values(container *[]Params) (int64, error) {
|
func (o *rawSet) Values(container *[]Params, cols ...string) (int64, error) {
|
||||||
return o.readValues(container)
|
return o.readValues(container, cols)
|
||||||
}
|
}
|
||||||
|
|
||||||
// query data to [][]interface
|
// query data to [][]interface
|
||||||
func (o *rawSet) ValuesList(container *[]ParamsList) (int64, error) {
|
func (o *rawSet) ValuesList(container *[]ParamsList, cols ...string) (int64, error) {
|
||||||
return o.readValues(container)
|
return o.readValues(container, cols)
|
||||||
}
|
}
|
||||||
|
|
||||||
// query data to []interface
|
// query data to []interface
|
||||||
func (o *rawSet) ValuesFlat(container *ParamsList) (int64, error) {
|
func (o *rawSet) ValuesFlat(container *ParamsList, cols ...string) (int64, error) {
|
||||||
return o.readValues(container)
|
return o.readValues(container, cols)
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all rows into map[string]interface with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to map[string]interface{}{
|
||||||
|
// "total": 100,
|
||||||
|
// "found": 200,
|
||||||
|
// }
|
||||||
|
func (o *rawSet) RowsToMap(result *Params, keyCol, valueCol string) (int64, error) {
|
||||||
|
return o.queryRowsTo(result, keyCol, valueCol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all rows into struct with specify key and value column name.
|
||||||
|
// keyCol = "name", valueCol = "value"
|
||||||
|
// table data
|
||||||
|
// name | value
|
||||||
|
// total | 100
|
||||||
|
// found | 200
|
||||||
|
// to struct {
|
||||||
|
// Total int
|
||||||
|
// Found int
|
||||||
|
// }
|
||||||
|
func (o *rawSet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) {
|
||||||
|
return o.queryRowsTo(ptrStruct, keyCol, valueCol)
|
||||||
}
|
}
|
||||||
|
|
||||||
// return prepared raw statement for used in times.
|
// return prepared raw statement for used in times.
|
||||||
|
@ -1642,3 +1642,41 @@ func TestTransaction(t *testing.T) {
|
|||||||
throwFail(t, AssertIs(num, 1))
|
throwFail(t, AssertIs(num, 1))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadOrCreate(t *testing.T) {
|
||||||
|
u := &User{
|
||||||
|
UserName: "Kyle",
|
||||||
|
Email: "kylemcc@gmail.com",
|
||||||
|
Password: "other_pass",
|
||||||
|
Status: 7,
|
||||||
|
IsStaff: false,
|
||||||
|
IsActive: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
created, pk, err := dORM.ReadOrCreate(u, "UserName")
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(created, true))
|
||||||
|
throwFail(t, AssertIs(u.UserName, "Kyle"))
|
||||||
|
throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com"))
|
||||||
|
throwFail(t, AssertIs(u.Password, "other_pass"))
|
||||||
|
throwFail(t, AssertIs(u.Status, 7))
|
||||||
|
throwFail(t, AssertIs(u.IsStaff, false))
|
||||||
|
throwFail(t, AssertIs(u.IsActive, true))
|
||||||
|
throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), test_Date))
|
||||||
|
throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), test_DateTime))
|
||||||
|
|
||||||
|
nu := &User{UserName: u.UserName, Email: "someotheremail@gmail.com"}
|
||||||
|
created, pk, err = dORM.ReadOrCreate(nu, "UserName")
|
||||||
|
throwFail(t, err)
|
||||||
|
throwFail(t, AssertIs(created, false))
|
||||||
|
throwFail(t, AssertIs(nu.Id, u.Id))
|
||||||
|
throwFail(t, AssertIs(pk, u.Id))
|
||||||
|
throwFail(t, AssertIs(nu.UserName, u.UserName))
|
||||||
|
throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above
|
||||||
|
throwFail(t, AssertIs(nu.Password, u.Password))
|
||||||
|
throwFail(t, AssertIs(nu.Status, u.Status))
|
||||||
|
throwFail(t, AssertIs(nu.IsStaff, u.IsStaff))
|
||||||
|
throwFail(t, AssertIs(nu.IsActive, u.IsActive))
|
||||||
|
|
||||||
|
dORM.Delete(u)
|
||||||
|
}
|
||||||
|
21
orm/types.go
21
orm/types.go
@ -23,6 +23,7 @@ type Fielder interface {
|
|||||||
// orm struct
|
// orm struct
|
||||||
type Ormer interface {
|
type Ormer interface {
|
||||||
Read(interface{}, ...string) error
|
Read(interface{}, ...string) error
|
||||||
|
ReadOrCreate(interface{}, string, ...string) (bool, int64, error)
|
||||||
Insert(interface{}) (int64, error)
|
Insert(interface{}) (int64, error)
|
||||||
InsertMulti(int, interface{}) (int64, error)
|
InsertMulti(int, interface{}) (int64, error)
|
||||||
Update(interface{}, ...string) (int64, error)
|
Update(interface{}, ...string) (int64, error)
|
||||||
@ -36,6 +37,7 @@ type Ormer interface {
|
|||||||
Rollback() error
|
Rollback() error
|
||||||
Raw(string, ...interface{}) RawSeter
|
Raw(string, ...interface{}) RawSeter
|
||||||
Driver() Driver
|
Driver() Driver
|
||||||
|
GetDB() dbQuerier
|
||||||
}
|
}
|
||||||
|
|
||||||
// insert prepared statement
|
// insert prepared statement
|
||||||
@ -63,6 +65,8 @@ type QuerySeter interface {
|
|||||||
Values(*[]Params, ...string) (int64, error)
|
Values(*[]Params, ...string) (int64, error)
|
||||||
ValuesList(*[]ParamsList, ...string) (int64, error)
|
ValuesList(*[]ParamsList, ...string) (int64, error)
|
||||||
ValuesFlat(*ParamsList, string) (int64, error)
|
ValuesFlat(*ParamsList, string) (int64, error)
|
||||||
|
RowsToMap(*Params, string, string) (int64, error)
|
||||||
|
RowsToStruct(interface{}, string, string) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// model to model query struct
|
// model to model query struct
|
||||||
@ -86,9 +90,11 @@ type RawSeter interface {
|
|||||||
QueryRow(...interface{}) error
|
QueryRow(...interface{}) error
|
||||||
QueryRows(...interface{}) (int64, error)
|
QueryRows(...interface{}) (int64, error)
|
||||||
SetArgs(...interface{}) RawSeter
|
SetArgs(...interface{}) RawSeter
|
||||||
Values(*[]Params) (int64, error)
|
Values(*[]Params, ...string) (int64, error)
|
||||||
ValuesList(*[]ParamsList) (int64, error)
|
ValuesList(*[]ParamsList, ...string) (int64, error)
|
||||||
ValuesFlat(*ParamsList) (int64, error)
|
ValuesFlat(*ParamsList, ...string) (int64, error)
|
||||||
|
RowsToMap(*Params, string, string) (int64, error)
|
||||||
|
RowsToStruct(interface{}, string, string) (int64, error)
|
||||||
Prepare() (RawPreparer, error)
|
Prepare() (RawPreparer, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +114,14 @@ type dbQuerier interface {
|
|||||||
QueryRow(query string, args ...interface{}) *sql.Row
|
QueryRow(query string, args ...interface{}) *sql.Row
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// type DB interface {
|
||||||
|
// Begin() (*sql.Tx, error)
|
||||||
|
// Prepare(query string) (stmtQuerier, error)
|
||||||
|
// Exec(query string, args ...interface{}) (sql.Result, error)
|
||||||
|
// Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||||
|
// QueryRow(query string, args ...interface{}) *sql.Row
|
||||||
|
// }
|
||||||
|
|
||||||
// transaction beginner
|
// transaction beginner
|
||||||
type txer interface {
|
type txer interface {
|
||||||
Begin() (*sql.Tx, error)
|
Begin() (*sql.Tx, error)
|
||||||
@ -138,6 +152,7 @@ type dbBaser interface {
|
|||||||
GenerateOperatorLeftCol(*fieldInfo, string, *string)
|
GenerateOperatorLeftCol(*fieldInfo, string, *string)
|
||||||
PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error)
|
PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error)
|
||||||
ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error)
|
ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error)
|
||||||
|
RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error)
|
||||||
MaxLimit() uint64
|
MaxLimit() uint64
|
||||||
TableQuote() string
|
TableQuote() string
|
||||||
ReplaceMarks(*string)
|
ReplaceMarks(*string)
|
||||||
|
@ -11,12 +11,15 @@ import (
|
|||||||
|
|
||||||
var cookiepder = &CookieProvider{}
|
var cookiepder = &CookieProvider{}
|
||||||
|
|
||||||
|
// Cookie SessionStore
|
||||||
type CookieSessionStore struct {
|
type CookieSessionStore struct {
|
||||||
sid string
|
sid string
|
||||||
values map[interface{}]interface{} // session data
|
values map[interface{}]interface{} // session data
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set value to cookie session.
|
||||||
|
// the value are encoded as gob with hash block string.
|
||||||
func (st *CookieSessionStore) Set(key, value interface{}) error {
|
func (st *CookieSessionStore) Set(key, value interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -24,6 +27,7 @@ func (st *CookieSessionStore) Set(key, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get value from cookie session
|
||||||
func (st *CookieSessionStore) Get(key interface{}) interface{} {
|
func (st *CookieSessionStore) Get(key interface{}) interface{} {
|
||||||
st.lock.RLock()
|
st.lock.RLock()
|
||||||
defer st.lock.RUnlock()
|
defer st.lock.RUnlock()
|
||||||
@ -35,6 +39,7 @@ func (st *CookieSessionStore) Get(key interface{}) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete value in cookie session
|
||||||
func (st *CookieSessionStore) Delete(key interface{}) error {
|
func (st *CookieSessionStore) Delete(key interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -42,6 +47,7 @@ func (st *CookieSessionStore) Delete(key interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean all values in cookie session
|
||||||
func (st *CookieSessionStore) Flush() error {
|
func (st *CookieSessionStore) Flush() error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -49,10 +55,12 @@ func (st *CookieSessionStore) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return id of this cookie session
|
||||||
func (st *CookieSessionStore) SessionID() string {
|
func (st *CookieSessionStore) SessionID() string {
|
||||||
return st.sid
|
return st.sid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write cookie session to http response cookie
|
||||||
func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
|
func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
str, err := encodeCookie(cookiepder.block,
|
str, err := encodeCookie(cookiepder.block,
|
||||||
cookiepder.config.SecurityKey,
|
cookiepder.config.SecurityKey,
|
||||||
@ -79,12 +87,21 @@ type cookieConfig struct {
|
|||||||
Maxage int `json:"maxage"`
|
Maxage int `json:"maxage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cookie session provider
|
||||||
type CookieProvider struct {
|
type CookieProvider struct {
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
config *cookieConfig
|
config *cookieConfig
|
||||||
block cipher.Block
|
block cipher.Block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init cookie session provider with max lifetime and config json.
|
||||||
|
// maxlifetime is ignored.
|
||||||
|
// json config:
|
||||||
|
// securityKey - hash string
|
||||||
|
// blockKey - gob encode hash string. it's saved as aes crypto.
|
||||||
|
// securityName - recognized name in encoded cookie string
|
||||||
|
// cookieName - cookie name
|
||||||
|
// maxage - cookie max life time.
|
||||||
func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error {
|
func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error {
|
||||||
pder.config = &cookieConfig{}
|
pder.config = &cookieConfig{}
|
||||||
err := json.Unmarshal([]byte(config), pder.config)
|
err := json.Unmarshal([]byte(config), pder.config)
|
||||||
@ -104,6 +121,8 @@ func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get SessionStore in cooke.
|
||||||
|
// decode cooke string to map and put into SessionStore with sid.
|
||||||
func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) {
|
func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) {
|
||||||
maps, _ := decodeCookie(pder.block,
|
maps, _ := decodeCookie(pder.block,
|
||||||
pder.config.SecurityKey,
|
pder.config.SecurityKey,
|
||||||
@ -116,26 +135,32 @@ func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) {
|
|||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Cookie session is always existed
|
||||||
func (pder *CookieProvider) SessionExist(sid string) bool {
|
func (pder *CookieProvider) SessionExist(sid string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, no used.
|
||||||
func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, no used.
|
||||||
func (pder *CookieProvider) SessionDestroy(sid string) error {
|
func (pder *CookieProvider) SessionDestroy(sid string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, no used.
|
||||||
func (pder *CookieProvider) SessionGC() {
|
func (pder *CookieProvider) SessionGC() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, return 0.
|
||||||
func (pder *CookieProvider) SessionAll() int {
|
func (pder *CookieProvider) SessionAll() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, no used.
|
||||||
func (pder *CookieProvider) SessionUpdate(sid string) error {
|
func (pder *CookieProvider) SessionUpdate(sid string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ var (
|
|||||||
gcmaxlifetime int64
|
gcmaxlifetime int64
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// File session store
|
||||||
type FileSessionStore struct {
|
type FileSessionStore struct {
|
||||||
f *os.File
|
f *os.File
|
||||||
sid string
|
sid string
|
||||||
@ -25,6 +26,7 @@ type FileSessionStore struct {
|
|||||||
values map[interface{}]interface{}
|
values map[interface{}]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set value to file session
|
||||||
func (fs *FileSessionStore) Set(key, value interface{}) error {
|
func (fs *FileSessionStore) Set(key, value interface{}) error {
|
||||||
fs.lock.Lock()
|
fs.lock.Lock()
|
||||||
defer fs.lock.Unlock()
|
defer fs.lock.Unlock()
|
||||||
@ -32,6 +34,7 @@ func (fs *FileSessionStore) Set(key, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get value from file session
|
||||||
func (fs *FileSessionStore) Get(key interface{}) interface{} {
|
func (fs *FileSessionStore) Get(key interface{}) interface{} {
|
||||||
fs.lock.RLock()
|
fs.lock.RLock()
|
||||||
defer fs.lock.RUnlock()
|
defer fs.lock.RUnlock()
|
||||||
@ -43,6 +46,7 @@ func (fs *FileSessionStore) Get(key interface{}) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete value in file session by given key
|
||||||
func (fs *FileSessionStore) Delete(key interface{}) error {
|
func (fs *FileSessionStore) Delete(key interface{}) error {
|
||||||
fs.lock.Lock()
|
fs.lock.Lock()
|
||||||
defer fs.lock.Unlock()
|
defer fs.lock.Unlock()
|
||||||
@ -50,6 +54,7 @@ func (fs *FileSessionStore) Delete(key interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean all values in file session
|
||||||
func (fs *FileSessionStore) Flush() error {
|
func (fs *FileSessionStore) Flush() error {
|
||||||
fs.lock.Lock()
|
fs.lock.Lock()
|
||||||
defer fs.lock.Unlock()
|
defer fs.lock.Unlock()
|
||||||
@ -57,10 +62,12 @@ func (fs *FileSessionStore) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get file session store id
|
||||||
func (fs *FileSessionStore) SessionID() string {
|
func (fs *FileSessionStore) SessionID() string {
|
||||||
return fs.sid
|
return fs.sid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write file session to local file with Gob string
|
||||||
func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
|
func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
defer fs.f.Close()
|
defer fs.f.Close()
|
||||||
b, err := encodeGob(fs.values)
|
b, err := encodeGob(fs.values)
|
||||||
@ -72,17 +79,23 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) {
|
|||||||
fs.f.Write(b)
|
fs.f.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// File session provider
|
||||||
type FileProvider struct {
|
type FileProvider struct {
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
savePath string
|
savePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init file session provider.
|
||||||
|
// savePath sets the session files path.
|
||||||
func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
|
func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
fp.maxlifetime = maxlifetime
|
fp.maxlifetime = maxlifetime
|
||||||
fp.savePath = savePath
|
fp.savePath = savePath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read file session by sid.
|
||||||
|
// if file is not exist, create it.
|
||||||
|
// the file path is generated from sid string.
|
||||||
func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) {
|
func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) {
|
||||||
err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
|
err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,6 +130,8 @@ func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) {
|
|||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check file session exist.
|
||||||
|
// it checkes the file named from sid exist or not.
|
||||||
func (fp *FileProvider) SessionExist(sid string) bool {
|
func (fp *FileProvider) SessionExist(sid string) bool {
|
||||||
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
_, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -126,16 +141,20 @@ func (fp *FileProvider) SessionExist(sid string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove all files in this save path
|
||||||
func (fp *FileProvider) SessionDestroy(sid string) error {
|
func (fp *FileProvider) SessionDestroy(sid string) error {
|
||||||
os.Remove(path.Join(fp.savePath))
|
os.Remove(path.Join(fp.savePath))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recycle files in save path
|
||||||
func (fp *FileProvider) SessionGC() {
|
func (fp *FileProvider) SessionGC() {
|
||||||
gcmaxlifetime = fp.maxlifetime
|
gcmaxlifetime = fp.maxlifetime
|
||||||
filepath.Walk(fp.savePath, gcpath)
|
filepath.Walk(fp.savePath, gcpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get active file session number.
|
||||||
|
// it walks save path to count files.
|
||||||
func (fp *FileProvider) SessionAll() int {
|
func (fp *FileProvider) SessionAll() int {
|
||||||
a := &activeSession{}
|
a := &activeSession{}
|
||||||
err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error {
|
err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error {
|
||||||
@ -148,6 +167,8 @@ func (fp *FileProvider) SessionAll() int {
|
|||||||
return a.total
|
return a.total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate new sid for file session.
|
||||||
|
// it delete old file and create new file named from new sid.
|
||||||
func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
||||||
err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777)
|
err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -197,6 +218,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, err
|
|||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove file in save path if expired
|
||||||
func gcpath(path string, info os.FileInfo, err error) error {
|
func gcpath(path string, info os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)}
|
var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)}
|
||||||
|
|
||||||
|
// memory session store.
|
||||||
|
// it saved sessions in a map in memory.
|
||||||
type MemSessionStore struct {
|
type MemSessionStore struct {
|
||||||
sid string //session id
|
sid string //session id
|
||||||
timeAccessed time.Time //last access time
|
timeAccessed time.Time //last access time
|
||||||
@ -16,6 +18,7 @@ type MemSessionStore struct {
|
|||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set value to memory session
|
||||||
func (st *MemSessionStore) Set(key, value interface{}) error {
|
func (st *MemSessionStore) Set(key, value interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -23,6 +26,7 @@ func (st *MemSessionStore) Set(key, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get value from memory session by key
|
||||||
func (st *MemSessionStore) Get(key interface{}) interface{} {
|
func (st *MemSessionStore) Get(key interface{}) interface{} {
|
||||||
st.lock.RLock()
|
st.lock.RLock()
|
||||||
defer st.lock.RUnlock()
|
defer st.lock.RUnlock()
|
||||||
@ -34,6 +38,7 @@ func (st *MemSessionStore) Get(key interface{}) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete in memory session by key
|
||||||
func (st *MemSessionStore) Delete(key interface{}) error {
|
func (st *MemSessionStore) Delete(key interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -41,6 +46,7 @@ func (st *MemSessionStore) Delete(key interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear all values in memory session
|
||||||
func (st *MemSessionStore) Flush() error {
|
func (st *MemSessionStore) Flush() error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -48,27 +54,31 @@ func (st *MemSessionStore) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get this id of memory session store
|
||||||
func (st *MemSessionStore) SessionID() string {
|
func (st *MemSessionStore) SessionID() string {
|
||||||
return st.sid
|
return st.sid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement method, no used.
|
||||||
func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) {
|
func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type MemProvider struct {
|
type MemProvider struct {
|
||||||
lock sync.RWMutex //用来锁
|
lock sync.RWMutex // locker
|
||||||
sessions map[string]*list.Element //用来存储在内存
|
sessions map[string]*list.Element // map in memory
|
||||||
list *list.List //用来做gc
|
list *list.List // for gc
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
savePath string
|
savePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init memory session
|
||||||
func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
|
func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
pder.maxlifetime = maxlifetime
|
pder.maxlifetime = maxlifetime
|
||||||
pder.savePath = savePath
|
pder.savePath = savePath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get memory session store by sid
|
||||||
func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) {
|
func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) {
|
||||||
pder.lock.RLock()
|
pder.lock.RLock()
|
||||||
if element, ok := pder.sessions[sid]; ok {
|
if element, ok := pder.sessions[sid]; ok {
|
||||||
@ -87,6 +97,7 @@ func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check session store exist in memory session by sid
|
||||||
func (pder *MemProvider) SessionExist(sid string) bool {
|
func (pder *MemProvider) SessionExist(sid string) bool {
|
||||||
pder.lock.RLock()
|
pder.lock.RLock()
|
||||||
defer pder.lock.RUnlock()
|
defer pder.lock.RUnlock()
|
||||||
@ -97,6 +108,7 @@ func (pder *MemProvider) SessionExist(sid string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate new sid for session store in memory session
|
||||||
func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
||||||
pder.lock.RLock()
|
pder.lock.RLock()
|
||||||
if element, ok := pder.sessions[oldsid]; ok {
|
if element, ok := pder.sessions[oldsid]; ok {
|
||||||
@ -120,6 +132,7 @@ func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete session store in memory session by id
|
||||||
func (pder *MemProvider) SessionDestroy(sid string) error {
|
func (pder *MemProvider) SessionDestroy(sid string) error {
|
||||||
pder.lock.Lock()
|
pder.lock.Lock()
|
||||||
defer pder.lock.Unlock()
|
defer pder.lock.Unlock()
|
||||||
@ -131,6 +144,7 @@ func (pder *MemProvider) SessionDestroy(sid string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clean expired session stores in memory session
|
||||||
func (pder *MemProvider) SessionGC() {
|
func (pder *MemProvider) SessionGC() {
|
||||||
pder.lock.RLock()
|
pder.lock.RLock()
|
||||||
for {
|
for {
|
||||||
@ -152,10 +166,12 @@ func (pder *MemProvider) SessionGC() {
|
|||||||
pder.lock.RUnlock()
|
pder.lock.RUnlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get count number of memory session
|
||||||
func (pder *MemProvider) SessionAll() int {
|
func (pder *MemProvider) SessionAll() int {
|
||||||
return pder.list.Len()
|
return pder.list.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expand time of session store by id in memory session
|
||||||
func (pder *MemProvider) SessionUpdate(sid string) error {
|
func (pder *MemProvider) SessionUpdate(sid string) error {
|
||||||
pder.lock.Lock()
|
pder.lock.Lock()
|
||||||
defer pder.lock.Unlock()
|
defer pder.lock.Unlock()
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package session
|
package session
|
||||||
|
|
||||||
|
// mysql session support need create table as sql:
|
||||||
// CREATE TABLE `session` (
|
// CREATE TABLE `session` (
|
||||||
// `session_key` char(64) NOT NULL,
|
// `session_key` char(64) NOT NULL,
|
||||||
// `session_data` blob,
|
// session_data` blob,
|
||||||
// `session_expiry` int(11) unsigned NOT NULL,
|
// `session_expiry` int(11) unsigned NOT NULL,
|
||||||
// PRIMARY KEY (`session_key`)
|
// PRIMARY KEY (`session_key`)
|
||||||
// ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
// ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
||||||
@ -18,6 +19,7 @@ import (
|
|||||||
|
|
||||||
var mysqlpder = &MysqlProvider{}
|
var mysqlpder = &MysqlProvider{}
|
||||||
|
|
||||||
|
// mysql session store
|
||||||
type MysqlSessionStore struct {
|
type MysqlSessionStore struct {
|
||||||
c *sql.DB
|
c *sql.DB
|
||||||
sid string
|
sid string
|
||||||
@ -25,6 +27,8 @@ type MysqlSessionStore struct {
|
|||||||
values map[interface{}]interface{}
|
values map[interface{}]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set value in mysql session.
|
||||||
|
// it is temp value in map.
|
||||||
func (st *MysqlSessionStore) Set(key, value interface{}) error {
|
func (st *MysqlSessionStore) Set(key, value interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -32,6 +36,7 @@ func (st *MysqlSessionStore) Set(key, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get value from mysql session
|
||||||
func (st *MysqlSessionStore) Get(key interface{}) interface{} {
|
func (st *MysqlSessionStore) Get(key interface{}) interface{} {
|
||||||
st.lock.RLock()
|
st.lock.RLock()
|
||||||
defer st.lock.RUnlock()
|
defer st.lock.RUnlock()
|
||||||
@ -43,6 +48,7 @@ func (st *MysqlSessionStore) Get(key interface{}) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete value in mysql session
|
||||||
func (st *MysqlSessionStore) Delete(key interface{}) error {
|
func (st *MysqlSessionStore) Delete(key interface{}) error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -50,6 +56,7 @@ func (st *MysqlSessionStore) Delete(key interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear all values in mysql session
|
||||||
func (st *MysqlSessionStore) Flush() error {
|
func (st *MysqlSessionStore) Flush() error {
|
||||||
st.lock.Lock()
|
st.lock.Lock()
|
||||||
defer st.lock.Unlock()
|
defer st.lock.Unlock()
|
||||||
@ -57,10 +64,13 @@ func (st *MysqlSessionStore) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get session id of this mysql session store
|
||||||
func (st *MysqlSessionStore) SessionID() string {
|
func (st *MysqlSessionStore) SessionID() string {
|
||||||
return st.sid
|
return st.sid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save mysql session values to database.
|
||||||
|
// must call this method to save values to database.
|
||||||
func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
|
func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
defer st.c.Close()
|
defer st.c.Close()
|
||||||
b, err := encodeGob(st.values)
|
b, err := encodeGob(st.values)
|
||||||
@ -72,11 +82,13 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mysql session provider
|
||||||
type MysqlProvider struct {
|
type MysqlProvider struct {
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
savePath string
|
savePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connect to mysql
|
||||||
func (mp *MysqlProvider) connectInit() *sql.DB {
|
func (mp *MysqlProvider) connectInit() *sql.DB {
|
||||||
db, e := sql.Open("mysql", mp.savePath)
|
db, e := sql.Open("mysql", mp.savePath)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
@ -85,12 +97,15 @@ func (mp *MysqlProvider) connectInit() *sql.DB {
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init mysql session.
|
||||||
|
// savepath is the connection string of mysql.
|
||||||
func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error {
|
func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
mp.maxlifetime = maxlifetime
|
mp.maxlifetime = maxlifetime
|
||||||
mp.savePath = savePath
|
mp.savePath = savePath
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get mysql session by sid
|
||||||
func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) {
|
func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
row := c.QueryRow("select session_data from session where session_key=?", sid)
|
row := c.QueryRow("select session_data from session where session_key=?", sid)
|
||||||
@ -113,6 +128,7 @@ func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) {
|
|||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check mysql session exist
|
||||||
func (mp *MysqlProvider) SessionExist(sid string) bool {
|
func (mp *MysqlProvider) SessionExist(sid string) bool {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
@ -126,6 +142,7 @@ func (mp *MysqlProvider) SessionExist(sid string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate new sid for mysql session
|
||||||
func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
row := c.QueryRow("select session_data from session where session_key=?", oldsid)
|
row := c.QueryRow("select session_data from session where session_key=?", oldsid)
|
||||||
@ -148,6 +165,7 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er
|
|||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete mysql session by sid
|
||||||
func (mp *MysqlProvider) SessionDestroy(sid string) error {
|
func (mp *MysqlProvider) SessionDestroy(sid string) error {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
c.Exec("DELETE FROM session where session_key=?", sid)
|
c.Exec("DELETE FROM session where session_key=?", sid)
|
||||||
@ -155,6 +173,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete expired values in mysql session
|
||||||
func (mp *MysqlProvider) SessionGC() {
|
func (mp *MysqlProvider) SessionGC() {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
|
c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime)
|
||||||
@ -162,6 +181,7 @@ func (mp *MysqlProvider) SessionGC() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// count values in mysql session
|
||||||
func (mp *MysqlProvider) SessionAll() int {
|
func (mp *MysqlProvider) SessionAll() int {
|
||||||
c := mp.connectInit()
|
c := mp.connectInit()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
@ -11,18 +11,21 @@ import (
|
|||||||
|
|
||||||
var redispder = &RedisProvider{}
|
var redispder = &RedisProvider{}
|
||||||
|
|
||||||
|
// redis max pool size
|
||||||
var MAX_POOL_SIZE = 100
|
var MAX_POOL_SIZE = 100
|
||||||
|
|
||||||
var redisPool chan redis.Conn
|
var redisPool chan redis.Conn
|
||||||
|
|
||||||
|
// redis session store
|
||||||
type RedisSessionStore struct {
|
type RedisSessionStore struct {
|
||||||
c redis.Conn
|
p *redis.Pool
|
||||||
sid string
|
sid string
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
values map[interface{}]interface{}
|
values map[interface{}]interface{}
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set value in redis session
|
||||||
func (rs *RedisSessionStore) Set(key, value interface{}) error {
|
func (rs *RedisSessionStore) Set(key, value interface{}) error {
|
||||||
rs.lock.Lock()
|
rs.lock.Lock()
|
||||||
defer rs.lock.Unlock()
|
defer rs.lock.Unlock()
|
||||||
@ -30,6 +33,7 @@ func (rs *RedisSessionStore) Set(key, value interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get value in redis session
|
||||||
func (rs *RedisSessionStore) Get(key interface{}) interface{} {
|
func (rs *RedisSessionStore) Get(key interface{}) interface{} {
|
||||||
rs.lock.RLock()
|
rs.lock.RLock()
|
||||||
defer rs.lock.RUnlock()
|
defer rs.lock.RUnlock()
|
||||||
@ -41,6 +45,7 @@ func (rs *RedisSessionStore) Get(key interface{}) interface{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete value in redis session
|
||||||
func (rs *RedisSessionStore) Delete(key interface{}) error {
|
func (rs *RedisSessionStore) Delete(key interface{}) error {
|
||||||
rs.lock.Lock()
|
rs.lock.Lock()
|
||||||
defer rs.lock.Unlock()
|
defer rs.lock.Unlock()
|
||||||
@ -48,6 +53,7 @@ func (rs *RedisSessionStore) Delete(key interface{}) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear all values in redis session
|
||||||
func (rs *RedisSessionStore) Flush() error {
|
func (rs *RedisSessionStore) Flush() error {
|
||||||
rs.lock.Lock()
|
rs.lock.Lock()
|
||||||
defer rs.lock.Unlock()
|
defer rs.lock.Unlock()
|
||||||
@ -55,20 +61,31 @@ func (rs *RedisSessionStore) Flush() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get redis session id
|
||||||
func (rs *RedisSessionStore) SessionID() string {
|
func (rs *RedisSessionStore) SessionID() string {
|
||||||
return rs.sid
|
return rs.sid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// save session values to redis
|
||||||
func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
|
func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) {
|
||||||
defer rs.c.Close()
|
c := rs.p.Get()
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
// if rs.values is empty, return directly
|
||||||
|
if len(rs.values) < 1 {
|
||||||
|
c.Do("DEL", rs.sid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
b, err := encodeGob(rs.values)
|
b, err := encodeGob(rs.values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rs.c.Do("SET", rs.sid, string(b))
|
|
||||||
rs.c.Do("EXPIRE", rs.sid, rs.maxlifetime)
|
c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redis session provider
|
||||||
type RedisProvider struct {
|
type RedisProvider struct {
|
||||||
maxlifetime int64
|
maxlifetime int64
|
||||||
savePath string
|
savePath string
|
||||||
@ -77,8 +94,9 @@ type RedisProvider struct {
|
|||||||
poollist *redis.Pool
|
poollist *redis.Pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init redis session
|
||||||
// savepath like redis server addr,pool size,password
|
// savepath like redis server addr,pool size,password
|
||||||
//127.0.0.1:6379,100,astaxie
|
// e.g. 127.0.0.1:6379,100,astaxie
|
||||||
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
||||||
rp.maxlifetime = maxlifetime
|
rp.maxlifetime = maxlifetime
|
||||||
configs := strings.Split(savePath, ",")
|
configs := strings.Split(savePath, ",")
|
||||||
@ -114,12 +132,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read redis session by sid
|
||||||
func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) {
|
func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) {
|
||||||
c := rp.poollist.Get()
|
c := rp.poollist.Get()
|
||||||
if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
|
defer c.Close()
|
||||||
c.Do("SET", sid)
|
|
||||||
}
|
|
||||||
c.Do("EXPIRE", sid, rp.maxlifetime)
|
|
||||||
kvs, err := redis.String(c.Do("GET", sid))
|
kvs, err := redis.String(c.Do("GET", sid))
|
||||||
var kv map[interface{}]interface{}
|
var kv map[interface{}]interface{}
|
||||||
if len(kvs) == 0 {
|
if len(kvs) == 0 {
|
||||||
@ -130,13 +147,16 @@ func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs := &RedisSessionStore{c: c, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
|
||||||
|
rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check redis session exist by sid
|
||||||
func (rp *RedisProvider) SessionExist(sid string) bool {
|
func (rp *RedisProvider) SessionExist(sid string) bool {
|
||||||
c := rp.poollist.Get()
|
c := rp.poollist.Get()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
|
if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 {
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
@ -144,13 +164,21 @@ func (rp *RedisProvider) SessionExist(sid string) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate new sid for redis session
|
||||||
func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) {
|
||||||
c := rp.poollist.Get()
|
c := rp.poollist.Get()
|
||||||
if existed, err := redis.Int(c.Do("EXISTS", oldsid)); err != nil || existed == 0 {
|
defer c.Close()
|
||||||
c.Do("SET", oldsid)
|
|
||||||
}
|
if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 {
|
||||||
|
// oldsid doesn't exists, set the new sid directly
|
||||||
|
// ignore error here, since if it return error
|
||||||
|
// the existed value will be 0
|
||||||
|
c.Do("SET", sid, "", "EX", rp.maxlifetime)
|
||||||
|
} else {
|
||||||
c.Do("RENAME", oldsid, sid)
|
c.Do("RENAME", oldsid, sid)
|
||||||
c.Do("EXPIRE", sid, rp.maxlifetime)
|
c.Do("EXPIRE", sid, rp.maxlifetime)
|
||||||
|
}
|
||||||
|
|
||||||
kvs, err := redis.String(c.Do("GET", sid))
|
kvs, err := redis.String(c.Do("GET", sid))
|
||||||
var kv map[interface{}]interface{}
|
var kv map[interface{}]interface{}
|
||||||
if len(kvs) == 0 {
|
if len(kvs) == 0 {
|
||||||
@ -161,24 +189,27 @@ func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rs := &RedisSessionStore{c: c, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
|
||||||
|
rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime}
|
||||||
return rs, nil
|
return rs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// delete redis session by id
|
||||||
func (rp *RedisProvider) SessionDestroy(sid string) error {
|
func (rp *RedisProvider) SessionDestroy(sid string) error {
|
||||||
c := rp.poollist.Get()
|
c := rp.poollist.Get()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
c.Do("DEL", sid)
|
c.Do("DEL", sid)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Impelment method, no used.
|
||||||
func (rp *RedisProvider) SessionGC() {
|
func (rp *RedisProvider) SessionGC() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// @todo
|
// @todo
|
||||||
func (rp *RedisProvider) SessionAll() int {
|
func (rp *RedisProvider) SessionAll() int {
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SessionStore contains all data for one session process with specific id.
|
||||||
type SessionStore interface {
|
type SessionStore interface {
|
||||||
Set(key, value interface{}) error //set session value
|
Set(key, value interface{}) error //set session value
|
||||||
Get(key interface{}) interface{} //get session value
|
Get(key interface{}) interface{} //get session value
|
||||||
@ -23,6 +24,8 @@ type SessionStore interface {
|
|||||||
Flush() error //delete all data
|
Flush() error //delete all data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provider contains global session methods and saved SessionStores.
|
||||||
|
// it can operate a SessionStore by its id.
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
SessionInit(gclifetime int64, config string) error
|
SessionInit(gclifetime int64, config string) error
|
||||||
SessionRead(sid string) (SessionStore, error)
|
SessionRead(sid string) (SessionStore, error)
|
||||||
@ -61,12 +64,20 @@ type managerConfig struct {
|
|||||||
ProviderConfig string `json:"providerConfig"`
|
ProviderConfig string `json:"providerConfig"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Manager contains Provider and its configuration.
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
provider Provider
|
provider Provider
|
||||||
config *managerConfig
|
config *managerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
//options
|
// Create new Manager with provider name and json config string.
|
||||||
|
// provider name:
|
||||||
|
// 1. cookie
|
||||||
|
// 2. file
|
||||||
|
// 3. memory
|
||||||
|
// 4. redis
|
||||||
|
// 5. mysql
|
||||||
|
// json config:
|
||||||
// 1. is https default false
|
// 1. is https default false
|
||||||
// 2. hashfunc default sha1
|
// 2. hashfunc default sha1
|
||||||
// 3. hashkey default beegosessionkey
|
// 3. hashkey default beegosessionkey
|
||||||
@ -102,7 +113,8 @@ func NewManager(provideName, config string) (*Manager, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//get Session
|
// Start session. generate or read the session id from http request.
|
||||||
|
// if session id exists, return SessionStore with this id.
|
||||||
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
||||||
cookie, err := r.Cookie(manager.config.CookieName)
|
cookie, err := r.Cookie(manager.config.CookieName)
|
||||||
if err != nil || cookie.Value == "" {
|
if err != nil || cookie.Value == "" {
|
||||||
@ -144,7 +156,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//Destroy sessionid
|
// Destroy session by its id in http request cookie.
|
||||||
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
||||||
cookie, err := r.Cookie(manager.config.CookieName)
|
cookie, err := r.Cookie(manager.config.CookieName)
|
||||||
if err != nil || cookie.Value == "" {
|
if err != nil || cookie.Value == "" {
|
||||||
@ -161,16 +173,20 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get SessionStore by its id.
|
||||||
func (manager *Manager) GetProvider(sid string) (sessions SessionStore, err error) {
|
func (manager *Manager) GetProvider(sid string) (sessions SessionStore, err error) {
|
||||||
sessions, err = manager.provider.SessionRead(sid)
|
sessions, err = manager.provider.SessionRead(sid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start session gc process.
|
||||||
|
// it can do gc in times after gc lifetime.
|
||||||
func (manager *Manager) GC() {
|
func (manager *Manager) GC() {
|
||||||
manager.provider.SessionGC()
|
manager.provider.SessionGC()
|
||||||
time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() })
|
time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regenerate a session id for this SessionStore who's id is saving in http request.
|
||||||
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) {
|
||||||
sid := manager.sessionId(r)
|
sid := manager.sessionId(r)
|
||||||
cookie, err := r.Cookie(manager.config.CookieName)
|
cookie, err := r.Cookie(manager.config.CookieName)
|
||||||
@ -198,20 +214,23 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get all active sessions count number.
|
||||||
func (manager *Manager) GetActiveSession() int {
|
func (manager *Manager) GetActiveSession() int {
|
||||||
return manager.provider.SessionAll()
|
return manager.provider.SessionAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set hash function for generating session id.
|
||||||
func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
|
func (manager *Manager) SetHashFunc(hasfunc, hashkey string) {
|
||||||
manager.config.SessionIDHashFunc = hasfunc
|
manager.config.SessionIDHashFunc = hasfunc
|
||||||
manager.config.SessionIDHashKey = hashkey
|
manager.config.SessionIDHashKey = hashkey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set cookie with https.
|
||||||
func (manager *Manager) SetSecure(secure bool) {
|
func (manager *Manager) SetSecure(secure bool) {
|
||||||
manager.config.Secure = secure
|
manager.config.Secure = secure
|
||||||
}
|
}
|
||||||
|
|
||||||
//remote_addr cruunixnano randdata
|
// generate session id with rand string, unix nano time, remote addr by hash function.
|
||||||
func (manager *Manager) sessionId(r *http.Request) (sid string) {
|
func (manager *Manager) sessionId(r *http.Request) (sid string) {
|
||||||
bs := make([]byte, 24)
|
bs := make([]byte, 24)
|
||||||
if _, err := io.ReadFull(rand.Reader, bs); err != nil {
|
if _, err := io.ReadFull(rand.Reader, bs); err != nil {
|
||||||
|
@ -29,16 +29,13 @@ type pointerInfo struct {
|
|||||||
used []int
|
used []int
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// print the data in console
|
// print the data in console
|
||||||
//
|
|
||||||
func Display(data ...interface{}) {
|
func Display(data ...interface{}) {
|
||||||
display(true, data...)
|
display(true, data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// return string
|
// return data print string
|
||||||
//
|
|
||||||
func GetDisplayString(data ...interface{}) string {
|
func GetDisplayString(data ...interface{}) string {
|
||||||
return display(false, data...)
|
return display(false, data...)
|
||||||
}
|
}
|
||||||
@ -67,9 +64,7 @@ func display(displayed bool, data ...interface{}) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// return data dump and format bytes
|
||||||
// return fomateinfo
|
|
||||||
//
|
|
||||||
func fomateinfo(headlen int, data ...interface{}) []byte {
|
func fomateinfo(headlen int, data ...interface{}) []byte {
|
||||||
var buf = new(bytes.Buffer)
|
var buf = new(bytes.Buffer)
|
||||||
|
|
||||||
@ -108,6 +103,7 @@ func fomateinfo(headlen int, data ...interface{}) []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check data is golang basic type
|
||||||
func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool {
|
func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
@ -158,6 +154,7 @@ func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo,
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dump value
|
||||||
func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) {
|
func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) {
|
||||||
var t = val.Kind()
|
var t = val.Kind()
|
||||||
|
|
||||||
@ -367,6 +364,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dump pointer value
|
||||||
func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
|
func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
|
||||||
var anyused = false
|
var anyused = false
|
||||||
var pointerNum = 0
|
var pointerNum = 0
|
||||||
@ -434,9 +432,7 @@ func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// get stack bytes
|
||||||
// get stack info
|
|
||||||
//
|
|
||||||
func stack(skip int, indent string) []byte {
|
func stack(skip int, indent string) []byte {
|
||||||
var buf = new(bytes.Buffer)
|
var buf = new(bytes.Buffer)
|
||||||
|
|
||||||
@ -455,7 +451,7 @@ func stack(skip int, indent string) []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// function returns, if possible, the name of the function containing the PC.
|
// return the name of the function containing the PC if possible,
|
||||||
func function(pc uintptr) []byte {
|
func function(pc uintptr) []byte {
|
||||||
fn := runtime.FuncForPC(pc)
|
fn := runtime.FuncForPC(pc)
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
|
@ -13,12 +13,15 @@ package toolbox
|
|||||||
|
|
||||||
//AddHealthCheck("database",&DatabaseCheck{})
|
//AddHealthCheck("database",&DatabaseCheck{})
|
||||||
|
|
||||||
|
// health checker map
|
||||||
var AdminCheckList map[string]HealthChecker
|
var AdminCheckList map[string]HealthChecker
|
||||||
|
|
||||||
|
// health checker interface
|
||||||
type HealthChecker interface {
|
type HealthChecker interface {
|
||||||
Check() error
|
Check() error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add health checker with name string
|
||||||
func AddHealthCheck(name string, hc HealthChecker) {
|
func AddHealthCheck(name string, hc HealthChecker) {
|
||||||
AdminCheckList[name] = hc
|
AdminCheckList[name] = hc
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ func init() {
|
|||||||
pid = os.Getpid()
|
pid = os.Getpid()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse input command string
|
||||||
func ProcessInput(input string, w io.Writer) {
|
func ProcessInput(input string, w io.Writer) {
|
||||||
switch input {
|
switch input {
|
||||||
case "lookup goroutine":
|
case "lookup goroutine":
|
||||||
@ -44,6 +45,7 @@ func ProcessInput(input string, w io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// record memory profile in pprof
|
||||||
func MemProf() {
|
func MemProf() {
|
||||||
if f, err := os.Create("mem-" + strconv.Itoa(pid) + ".memprof"); err != nil {
|
if f, err := os.Create("mem-" + strconv.Itoa(pid) + ".memprof"); err != nil {
|
||||||
log.Fatal("record memory profile failed: %v", err)
|
log.Fatal("record memory profile failed: %v", err)
|
||||||
@ -54,6 +56,7 @@ func MemProf() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start cpu profile monitor
|
||||||
func StartCPUProfile() {
|
func StartCPUProfile() {
|
||||||
f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
|
f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -62,10 +65,12 @@ func StartCPUProfile() {
|
|||||||
pprof.StartCPUProfile(f)
|
pprof.StartCPUProfile(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stop cpu profile monitor
|
||||||
func StopCPUProfile() {
|
func StopCPUProfile() {
|
||||||
pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// print gc information to io.Writer
|
||||||
func PrintGCSummary(w io.Writer) {
|
func PrintGCSummary(w io.Writer) {
|
||||||
memStats := &runtime.MemStats{}
|
memStats := &runtime.MemStats{}
|
||||||
runtime.ReadMemStats(memStats)
|
runtime.ReadMemStats(memStats)
|
||||||
@ -114,7 +119,7 @@ func avg(items []time.Duration) time.Duration {
|
|||||||
return time.Duration(int64(sum) / int64(len(items)))
|
return time.Duration(int64(sum) / int64(len(items)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// human readable format
|
// format bytes number friendly
|
||||||
func toH(bytes uint64) string {
|
func toH(bytes uint64) string {
|
||||||
switch {
|
switch {
|
||||||
case bytes < 1024:
|
case bytes < 1024:
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Statistics struct
|
||||||
type Statistics struct {
|
type Statistics struct {
|
||||||
RequestUrl string
|
RequestUrl string
|
||||||
RequestController string
|
RequestController string
|
||||||
@ -16,12 +17,15 @@ type Statistics struct {
|
|||||||
TotalTime time.Duration
|
TotalTime time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UrlMap contains several statistics struct to log different data
|
||||||
type UrlMap struct {
|
type UrlMap struct {
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
LengthLimit int //limit the urlmap's length if it's equal to 0 there's no limit
|
LengthLimit int //limit the urlmap's length if it's equal to 0 there's no limit
|
||||||
urlmap map[string]map[string]*Statistics
|
urlmap map[string]map[string]*Statistics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add statistics task.
|
||||||
|
// it needs request method, request url, request controller and statistics time duration
|
||||||
func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController string, requesttime time.Duration) {
|
func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController string, requesttime time.Duration) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
@ -65,6 +69,7 @@ func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController stri
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// put url statistics result in io.Writer
|
||||||
func (m *UrlMap) GetMap(rw io.Writer) {
|
func (m *UrlMap) GetMap(rw io.Writer) {
|
||||||
m.lock.RLock()
|
m.lock.RLock()
|
||||||
defer m.lock.RUnlock()
|
defer m.lock.RUnlock()
|
||||||
@ -78,6 +83,7 @@ func (m *UrlMap) GetMap(rw io.Writer) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// global statistics data map
|
||||||
var StatisticsMap *UrlMap
|
var StatisticsMap *UrlMap
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -53,6 +53,7 @@ const (
|
|||||||
starBit = 1 << 63
|
starBit = 1 << 63
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// time taks schedule
|
||||||
type Schedule struct {
|
type Schedule struct {
|
||||||
Second uint64
|
Second uint64
|
||||||
Minute uint64
|
Minute uint64
|
||||||
@ -62,8 +63,10 @@ type Schedule struct {
|
|||||||
Week uint64
|
Week uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// task func type
|
||||||
type TaskFunc func() error
|
type TaskFunc func() error
|
||||||
|
|
||||||
|
// task interface
|
||||||
type Tasker interface {
|
type Tasker interface {
|
||||||
GetStatus() string
|
GetStatus() string
|
||||||
Run() error
|
Run() error
|
||||||
@ -73,21 +76,24 @@ type Tasker interface {
|
|||||||
GetPrev() time.Time
|
GetPrev() time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// task error
|
||||||
type taskerr struct {
|
type taskerr struct {
|
||||||
t time.Time
|
t time.Time
|
||||||
errinfo string
|
errinfo string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// task struct
|
||||||
type Task struct {
|
type Task struct {
|
||||||
Taskname string
|
Taskname string
|
||||||
Spec *Schedule
|
Spec *Schedule
|
||||||
DoFunc TaskFunc
|
DoFunc TaskFunc
|
||||||
Prev time.Time
|
Prev time.Time
|
||||||
Next time.Time
|
Next time.Time
|
||||||
Errlist []*taskerr //errtime:errinfo
|
Errlist []*taskerr // like errtime:errinfo
|
||||||
ErrLimit int //max length for the errlist 0 stand for there' no limit
|
ErrLimit int // max length for the errlist, 0 stand for no limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add new task with name, time and func
|
||||||
func NewTask(tname string, spec string, f TaskFunc) *Task {
|
func NewTask(tname string, spec string, f TaskFunc) *Task {
|
||||||
|
|
||||||
task := &Task{
|
task := &Task{
|
||||||
@ -99,6 +105,7 @@ func NewTask(tname string, spec string, f TaskFunc) *Task {
|
|||||||
return task
|
return task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get current task status
|
||||||
func (tk *Task) GetStatus() string {
|
func (tk *Task) GetStatus() string {
|
||||||
var str string
|
var str string
|
||||||
for _, v := range tk.Errlist {
|
for _, v := range tk.Errlist {
|
||||||
@ -107,6 +114,7 @@ func (tk *Task) GetStatus() string {
|
|||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run task
|
||||||
func (tk *Task) Run() error {
|
func (tk *Task) Run() error {
|
||||||
err := tk.DoFunc()
|
err := tk.DoFunc()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -117,53 +125,58 @@ func (tk *Task) Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set next time for this task
|
||||||
func (tk *Task) SetNext(now time.Time) {
|
func (tk *Task) SetNext(now time.Time) {
|
||||||
tk.Next = tk.Spec.Next(now)
|
tk.Next = tk.Spec.Next(now)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the next call time of this task
|
||||||
func (tk *Task) GetNext() time.Time {
|
func (tk *Task) GetNext() time.Time {
|
||||||
return tk.Next
|
return tk.Next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set prev time of this task
|
||||||
func (tk *Task) SetPrev(now time.Time) {
|
func (tk *Task) SetPrev(now time.Time) {
|
||||||
tk.Prev = now
|
tk.Prev = now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get prev time of this task
|
||||||
func (tk *Task) GetPrev() time.Time {
|
func (tk *Task) GetPrev() time.Time {
|
||||||
return tk.Prev
|
return tk.Prev
|
||||||
}
|
}
|
||||||
|
|
||||||
//前6个字段分别表示:
|
// six columns mean:
|
||||||
// 秒钟:0-59
|
// second:0-59
|
||||||
// 分钟:0-59
|
// minute:0-59
|
||||||
// 小时:1-23
|
// hour:1-23
|
||||||
// 日期:1-31
|
// day:1-31
|
||||||
// 月份:1-12
|
// month:1-12
|
||||||
// 星期:0-6(0表示周日)
|
// week:0-6(0 means Sunday)
|
||||||
|
|
||||||
//还可以用一些特殊符号:
|
// some signals:
|
||||||
// *: 表示任何时刻
|
// *: any time
|
||||||
// ,: 表示分割,如第三段里:2,4,表示2点和4点执行
|
// ,: separate signal
|
||||||
// -:表示一个段,如第三端里: 1-5,就表示1到5点
|
// -:duration
|
||||||
// /n : 表示每个n的单位执行一次,如第三段里,*/1, 就表示每隔1个小时执行一次命令。也可以写成1-23/1.
|
// /n : do as n times of time duration
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// 0/30 * * * * * 每30秒 执行
|
// 0/30 * * * * * every 30s
|
||||||
// 0 43 21 * * * 21:43 执行
|
// 0 43 21 * * * 21:43
|
||||||
// 0 15 05 * * * 05:15 执行
|
// 0 15 05 * * * 05:15
|
||||||
// 0 0 17 * * * 17:00 执行
|
// 0 0 17 * * * 17:00
|
||||||
// 0 0 17 * * 1 每周一的 17:00 执行
|
// 0 0 17 * * 1 17:00 in every Monday
|
||||||
// 0 0,10 17 * * 0,2,3 每周日,周二,周三的 17:00和 17:10 执行
|
// 0 0,10 17 * * 0,2,3 17:00 and 17:10 in every Sunday, Tuesday and Wednesday
|
||||||
// 0 0-10 17 1 * * 毎月1日从 17:00到7:10 毎隔1分钟 执行
|
// 0 0-10 17 1 * * 17:00 to 17:10 in 1 min duration each time on the first day of month
|
||||||
// 0 0 0 1,15 * 1 毎月1日和 15日和 一日的 0:00 执行
|
// 0 0 0 1,15 * 1 0:00 on the 1st day and 15th day of month
|
||||||
// 0 42 4 1 * * 毎月1日的 4:42分 执行
|
// 0 42 4 1 * * 4:42 on the 1st day of month
|
||||||
// 0 0 21 * * 1-6 周一到周六 21:00 执行
|
// 0 0 21 * * 1-6 21:00 from Monday to Saturday
|
||||||
// 0 0,10,20,30,40,50 * * * * 每隔10分 执行
|
// 0 0,10,20,30,40,50 * * * * every 10 min duration
|
||||||
// 0 */10 * * * * 每隔10分 执行
|
// 0 */10 * * * * every 10 min duration
|
||||||
// 0 * 1 * * * 从1:0到1:59 每隔1分钟 执行
|
// 0 * 1 * * * 1:00 to 1:59 in 1 min duration each time
|
||||||
// 0 0 1 * * * 1:00 执行
|
// 0 0 1 * * * 1:00
|
||||||
// 0 0 */1 * * * 毎时0分 每隔1小时 执行
|
// 0 0 */1 * * * 0 min of hour in 1 hour duration
|
||||||
// 0 0 * * * * 毎时0分 每隔1小时 执行
|
// 0 0 * * * * 0 min of hour in 1 hour duration
|
||||||
// 0 2 8-20/3 * * * 8:02,11:02,14:02,17:02,20:02 执行
|
// 0 2 8-20/3 * * * 8:02, 11:02, 14:02, 17:02, 20:02
|
||||||
// 0 30 5 1,15 * * 1日 和 15日的 5:30 执行
|
// 0 30 5 1,15 * * 5:30 on the 1st day and 15th day of month
|
||||||
func (t *Task) SetCron(spec string) {
|
func (t *Task) SetCron(spec string) {
|
||||||
t.Spec = t.parse(spec)
|
t.Spec = t.parse(spec)
|
||||||
}
|
}
|
||||||
@ -252,6 +265,7 @@ func (t *Task) parseSpec(spec string) *Schedule {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set schedule to next time
|
||||||
func (s *Schedule) Next(t time.Time) time.Time {
|
func (s *Schedule) Next(t time.Time) time.Time {
|
||||||
|
|
||||||
// Start at the earliest possible time (the upcoming second).
|
// Start at the earliest possible time (the upcoming second).
|
||||||
@ -349,6 +363,7 @@ func dayMatches(s *Schedule, t time.Time) bool {
|
|||||||
return domMatch || dowMatch
|
return domMatch || dowMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start all tasks
|
||||||
func StartTask() {
|
func StartTask() {
|
||||||
go run()
|
go run()
|
||||||
}
|
}
|
||||||
@ -388,10 +403,12 @@ func run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// start all tasks
|
||||||
func StopTask() {
|
func StopTask() {
|
||||||
stop <- true
|
stop <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add task with name
|
||||||
func AddTask(taskname string, t Tasker) {
|
func AddTask(taskname string, t Tasker) {
|
||||||
AdminTaskList[taskname] = t
|
AdminTaskList[taskname] = t
|
||||||
}
|
}
|
||||||
@ -402,6 +419,7 @@ type MapSorter struct {
|
|||||||
Vals []Tasker
|
Vals []Tasker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create new tasker map
|
||||||
func NewMapSorter(m map[string]Tasker) *MapSorter {
|
func NewMapSorter(m map[string]Tasker) *MapSorter {
|
||||||
ms := &MapSorter{
|
ms := &MapSorter{
|
||||||
Keys: make([]string, 0, len(m)),
|
Keys: make([]string, 0, len(m)),
|
||||||
@ -414,6 +432,7 @@ func NewMapSorter(m map[string]Tasker) *MapSorter {
|
|||||||
return ms
|
return ms
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sort tasker map
|
||||||
func (ms *MapSorter) Sort() {
|
func (ms *MapSorter) Sort() {
|
||||||
sort.Sort(ms)
|
sort.Sort(ms)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user