Merge branch 'develop' of git://github.com/astaxie/beego
Showing
20 changed files
with
666 additions
and
149 deletions
| ... | @@ -45,6 +45,7 @@ type Controller struct { | ... | @@ -45,6 +45,7 @@ type Controller struct { |
| 45 | CruSession session.SessionStore | 45 | CruSession session.SessionStore |
| 46 | XSRFExpire int | 46 | XSRFExpire int |
| 47 | AppController interface{} | 47 | AppController interface{} |
| 48 | EnableReander bool | ||
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | // ControllerInterface is an interface to uniform all controller handler. | 51 | // ControllerInterface is an interface to uniform all controller handler. |
| ... | @@ -74,6 +75,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin | ... | @@ -74,6 +75,7 @@ func (c *Controller) Init(ctx *context.Context, controllerName, actionName strin |
| 74 | c.Ctx = ctx | 75 | c.Ctx = ctx |
| 75 | c.TplExt = "tpl" | 76 | c.TplExt = "tpl" |
| 76 | c.AppController = app | 77 | c.AppController = app |
| 78 | c.EnableReander = true | ||
| 77 | } | 79 | } |
| 78 | 80 | ||
| 79 | // Prepare runs after Init before request function execution. | 81 | // Prepare runs after Init before request function execution. |
| ... | @@ -123,6 +125,9 @@ func (c *Controller) Options() { | ... | @@ -123,6 +125,9 @@ func (c *Controller) Options() { |
| 123 | 125 | ||
| 124 | // Render sends the response with rendered template bytes as text/html type. | 126 | // Render sends the response with rendered template bytes as text/html type. |
| 125 | func (c *Controller) Render() error { | 127 | func (c *Controller) Render() error { |
| 128 | if !c.EnableReander { | ||
| 129 | return nil | ||
| 130 | } | ||
| 126 | rb, err := c.RenderBytes() | 131 | rb, err := c.RenderBytes() |
| 127 | 132 | ||
| 128 | if err != nil { | 133 | if err != nil { |
| ... | @@ -398,6 +403,7 @@ func (c *Controller) SessionRegenerateID() { | ... | @@ -398,6 +403,7 @@ func (c *Controller) SessionRegenerateID() { |
| 398 | 403 | ||
| 399 | // DestroySession cleans session data and session cookie. | 404 | // DestroySession cleans session data and session cookie. |
| 400 | func (c *Controller) DestroySession() { | 405 | func (c *Controller) DestroySession() { |
| 406 | c.Ctx.Input.CruSession.Flush() | ||
| 401 | GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request) | 407 | GlobalSessions.SessionDestroy(c.Ctx.ResponseWriter, c.Ctx.Request) |
| 402 | } | 408 | } |
| 403 | 409 | ... | ... |
| ... | @@ -4,8 +4,28 @@ import ( | ... | @@ -4,8 +4,28 @@ import ( |
| 4 | "encoding/json" | 4 | "encoding/json" |
| 5 | "log" | 5 | "log" |
| 6 | "os" | 6 | "os" |
| 7 | "runtime" | ||
| 7 | ) | 8 | ) |
| 8 | 9 | ||
| 10 | type Brush func(string) string | ||
| 11 | |||
| 12 | func NewBrush(color string) Brush { | ||
| 13 | pre := "\033[" | ||
| 14 | reset := "\033[0m" | ||
| 15 | return func(text string) string { | ||
| 16 | return pre + color + "m" + text + reset | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | var colors = []Brush{ | ||
| 21 | NewBrush("1;36"), // Trace cyan | ||
| 22 | NewBrush("1;34"), // Debug blue | ||
| 23 | NewBrush("1;32"), // Info green | ||
| 24 | NewBrush("1;33"), // Warn yellow | ||
| 25 | NewBrush("1;31"), // Error red | ||
| 26 | NewBrush("1;35"), // Critical purple | ||
| 27 | } | ||
| 28 | |||
| 9 | // ConsoleWriter implements LoggerInterface and writes messages to terminal. | 29 | // ConsoleWriter implements LoggerInterface and writes messages to terminal. |
| 10 | type ConsoleWriter struct { | 30 | type ConsoleWriter struct { |
| 11 | lg *log.Logger | 31 | lg *log.Logger |
| ... | @@ -35,7 +55,11 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error { | ... | @@ -35,7 +55,11 @@ func (c *ConsoleWriter) WriteMsg(msg string, level int) error { |
| 35 | if level < c.Level { | 55 | if level < c.Level { |
| 36 | return nil | 56 | return nil |
| 37 | } | 57 | } |
| 38 | c.lg.Println(msg) | 58 | if goos := runtime.GOOS; goos == "windows" { |
| 59 | c.lg.Println(msg) | ||
| 60 | } else { | ||
| 61 | c.lg.Println(colors[level](msg)) | ||
| 62 | } | ||
| 39 | return nil | 63 | return nil |
| 40 | } | 64 | } |
| 41 | 65 | ... | ... |
| ... | @@ -1350,6 +1350,10 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond | ... | @@ -1350,6 +1350,10 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond |
| 1350 | return cnt, nil | 1350 | return cnt, nil |
| 1351 | } | 1351 | } |
| 1352 | 1352 | ||
| 1353 | func (d *dbBase) RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error) { | ||
| 1354 | return 0, nil | ||
| 1355 | } | ||
| 1356 | |||
| 1353 | // flag of update joined record. | 1357 | // flag of update joined record. |
| 1354 | func (d *dbBase) SupportUpdateJoin() bool { | 1358 | func (d *dbBase) SupportUpdateJoin() bool { |
| 1355 | return true | 1359 | return true | ... | ... |
| ... | @@ -3,7 +3,6 @@ package orm | ... | @@ -3,7 +3,6 @@ package orm |
| 3 | import ( | 3 | import ( |
| 4 | "database/sql" | 4 | "database/sql" |
| 5 | "fmt" | 5 | "fmt" |
| 6 | "os" | ||
| 7 | "reflect" | 6 | "reflect" |
| 8 | "sync" | 7 | "sync" |
| 9 | "time" | 8 | "time" |
| ... | @@ -13,11 +12,11 @@ import ( | ... | @@ -13,11 +12,11 @@ import ( |
| 13 | type DriverType int | 12 | type DriverType int |
| 14 | 13 | ||
| 15 | const ( | 14 | const ( |
| 16 | _ DriverType = iota // int enum type | 15 | _ DriverType = iota // int enum type |
| 17 | DR_MySQL // mysql | 16 | DR_MySQL // mysql |
| 18 | DR_Sqlite // sqlite | 17 | DR_Sqlite // sqlite |
| 19 | DR_Oracle // oracle | 18 | DR_Oracle // oracle |
| 20 | DR_Postgres // pgsql | 19 | DR_Postgres // pgsql |
| 21 | ) | 20 | ) |
| 22 | 21 | ||
| 23 | // database driver string. | 22 | // database driver string. |
| ... | @@ -96,40 +95,15 @@ type alias struct { | ... | @@ -96,40 +95,15 @@ type alias struct { |
| 96 | Engine string | 95 | Engine string |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | // Setting the database connect params. Use the database driver self dataSource args. | 98 | func detectTZ(al *alias) { |
| 100 | func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) { | ||
| 101 | al := new(alias) | ||
| 102 | al.Name = aliasName | ||
| 103 | al.DriverName = driverName | ||
| 104 | al.DataSource = dataSource | ||
| 105 | |||
| 106 | var ( | ||
| 107 | err error | ||
| 108 | ) | ||
| 109 | |||
| 110 | if dr, ok := drivers[driverName]; ok { | ||
| 111 | al.DbBaser = dbBasers[dr] | ||
| 112 | al.Driver = dr | ||
| 113 | } else { | ||
| 114 | err = fmt.Errorf("driver name `%s` have not registered", driverName) | ||
| 115 | goto end | ||
| 116 | } | ||
| 117 | |||
| 118 | if dataBaseCache.add(aliasName, al) == false { | ||
| 119 | err = fmt.Errorf("db name `%s` already registered, cannot reuse", aliasName) | ||
| 120 | goto end | ||
| 121 | } | ||
| 122 | |||
| 123 | al.DB, err = sql.Open(driverName, dataSource) | ||
| 124 | if err != nil { | ||
| 125 | err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error()) | ||
| 126 | goto end | ||
| 127 | } | ||
| 128 | |||
| 129 | // orm timezone system match database | 99 | // orm timezone system match database |
| 130 | // default use Local | 100 | // default use Local |
| 131 | al.TZ = time.Local | 101 | al.TZ = time.Local |
| 132 | 102 | ||
| 103 | if al.DriverName == "sphinx" { | ||
| 104 | return | ||
| 105 | } | ||
| 106 | |||
| 133 | switch al.Driver { | 107 | switch al.Driver { |
| 134 | case DR_MySQL: | 108 | case DR_MySQL: |
| 135 | row := al.DB.QueryRow("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP)") | 109 | row := al.DB.QueryRow("SELECT TIMEDIFF(NOW(), UTC_TIMESTAMP)") |
| ... | @@ -173,6 +147,60 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) { | ... | @@ -173,6 +147,60 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) { |
| 173 | DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error()) | 147 | DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error()) |
| 174 | } | 148 | } |
| 175 | } | 149 | } |
| 150 | } | ||
| 151 | |||
| 152 | func addAliasWthDB(aliasName, driverName string, db *sql.DB) (*alias, error) { | ||
| 153 | al := new(alias) | ||
| 154 | al.Name = aliasName | ||
| 155 | al.DriverName = driverName | ||
| 156 | al.DB = db | ||
| 157 | |||
| 158 | if dr, ok := drivers[driverName]; ok { | ||
| 159 | al.DbBaser = dbBasers[dr] | ||
| 160 | al.Driver = dr | ||
| 161 | } else { | ||
| 162 | return nil, fmt.Errorf("driver name `%s` have not registered", driverName) | ||
| 163 | } | ||
| 164 | |||
| 165 | err := db.Ping() | ||
| 166 | if err != nil { | ||
| 167 | return nil, fmt.Errorf("register db Ping `%s`, %s", aliasName, err.Error()) | ||
| 168 | } | ||
| 169 | |||
| 170 | if dataBaseCache.add(aliasName, al) == false { | ||
| 171 | return nil, fmt.Errorf("db name `%s` already registered, cannot reuse", aliasName) | ||
| 172 | } | ||
| 173 | |||
| 174 | return al, nil | ||
| 175 | } | ||
| 176 | |||
| 177 | func AddAliasWthDB(aliasName, driverName string, db *sql.DB) error { | ||
| 178 | _, err := addAliasWthDB(aliasName, driverName, db) | ||
| 179 | return err | ||
| 180 | } | ||
| 181 | |||
| 182 | // Setting the database connect params. Use the database driver self dataSource args. | ||
| 183 | func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) error { | ||
| 184 | var ( | ||
| 185 | err error | ||
| 186 | db *sql.DB | ||
| 187 | al *alias | ||
| 188 | ) | ||
| 189 | |||
| 190 | db, err = sql.Open(driverName, dataSource) | ||
| 191 | if err != nil { | ||
| 192 | err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error()) | ||
| 193 | goto end | ||
| 194 | } | ||
| 195 | |||
| 196 | al, err = addAliasWthDB(aliasName, driverName, db) | ||
| 197 | if err != nil { | ||
| 198 | goto end | ||
| 199 | } | ||
| 200 | |||
| 201 | al.DataSource = dataSource | ||
| 202 | |||
| 203 | detectTZ(al) | ||
| 176 | 204 | ||
| 177 | for i, v := range params { | 205 | for i, v := range params { |
| 178 | switch i { | 206 | switch i { |
| ... | @@ -183,39 +211,37 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) { | ... | @@ -183,39 +211,37 @@ func RegisterDataBase(aliasName, driverName, dataSource string, params ...int) { |
| 183 | } | 211 | } |
| 184 | } | 212 | } |
| 185 | 213 | ||
| 186 | err = al.DB.Ping() | ||
| 187 | if err != nil { | ||
| 188 | err = fmt.Errorf("register db `%s`, %s", aliasName, err.Error()) | ||
| 189 | goto end | ||
| 190 | } | ||
| 191 | |||
| 192 | end: | 214 | end: |
| 193 | if err != nil { | 215 | if err != nil { |
| 194 | fmt.Println(err.Error()) | 216 | if db != nil { |
| 195 | os.Exit(2) | 217 | db.Close() |
| 218 | } | ||
| 219 | DebugLog.Println(err.Error()) | ||
| 196 | } | 220 | } |
| 221 | |||
| 222 | return err | ||
| 197 | } | 223 | } |
| 198 | 224 | ||
| 199 | // Register a database driver use specify driver name, this can be definition the driver is which database type. | 225 | // Register a database driver use specify driver name, this can be definition the driver is which database type. |
| 200 | func RegisterDriver(driverName string, typ DriverType) { | 226 | func RegisterDriver(driverName string, typ DriverType) error { |
| 201 | if t, ok := drivers[driverName]; ok == false { | 227 | if t, ok := drivers[driverName]; ok == false { |
| 202 | drivers[driverName] = typ | 228 | drivers[driverName] = typ |
| 203 | } else { | 229 | } else { |
| 204 | if t != typ { | 230 | if t != typ { |
| 205 | fmt.Sprintf("driverName `%s` db driver already registered and is other type\n", driverName) | 231 | return fmt.Errorf("driverName `%s` db driver already registered and is other type\n", driverName) |
| 206 | os.Exit(2) | ||
| 207 | } | 232 | } |
| 208 | } | 233 | } |
| 234 | return nil | ||
| 209 | } | 235 | } |
| 210 | 236 | ||
| 211 | // Change the database default used timezone | 237 | // Change the database default used timezone |
| 212 | func SetDataBaseTZ(aliasName string, tz *time.Location) { | 238 | func SetDataBaseTZ(aliasName string, tz *time.Location) error { |
| 213 | if al, ok := dataBaseCache.get(aliasName); ok { | 239 | if al, ok := dataBaseCache.get(aliasName); ok { |
| 214 | al.TZ = tz | 240 | al.TZ = tz |
| 215 | } else { | 241 | } else { |
| 216 | fmt.Sprintf("DataBase name `%s` not registered\n", aliasName) | 242 | return fmt.Errorf("DataBase name `%s` not registered\n", aliasName) |
| 217 | os.Exit(2) | ||
| 218 | } | 243 | } |
| 244 | return nil | ||
| 219 | } | 245 | } |
| 220 | 246 | ||
| 221 | // Change the max idle conns for *sql.DB, use specify database alias name | 247 | // Change the max idle conns for *sql.DB, use specify database alias name | ... | ... |
| ... | @@ -74,6 +74,20 @@ func (o *orm) Read(md interface{}, cols ...string) error { | ... | @@ -74,6 +74,20 @@ func (o *orm) Read(md interface{}, cols ...string) error { |
| 74 | return nil | 74 | return nil |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | // Try to read a row from the database, or insert one if it doesn't exist | ||
| 78 | func (o *orm) ReadOrCreate(md interface{}, col1 string, cols ...string) (bool, int64, error) { | ||
| 79 | cols = append([]string{col1}, cols...) | ||
| 80 | mi, ind := o.getMiInd(md, true) | ||
| 81 | err := o.alias.DbBaser.Read(o.db, mi, ind, o.alias.TZ, cols) | ||
| 82 | if err == ErrNoRows { | ||
| 83 | // Create | ||
| 84 | id, err := o.Insert(md) | ||
| 85 | return (err == nil), id, err | ||
| 86 | } | ||
| 87 | |||
| 88 | return false, ind.Field(mi.fields.pk.fieldIndex).Int(), err | ||
| 89 | } | ||
| 90 | |||
| 77 | // insert model data to database | 91 | // insert model data to database |
| 78 | func (o *orm) Insert(md interface{}) (int64, error) { | 92 | func (o *orm) Insert(md interface{}) (int64, error) { |
| 79 | mi, ind := o.getMiInd(md, true) | 93 | mi, ind := o.getMiInd(md, true) |
| ... | @@ -425,6 +439,12 @@ func (o *orm) Driver() Driver { | ... | @@ -425,6 +439,12 @@ func (o *orm) Driver() Driver { |
| 425 | return driver(o.alias.Name) | 439 | return driver(o.alias.Name) |
| 426 | } | 440 | } |
| 427 | 441 | ||
| 442 | func (o *orm) GetDB() dbQuerier { | ||
| 443 | panic(ErrNotImplement) | ||
| 444 | // not enough | ||
| 445 | return o.db | ||
| 446 | } | ||
| 447 | |||
| 428 | // create new orm | 448 | // create new orm |
| 429 | func NewOrm() Ormer { | 449 | func NewOrm() Ormer { |
| 430 | BootStrap() // execute only once | 450 | BootStrap() // execute only once |
| ... | @@ -436,3 +456,30 @@ func NewOrm() Ormer { | ... | @@ -436,3 +456,30 @@ func NewOrm() Ormer { |
| 436 | } | 456 | } |
| 437 | return o | 457 | return o |
| 438 | } | 458 | } |
| 459 | |||
| 460 | // create a new ormer object with specify *sql.DB for query | ||
| 461 | func NewOrmWithDB(driverName, aliasName string, db *sql.DB) (Ormer, error) { | ||
| 462 | var al *alias | ||
| 463 | |||
| 464 | if dr, ok := drivers[driverName]; ok { | ||
| 465 | al = new(alias) | ||
| 466 | al.DbBaser = dbBasers[dr] | ||
| 467 | al.Driver = dr | ||
| 468 | } else { | ||
| 469 | return nil, fmt.Errorf("driver name `%s` have not registered", driverName) | ||
| 470 | } | ||
| 471 | |||
| 472 | al.Name = aliasName | ||
| 473 | al.DriverName = driverName | ||
| 474 | |||
| 475 | o := new(orm) | ||
| 476 | o.alias = al | ||
| 477 | |||
| 478 | if Debug { | ||
| 479 | o.db = newDbQueryLog(o.alias, db) | ||
| 480 | } else { | ||
| 481 | o.db = db | ||
| 482 | } | ||
| 483 | |||
| 484 | return o, nil | ||
| 485 | } | ... | ... |
| ... | @@ -197,6 +197,36 @@ func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) { | ... | @@ -197,6 +197,36 @@ func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) { |
| 197 | return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ) | 197 | return o.orm.alias.DbBaser.ReadValues(o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ) |
| 198 | } | 198 | } |
| 199 | 199 | ||
| 200 | // query all rows into map[string]interface with specify key and value column name. | ||
| 201 | // keyCol = "name", valueCol = "value" | ||
| 202 | // table data | ||
| 203 | // name | value | ||
| 204 | // total | 100 | ||
| 205 | // found | 200 | ||
| 206 | // to map[string]interface{}{ | ||
| 207 | // "total": 100, | ||
| 208 | // "found": 200, | ||
| 209 | // } | ||
| 210 | func (o *querySet) RowsToMap(result *Params, keyCol, valueCol string) (int64, error) { | ||
| 211 | panic(ErrNotImplement) | ||
| 212 | return o.orm.alias.DbBaser.RowsTo(o.orm.db, o, o.mi, o.cond, result, keyCol, valueCol, o.orm.alias.TZ) | ||
| 213 | } | ||
| 214 | |||
| 215 | // query all rows into struct with specify key and value column name. | ||
| 216 | // keyCol = "name", valueCol = "value" | ||
| 217 | // table data | ||
| 218 | // name | value | ||
| 219 | // total | 100 | ||
| 220 | // found | 200 | ||
| 221 | // to struct { | ||
| 222 | // Total int | ||
| 223 | // Found int | ||
| 224 | // } | ||
| 225 | func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) { | ||
| 226 | panic(ErrNotImplement) | ||
| 227 | return o.orm.alias.DbBaser.RowsTo(o.orm.db, o, o.mi, o.cond, ptrStruct, keyCol, valueCol, o.orm.alias.TZ) | ||
| 228 | } | ||
| 229 | |||
| 200 | // create new QuerySeter. | 230 | // create new QuerySeter. |
| 201 | func newQuerySet(orm *orm, mi *modelInfo) QuerySeter { | 231 | func newQuerySet(orm *orm, mi *modelInfo) QuerySeter { |
| 202 | o := new(querySet) | 232 | o := new(querySet) | ... | ... |
| ... | @@ -518,7 +518,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { | ... | @@ -518,7 +518,7 @@ func (o *rawSet) QueryRows(containers ...interface{}) (int64, error) { |
| 518 | return cnt, nil | 518 | return cnt, nil |
| 519 | } | 519 | } |
| 520 | 520 | ||
| 521 | func (o *rawSet) readValues(container interface{}) (int64, error) { | 521 | func (o *rawSet) readValues(container interface{}, needCols []string) (int64, error) { |
| 522 | var ( | 522 | var ( |
| 523 | maps []Params | 523 | maps []Params |
| 524 | lists []ParamsList | 524 | lists []ParamsList |
| ... | @@ -552,20 +552,38 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { | ... | @@ -552,20 +552,38 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { |
| 552 | defer rs.Close() | 552 | defer rs.Close() |
| 553 | 553 | ||
| 554 | var ( | 554 | var ( |
| 555 | refs []interface{} | 555 | refs []interface{} |
| 556 | cnt int64 | 556 | cnt int64 |
| 557 | cols []string | 557 | cols []string |
| 558 | indexs []int | ||
| 558 | ) | 559 | ) |
| 560 | |||
| 559 | for rs.Next() { | 561 | for rs.Next() { |
| 560 | if cnt == 0 { | 562 | if cnt == 0 { |
| 561 | if columns, err := rs.Columns(); err != nil { | 563 | if columns, err := rs.Columns(); err != nil { |
| 562 | return 0, err | 564 | return 0, err |
| 563 | } else { | 565 | } else { |
| 566 | if len(needCols) > 0 { | ||
| 567 | indexs = make([]int, 0, len(needCols)) | ||
| 568 | } else { | ||
| 569 | indexs = make([]int, 0, len(columns)) | ||
| 570 | } | ||
| 571 | |||
| 564 | cols = columns | 572 | cols = columns |
| 565 | refs = make([]interface{}, len(cols)) | 573 | refs = make([]interface{}, len(cols)) |
| 566 | for i, _ := range refs { | 574 | for i, _ := range refs { |
| 567 | var ref sql.NullString | 575 | var ref sql.NullString |
| 568 | refs[i] = &ref | 576 | refs[i] = &ref |
| 577 | |||
| 578 | if len(needCols) > 0 { | ||
| 579 | for _, c := range needCols { | ||
| 580 | if c == cols[i] { | ||
| 581 | indexs = append(indexs, i) | ||
| 582 | } | ||
| 583 | } | ||
| 584 | } else { | ||
| 585 | indexs = append(indexs, i) | ||
| 586 | } | ||
| 569 | } | 587 | } |
| 570 | } | 588 | } |
| 571 | } | 589 | } |
| ... | @@ -577,7 +595,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { | ... | @@ -577,7 +595,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { |
| 577 | switch typ { | 595 | switch typ { |
| 578 | case 1: | 596 | case 1: |
| 579 | params := make(Params, len(cols)) | 597 | params := make(Params, len(cols)) |
| 580 | for i, ref := range refs { | 598 | for _, i := range indexs { |
| 599 | ref := refs[i] | ||
| 581 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) | 600 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) |
| 582 | if value.Valid { | 601 | if value.Valid { |
| 583 | params[cols[i]] = value.String | 602 | params[cols[i]] = value.String |
| ... | @@ -588,7 +607,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { | ... | @@ -588,7 +607,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { |
| 588 | maps = append(maps, params) | 607 | maps = append(maps, params) |
| 589 | case 2: | 608 | case 2: |
| 590 | params := make(ParamsList, 0, len(cols)) | 609 | params := make(ParamsList, 0, len(cols)) |
| 591 | for _, ref := range refs { | 610 | for _, i := range indexs { |
| 611 | ref := refs[i] | ||
| 592 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) | 612 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) |
| 593 | if value.Valid { | 613 | if value.Valid { |
| 594 | params = append(params, value.String) | 614 | params = append(params, value.String) |
| ... | @@ -598,7 +618,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { | ... | @@ -598,7 +618,8 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { |
| 598 | } | 618 | } |
| 599 | lists = append(lists, params) | 619 | lists = append(lists, params) |
| 600 | case 3: | 620 | case 3: |
| 601 | for _, ref := range refs { | 621 | for _, i := range indexs { |
| 622 | ref := refs[i] | ||
| 602 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) | 623 | value := reflect.Indirect(reflect.ValueOf(ref)).Interface().(sql.NullString) |
| 603 | if value.Valid { | 624 | if value.Valid { |
| 604 | list = append(list, value.String) | 625 | list = append(list, value.String) |
| ... | @@ -623,19 +644,163 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { | ... | @@ -623,19 +644,163 @@ func (o *rawSet) readValues(container interface{}) (int64, error) { |
| 623 | return cnt, nil | 644 | return cnt, nil |
| 624 | } | 645 | } |
| 625 | 646 | ||
| 647 | func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (int64, error) { | ||
| 648 | var ( | ||
| 649 | maps Params | ||
| 650 | ind *reflect.Value | ||
| 651 | ) | ||
| 652 | |||
| 653 | typ := 0 | ||
| 654 | switch container.(type) { | ||
| 655 | case *Params: | ||
| 656 | typ = 1 | ||
| 657 | default: | ||
| 658 | typ = 2 | ||
| 659 | vl := reflect.ValueOf(container) | ||
| 660 | id := reflect.Indirect(vl) | ||
| 661 | if vl.Kind() != reflect.Ptr || id.Kind() != reflect.Struct { | ||
| 662 | panic(fmt.Errorf("<RawSeter> RowsTo unsupport type `%T` need ptr struct", container)) | ||
| 663 | } | ||
| 664 | |||
| 665 | ind = &id | ||
| 666 | } | ||
| 667 | |||
| 668 | query := o.query | ||
| 669 | o.orm.alias.DbBaser.ReplaceMarks(&query) | ||
| 670 | |||
| 671 | args := getFlatParams(nil, o.args, o.orm.alias.TZ) | ||
| 672 | |||
| 673 | var rs *sql.Rows | ||
| 674 | if r, err := o.orm.db.Query(query, args...); err != nil { | ||
| 675 | return 0, err | ||
| 676 | } else { | ||
| 677 | rs = r | ||
| 678 | } | ||
| 679 | |||
| 680 | defer rs.Close() | ||
| 681 | |||
| 682 | var ( | ||
| 683 | refs []interface{} | ||
| 684 | cnt int64 | ||
| 685 | cols []string | ||
| 686 | ) | ||
| 687 | |||
| 688 | var ( | ||
| 689 | keyIndex = -1 | ||
| 690 | valueIndex = -1 | ||
| 691 | ) | ||
| 692 | |||
| 693 | for rs.Next() { | ||
| 694 | if cnt == 0 { | ||
| 695 | if columns, err := rs.Columns(); err != nil { | ||
| 696 | return 0, err | ||
| 697 | } else { | ||
| 698 | cols = columns | ||
| 699 | refs = make([]interface{}, len(cols)) | ||
| 700 | for i, _ := range refs { | ||
| 701 | if keyCol == cols[i] { | ||
| 702 | keyIndex = i | ||
| 703 | } | ||
| 704 | |||
| 705 | if typ == 1 || keyIndex == i { | ||
| 706 | var ref sql.NullString | ||
| 707 | refs[i] = &ref | ||
| 708 | } else { | ||
| 709 | var ref interface{} | ||
| 710 | refs[i] = &ref | ||
| 711 | } | ||
| 712 | |||
| 713 | if valueCol == cols[i] { | ||
| 714 | valueIndex = i | ||
| 715 | } | ||
| 716 | } | ||
| 717 | |||
| 718 | if keyIndex == -1 || valueIndex == -1 { | ||
| 719 | panic(fmt.Errorf("<RawSeter> RowsTo unknown key, value column name `%s: %s`", keyCol, valueCol)) | ||
| 720 | } | ||
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | if err := rs.Scan(refs...); err != nil { | ||
| 725 | return 0, err | ||
| 726 | } | ||
| 727 | |||
| 728 | if cnt == 0 { | ||
| 729 | switch typ { | ||
| 730 | case 1: | ||
| 731 | maps = make(Params) | ||
| 732 | } | ||
| 733 | } | ||
| 734 | |||
| 735 | key := reflect.Indirect(reflect.ValueOf(refs[keyIndex])).Interface().(sql.NullString).String | ||
| 736 | |||
| 737 | switch typ { | ||
| 738 | case 1: | ||
| 739 | value := reflect.Indirect(reflect.ValueOf(refs[valueIndex])).Interface().(sql.NullString) | ||
| 740 | if value.Valid { | ||
| 741 | maps[key] = value.String | ||
| 742 | } else { | ||
| 743 | maps[key] = nil | ||
| 744 | } | ||
| 745 | |||
| 746 | default: | ||
| 747 | if id := ind.FieldByName(camelString(key)); id.IsValid() { | ||
| 748 | o.setFieldValue(id, reflect.ValueOf(refs[valueIndex]).Elem().Interface()) | ||
| 749 | } | ||
| 750 | } | ||
| 751 | |||
| 752 | cnt++ | ||
| 753 | } | ||
| 754 | |||
| 755 | if typ == 1 { | ||
| 756 | v, _ := container.(*Params) | ||
| 757 | *v = maps | ||
| 758 | } | ||
| 759 | |||
| 760 | return cnt, nil | ||
| 761 | } | ||
| 762 | |||
| 626 | // query data to []map[string]interface | 763 | // query data to []map[string]interface |
| 627 | func (o *rawSet) Values(container *[]Params) (int64, error) { | 764 | func (o *rawSet) Values(container *[]Params, cols ...string) (int64, error) { |
| 628 | return o.readValues(container) | 765 | return o.readValues(container, cols) |
| 629 | } | 766 | } |
| 630 | 767 | ||
| 631 | // query data to [][]interface | 768 | // query data to [][]interface |
| 632 | func (o *rawSet) ValuesList(container *[]ParamsList) (int64, error) { | 769 | func (o *rawSet) ValuesList(container *[]ParamsList, cols ...string) (int64, error) { |
| 633 | return o.readValues(container) | 770 | return o.readValues(container, cols) |
| 634 | } | 771 | } |
| 635 | 772 | ||
| 636 | // query data to []interface | 773 | // query data to []interface |
| 637 | func (o *rawSet) ValuesFlat(container *ParamsList) (int64, error) { | 774 | func (o *rawSet) ValuesFlat(container *ParamsList, cols ...string) (int64, error) { |
| 638 | return o.readValues(container) | 775 | return o.readValues(container, cols) |
| 776 | } | ||
| 777 | |||
| 778 | // query all rows into map[string]interface with specify key and value column name. | ||
| 779 | // keyCol = "name", valueCol = "value" | ||
| 780 | // table data | ||
| 781 | // name | value | ||
| 782 | // total | 100 | ||
| 783 | // found | 200 | ||
| 784 | // to map[string]interface{}{ | ||
| 785 | // "total": 100, | ||
| 786 | // "found": 200, | ||
| 787 | // } | ||
| 788 | func (o *rawSet) RowsToMap(result *Params, keyCol, valueCol string) (int64, error) { | ||
| 789 | return o.queryRowsTo(result, keyCol, valueCol) | ||
| 790 | } | ||
| 791 | |||
| 792 | // query all rows into struct with specify key and value column name. | ||
| 793 | // keyCol = "name", valueCol = "value" | ||
| 794 | // table data | ||
| 795 | // name | value | ||
| 796 | // total | 100 | ||
| 797 | // found | 200 | ||
| 798 | // to struct { | ||
| 799 | // Total int | ||
| 800 | // Found int | ||
| 801 | // } | ||
| 802 | func (o *rawSet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) { | ||
| 803 | return o.queryRowsTo(ptrStruct, keyCol, valueCol) | ||
| 639 | } | 804 | } |
| 640 | 805 | ||
| 641 | // return prepared raw statement for used in times. | 806 | // return prepared raw statement for used in times. | ... | ... |
| ... | @@ -1642,3 +1642,41 @@ func TestTransaction(t *testing.T) { | ... | @@ -1642,3 +1642,41 @@ func TestTransaction(t *testing.T) { |
| 1642 | throwFail(t, AssertIs(num, 1)) | 1642 | throwFail(t, AssertIs(num, 1)) |
| 1643 | 1643 | ||
| 1644 | } | 1644 | } |
| 1645 | |||
| 1646 | func TestReadOrCreate(t *testing.T) { | ||
| 1647 | u := &User{ | ||
| 1648 | UserName: "Kyle", | ||
| 1649 | Email: "kylemcc@gmail.com", | ||
| 1650 | Password: "other_pass", | ||
| 1651 | Status: 7, | ||
| 1652 | IsStaff: false, | ||
| 1653 | IsActive: true, | ||
| 1654 | } | ||
| 1655 | |||
| 1656 | created, pk, err := dORM.ReadOrCreate(u, "UserName") | ||
| 1657 | throwFail(t, err) | ||
| 1658 | throwFail(t, AssertIs(created, true)) | ||
| 1659 | throwFail(t, AssertIs(u.UserName, "Kyle")) | ||
| 1660 | throwFail(t, AssertIs(u.Email, "kylemcc@gmail.com")) | ||
| 1661 | throwFail(t, AssertIs(u.Password, "other_pass")) | ||
| 1662 | throwFail(t, AssertIs(u.Status, 7)) | ||
| 1663 | throwFail(t, AssertIs(u.IsStaff, false)) | ||
| 1664 | throwFail(t, AssertIs(u.IsActive, true)) | ||
| 1665 | throwFail(t, AssertIs(u.Created.In(DefaultTimeLoc), u.Created.In(DefaultTimeLoc), test_Date)) | ||
| 1666 | throwFail(t, AssertIs(u.Updated.In(DefaultTimeLoc), u.Updated.In(DefaultTimeLoc), test_DateTime)) | ||
| 1667 | |||
| 1668 | nu := &User{UserName: u.UserName, Email: "someotheremail@gmail.com"} | ||
| 1669 | created, pk, err = dORM.ReadOrCreate(nu, "UserName") | ||
| 1670 | throwFail(t, err) | ||
| 1671 | throwFail(t, AssertIs(created, false)) | ||
| 1672 | throwFail(t, AssertIs(nu.Id, u.Id)) | ||
| 1673 | throwFail(t, AssertIs(pk, u.Id)) | ||
| 1674 | throwFail(t, AssertIs(nu.UserName, u.UserName)) | ||
| 1675 | throwFail(t, AssertIs(nu.Email, u.Email)) // should contain the value in the table, not the one specified above | ||
| 1676 | throwFail(t, AssertIs(nu.Password, u.Password)) | ||
| 1677 | throwFail(t, AssertIs(nu.Status, u.Status)) | ||
| 1678 | throwFail(t, AssertIs(nu.IsStaff, u.IsStaff)) | ||
| 1679 | throwFail(t, AssertIs(nu.IsActive, u.IsActive)) | ||
| 1680 | |||
| 1681 | dORM.Delete(u) | ||
| 1682 | } | ... | ... |
| ... | @@ -23,6 +23,7 @@ type Fielder interface { | ... | @@ -23,6 +23,7 @@ type Fielder interface { |
| 23 | // orm struct | 23 | // orm struct |
| 24 | type Ormer interface { | 24 | type Ormer interface { |
| 25 | Read(interface{}, ...string) error | 25 | Read(interface{}, ...string) error |
| 26 | ReadOrCreate(interface{}, string, ...string) (bool, int64, error) | ||
| 26 | Insert(interface{}) (int64, error) | 27 | Insert(interface{}) (int64, error) |
| 27 | InsertMulti(int, interface{}) (int64, error) | 28 | InsertMulti(int, interface{}) (int64, error) |
| 28 | Update(interface{}, ...string) (int64, error) | 29 | Update(interface{}, ...string) (int64, error) |
| ... | @@ -36,6 +37,7 @@ type Ormer interface { | ... | @@ -36,6 +37,7 @@ type Ormer interface { |
| 36 | Rollback() error | 37 | Rollback() error |
| 37 | Raw(string, ...interface{}) RawSeter | 38 | Raw(string, ...interface{}) RawSeter |
| 38 | Driver() Driver | 39 | Driver() Driver |
| 40 | GetDB() dbQuerier | ||
| 39 | } | 41 | } |
| 40 | 42 | ||
| 41 | // insert prepared statement | 43 | // insert prepared statement |
| ... | @@ -63,6 +65,8 @@ type QuerySeter interface { | ... | @@ -63,6 +65,8 @@ type QuerySeter interface { |
| 63 | Values(*[]Params, ...string) (int64, error) | 65 | Values(*[]Params, ...string) (int64, error) |
| 64 | ValuesList(*[]ParamsList, ...string) (int64, error) | 66 | ValuesList(*[]ParamsList, ...string) (int64, error) |
| 65 | ValuesFlat(*ParamsList, string) (int64, error) | 67 | ValuesFlat(*ParamsList, string) (int64, error) |
| 68 | RowsToMap(*Params, string, string) (int64, error) | ||
| 69 | RowsToStruct(interface{}, string, string) (int64, error) | ||
| 66 | } | 70 | } |
| 67 | 71 | ||
| 68 | // model to model query struct | 72 | // model to model query struct |
| ... | @@ -86,9 +90,11 @@ type RawSeter interface { | ... | @@ -86,9 +90,11 @@ type RawSeter interface { |
| 86 | QueryRow(...interface{}) error | 90 | QueryRow(...interface{}) error |
| 87 | QueryRows(...interface{}) (int64, error) | 91 | QueryRows(...interface{}) (int64, error) |
| 88 | SetArgs(...interface{}) RawSeter | 92 | SetArgs(...interface{}) RawSeter |
| 89 | Values(*[]Params) (int64, error) | 93 | Values(*[]Params, ...string) (int64, error) |
| 90 | ValuesList(*[]ParamsList) (int64, error) | 94 | ValuesList(*[]ParamsList, ...string) (int64, error) |
| 91 | ValuesFlat(*ParamsList) (int64, error) | 95 | ValuesFlat(*ParamsList, ...string) (int64, error) |
| 96 | RowsToMap(*Params, string, string) (int64, error) | ||
| 97 | RowsToStruct(interface{}, string, string) (int64, error) | ||
| 92 | Prepare() (RawPreparer, error) | 98 | Prepare() (RawPreparer, error) |
| 93 | } | 99 | } |
| 94 | 100 | ||
| ... | @@ -108,6 +114,14 @@ type dbQuerier interface { | ... | @@ -108,6 +114,14 @@ type dbQuerier interface { |
| 108 | QueryRow(query string, args ...interface{}) *sql.Row | 114 | QueryRow(query string, args ...interface{}) *sql.Row |
| 109 | } | 115 | } |
| 110 | 116 | ||
| 117 | // type DB interface { | ||
| 118 | // Begin() (*sql.Tx, error) | ||
| 119 | // Prepare(query string) (stmtQuerier, error) | ||
| 120 | // Exec(query string, args ...interface{}) (sql.Result, error) | ||
| 121 | // Query(query string, args ...interface{}) (*sql.Rows, error) | ||
| 122 | // QueryRow(query string, args ...interface{}) *sql.Row | ||
| 123 | // } | ||
| 124 | |||
| 111 | // transaction beginner | 125 | // transaction beginner |
| 112 | type txer interface { | 126 | type txer interface { |
| 113 | Begin() (*sql.Tx, error) | 127 | Begin() (*sql.Tx, error) |
| ... | @@ -138,6 +152,7 @@ type dbBaser interface { | ... | @@ -138,6 +152,7 @@ type dbBaser interface { |
| 138 | GenerateOperatorLeftCol(*fieldInfo, string, *string) | 152 | GenerateOperatorLeftCol(*fieldInfo, string, *string) |
| 139 | PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error) | 153 | PrepareInsert(dbQuerier, *modelInfo) (stmtQuerier, string, error) |
| 140 | ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error) | 154 | ReadValues(dbQuerier, *querySet, *modelInfo, *Condition, []string, interface{}, *time.Location) (int64, error) |
| 155 | RowsTo(dbQuerier, *querySet, *modelInfo, *Condition, interface{}, string, string, *time.Location) (int64, error) | ||
| 141 | MaxLimit() uint64 | 156 | MaxLimit() uint64 |
| 142 | TableQuote() string | 157 | TableQuote() string |
| 143 | ReplaceMarks(*string) | 158 | ReplaceMarks(*string) | ... | ... |
| ... | @@ -11,12 +11,15 @@ import ( | ... | @@ -11,12 +11,15 @@ import ( |
| 11 | 11 | ||
| 12 | var cookiepder = &CookieProvider{} | 12 | var cookiepder = &CookieProvider{} |
| 13 | 13 | ||
| 14 | // Cookie SessionStore | ||
| 14 | type CookieSessionStore struct { | 15 | type CookieSessionStore struct { |
| 15 | sid string | 16 | sid string |
| 16 | values map[interface{}]interface{} //session data | 17 | values map[interface{}]interface{} // session data |
| 17 | lock sync.RWMutex | 18 | lock sync.RWMutex |
| 18 | } | 19 | } |
| 19 | 20 | ||
| 21 | // Set value to cookie session. | ||
| 22 | // the value are encoded as gob with hash block string. | ||
| 20 | func (st *CookieSessionStore) Set(key, value interface{}) error { | 23 | func (st *CookieSessionStore) Set(key, value interface{}) error { |
| 21 | st.lock.Lock() | 24 | st.lock.Lock() |
| 22 | defer st.lock.Unlock() | 25 | defer st.lock.Unlock() |
| ... | @@ -24,6 +27,7 @@ func (st *CookieSessionStore) Set(key, value interface{}) error { | ... | @@ -24,6 +27,7 @@ func (st *CookieSessionStore) Set(key, value interface{}) error { |
| 24 | return nil | 27 | return nil |
| 25 | } | 28 | } |
| 26 | 29 | ||
| 30 | // Get value from cookie session | ||
| 27 | func (st *CookieSessionStore) Get(key interface{}) interface{} { | 31 | func (st *CookieSessionStore) Get(key interface{}) interface{} { |
| 28 | st.lock.RLock() | 32 | st.lock.RLock() |
| 29 | defer st.lock.RUnlock() | 33 | defer st.lock.RUnlock() |
| ... | @@ -35,6 +39,7 @@ func (st *CookieSessionStore) Get(key interface{}) interface{} { | ... | @@ -35,6 +39,7 @@ func (st *CookieSessionStore) Get(key interface{}) interface{} { |
| 35 | return nil | 39 | return nil |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 42 | // Delete value in cookie session | ||
| 38 | func (st *CookieSessionStore) Delete(key interface{}) error { | 43 | func (st *CookieSessionStore) Delete(key interface{}) error { |
| 39 | st.lock.Lock() | 44 | st.lock.Lock() |
| 40 | defer st.lock.Unlock() | 45 | defer st.lock.Unlock() |
| ... | @@ -42,6 +47,7 @@ func (st *CookieSessionStore) Delete(key interface{}) error { | ... | @@ -42,6 +47,7 @@ func (st *CookieSessionStore) Delete(key interface{}) error { |
| 42 | return nil | 47 | return nil |
| 43 | } | 48 | } |
| 44 | 49 | ||
| 50 | // Clean all values in cookie session | ||
| 45 | func (st *CookieSessionStore) Flush() error { | 51 | func (st *CookieSessionStore) Flush() error { |
| 46 | st.lock.Lock() | 52 | st.lock.Lock() |
| 47 | defer st.lock.Unlock() | 53 | defer st.lock.Unlock() |
| ... | @@ -49,10 +55,12 @@ func (st *CookieSessionStore) Flush() error { | ... | @@ -49,10 +55,12 @@ func (st *CookieSessionStore) Flush() error { |
| 49 | return nil | 55 | return nil |
| 50 | } | 56 | } |
| 51 | 57 | ||
| 58 | // Return id of this cookie session | ||
| 52 | func (st *CookieSessionStore) SessionID() string { | 59 | func (st *CookieSessionStore) SessionID() string { |
| 53 | return st.sid | 60 | return st.sid |
| 54 | } | 61 | } |
| 55 | 62 | ||
| 63 | // Write cookie session to http response cookie | ||
| 56 | func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { | 64 | func (st *CookieSessionStore) SessionRelease(w http.ResponseWriter) { |
| 57 | str, err := encodeCookie(cookiepder.block, | 65 | str, err := encodeCookie(cookiepder.block, |
| 58 | cookiepder.config.SecurityKey, | 66 | cookiepder.config.SecurityKey, |
| ... | @@ -79,12 +87,21 @@ type cookieConfig struct { | ... | @@ -79,12 +87,21 @@ type cookieConfig struct { |
| 79 | Maxage int `json:"maxage"` | 87 | Maxage int `json:"maxage"` |
| 80 | } | 88 | } |
| 81 | 89 | ||
| 90 | // Cookie session provider | ||
| 82 | type CookieProvider struct { | 91 | type CookieProvider struct { |
| 83 | maxlifetime int64 | 92 | maxlifetime int64 |
| 84 | config *cookieConfig | 93 | config *cookieConfig |
| 85 | block cipher.Block | 94 | block cipher.Block |
| 86 | } | 95 | } |
| 87 | 96 | ||
| 97 | // Init cookie session provider with max lifetime and config json. | ||
| 98 | // maxlifetime is ignored. | ||
| 99 | // json config: | ||
| 100 | // securityKey - hash string | ||
| 101 | // blockKey - gob encode hash string. it's saved as aes crypto. | ||
| 102 | // securityName - recognized name in encoded cookie string | ||
| 103 | // cookieName - cookie name | ||
| 104 | // maxage - cookie max life time. | ||
| 88 | func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error { | 105 | func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error { |
| 89 | pder.config = &cookieConfig{} | 106 | pder.config = &cookieConfig{} |
| 90 | err := json.Unmarshal([]byte(config), pder.config) | 107 | err := json.Unmarshal([]byte(config), pder.config) |
| ... | @@ -104,6 +121,8 @@ func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error | ... | @@ -104,6 +121,8 @@ func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error |
| 104 | return nil | 121 | return nil |
| 105 | } | 122 | } |
| 106 | 123 | ||
| 124 | // Get SessionStore in cooke. | ||
| 125 | // decode cooke string to map and put into SessionStore with sid. | ||
| 107 | func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) { | 126 | func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) { |
| 108 | maps, _ := decodeCookie(pder.block, | 127 | maps, _ := decodeCookie(pder.block, |
| 109 | pder.config.SecurityKey, | 128 | pder.config.SecurityKey, |
| ... | @@ -116,26 +135,32 @@ func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) { | ... | @@ -116,26 +135,32 @@ func (pder *CookieProvider) SessionRead(sid string) (SessionStore, error) { |
| 116 | return rs, nil | 135 | return rs, nil |
| 117 | } | 136 | } |
| 118 | 137 | ||
| 138 | // Cookie session is always existed | ||
| 119 | func (pder *CookieProvider) SessionExist(sid string) bool { | 139 | func (pder *CookieProvider) SessionExist(sid string) bool { |
| 120 | return true | 140 | return true |
| 121 | } | 141 | } |
| 122 | 142 | ||
| 143 | // Implement method, no used. | ||
| 123 | func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { | 144 | func (pder *CookieProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { |
| 124 | return nil, nil | 145 | return nil, nil |
| 125 | } | 146 | } |
| 126 | 147 | ||
| 148 | // Implement method, no used. | ||
| 127 | func (pder *CookieProvider) SessionDestroy(sid string) error { | 149 | func (pder *CookieProvider) SessionDestroy(sid string) error { |
| 128 | return nil | 150 | return nil |
| 129 | } | 151 | } |
| 130 | 152 | ||
| 153 | // Implement method, no used. | ||
| 131 | func (pder *CookieProvider) SessionGC() { | 154 | func (pder *CookieProvider) SessionGC() { |
| 132 | return | 155 | return |
| 133 | } | 156 | } |
| 134 | 157 | ||
| 158 | // Implement method, return 0. | ||
| 135 | func (pder *CookieProvider) SessionAll() int { | 159 | func (pder *CookieProvider) SessionAll() int { |
| 136 | return 0 | 160 | return 0 |
| 137 | } | 161 | } |
| 138 | 162 | ||
| 163 | // Implement method, no used. | ||
| 139 | func (pder *CookieProvider) SessionUpdate(sid string) error { | 164 | func (pder *CookieProvider) SessionUpdate(sid string) error { |
| 140 | return nil | 165 | return nil |
| 141 | } | 166 | } | ... | ... |
| ... | @@ -18,6 +18,7 @@ var ( | ... | @@ -18,6 +18,7 @@ var ( |
| 18 | gcmaxlifetime int64 | 18 | gcmaxlifetime int64 |
| 19 | ) | 19 | ) |
| 20 | 20 | ||
| 21 | // File session store | ||
| 21 | type FileSessionStore struct { | 22 | type FileSessionStore struct { |
| 22 | f *os.File | 23 | f *os.File |
| 23 | sid string | 24 | sid string |
| ... | @@ -25,6 +26,7 @@ type FileSessionStore struct { | ... | @@ -25,6 +26,7 @@ type FileSessionStore struct { |
| 25 | values map[interface{}]interface{} | 26 | values map[interface{}]interface{} |
| 26 | } | 27 | } |
| 27 | 28 | ||
| 29 | // Set value to file session | ||
| 28 | func (fs *FileSessionStore) Set(key, value interface{}) error { | 30 | func (fs *FileSessionStore) Set(key, value interface{}) error { |
| 29 | fs.lock.Lock() | 31 | fs.lock.Lock() |
| 30 | defer fs.lock.Unlock() | 32 | defer fs.lock.Unlock() |
| ... | @@ -32,6 +34,7 @@ func (fs *FileSessionStore) Set(key, value interface{}) error { | ... | @@ -32,6 +34,7 @@ func (fs *FileSessionStore) Set(key, value interface{}) error { |
| 32 | return nil | 34 | return nil |
| 33 | } | 35 | } |
| 34 | 36 | ||
| 37 | // Get value from file session | ||
| 35 | func (fs *FileSessionStore) Get(key interface{}) interface{} { | 38 | func (fs *FileSessionStore) Get(key interface{}) interface{} { |
| 36 | fs.lock.RLock() | 39 | fs.lock.RLock() |
| 37 | defer fs.lock.RUnlock() | 40 | defer fs.lock.RUnlock() |
| ... | @@ -43,6 +46,7 @@ func (fs *FileSessionStore) Get(key interface{}) interface{} { | ... | @@ -43,6 +46,7 @@ func (fs *FileSessionStore) Get(key interface{}) interface{} { |
| 43 | return nil | 46 | return nil |
| 44 | } | 47 | } |
| 45 | 48 | ||
| 49 | // Delete value in file session by given key | ||
| 46 | func (fs *FileSessionStore) Delete(key interface{}) error { | 50 | func (fs *FileSessionStore) Delete(key interface{}) error { |
| 47 | fs.lock.Lock() | 51 | fs.lock.Lock() |
| 48 | defer fs.lock.Unlock() | 52 | defer fs.lock.Unlock() |
| ... | @@ -50,6 +54,7 @@ func (fs *FileSessionStore) Delete(key interface{}) error { | ... | @@ -50,6 +54,7 @@ func (fs *FileSessionStore) Delete(key interface{}) error { |
| 50 | return nil | 54 | return nil |
| 51 | } | 55 | } |
| 52 | 56 | ||
| 57 | // Clean all values in file session | ||
| 53 | func (fs *FileSessionStore) Flush() error { | 58 | func (fs *FileSessionStore) Flush() error { |
| 54 | fs.lock.Lock() | 59 | fs.lock.Lock() |
| 55 | defer fs.lock.Unlock() | 60 | defer fs.lock.Unlock() |
| ... | @@ -57,10 +62,12 @@ func (fs *FileSessionStore) Flush() error { | ... | @@ -57,10 +62,12 @@ func (fs *FileSessionStore) Flush() error { |
| 57 | return nil | 62 | return nil |
| 58 | } | 63 | } |
| 59 | 64 | ||
| 65 | // Get file session store id | ||
| 60 | func (fs *FileSessionStore) SessionID() string { | 66 | func (fs *FileSessionStore) SessionID() string { |
| 61 | return fs.sid | 67 | return fs.sid |
| 62 | } | 68 | } |
| 63 | 69 | ||
| 70 | // Write file session to local file with Gob string | ||
| 64 | func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { | 71 | func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { |
| 65 | defer fs.f.Close() | 72 | defer fs.f.Close() |
| 66 | b, err := encodeGob(fs.values) | 73 | b, err := encodeGob(fs.values) |
| ... | @@ -72,17 +79,23 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { | ... | @@ -72,17 +79,23 @@ func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { |
| 72 | fs.f.Write(b) | 79 | fs.f.Write(b) |
| 73 | } | 80 | } |
| 74 | 81 | ||
| 82 | // File session provider | ||
| 75 | type FileProvider struct { | 83 | type FileProvider struct { |
| 76 | maxlifetime int64 | 84 | maxlifetime int64 |
| 77 | savePath string | 85 | savePath string |
| 78 | } | 86 | } |
| 79 | 87 | ||
| 88 | // Init file session provider. | ||
| 89 | // savePath sets the session files path. | ||
| 80 | func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { | 90 | func (fp *FileProvider) SessionInit(maxlifetime int64, savePath string) error { |
| 81 | fp.maxlifetime = maxlifetime | 91 | fp.maxlifetime = maxlifetime |
| 82 | fp.savePath = savePath | 92 | fp.savePath = savePath |
| 83 | return nil | 93 | return nil |
| 84 | } | 94 | } |
| 85 | 95 | ||
| 96 | // Read file session by sid. | ||
| 97 | // if file is not exist, create it. | ||
| 98 | // the file path is generated from sid string. | ||
| 86 | func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) { | 99 | func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) { |
| 87 | err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777) | 100 | err := os.MkdirAll(path.Join(fp.savePath, string(sid[0]), string(sid[1])), 0777) |
| 88 | if err != nil { | 101 | if err != nil { |
| ... | @@ -117,6 +130,8 @@ func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) { | ... | @@ -117,6 +130,8 @@ func (fp *FileProvider) SessionRead(sid string) (SessionStore, error) { |
| 117 | return ss, nil | 130 | return ss, nil |
| 118 | } | 131 | } |
| 119 | 132 | ||
| 133 | // Check file session exist. | ||
| 134 | // it checkes the file named from sid exist or not. | ||
| 120 | func (fp *FileProvider) SessionExist(sid string) bool { | 135 | func (fp *FileProvider) SessionExist(sid string) bool { |
| 121 | _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) | 136 | _, err := os.Stat(path.Join(fp.savePath, string(sid[0]), string(sid[1]), sid)) |
| 122 | if err == nil { | 137 | if err == nil { |
| ... | @@ -126,16 +141,20 @@ func (fp *FileProvider) SessionExist(sid string) bool { | ... | @@ -126,16 +141,20 @@ func (fp *FileProvider) SessionExist(sid string) bool { |
| 126 | } | 141 | } |
| 127 | } | 142 | } |
| 128 | 143 | ||
| 144 | // Remove all files in this save path | ||
| 129 | func (fp *FileProvider) SessionDestroy(sid string) error { | 145 | func (fp *FileProvider) SessionDestroy(sid string) error { |
| 130 | os.Remove(path.Join(fp.savePath)) | 146 | os.Remove(path.Join(fp.savePath)) |
| 131 | return nil | 147 | return nil |
| 132 | } | 148 | } |
| 133 | 149 | ||
| 150 | // Recycle files in save path | ||
| 134 | func (fp *FileProvider) SessionGC() { | 151 | func (fp *FileProvider) SessionGC() { |
| 135 | gcmaxlifetime = fp.maxlifetime | 152 | gcmaxlifetime = fp.maxlifetime |
| 136 | filepath.Walk(fp.savePath, gcpath) | 153 | filepath.Walk(fp.savePath, gcpath) |
| 137 | } | 154 | } |
| 138 | 155 | ||
| 156 | // Get active file session number. | ||
| 157 | // it walks save path to count files. | ||
| 139 | func (fp *FileProvider) SessionAll() int { | 158 | func (fp *FileProvider) SessionAll() int { |
| 140 | a := &activeSession{} | 159 | a := &activeSession{} |
| 141 | err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error { | 160 | err := filepath.Walk(fp.savePath, func(path string, f os.FileInfo, err error) error { |
| ... | @@ -148,6 +167,8 @@ func (fp *FileProvider) SessionAll() int { | ... | @@ -148,6 +167,8 @@ func (fp *FileProvider) SessionAll() int { |
| 148 | return a.total | 167 | return a.total |
| 149 | } | 168 | } |
| 150 | 169 | ||
| 170 | // Generate new sid for file session. | ||
| 171 | // it delete old file and create new file named from new sid. | ||
| 151 | func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { | 172 | func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { |
| 152 | err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777) | 173 | err := os.MkdirAll(path.Join(fp.savePath, string(oldsid[0]), string(oldsid[1])), 0777) |
| 153 | if err != nil { | 174 | if err != nil { |
| ... | @@ -197,6 +218,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, err | ... | @@ -197,6 +218,7 @@ func (fp *FileProvider) SessionRegenerate(oldsid, sid string) (SessionStore, err |
| 197 | return ss, nil | 218 | return ss, nil |
| 198 | } | 219 | } |
| 199 | 220 | ||
| 221 | // remove file in save path if expired | ||
| 200 | func gcpath(path string, info os.FileInfo, err error) error { | 222 | func gcpath(path string, info os.FileInfo, err error) error { |
| 201 | if err != nil { | 223 | if err != nil { |
| 202 | return err | 224 | return err | ... | ... |
| ... | @@ -9,6 +9,8 @@ import ( | ... | @@ -9,6 +9,8 @@ import ( |
| 9 | 9 | ||
| 10 | var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} | 10 | var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} |
| 11 | 11 | ||
| 12 | // memory session store. | ||
| 13 | // it saved sessions in a map in memory. | ||
| 12 | type MemSessionStore struct { | 14 | type MemSessionStore struct { |
| 13 | sid string //session id | 15 | sid string //session id |
| 14 | timeAccessed time.Time //last access time | 16 | timeAccessed time.Time //last access time |
| ... | @@ -16,6 +18,7 @@ type MemSessionStore struct { | ... | @@ -16,6 +18,7 @@ type MemSessionStore struct { |
| 16 | lock sync.RWMutex | 18 | lock sync.RWMutex |
| 17 | } | 19 | } |
| 18 | 20 | ||
| 21 | // set value to memory session | ||
| 19 | func (st *MemSessionStore) Set(key, value interface{}) error { | 22 | func (st *MemSessionStore) Set(key, value interface{}) error { |
| 20 | st.lock.Lock() | 23 | st.lock.Lock() |
| 21 | defer st.lock.Unlock() | 24 | defer st.lock.Unlock() |
| ... | @@ -23,6 +26,7 @@ func (st *MemSessionStore) Set(key, value interface{}) error { | ... | @@ -23,6 +26,7 @@ func (st *MemSessionStore) Set(key, value interface{}) error { |
| 23 | return nil | 26 | return nil |
| 24 | } | 27 | } |
| 25 | 28 | ||
| 29 | // get value from memory session by key | ||
| 26 | func (st *MemSessionStore) Get(key interface{}) interface{} { | 30 | func (st *MemSessionStore) Get(key interface{}) interface{} { |
| 27 | st.lock.RLock() | 31 | st.lock.RLock() |
| 28 | defer st.lock.RUnlock() | 32 | defer st.lock.RUnlock() |
| ... | @@ -34,6 +38,7 @@ func (st *MemSessionStore) Get(key interface{}) interface{} { | ... | @@ -34,6 +38,7 @@ func (st *MemSessionStore) Get(key interface{}) interface{} { |
| 34 | return nil | 38 | return nil |
| 35 | } | 39 | } |
| 36 | 40 | ||
| 41 | // delete in memory session by key | ||
| 37 | func (st *MemSessionStore) Delete(key interface{}) error { | 42 | func (st *MemSessionStore) Delete(key interface{}) error { |
| 38 | st.lock.Lock() | 43 | st.lock.Lock() |
| 39 | defer st.lock.Unlock() | 44 | defer st.lock.Unlock() |
| ... | @@ -41,6 +46,7 @@ func (st *MemSessionStore) Delete(key interface{}) error { | ... | @@ -41,6 +46,7 @@ func (st *MemSessionStore) Delete(key interface{}) error { |
| 41 | return nil | 46 | return nil |
| 42 | } | 47 | } |
| 43 | 48 | ||
| 49 | // clear all values in memory session | ||
| 44 | func (st *MemSessionStore) Flush() error { | 50 | func (st *MemSessionStore) Flush() error { |
| 45 | st.lock.Lock() | 51 | st.lock.Lock() |
| 46 | defer st.lock.Unlock() | 52 | defer st.lock.Unlock() |
| ... | @@ -48,27 +54,31 @@ func (st *MemSessionStore) Flush() error { | ... | @@ -48,27 +54,31 @@ func (st *MemSessionStore) Flush() error { |
| 48 | return nil | 54 | return nil |
| 49 | } | 55 | } |
| 50 | 56 | ||
| 57 | // get this id of memory session store | ||
| 51 | func (st *MemSessionStore) SessionID() string { | 58 | func (st *MemSessionStore) SessionID() string { |
| 52 | return st.sid | 59 | return st.sid |
| 53 | } | 60 | } |
| 54 | 61 | ||
| 62 | // Implement method, no used. | ||
| 55 | func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { | 63 | func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { |
| 56 | } | 64 | } |
| 57 | 65 | ||
| 58 | type MemProvider struct { | 66 | type MemProvider struct { |
| 59 | lock sync.RWMutex //用来锁 | 67 | lock sync.RWMutex // locker |
| 60 | sessions map[string]*list.Element //用来存储在内存 | 68 | sessions map[string]*list.Element // map in memory |
| 61 | list *list.List //用来做gc | 69 | list *list.List // for gc |
| 62 | maxlifetime int64 | 70 | maxlifetime int64 |
| 63 | savePath string | 71 | savePath string |
| 64 | } | 72 | } |
| 65 | 73 | ||
| 74 | // init memory session | ||
| 66 | func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { | 75 | func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { |
| 67 | pder.maxlifetime = maxlifetime | 76 | pder.maxlifetime = maxlifetime |
| 68 | pder.savePath = savePath | 77 | pder.savePath = savePath |
| 69 | return nil | 78 | return nil |
| 70 | } | 79 | } |
| 71 | 80 | ||
| 81 | // get memory session store by sid | ||
| 72 | func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { | 82 | func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { |
| 73 | pder.lock.RLock() | 83 | pder.lock.RLock() |
| 74 | if element, ok := pder.sessions[sid]; ok { | 84 | if element, ok := pder.sessions[sid]; ok { |
| ... | @@ -87,6 +97,7 @@ func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { | ... | @@ -87,6 +97,7 @@ func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { |
| 87 | return nil, nil | 97 | return nil, nil |
| 88 | } | 98 | } |
| 89 | 99 | ||
| 100 | // check session store exist in memory session by sid | ||
| 90 | func (pder *MemProvider) SessionExist(sid string) bool { | 101 | func (pder *MemProvider) SessionExist(sid string) bool { |
| 91 | pder.lock.RLock() | 102 | pder.lock.RLock() |
| 92 | defer pder.lock.RUnlock() | 103 | defer pder.lock.RUnlock() |
| ... | @@ -97,6 +108,7 @@ func (pder *MemProvider) SessionExist(sid string) bool { | ... | @@ -97,6 +108,7 @@ func (pder *MemProvider) SessionExist(sid string) bool { |
| 97 | } | 108 | } |
| 98 | } | 109 | } |
| 99 | 110 | ||
| 111 | // generate new sid for session store in memory session | ||
| 100 | func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { | 112 | func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { |
| 101 | pder.lock.RLock() | 113 | pder.lock.RLock() |
| 102 | if element, ok := pder.sessions[oldsid]; ok { | 114 | if element, ok := pder.sessions[oldsid]; ok { |
| ... | @@ -120,6 +132,7 @@ func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er | ... | @@ -120,6 +132,7 @@ func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er |
| 120 | return nil, nil | 132 | return nil, nil |
| 121 | } | 133 | } |
| 122 | 134 | ||
| 135 | // delete session store in memory session by id | ||
| 123 | func (pder *MemProvider) SessionDestroy(sid string) error { | 136 | func (pder *MemProvider) SessionDestroy(sid string) error { |
| 124 | pder.lock.Lock() | 137 | pder.lock.Lock() |
| 125 | defer pder.lock.Unlock() | 138 | defer pder.lock.Unlock() |
| ... | @@ -131,6 +144,7 @@ func (pder *MemProvider) SessionDestroy(sid string) error { | ... | @@ -131,6 +144,7 @@ func (pder *MemProvider) SessionDestroy(sid string) error { |
| 131 | return nil | 144 | return nil |
| 132 | } | 145 | } |
| 133 | 146 | ||
| 147 | // clean expired session stores in memory session | ||
| 134 | func (pder *MemProvider) SessionGC() { | 148 | func (pder *MemProvider) SessionGC() { |
| 135 | pder.lock.RLock() | 149 | pder.lock.RLock() |
| 136 | for { | 150 | for { |
| ... | @@ -152,10 +166,12 @@ func (pder *MemProvider) SessionGC() { | ... | @@ -152,10 +166,12 @@ func (pder *MemProvider) SessionGC() { |
| 152 | pder.lock.RUnlock() | 166 | pder.lock.RUnlock() |
| 153 | } | 167 | } |
| 154 | 168 | ||
| 169 | // get count number of memory session | ||
| 155 | func (pder *MemProvider) SessionAll() int { | 170 | func (pder *MemProvider) SessionAll() int { |
| 156 | return pder.list.Len() | 171 | return pder.list.Len() |
| 157 | } | 172 | } |
| 158 | 173 | ||
| 174 | // expand time of session store by id in memory session | ||
| 159 | func (pder *MemProvider) SessionUpdate(sid string) error { | 175 | func (pder *MemProvider) SessionUpdate(sid string) error { |
| 160 | pder.lock.Lock() | 176 | pder.lock.Lock() |
| 161 | defer pder.lock.Unlock() | 177 | defer pder.lock.Unlock() | ... | ... |
| 1 | package session | 1 | package session |
| 2 | 2 | ||
| 3 | //CREATE TABLE `session` ( | 3 | // mysql session support need create table as sql: |
| 4 | // `session_key` char(64) NOT NULL, | 4 | // CREATE TABLE `session` ( |
| 5 | // `session_data` blob, | 5 | // `session_key` char(64) NOT NULL, |
| 6 | // `session_expiry` int(11) unsigned NOT NULL, | 6 | // session_data` blob, |
| 7 | // PRIMARY KEY (`session_key`) | 7 | // `session_expiry` int(11) unsigned NOT NULL, |
| 8 | //) ENGINE=MyISAM DEFAULT CHARSET=utf8; | 8 | // PRIMARY KEY (`session_key`) |
| 9 | // ) ENGINE=MyISAM DEFAULT CHARSET=utf8; | ||
| 9 | 10 | ||
| 10 | import ( | 11 | import ( |
| 11 | "database/sql" | 12 | "database/sql" |
| ... | @@ -18,6 +19,7 @@ import ( | ... | @@ -18,6 +19,7 @@ import ( |
| 18 | 19 | ||
| 19 | var mysqlpder = &MysqlProvider{} | 20 | var mysqlpder = &MysqlProvider{} |
| 20 | 21 | ||
| 22 | // mysql session store | ||
| 21 | type MysqlSessionStore struct { | 23 | type MysqlSessionStore struct { |
| 22 | c *sql.DB | 24 | c *sql.DB |
| 23 | sid string | 25 | sid string |
| ... | @@ -25,6 +27,8 @@ type MysqlSessionStore struct { | ... | @@ -25,6 +27,8 @@ type MysqlSessionStore struct { |
| 25 | values map[interface{}]interface{} | 27 | values map[interface{}]interface{} |
| 26 | } | 28 | } |
| 27 | 29 | ||
| 30 | // set value in mysql session. | ||
| 31 | // it is temp value in map. | ||
| 28 | func (st *MysqlSessionStore) Set(key, value interface{}) error { | 32 | func (st *MysqlSessionStore) Set(key, value interface{}) error { |
| 29 | st.lock.Lock() | 33 | st.lock.Lock() |
| 30 | defer st.lock.Unlock() | 34 | defer st.lock.Unlock() |
| ... | @@ -32,6 +36,7 @@ func (st *MysqlSessionStore) Set(key, value interface{}) error { | ... | @@ -32,6 +36,7 @@ func (st *MysqlSessionStore) Set(key, value interface{}) error { |
| 32 | return nil | 36 | return nil |
| 33 | } | 37 | } |
| 34 | 38 | ||
| 39 | // get value from mysql session | ||
| 35 | func (st *MysqlSessionStore) Get(key interface{}) interface{} { | 40 | func (st *MysqlSessionStore) Get(key interface{}) interface{} { |
| 36 | st.lock.RLock() | 41 | st.lock.RLock() |
| 37 | defer st.lock.RUnlock() | 42 | defer st.lock.RUnlock() |
| ... | @@ -43,6 +48,7 @@ func (st *MysqlSessionStore) Get(key interface{}) interface{} { | ... | @@ -43,6 +48,7 @@ func (st *MysqlSessionStore) Get(key interface{}) interface{} { |
| 43 | return nil | 48 | return nil |
| 44 | } | 49 | } |
| 45 | 50 | ||
| 51 | // delete value in mysql session | ||
| 46 | func (st *MysqlSessionStore) Delete(key interface{}) error { | 52 | func (st *MysqlSessionStore) Delete(key interface{}) error { |
| 47 | st.lock.Lock() | 53 | st.lock.Lock() |
| 48 | defer st.lock.Unlock() | 54 | defer st.lock.Unlock() |
| ... | @@ -50,6 +56,7 @@ func (st *MysqlSessionStore) Delete(key interface{}) error { | ... | @@ -50,6 +56,7 @@ func (st *MysqlSessionStore) Delete(key interface{}) error { |
| 50 | return nil | 56 | return nil |
| 51 | } | 57 | } |
| 52 | 58 | ||
| 59 | // clear all values in mysql session | ||
| 53 | func (st *MysqlSessionStore) Flush() error { | 60 | func (st *MysqlSessionStore) Flush() error { |
| 54 | st.lock.Lock() | 61 | st.lock.Lock() |
| 55 | defer st.lock.Unlock() | 62 | defer st.lock.Unlock() |
| ... | @@ -57,10 +64,13 @@ func (st *MysqlSessionStore) Flush() error { | ... | @@ -57,10 +64,13 @@ func (st *MysqlSessionStore) Flush() error { |
| 57 | return nil | 64 | return nil |
| 58 | } | 65 | } |
| 59 | 66 | ||
| 67 | // get session id of this mysql session store | ||
| 60 | func (st *MysqlSessionStore) SessionID() string { | 68 | func (st *MysqlSessionStore) SessionID() string { |
| 61 | return st.sid | 69 | return st.sid |
| 62 | } | 70 | } |
| 63 | 71 | ||
| 72 | // save mysql session values to database. | ||
| 73 | // must call this method to save values to database. | ||
| 64 | func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { | 74 | func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { |
| 65 | defer st.c.Close() | 75 | defer st.c.Close() |
| 66 | b, err := encodeGob(st.values) | 76 | b, err := encodeGob(st.values) |
| ... | @@ -72,11 +82,13 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { | ... | @@ -72,11 +82,13 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { |
| 72 | 82 | ||
| 73 | } | 83 | } |
| 74 | 84 | ||
| 85 | // mysql session provider | ||
| 75 | type MysqlProvider struct { | 86 | type MysqlProvider struct { |
| 76 | maxlifetime int64 | 87 | maxlifetime int64 |
| 77 | savePath string | 88 | savePath string |
| 78 | } | 89 | } |
| 79 | 90 | ||
| 91 | // connect to mysql | ||
| 80 | func (mp *MysqlProvider) connectInit() *sql.DB { | 92 | func (mp *MysqlProvider) connectInit() *sql.DB { |
| 81 | db, e := sql.Open("mysql", mp.savePath) | 93 | db, e := sql.Open("mysql", mp.savePath) |
| 82 | if e != nil { | 94 | if e != nil { |
| ... | @@ -85,12 +97,15 @@ func (mp *MysqlProvider) connectInit() *sql.DB { | ... | @@ -85,12 +97,15 @@ func (mp *MysqlProvider) connectInit() *sql.DB { |
| 85 | return db | 97 | return db |
| 86 | } | 98 | } |
| 87 | 99 | ||
| 100 | // init mysql session. | ||
| 101 | // savepath is the connection string of mysql. | ||
| 88 | func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error { | 102 | func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error { |
| 89 | mp.maxlifetime = maxlifetime | 103 | mp.maxlifetime = maxlifetime |
| 90 | mp.savePath = savePath | 104 | mp.savePath = savePath |
| 91 | return nil | 105 | return nil |
| 92 | } | 106 | } |
| 93 | 107 | ||
| 108 | // get mysql session by sid | ||
| 94 | func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) { | 109 | func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) { |
| 95 | c := mp.connectInit() | 110 | c := mp.connectInit() |
| 96 | row := c.QueryRow("select session_data from session where session_key=?", sid) | 111 | row := c.QueryRow("select session_data from session where session_key=?", sid) |
| ... | @@ -113,6 +128,7 @@ func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) { | ... | @@ -113,6 +128,7 @@ func (mp *MysqlProvider) SessionRead(sid string) (SessionStore, error) { |
| 113 | return rs, nil | 128 | return rs, nil |
| 114 | } | 129 | } |
| 115 | 130 | ||
| 131 | // check mysql session exist | ||
| 116 | func (mp *MysqlProvider) SessionExist(sid string) bool { | 132 | func (mp *MysqlProvider) SessionExist(sid string) bool { |
| 117 | c := mp.connectInit() | 133 | c := mp.connectInit() |
| 118 | defer c.Close() | 134 | defer c.Close() |
| ... | @@ -126,6 +142,7 @@ func (mp *MysqlProvider) SessionExist(sid string) bool { | ... | @@ -126,6 +142,7 @@ func (mp *MysqlProvider) SessionExist(sid string) bool { |
| 126 | } | 142 | } |
| 127 | } | 143 | } |
| 128 | 144 | ||
| 145 | // generate new sid for mysql session | ||
| 129 | func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { | 146 | func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { |
| 130 | c := mp.connectInit() | 147 | c := mp.connectInit() |
| 131 | row := c.QueryRow("select session_data from session where session_key=?", oldsid) | 148 | 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 | ... | @@ -148,6 +165,7 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er |
| 148 | return rs, nil | 165 | return rs, nil |
| 149 | } | 166 | } |
| 150 | 167 | ||
| 168 | // delete mysql session by sid | ||
| 151 | func (mp *MysqlProvider) SessionDestroy(sid string) error { | 169 | func (mp *MysqlProvider) SessionDestroy(sid string) error { |
| 152 | c := mp.connectInit() | 170 | c := mp.connectInit() |
| 153 | c.Exec("DELETE FROM session where session_key=?", sid) | 171 | c.Exec("DELETE FROM session where session_key=?", sid) |
| ... | @@ -155,6 +173,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error { | ... | @@ -155,6 +173,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error { |
| 155 | return nil | 173 | return nil |
| 156 | } | 174 | } |
| 157 | 175 | ||
| 176 | // delete expired values in mysql session | ||
| 158 | func (mp *MysqlProvider) SessionGC() { | 177 | func (mp *MysqlProvider) SessionGC() { |
| 159 | c := mp.connectInit() | 178 | c := mp.connectInit() |
| 160 | c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) | 179 | c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) |
| ... | @@ -162,6 +181,7 @@ func (mp *MysqlProvider) SessionGC() { | ... | @@ -162,6 +181,7 @@ func (mp *MysqlProvider) SessionGC() { |
| 162 | return | 181 | return |
| 163 | } | 182 | } |
| 164 | 183 | ||
| 184 | // count values in mysql session | ||
| 165 | func (mp *MysqlProvider) SessionAll() int { | 185 | func (mp *MysqlProvider) SessionAll() int { |
| 166 | c := mp.connectInit() | 186 | c := mp.connectInit() |
| 167 | defer c.Close() | 187 | defer c.Close() | ... | ... |
| ... | @@ -11,18 +11,21 @@ import ( | ... | @@ -11,18 +11,21 @@ import ( |
| 11 | 11 | ||
| 12 | var redispder = &RedisProvider{} | 12 | var redispder = &RedisProvider{} |
| 13 | 13 | ||
| 14 | // redis max pool size | ||
| 14 | var MAX_POOL_SIZE = 100 | 15 | var MAX_POOL_SIZE = 100 |
| 15 | 16 | ||
| 16 | var redisPool chan redis.Conn | 17 | var redisPool chan redis.Conn |
| 17 | 18 | ||
| 19 | // redis session store | ||
| 18 | type RedisSessionStore struct { | 20 | type RedisSessionStore struct { |
| 19 | c redis.Conn | 21 | p *redis.Pool |
| 20 | sid string | 22 | sid string |
| 21 | lock sync.RWMutex | 23 | lock sync.RWMutex |
| 22 | values map[interface{}]interface{} | 24 | values map[interface{}]interface{} |
| 23 | maxlifetime int64 | 25 | maxlifetime int64 |
| 24 | } | 26 | } |
| 25 | 27 | ||
| 28 | // set value in redis session | ||
| 26 | func (rs *RedisSessionStore) Set(key, value interface{}) error { | 29 | func (rs *RedisSessionStore) Set(key, value interface{}) error { |
| 27 | rs.lock.Lock() | 30 | rs.lock.Lock() |
| 28 | defer rs.lock.Unlock() | 31 | defer rs.lock.Unlock() |
| ... | @@ -30,6 +33,7 @@ func (rs *RedisSessionStore) Set(key, value interface{}) error { | ... | @@ -30,6 +33,7 @@ func (rs *RedisSessionStore) Set(key, value interface{}) error { |
| 30 | return nil | 33 | return nil |
| 31 | } | 34 | } |
| 32 | 35 | ||
| 36 | // get value in redis session | ||
| 33 | func (rs *RedisSessionStore) Get(key interface{}) interface{} { | 37 | func (rs *RedisSessionStore) Get(key interface{}) interface{} { |
| 34 | rs.lock.RLock() | 38 | rs.lock.RLock() |
| 35 | defer rs.lock.RUnlock() | 39 | defer rs.lock.RUnlock() |
| ... | @@ -41,6 +45,7 @@ func (rs *RedisSessionStore) Get(key interface{}) interface{} { | ... | @@ -41,6 +45,7 @@ func (rs *RedisSessionStore) Get(key interface{}) interface{} { |
| 41 | return nil | 45 | return nil |
| 42 | } | 46 | } |
| 43 | 47 | ||
| 48 | // delete value in redis session | ||
| 44 | func (rs *RedisSessionStore) Delete(key interface{}) error { | 49 | func (rs *RedisSessionStore) Delete(key interface{}) error { |
| 45 | rs.lock.Lock() | 50 | rs.lock.Lock() |
| 46 | defer rs.lock.Unlock() | 51 | defer rs.lock.Unlock() |
| ... | @@ -48,6 +53,7 @@ func (rs *RedisSessionStore) Delete(key interface{}) error { | ... | @@ -48,6 +53,7 @@ func (rs *RedisSessionStore) Delete(key interface{}) error { |
| 48 | return nil | 53 | return nil |
| 49 | } | 54 | } |
| 50 | 55 | ||
| 56 | // clear all values in redis session | ||
| 51 | func (rs *RedisSessionStore) Flush() error { | 57 | func (rs *RedisSessionStore) Flush() error { |
| 52 | rs.lock.Lock() | 58 | rs.lock.Lock() |
| 53 | defer rs.lock.Unlock() | 59 | defer rs.lock.Unlock() |
| ... | @@ -55,20 +61,31 @@ func (rs *RedisSessionStore) Flush() error { | ... | @@ -55,20 +61,31 @@ func (rs *RedisSessionStore) Flush() error { |
| 55 | return nil | 61 | return nil |
| 56 | } | 62 | } |
| 57 | 63 | ||
| 64 | // get redis session id | ||
| 58 | func (rs *RedisSessionStore) SessionID() string { | 65 | func (rs *RedisSessionStore) SessionID() string { |
| 59 | return rs.sid | 66 | return rs.sid |
| 60 | } | 67 | } |
| 61 | 68 | ||
| 69 | // save session values to redis | ||
| 62 | func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) { | 70 | func (rs *RedisSessionStore) SessionRelease(w http.ResponseWriter) { |
| 63 | defer rs.c.Close() | 71 | c := rs.p.Get() |
| 72 | defer c.Close() | ||
| 73 | |||
| 74 | // if rs.values is empty, return directly | ||
| 75 | if len(rs.values) < 1 { | ||
| 76 | c.Do("DEL", rs.sid) | ||
| 77 | return | ||
| 78 | } | ||
| 79 | |||
| 64 | b, err := encodeGob(rs.values) | 80 | b, err := encodeGob(rs.values) |
| 65 | if err != nil { | 81 | if err != nil { |
| 66 | return | 82 | return |
| 67 | } | 83 | } |
| 68 | rs.c.Do("SET", rs.sid, string(b)) | 84 | |
| 69 | rs.c.Do("EXPIRE", rs.sid, rs.maxlifetime) | 85 | c.Do("SET", rs.sid, string(b), "EX", rs.maxlifetime) |
| 70 | } | 86 | } |
| 71 | 87 | ||
| 88 | // redis session provider | ||
| 72 | type RedisProvider struct { | 89 | type RedisProvider struct { |
| 73 | maxlifetime int64 | 90 | maxlifetime int64 |
| 74 | savePath string | 91 | savePath string |
| ... | @@ -77,8 +94,9 @@ type RedisProvider struct { | ... | @@ -77,8 +94,9 @@ type RedisProvider struct { |
| 77 | poollist *redis.Pool | 94 | poollist *redis.Pool |
| 78 | } | 95 | } |
| 79 | 96 | ||
| 80 | //savepath like redisserveraddr,poolsize,password | 97 | // init redis session |
| 81 | //127.0.0.1:6379,100,astaxie | 98 | // savepath like redis server addr,pool size,password |
| 99 | // e.g. 127.0.0.1:6379,100,astaxie | ||
| 82 | func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { | 100 | func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { |
| 83 | rp.maxlifetime = maxlifetime | 101 | rp.maxlifetime = maxlifetime |
| 84 | configs := strings.Split(savePath, ",") | 102 | configs := strings.Split(savePath, ",") |
| ... | @@ -114,12 +132,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { | ... | @@ -114,12 +132,11 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { |
| 114 | return nil | 132 | return nil |
| 115 | } | 133 | } |
| 116 | 134 | ||
| 135 | // read redis session by sid | ||
| 117 | func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) { | 136 | func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) { |
| 118 | c := rp.poollist.Get() | 137 | c := rp.poollist.Get() |
| 119 | if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 { | 138 | defer c.Close() |
| 120 | c.Do("SET", sid) | 139 | |
| 121 | } | ||
| 122 | c.Do("EXPIRE", sid, rp.maxlifetime) | ||
| 123 | kvs, err := redis.String(c.Do("GET", sid)) | 140 | kvs, err := redis.String(c.Do("GET", sid)) |
| 124 | var kv map[interface{}]interface{} | 141 | var kv map[interface{}]interface{} |
| 125 | if len(kvs) == 0 { | 142 | if len(kvs) == 0 { |
| ... | @@ -130,13 +147,16 @@ func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) { | ... | @@ -130,13 +147,16 @@ func (rp *RedisProvider) SessionRead(sid string) (SessionStore, error) { |
| 130 | return nil, err | 147 | return nil, err |
| 131 | } | 148 | } |
| 132 | } | 149 | } |
| 133 | rs := &RedisSessionStore{c: c, sid: sid, values: kv, maxlifetime: rp.maxlifetime} | 150 | |
| 151 | rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime} | ||
| 134 | return rs, nil | 152 | return rs, nil |
| 135 | } | 153 | } |
| 136 | 154 | ||
| 155 | // check redis session exist by sid | ||
| 137 | func (rp *RedisProvider) SessionExist(sid string) bool { | 156 | func (rp *RedisProvider) SessionExist(sid string) bool { |
| 138 | c := rp.poollist.Get() | 157 | c := rp.poollist.Get() |
| 139 | defer c.Close() | 158 | defer c.Close() |
| 159 | |||
| 140 | if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 { | 160 | if existed, err := redis.Int(c.Do("EXISTS", sid)); err != nil || existed == 0 { |
| 141 | return false | 161 | return false |
| 142 | } else { | 162 | } else { |
| ... | @@ -144,13 +164,21 @@ func (rp *RedisProvider) SessionExist(sid string) bool { | ... | @@ -144,13 +164,21 @@ func (rp *RedisProvider) SessionExist(sid string) bool { |
| 144 | } | 164 | } |
| 145 | } | 165 | } |
| 146 | 166 | ||
| 167 | // generate new sid for redis session | ||
| 147 | func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { | 168 | func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { |
| 148 | c := rp.poollist.Get() | 169 | c := rp.poollist.Get() |
| 149 | if existed, err := redis.Int(c.Do("EXISTS", oldsid)); err != nil || existed == 0 { | 170 | defer c.Close() |
| 150 | c.Do("SET", oldsid) | 171 | |
| 172 | if existed, _ := redis.Int(c.Do("EXISTS", oldsid)); existed == 0 { | ||
| 173 | // oldsid doesn't exists, set the new sid directly | ||
| 174 | // ignore error here, since if it return error | ||
| 175 | // the existed value will be 0 | ||
| 176 | c.Do("SET", sid, "", "EX", rp.maxlifetime) | ||
| 177 | } else { | ||
| 178 | c.Do("RENAME", oldsid, sid) | ||
| 179 | c.Do("EXPIRE", sid, rp.maxlifetime) | ||
| 151 | } | 180 | } |
| 152 | c.Do("RENAME", oldsid, sid) | 181 | |
| 153 | c.Do("EXPIRE", sid, rp.maxlifetime) | ||
| 154 | kvs, err := redis.String(c.Do("GET", sid)) | 182 | kvs, err := redis.String(c.Do("GET", sid)) |
| 155 | var kv map[interface{}]interface{} | 183 | var kv map[interface{}]interface{} |
| 156 | if len(kvs) == 0 { | 184 | if len(kvs) == 0 { |
| ... | @@ -161,24 +189,27 @@ func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er | ... | @@ -161,24 +189,27 @@ func (rp *RedisProvider) SessionRegenerate(oldsid, sid string) (SessionStore, er |
| 161 | return nil, err | 189 | return nil, err |
| 162 | } | 190 | } |
| 163 | } | 191 | } |
| 164 | rs := &RedisSessionStore{c: c, sid: sid, values: kv, maxlifetime: rp.maxlifetime} | 192 | |
| 193 | rs := &RedisSessionStore{p: rp.poollist, sid: sid, values: kv, maxlifetime: rp.maxlifetime} | ||
| 165 | return rs, nil | 194 | return rs, nil |
| 166 | } | 195 | } |
| 167 | 196 | ||
| 197 | // delete redis session by id | ||
| 168 | func (rp *RedisProvider) SessionDestroy(sid string) error { | 198 | func (rp *RedisProvider) SessionDestroy(sid string) error { |
| 169 | c := rp.poollist.Get() | 199 | c := rp.poollist.Get() |
| 170 | defer c.Close() | 200 | defer c.Close() |
| 201 | |||
| 171 | c.Do("DEL", sid) | 202 | c.Do("DEL", sid) |
| 172 | return nil | 203 | return nil |
| 173 | } | 204 | } |
| 174 | 205 | ||
| 206 | // Impelment method, no used. | ||
| 175 | func (rp *RedisProvider) SessionGC() { | 207 | func (rp *RedisProvider) SessionGC() { |
| 176 | return | 208 | return |
| 177 | } | 209 | } |
| 178 | 210 | ||
| 179 | //@todo | 211 | // @todo |
| 180 | func (rp *RedisProvider) SessionAll() int { | 212 | func (rp *RedisProvider) SessionAll() int { |
| 181 | |||
| 182 | return 0 | 213 | return 0 |
| 183 | } | 214 | } |
| 184 | 215 | ... | ... |
| ... | @@ -14,6 +14,7 @@ import ( | ... | @@ -14,6 +14,7 @@ import ( |
| 14 | "time" | 14 | "time" |
| 15 | ) | 15 | ) |
| 16 | 16 | ||
| 17 | // SessionStore contains all data for one session process with specific id. | ||
| 17 | type SessionStore interface { | 18 | type SessionStore interface { |
| 18 | Set(key, value interface{}) error //set session value | 19 | Set(key, value interface{}) error //set session value |
| 19 | Get(key interface{}) interface{} //get session value | 20 | Get(key interface{}) interface{} //get session value |
| ... | @@ -23,6 +24,8 @@ type SessionStore interface { | ... | @@ -23,6 +24,8 @@ type SessionStore interface { |
| 23 | Flush() error //delete all data | 24 | Flush() error //delete all data |
| 24 | } | 25 | } |
| 25 | 26 | ||
| 27 | // Provider contains global session methods and saved SessionStores. | ||
| 28 | // it can operate a SessionStore by its id. | ||
| 26 | type Provider interface { | 29 | type Provider interface { |
| 27 | SessionInit(gclifetime int64, config string) error | 30 | SessionInit(gclifetime int64, config string) error |
| 28 | SessionRead(sid string) (SessionStore, error) | 31 | SessionRead(sid string) (SessionStore, error) |
| ... | @@ -61,16 +64,24 @@ type managerConfig struct { | ... | @@ -61,16 +64,24 @@ type managerConfig struct { |
| 61 | ProviderConfig string `json:"providerConfig"` | 64 | ProviderConfig string `json:"providerConfig"` |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 67 | // Manager contains Provider and its configuration. | ||
| 64 | type Manager struct { | 68 | type Manager struct { |
| 65 | provider Provider | 69 | provider Provider |
| 66 | config *managerConfig | 70 | config *managerConfig |
| 67 | } | 71 | } |
| 68 | 72 | ||
| 69 | //options | 73 | // Create new Manager with provider name and json config string. |
| 70 | //1. is https default false | 74 | // provider name: |
| 71 | //2. hashfunc default sha1 | 75 | // 1. cookie |
| 72 | //3. hashkey default beegosessionkey | 76 | // 2. file |
| 73 | //4. maxage default is none | 77 | // 3. memory |
| 78 | // 4. redis | ||
| 79 | // 5. mysql | ||
| 80 | // json config: | ||
| 81 | // 1. is https default false | ||
| 82 | // 2. hashfunc default sha1 | ||
| 83 | // 3. hashkey default beegosessionkey | ||
| 84 | // 4. maxage default is none | ||
| 74 | func NewManager(provideName, config string) (*Manager, error) { | 85 | func NewManager(provideName, config string) (*Manager, error) { |
| 75 | provider, ok := provides[provideName] | 86 | provider, ok := provides[provideName] |
| 76 | if !ok { | 87 | if !ok { |
| ... | @@ -102,7 +113,8 @@ func NewManager(provideName, config string) (*Manager, error) { | ... | @@ -102,7 +113,8 @@ func NewManager(provideName, config string) (*Manager, error) { |
| 102 | }, nil | 113 | }, nil |
| 103 | } | 114 | } |
| 104 | 115 | ||
| 105 | //get Session | 116 | // Start session. generate or read the session id from http request. |
| 117 | // if session id exists, return SessionStore with this id. | ||
| 106 | func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { | 118 | func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session SessionStore) { |
| 107 | cookie, err := r.Cookie(manager.config.CookieName) | 119 | cookie, err := r.Cookie(manager.config.CookieName) |
| 108 | if err != nil || cookie.Value == "" { | 120 | if err != nil || cookie.Value == "" { |
| ... | @@ -144,7 +156,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se | ... | @@ -144,7 +156,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se |
| 144 | return | 156 | return |
| 145 | } | 157 | } |
| 146 | 158 | ||
| 147 | //Destroy sessionid | 159 | // Destroy session by its id in http request cookie. |
| 148 | func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { | 160 | func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { |
| 149 | cookie, err := r.Cookie(manager.config.CookieName) | 161 | cookie, err := r.Cookie(manager.config.CookieName) |
| 150 | if err != nil || cookie.Value == "" { | 162 | if err != nil || cookie.Value == "" { |
| ... | @@ -161,16 +173,20 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { | ... | @@ -161,16 +173,20 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { |
| 161 | } | 173 | } |
| 162 | } | 174 | } |
| 163 | 175 | ||
| 176 | // Get SessionStore by its id. | ||
| 164 | func (manager *Manager) GetProvider(sid string) (sessions SessionStore, err error) { | 177 | func (manager *Manager) GetProvider(sid string) (sessions SessionStore, err error) { |
| 165 | sessions, err = manager.provider.SessionRead(sid) | 178 | sessions, err = manager.provider.SessionRead(sid) |
| 166 | return | 179 | return |
| 167 | } | 180 | } |
| 168 | 181 | ||
| 182 | // Start session gc process. | ||
| 183 | // it can do gc in times after gc lifetime. | ||
| 169 | func (manager *Manager) GC() { | 184 | func (manager *Manager) GC() { |
| 170 | manager.provider.SessionGC() | 185 | manager.provider.SessionGC() |
| 171 | time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() }) | 186 | time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() }) |
| 172 | } | 187 | } |
| 173 | 188 | ||
| 189 | // Regenerate a session id for this SessionStore who's id is saving in http request. | ||
| 174 | func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { | 190 | func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Request) (session SessionStore) { |
| 175 | sid := manager.sessionId(r) | 191 | sid := manager.sessionId(r) |
| 176 | cookie, err := r.Cookie(manager.config.CookieName) | 192 | cookie, err := r.Cookie(manager.config.CookieName) |
| ... | @@ -198,20 +214,23 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque | ... | @@ -198,20 +214,23 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque |
| 198 | return | 214 | return |
| 199 | } | 215 | } |
| 200 | 216 | ||
| 217 | // Get all active sessions count number. | ||
| 201 | func (manager *Manager) GetActiveSession() int { | 218 | func (manager *Manager) GetActiveSession() int { |
| 202 | return manager.provider.SessionAll() | 219 | return manager.provider.SessionAll() |
| 203 | } | 220 | } |
| 204 | 221 | ||
| 222 | // Set hash function for generating session id. | ||
| 205 | func (manager *Manager) SetHashFunc(hasfunc, hashkey string) { | 223 | func (manager *Manager) SetHashFunc(hasfunc, hashkey string) { |
| 206 | manager.config.SessionIDHashFunc = hasfunc | 224 | manager.config.SessionIDHashFunc = hasfunc |
| 207 | manager.config.SessionIDHashKey = hashkey | 225 | manager.config.SessionIDHashKey = hashkey |
| 208 | } | 226 | } |
| 209 | 227 | ||
| 228 | // Set cookie with https. | ||
| 210 | func (manager *Manager) SetSecure(secure bool) { | 229 | func (manager *Manager) SetSecure(secure bool) { |
| 211 | manager.config.Secure = secure | 230 | manager.config.Secure = secure |
| 212 | } | 231 | } |
| 213 | 232 | ||
| 214 | //remote_addr cruunixnano randdata | 233 | // generate session id with rand string, unix nano time, remote addr by hash function. |
| 215 | func (manager *Manager) sessionId(r *http.Request) (sid string) { | 234 | func (manager *Manager) sessionId(r *http.Request) (sid string) { |
| 216 | bs := make([]byte, 24) | 235 | bs := make([]byte, 24) |
| 217 | if _, err := io.ReadFull(rand.Reader, bs); err != nil { | 236 | if _, err := io.ReadFull(rand.Reader, bs); err != nil { | ... | ... |
| ... | @@ -29,16 +29,13 @@ type pointerInfo struct { | ... | @@ -29,16 +29,13 @@ type pointerInfo struct { |
| 29 | used []int | 29 | used []int |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | // | ||
| 33 | // print the data in console | 32 | // print the data in console |
| 34 | // | ||
| 35 | func Display(data ...interface{}) { | 33 | func Display(data ...interface{}) { |
| 36 | display(true, data...) | 34 | display(true, data...) |
| 37 | } | 35 | } |
| 38 | 36 | ||
| 39 | // | 37 | |
| 40 | // return string | 38 | // return data print string |
| 41 | // | ||
| 42 | func GetDisplayString(data ...interface{}) string { | 39 | func GetDisplayString(data ...interface{}) string { |
| 43 | return display(false, data...) | 40 | return display(false, data...) |
| 44 | } | 41 | } |
| ... | @@ -67,9 +64,7 @@ func display(displayed bool, data ...interface{}) string { | ... | @@ -67,9 +64,7 @@ func display(displayed bool, data ...interface{}) string { |
| 67 | return buf.String() | 64 | return buf.String() |
| 68 | } | 65 | } |
| 69 | 66 | ||
| 70 | // | 67 | // return data dump and format bytes |
| 71 | // return fomateinfo | ||
| 72 | // | ||
| 73 | func fomateinfo(headlen int, data ...interface{}) []byte { | 68 | func fomateinfo(headlen int, data ...interface{}) []byte { |
| 74 | var buf = new(bytes.Buffer) | 69 | var buf = new(bytes.Buffer) |
| 75 | 70 | ||
| ... | @@ -108,6 +103,7 @@ func fomateinfo(headlen int, data ...interface{}) []byte { | ... | @@ -108,6 +103,7 @@ func fomateinfo(headlen int, data ...interface{}) []byte { |
| 108 | return buf.Bytes() | 103 | return buf.Bytes() |
| 109 | } | 104 | } |
| 110 | 105 | ||
| 106 | // check data is golang basic type | ||
| 111 | func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool { | 107 | func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool { |
| 112 | switch kind { | 108 | switch kind { |
| 113 | case reflect.Bool: | 109 | case reflect.Bool: |
| ... | @@ -158,6 +154,7 @@ func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, | ... | @@ -158,6 +154,7 @@ func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, |
| 158 | return false | 154 | return false |
| 159 | } | 155 | } |
| 160 | 156 | ||
| 157 | // dump value | ||
| 161 | func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) { | 158 | func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) { |
| 162 | var t = val.Kind() | 159 | var t = val.Kind() |
| 163 | 160 | ||
| ... | @@ -367,6 +364,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, | ... | @@ -367,6 +364,7 @@ func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, |
| 367 | } | 364 | } |
| 368 | } | 365 | } |
| 369 | 366 | ||
| 367 | // dump pointer value | ||
| 370 | func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { | 368 | func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { |
| 371 | var anyused = false | 369 | var anyused = false |
| 372 | var pointerNum = 0 | 370 | var pointerNum = 0 |
| ... | @@ -434,9 +432,7 @@ func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { | ... | @@ -434,9 +432,7 @@ func printPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) { |
| 434 | } | 432 | } |
| 435 | } | 433 | } |
| 436 | 434 | ||
| 437 | // | 435 | // get stack bytes |
| 438 | // get stack info | ||
| 439 | // | ||
| 440 | func stack(skip int, indent string) []byte { | 436 | func stack(skip int, indent string) []byte { |
| 441 | var buf = new(bytes.Buffer) | 437 | var buf = new(bytes.Buffer) |
| 442 | 438 | ||
| ... | @@ -455,7 +451,7 @@ func stack(skip int, indent string) []byte { | ... | @@ -455,7 +451,7 @@ func stack(skip int, indent string) []byte { |
| 455 | return buf.Bytes() | 451 | return buf.Bytes() |
| 456 | } | 452 | } |
| 457 | 453 | ||
| 458 | // function returns, if possible, the name of the function containing the PC. | 454 | // return the name of the function containing the PC if possible, |
| 459 | func function(pc uintptr) []byte { | 455 | func function(pc uintptr) []byte { |
| 460 | fn := runtime.FuncForPC(pc) | 456 | fn := runtime.FuncForPC(pc) |
| 461 | if fn == nil { | 457 | if fn == nil { | ... | ... |
| ... | @@ -13,12 +13,15 @@ package toolbox | ... | @@ -13,12 +13,15 @@ package toolbox |
| 13 | 13 | ||
| 14 | //AddHealthCheck("database",&DatabaseCheck{}) | 14 | //AddHealthCheck("database",&DatabaseCheck{}) |
| 15 | 15 | ||
| 16 | // health checker map | ||
| 16 | var AdminCheckList map[string]HealthChecker | 17 | var AdminCheckList map[string]HealthChecker |
| 17 | 18 | ||
| 19 | // health checker interface | ||
| 18 | type HealthChecker interface { | 20 | type HealthChecker interface { |
| 19 | Check() error | 21 | Check() error |
| 20 | } | 22 | } |
| 21 | 23 | ||
| 24 | // add health checker with name string | ||
| 22 | func AddHealthCheck(name string, hc HealthChecker) { | 25 | func AddHealthCheck(name string, hc HealthChecker) { |
| 23 | AdminCheckList[name] = hc | 26 | AdminCheckList[name] = hc |
| 24 | } | 27 | } | ... | ... |
| ... | @@ -19,6 +19,7 @@ func init() { | ... | @@ -19,6 +19,7 @@ func init() { |
| 19 | pid = os.Getpid() | 19 | pid = os.Getpid() |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | // parse input command string | ||
| 22 | func ProcessInput(input string, w io.Writer) { | 23 | func ProcessInput(input string, w io.Writer) { |
| 23 | switch input { | 24 | switch input { |
| 24 | case "lookup goroutine": | 25 | case "lookup goroutine": |
| ... | @@ -44,6 +45,7 @@ func ProcessInput(input string, w io.Writer) { | ... | @@ -44,6 +45,7 @@ func ProcessInput(input string, w io.Writer) { |
| 44 | } | 45 | } |
| 45 | } | 46 | } |
| 46 | 47 | ||
| 48 | // record memory profile in pprof | ||
| 47 | func MemProf() { | 49 | func MemProf() { |
| 48 | if f, err := os.Create("mem-" + strconv.Itoa(pid) + ".memprof"); err != nil { | 50 | if f, err := os.Create("mem-" + strconv.Itoa(pid) + ".memprof"); err != nil { |
| 49 | log.Fatal("record memory profile failed: %v", err) | 51 | log.Fatal("record memory profile failed: %v", err) |
| ... | @@ -54,6 +56,7 @@ func MemProf() { | ... | @@ -54,6 +56,7 @@ func MemProf() { |
| 54 | } | 56 | } |
| 55 | } | 57 | } |
| 56 | 58 | ||
| 59 | // start cpu profile monitor | ||
| 57 | func StartCPUProfile() { | 60 | func StartCPUProfile() { |
| 58 | f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof") | 61 | f, err := os.Create("cpu-" + strconv.Itoa(pid) + ".pprof") |
| 59 | if err != nil { | 62 | if err != nil { |
| ... | @@ -62,10 +65,12 @@ func StartCPUProfile() { | ... | @@ -62,10 +65,12 @@ func StartCPUProfile() { |
| 62 | pprof.StartCPUProfile(f) | 65 | pprof.StartCPUProfile(f) |
| 63 | } | 66 | } |
| 64 | 67 | ||
| 68 | // stop cpu profile monitor | ||
| 65 | func StopCPUProfile() { | 69 | func StopCPUProfile() { |
| 66 | pprof.StopCPUProfile() | 70 | pprof.StopCPUProfile() |
| 67 | } | 71 | } |
| 68 | 72 | ||
| 73 | // print gc information to io.Writer | ||
| 69 | func PrintGCSummary(w io.Writer) { | 74 | func PrintGCSummary(w io.Writer) { |
| 70 | memStats := &runtime.MemStats{} | 75 | memStats := &runtime.MemStats{} |
| 71 | runtime.ReadMemStats(memStats) | 76 | runtime.ReadMemStats(memStats) |
| ... | @@ -114,7 +119,7 @@ func avg(items []time.Duration) time.Duration { | ... | @@ -114,7 +119,7 @@ func avg(items []time.Duration) time.Duration { |
| 114 | return time.Duration(int64(sum) / int64(len(items))) | 119 | return time.Duration(int64(sum) / int64(len(items))) |
| 115 | } | 120 | } |
| 116 | 121 | ||
| 117 | // human readable format | 122 | // format bytes number friendly |
| 118 | func toH(bytes uint64) string { | 123 | func toH(bytes uint64) string { |
| 119 | switch { | 124 | switch { |
| 120 | case bytes < 1024: | 125 | case bytes < 1024: | ... | ... |
| ... | @@ -7,6 +7,7 @@ import ( | ... | @@ -7,6 +7,7 @@ import ( |
| 7 | "time" | 7 | "time" |
| 8 | ) | 8 | ) |
| 9 | 9 | ||
| 10 | // Statistics struct | ||
| 10 | type Statistics struct { | 11 | type Statistics struct { |
| 11 | RequestUrl string | 12 | RequestUrl string |
| 12 | RequestController string | 13 | RequestController string |
| ... | @@ -16,12 +17,15 @@ type Statistics struct { | ... | @@ -16,12 +17,15 @@ type Statistics struct { |
| 16 | TotalTime time.Duration | 17 | TotalTime time.Duration |
| 17 | } | 18 | } |
| 18 | 19 | ||
| 20 | // UrlMap contains several statistics struct to log different data | ||
| 19 | type UrlMap struct { | 21 | type UrlMap struct { |
| 20 | lock sync.RWMutex | 22 | lock sync.RWMutex |
| 21 | LengthLimit int //limit the urlmap's length if it's equal to 0 there's no limit | 23 | LengthLimit int //limit the urlmap's length if it's equal to 0 there's no limit |
| 22 | urlmap map[string]map[string]*Statistics | 24 | urlmap map[string]map[string]*Statistics |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 27 | // add statistics task. | ||
| 28 | // it needs request method, request url, request controller and statistics time duration | ||
| 25 | func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController string, requesttime time.Duration) { | 29 | func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController string, requesttime time.Duration) { |
| 26 | m.lock.Lock() | 30 | m.lock.Lock() |
| 27 | defer m.lock.Unlock() | 31 | defer m.lock.Unlock() |
| ... | @@ -65,6 +69,7 @@ func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController stri | ... | @@ -65,6 +69,7 @@ func (m *UrlMap) AddStatistics(requestMethod, requestUrl, requestController stri |
| 65 | } | 69 | } |
| 66 | } | 70 | } |
| 67 | 71 | ||
| 72 | // put url statistics result in io.Writer | ||
| 68 | func (m *UrlMap) GetMap(rw io.Writer) { | 73 | func (m *UrlMap) GetMap(rw io.Writer) { |
| 69 | m.lock.RLock() | 74 | m.lock.RLock() |
| 70 | defer m.lock.RUnlock() | 75 | defer m.lock.RUnlock() |
| ... | @@ -78,6 +83,7 @@ func (m *UrlMap) GetMap(rw io.Writer) { | ... | @@ -78,6 +83,7 @@ func (m *UrlMap) GetMap(rw io.Writer) { |
| 78 | } | 83 | } |
| 79 | } | 84 | } |
| 80 | 85 | ||
| 86 | // global statistics data map | ||
| 81 | var StatisticsMap *UrlMap | 87 | var StatisticsMap *UrlMap |
| 82 | 88 | ||
| 83 | func init() { | 89 | func init() { | ... | ... |
| ... | @@ -53,6 +53,7 @@ const ( | ... | @@ -53,6 +53,7 @@ const ( |
| 53 | starBit = 1 << 63 | 53 | starBit = 1 << 63 |
| 54 | ) | 54 | ) |
| 55 | 55 | ||
| 56 | // time taks schedule | ||
| 56 | type Schedule struct { | 57 | type Schedule struct { |
| 57 | Second uint64 | 58 | Second uint64 |
| 58 | Minute uint64 | 59 | Minute uint64 |
| ... | @@ -62,8 +63,10 @@ type Schedule struct { | ... | @@ -62,8 +63,10 @@ type Schedule struct { |
| 62 | Week uint64 | 63 | Week uint64 |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 66 | // task func type | ||
| 65 | type TaskFunc func() error | 67 | type TaskFunc func() error |
| 66 | 68 | ||
| 69 | // task interface | ||
| 67 | type Tasker interface { | 70 | type Tasker interface { |
| 68 | GetStatus() string | 71 | GetStatus() string |
| 69 | Run() error | 72 | Run() error |
| ... | @@ -73,21 +76,24 @@ type Tasker interface { | ... | @@ -73,21 +76,24 @@ type Tasker interface { |
| 73 | GetPrev() time.Time | 76 | GetPrev() time.Time |
| 74 | } | 77 | } |
| 75 | 78 | ||
| 79 | // task error | ||
| 76 | type taskerr struct { | 80 | type taskerr struct { |
| 77 | t time.Time | 81 | t time.Time |
| 78 | errinfo string | 82 | errinfo string |
| 79 | } | 83 | } |
| 80 | 84 | ||
| 85 | // task struct | ||
| 81 | type Task struct { | 86 | type Task struct { |
| 82 | Taskname string | 87 | Taskname string |
| 83 | Spec *Schedule | 88 | Spec *Schedule |
| 84 | DoFunc TaskFunc | 89 | DoFunc TaskFunc |
| 85 | Prev time.Time | 90 | Prev time.Time |
| 86 | Next time.Time | 91 | Next time.Time |
| 87 | Errlist []*taskerr //errtime:errinfo | 92 | Errlist []*taskerr // like errtime:errinfo |
| 88 | ErrLimit int //max length for the errlist 0 stand for there' no limit | 93 | ErrLimit int // max length for the errlist, 0 stand for no limit |
| 89 | } | 94 | } |
| 90 | 95 | ||
| 96 | // add new task with name, time and func | ||
| 91 | func NewTask(tname string, spec string, f TaskFunc) *Task { | 97 | func NewTask(tname string, spec string, f TaskFunc) *Task { |
| 92 | 98 | ||
| 93 | task := &Task{ | 99 | task := &Task{ |
| ... | @@ -99,6 +105,7 @@ func NewTask(tname string, spec string, f TaskFunc) *Task { | ... | @@ -99,6 +105,7 @@ func NewTask(tname string, spec string, f TaskFunc) *Task { |
| 99 | return task | 105 | return task |
| 100 | } | 106 | } |
| 101 | 107 | ||
| 108 | // get current task status | ||
| 102 | func (tk *Task) GetStatus() string { | 109 | func (tk *Task) GetStatus() string { |
| 103 | var str string | 110 | var str string |
| 104 | for _, v := range tk.Errlist { | 111 | for _, v := range tk.Errlist { |
| ... | @@ -107,6 +114,7 @@ func (tk *Task) GetStatus() string { | ... | @@ -107,6 +114,7 @@ func (tk *Task) GetStatus() string { |
| 107 | return str | 114 | return str |
| 108 | } | 115 | } |
| 109 | 116 | ||
| 117 | // run task | ||
| 110 | func (tk *Task) Run() error { | 118 | func (tk *Task) Run() error { |
| 111 | err := tk.DoFunc() | 119 | err := tk.DoFunc() |
| 112 | if err != nil { | 120 | if err != nil { |
| ... | @@ -117,53 +125,58 @@ func (tk *Task) Run() error { | ... | @@ -117,53 +125,58 @@ func (tk *Task) Run() error { |
| 117 | return err | 125 | return err |
| 118 | } | 126 | } |
| 119 | 127 | ||
| 128 | // set next time for this task | ||
| 120 | func (tk *Task) SetNext(now time.Time) { | 129 | func (tk *Task) SetNext(now time.Time) { |
| 121 | tk.Next = tk.Spec.Next(now) | 130 | tk.Next = tk.Spec.Next(now) |
| 122 | } | 131 | } |
| 123 | 132 | ||
| 133 | // get the next call time of this task | ||
| 124 | func (tk *Task) GetNext() time.Time { | 134 | func (tk *Task) GetNext() time.Time { |
| 125 | return tk.Next | 135 | return tk.Next |
| 126 | } | 136 | } |
| 137 | |||
| 138 | // set prev time of this task | ||
| 127 | func (tk *Task) SetPrev(now time.Time) { | 139 | func (tk *Task) SetPrev(now time.Time) { |
| 128 | tk.Prev = now | 140 | tk.Prev = now |
| 129 | } | 141 | } |
| 130 | 142 | ||
| 143 | // get prev time of this task | ||
| 131 | func (tk *Task) GetPrev() time.Time { | 144 | func (tk *Task) GetPrev() time.Time { |
| 132 | return tk.Prev | 145 | return tk.Prev |
| 133 | } | 146 | } |
| 134 | 147 | ||
| 135 | //前6个字段分别表示: | 148 | // six columns mean: |
| 136 | // 秒钟:0-59 | 149 | // second:0-59 |
| 137 | // 分钟:0-59 | 150 | // minute:0-59 |
| 138 | // 小时:1-23 | 151 | // hour:1-23 |
| 139 | // 日期:1-31 | 152 | // day:1-31 |
| 140 | // 月份:1-12 | 153 | // month:1-12 |
| 141 | // 星期:0-6(0表示周日) | 154 | // week:0-6(0 means Sunday) |
| 142 | 155 | ||
| 143 | //还可以用一些特殊符号: | 156 | // some signals: |
| 144 | // *: 表示任何时刻 | 157 | // *: any time |
| 145 | // ,: 表示分割,如第三段里:2,4,表示2点和4点执行 | 158 | // ,: separate signal |
| 146 | // -:表示一个段,如第三端里: 1-5,就表示1到5点 | 159 | // -:duration |
| 147 | // /n : 表示每个n的单位执行一次,如第三段里,*/1, 就表示每隔1个小时执行一次命令。也可以写成1-23/1. | 160 | // /n : do as n times of time duration |
| 148 | ///////////////////////////////////////////////////////// | 161 | ///////////////////////////////////////////////////////// |
| 149 | // 0/30 * * * * * 每30秒 执行 | 162 | // 0/30 * * * * * every 30s |
| 150 | // 0 43 21 * * * 21:43 执行 | 163 | // 0 43 21 * * * 21:43 |
| 151 | // 0 15 05 * * * 05:15 执行 | 164 | // 0 15 05 * * * 05:15 |
| 152 | // 0 0 17 * * * 17:00 执行 | 165 | // 0 0 17 * * * 17:00 |
| 153 | // 0 0 17 * * 1 每周一的 17:00 执行 | 166 | // 0 0 17 * * 1 17:00 in every Monday |
| 154 | // 0 0,10 17 * * 0,2,3 每周日,周二,周三的 17:00和 17:10 执行 | 167 | // 0 0,10 17 * * 0,2,3 17:00 and 17:10 in every Sunday, Tuesday and Wednesday |
| 155 | // 0 0-10 17 1 * * 毎月1日从 17:00到7:10 毎隔1分钟 执行 | 168 | // 0 0-10 17 1 * * 17:00 to 17:10 in 1 min duration each time on the first day of month |
| 156 | // 0 0 0 1,15 * 1 毎月1日和 15日和 一日的 0:00 执行 | 169 | // 0 0 0 1,15 * 1 0:00 on the 1st day and 15th day of month |
| 157 | // 0 42 4 1 * * 毎月1日的 4:42分 执行 | 170 | // 0 42 4 1 * * 4:42 on the 1st day of month |
| 158 | // 0 0 21 * * 1-6 周一到周六 21:00 执行 | 171 | // 0 0 21 * * 1-6 21:00 from Monday to Saturday |
| 159 | // 0 0,10,20,30,40,50 * * * * 每隔10分 执行 | 172 | // 0 0,10,20,30,40,50 * * * * every 10 min duration |
| 160 | // 0 */10 * * * * 每隔10分 执行 | 173 | // 0 */10 * * * * every 10 min duration |
| 161 | // 0 * 1 * * * 从1:0到1:59 每隔1分钟 执行 | 174 | // 0 * 1 * * * 1:00 to 1:59 in 1 min duration each time |
| 162 | // 0 0 1 * * * 1:00 执行 | 175 | // 0 0 1 * * * 1:00 |
| 163 | // 0 0 */1 * * * 毎时0分 每隔1小时 执行 | 176 | // 0 0 */1 * * * 0 min of hour in 1 hour duration |
| 164 | // 0 0 * * * * 毎时0分 每隔1小时 执行 | 177 | // 0 0 * * * * 0 min of hour in 1 hour duration |
| 165 | // 0 2 8-20/3 * * * 8:02,11:02,14:02,17:02,20:02 执行 | 178 | // 0 2 8-20/3 * * * 8:02, 11:02, 14:02, 17:02, 20:02 |
| 166 | // 0 30 5 1,15 * * 1日 和 15日的 5:30 执行 | 179 | // 0 30 5 1,15 * * 5:30 on the 1st day and 15th day of month |
| 167 | func (t *Task) SetCron(spec string) { | 180 | func (t *Task) SetCron(spec string) { |
| 168 | t.Spec = t.parse(spec) | 181 | t.Spec = t.parse(spec) |
| 169 | } | 182 | } |
| ... | @@ -252,6 +265,7 @@ func (t *Task) parseSpec(spec string) *Schedule { | ... | @@ -252,6 +265,7 @@ func (t *Task) parseSpec(spec string) *Schedule { |
| 252 | return nil | 265 | return nil |
| 253 | } | 266 | } |
| 254 | 267 | ||
| 268 | // set schedule to next time | ||
| 255 | func (s *Schedule) Next(t time.Time) time.Time { | 269 | func (s *Schedule) Next(t time.Time) time.Time { |
| 256 | 270 | ||
| 257 | // Start at the earliest possible time (the upcoming second). | 271 | // Start at the earliest possible time (the upcoming second). |
| ... | @@ -349,6 +363,7 @@ func dayMatches(s *Schedule, t time.Time) bool { | ... | @@ -349,6 +363,7 @@ func dayMatches(s *Schedule, t time.Time) bool { |
| 349 | return domMatch || dowMatch | 363 | return domMatch || dowMatch |
| 350 | } | 364 | } |
| 351 | 365 | ||
| 366 | // start all tasks | ||
| 352 | func StartTask() { | 367 | func StartTask() { |
| 353 | go run() | 368 | go run() |
| 354 | } | 369 | } |
| ... | @@ -388,20 +403,23 @@ func run() { | ... | @@ -388,20 +403,23 @@ func run() { |
| 388 | } | 403 | } |
| 389 | } | 404 | } |
| 390 | 405 | ||
| 406 | // start all tasks | ||
| 391 | func StopTask() { | 407 | func StopTask() { |
| 392 | stop <- true | 408 | stop <- true |
| 393 | } | 409 | } |
| 394 | 410 | ||
| 411 | // add task with name | ||
| 395 | func AddTask(taskname string, t Tasker) { | 412 | func AddTask(taskname string, t Tasker) { |
| 396 | AdminTaskList[taskname] = t | 413 | AdminTaskList[taskname] = t |
| 397 | } | 414 | } |
| 398 | 415 | ||
| 399 | //sort map for tasker | 416 | // sort map for tasker |
| 400 | type MapSorter struct { | 417 | type MapSorter struct { |
| 401 | Keys []string | 418 | Keys []string |
| 402 | Vals []Tasker | 419 | Vals []Tasker |
| 403 | } | 420 | } |
| 404 | 421 | ||
| 422 | // create new tasker map | ||
| 405 | func NewMapSorter(m map[string]Tasker) *MapSorter { | 423 | func NewMapSorter(m map[string]Tasker) *MapSorter { |
| 406 | ms := &MapSorter{ | 424 | ms := &MapSorter{ |
| 407 | Keys: make([]string, 0, len(m)), | 425 | Keys: make([]string, 0, len(m)), |
| ... | @@ -414,6 +432,7 @@ func NewMapSorter(m map[string]Tasker) *MapSorter { | ... | @@ -414,6 +432,7 @@ func NewMapSorter(m map[string]Tasker) *MapSorter { |
| 414 | return ms | 432 | return ms |
| 415 | } | 433 | } |
| 416 | 434 | ||
| 435 | // sort tasker map | ||
| 417 | func (ms *MapSorter) Sort() { | 436 | func (ms *MapSorter) Sort() { |
| 418 | sort.Sort(ms) | 437 | sort.Sort(ms) |
| 419 | } | 438 | } | ... | ... |
-
Please register or sign in to post a comment