From 43c977ab625c186df8cde3e9743622d6bf3b984a Mon Sep 17 00:00:00 2001 From: unphp Date: Wed, 12 Mar 2014 17:20:53 +0800 Subject: [PATCH 01/22] Update router.go To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter --- router.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/router.go b/router.go index 6a7f8bf6..3705ddbc 100644 --- a/router.go +++ b/router.go @@ -44,6 +44,11 @@ var ( "GetControllerAndAction"} ) +// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter +func ExceptMethodAppend(action string) { + exceptMethod = append(exceptMethod, action) +} + type controllerInfo struct { pattern string regex *regexp.Regexp From 95e67ba2c2a701b7be3c48c8e9076a4ce14e12fa Mon Sep 17 00:00:00 2001 From: slene Date: Thu, 13 Mar 2014 23:31:47 +0800 Subject: [PATCH 02/22] orm now support custom builtin types as model struct field or query args fix #489 --- orm/db_utils.go | 112 ++++++++++++++++++++++++-------------------- orm/models_test.go | 39 +++++++++++++++ orm/models_utils.go | 39 ++++++++------- orm/orm_test.go | 45 ++++++++++++++++-- 4 files changed, 165 insertions(+), 70 deletions(-) diff --git a/orm/db_utils.go b/orm/db_utils.go index 34de8186..3c8d2d23 100644 --- a/orm/db_utils.go +++ b/orm/db_utils.go @@ -51,9 +51,16 @@ outFor: continue } - switch v := arg.(type) { - case []byte: - case string: + kind := val.Kind() + if kind == reflect.Ptr { + val = val.Elem() + kind = val.Kind() + arg = val.Interface() + } + + switch kind { + case reflect.String: + v := val.String() if fi != nil { if fi.fieldType == TypeDateField || fi.fieldType == TypeDateTimeField { var t time.Time @@ -78,61 +85,66 @@ outFor: } } arg = v - case time.Time: - if fi != nil && fi.fieldType == TypeDateField { - arg = v.In(tz).Format(format_Date) - } else { - arg = v.In(tz).Format(format_DateTime) - } - default: - kind := val.Kind() - switch kind { - case reflect.Slice, reflect.Array: - - var args []interface{} - for i := 0; i < val.Len(); i++ { - v := val.Index(i) - - var vu interface{} - if v.CanInterface() { - vu = v.Interface() - } - - if vu == nil { - continue - } - - args = append(args, vu) - } - - if len(args) > 0 { - p := getFlatParams(fi, args, tz) - params = append(params, p...) - } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + arg = val.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + arg = val.Uint() + case reflect.Float32: + arg, _ = StrTo(ToStr(arg)).Float64() + case reflect.Float64: + arg = val.Float() + case reflect.Bool: + arg = val.Bool() + case reflect.Slice, reflect.Array: + if _, ok := arg.([]byte); ok { continue outFor + } - case reflect.Ptr, reflect.Struct: - ind := reflect.Indirect(val) + var args []interface{} + for i := 0; i < val.Len(); i++ { + v := val.Index(i) - if ind.Kind() == reflect.Struct { - typ := ind.Type() - name := getFullName(typ) - var value interface{} - if mmi, ok := modelCache.getByFN(name); ok { - if _, vu, exist := getExistPk(mmi, ind); exist { - value = vu - } - } - arg = value + var vu interface{} + if v.CanInterface() { + vu = v.Interface() + } - if arg == nil { - panic(fmt.Errorf("need a valid args value, unknown table or value `%s`", name)) - } + if vu == nil { + continue + } + + args = append(args, vu) + } + + if len(args) > 0 { + p := getFlatParams(fi, args, tz) + params = append(params, p...) + } + continue outFor + case reflect.Struct: + if v, ok := arg.(time.Time); ok { + if fi != nil && fi.fieldType == TypeDateField { + arg = v.In(tz).Format(format_Date) } else { - arg = ind.Interface() + arg = v.In(tz).Format(format_DateTime) + } + } else { + typ := val.Type() + name := getFullName(typ) + var value interface{} + if mmi, ok := modelCache.getByFN(name); ok { + if _, vu, exist := getExistPk(mmi, val); exist { + value = vu + } + } + arg = value + + if arg == nil { + panic(fmt.Errorf("need a valid args value, unknown table or value `%s`", name)) } } } + params = append(params, arg) } return diff --git a/orm/models_test.go b/orm/models_test.go index 168c091a..8564dcb8 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -144,6 +144,45 @@ type DataNull struct { NullInt64 sql.NullInt64 `orm:"null"` } +type String string +type Boolean bool +type Byte byte +type Rune rune +type Int int +type Int8 int8 +type Int16 int16 +type Int32 int32 +type Int64 int64 +type Uint uint +type Uint8 uint8 +type Uint16 uint16 +type Uint32 uint32 +type Uint64 uint64 +type Float32 float64 +type Float64 float64 + +type DataCustom struct { + Id int + Boolean Boolean + Char string `orm:"size(50)"` + Text string `orm:"type(text)"` + Byte Byte + Rune Rune + Int Int + Int8 Int8 + Int16 Int16 + Int32 Int32 + Int64 Int64 + Uint Uint + Uint8 Uint8 + Uint16 Uint16 + Uint32 Uint32 + Uint64 Uint64 + Float32 Float32 + Float64 Float64 + Decimal Float64 `orm:"digits(8);decimals(4)"` +} + // only for mysql type UserBig struct { Id uint64 diff --git a/orm/models_utils.go b/orm/models_utils.go index 759093ef..f6cb14ec 100644 --- a/orm/models_utils.go +++ b/orm/models_utils.go @@ -99,34 +99,41 @@ func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col // return field type as type constant from reflect.Value func getFieldType(val reflect.Value) (ft int, err error) { elm := reflect.Indirect(val) - switch elm.Interface().(type) { - case int8: + switch elm.Kind() { + case reflect.Int8: ft = TypeBitField - case int16: + case reflect.Int16: ft = TypeSmallIntegerField - case int32, int: + case reflect.Int32, reflect.Int: ft = TypeIntegerField - case int64, sql.NullInt64: + case reflect.Int64: ft = TypeBigIntegerField - case uint8: + case reflect.Uint8: ft = TypePositiveBitField - case uint16: + case reflect.Uint16: ft = TypePositiveSmallIntegerField - case uint32, uint: + case reflect.Uint32, reflect.Uint: ft = TypePositiveIntegerField - case uint64: + case reflect.Uint64: ft = TypePositiveBigIntegerField - case float32, float64, sql.NullFloat64: + case reflect.Float32, reflect.Float64: ft = TypeFloatField - case bool, sql.NullBool: + case reflect.Bool: ft = TypeBooleanField - case string, sql.NullString: + case reflect.String: ft = TypeCharField default: - if elm.CanInterface() { - if _, ok := elm.Interface().(time.Time); ok { - ft = TypeDateTimeField - } + switch elm.Interface().(type) { + case sql.NullInt64: + ft = TypeBigIntegerField + case sql.NullFloat64: + ft = TypeFloatField + case sql.NullBool: + ft = TypeBooleanField + case sql.NullString: + ft = TypeCharField + case time.Time: + ft = TypeDateTimeField } } if ft&IsFieldType == 0 { diff --git a/orm/orm_test.go b/orm/orm_test.go index 69f2fc86..0f8ad81a 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -149,7 +149,7 @@ func TestGetDB(t *testing.T) { } func TestSyncDb(t *testing.T) { - RegisterModel(new(Data), new(DataNull)) + RegisterModel(new(Data), new(DataNull), new(DataCustom)) RegisterModel(new(User)) RegisterModel(new(Profile)) RegisterModel(new(Post)) @@ -165,7 +165,7 @@ func TestSyncDb(t *testing.T) { } func TestRegisterModels(t *testing.T) { - RegisterModel(new(Data), new(DataNull)) + RegisterModel(new(Data), new(DataNull), new(DataCustom)) RegisterModel(new(User)) RegisterModel(new(Profile)) RegisterModel(new(Post)) @@ -309,6 +309,39 @@ func TestNullDataTypes(t *testing.T) { throwFail(t, AssertIs(d.NullFloat64.Float64, 42.42)) } +func TestDataCustomTypes(t *testing.T) { + d := DataCustom{} + ind := reflect.Indirect(reflect.ValueOf(&d)) + + for name, value := range Data_Values { + e := ind.FieldByName(name) + if !e.IsValid() { + continue + } + e.Set(reflect.ValueOf(value).Convert(e.Type())) + } + + id, err := dORM.Insert(&d) + throwFail(t, err) + throwFail(t, AssertIs(id, 1)) + + d = DataCustom{Id: 1} + err = dORM.Read(&d) + throwFail(t, err) + + ind = reflect.Indirect(reflect.ValueOf(&d)) + + for name, value := range Data_Values { + e := ind.FieldByName(name) + if !e.IsValid() { + continue + } + vu := e.Interface() + value = reflect.ValueOf(value).Convert(e.Type()).Interface() + throwFail(t, AssertIs(vu == value, true), value, vu) + } +} + func TestCRUD(t *testing.T) { profile := NewProfile() profile.Age = 30 @@ -562,6 +595,10 @@ func TestOperators(t *testing.T) { throwFail(t, err) throwFail(t, AssertIs(num, 1)) + num, err = qs.Filter("user_name__exact", String("slene")).Count() + throwFail(t, err) + throwFail(t, AssertIs(num, 1)) + num, err = qs.Filter("user_name__exact", "slene").Count() throwFail(t, err) throwFail(t, AssertIs(num, 1)) @@ -602,11 +639,11 @@ func TestOperators(t *testing.T) { throwFail(t, err) throwFail(t, AssertIs(num, 3)) - num, err = qs.Filter("status__lt", 3).Count() + num, err = qs.Filter("status__lt", Uint(3)).Count() throwFail(t, err) throwFail(t, AssertIs(num, 2)) - num, err = qs.Filter("status__lte", 3).Count() + num, err = qs.Filter("status__lte", Int(3)).Count() throwFail(t, err) throwFail(t, AssertIs(num, 3)) From 8188873216655b6655235127e39a26d27bc6e703 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Fri, 14 Mar 2014 12:00:53 +0800 Subject: [PATCH 03/22] omit the data init --- controller.go | 1 - 1 file changed, 1 deletion(-) diff --git a/controller.go b/controller.go index c9ad10e9..16bef4df 100644 --- a/controller.go +++ b/controller.go @@ -62,7 +62,6 @@ type ControllerInterface interface { // Init generates default values of controller operations. func (c *Controller) Init(ctx *context.Context, controllerName, actionName string, app interface{}) { - c.Data = make(map[interface{}]interface{}) c.Layout = "" c.TplNames = "" c.controllerName = controllerName From 4951314837af08eaee05d07e9b6bb0ea30446678 Mon Sep 17 00:00:00 2001 From: Jared Folkins Date: Thu, 13 Mar 2014 22:30:12 -0700 Subject: [PATCH 04/22] added FlashName,FlashSeperator, & Tests --- .gitignore | 2 ++ config.go | 13 +++++++++++++ config_test.go | 15 +++++++++++++++ flash.go | 17 ++++++----------- flash_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 config_test.go create mode 100644 flash_test.go diff --git a/.gitignore b/.gitignore index e43b0f98..482e34b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .DS_Store +*.swp +*.swo diff --git a/config.go b/config.go index 7c21d696..54de325c 100644 --- a/config.go +++ b/config.go @@ -58,6 +58,8 @@ var ( EnableAdmin bool // flag of enable admin module to log every request info. AdminHttpAddr string // http server configurations for admin module. AdminHttpPort int + FlashName string // name of the flash variable found in response header and cookie + FlashSeperator string // used to seperate flash key:value ) func init() { @@ -123,6 +125,9 @@ func init() { AdminHttpAddr = "127.0.0.1" AdminHttpPort = 8088 + FlashName = "BEEGO_FLASH" + FlashSeperator = "BEEGOFLASH" + runtime.GOMAXPROCS(runtime.NumCPU()) // init BeeLogger @@ -271,6 +276,14 @@ func ParseConfig() (err error) { BeegoServerName = serverName } + if flashname := AppConfig.String("FlashName"); flashname != "" { + FlashName = flashname + } + + if flashseperator := AppConfig.String("FlashSeperator"); flashseperator != "" { + FlashSeperator = flashseperator + } + if sd := AppConfig.String("StaticDir"); sd != "" { for k := range StaticDir { delete(StaticDir, k) diff --git a/config_test.go b/config_test.go new file mode 100644 index 00000000..d400fd5d --- /dev/null +++ b/config_test.go @@ -0,0 +1,15 @@ +package beego + +import ( + "testing" +) + +func TestDefaults(t *testing.T) { + if FlashName != "BEEGO_FLASH" { + t.Errorf("FlashName was not set to default.") + } + + if FlashSeperator != "BEEGOFLASH" { + t.Errorf("FlashName was not set to default.") + } +} diff --git a/flash.go b/flash.go index f435c155..3cb4a66a 100644 --- a/flash.go +++ b/flash.go @@ -6,9 +6,6 @@ import ( "strings" ) -// the separation string when encoding flash data. -const BEEGO_FLASH_SEP = "#BEEGOFLASH#" - // FlashData is a tools to maintain data when using across request. type FlashData struct { Data map[string]string @@ -54,29 +51,27 @@ func (fd *FlashData) Store(c *Controller) { c.Data["flash"] = fd.Data var flashValue string for key, value := range fd.Data { - flashValue += "\x00" + key + BEEGO_FLASH_SEP + value + "\x00" + flashValue += "\x00" + key + "\x23" + FlashSeperator + "\x23" + value + "\x00" } - c.Ctx.SetCookie("BEEGO_FLASH", url.QueryEscape(flashValue), 0, "/") + c.Ctx.SetCookie(FlashName, url.QueryEscape(flashValue), 0, "/") } // ReadFromRequest parsed flash data from encoded values in cookie. func ReadFromRequest(c *Controller) *FlashData { - flash := &FlashData{ - Data: make(map[string]string), - } - if cookie, err := c.Ctx.Request.Cookie("BEEGO_FLASH"); err == nil { + flash := NewFlash() + if cookie, err := c.Ctx.Request.Cookie(FlashName); err == nil { v, _ := url.QueryUnescape(cookie.Value) vals := strings.Split(v, "\x00") for _, v := range vals { if len(v) > 0 { - kv := strings.Split(v, BEEGO_FLASH_SEP) + kv := strings.Split(v, FlashSeperator) if len(kv) == 2 { flash.Data[kv[0]] = kv[1] } } } //read one time then delete it - c.Ctx.SetCookie("BEEGO_FLASH", "", -1, "/") + c.Ctx.SetCookie(FlashName, "", -1, "/") } c.Data["flash"] = flash.Data return flash diff --git a/flash_test.go b/flash_test.go new file mode 100644 index 00000000..d329bae4 --- /dev/null +++ b/flash_test.go @@ -0,0 +1,40 @@ +package beego + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" +) + +type TestFlashController struct { + Controller +} + +func (this *TestFlashController) TestWriteFlash() { + flash := NewFlash() + flash.Notice("TestFlashString") + flash.Store(&this.Controller) + // we choose to serve json because we don't want to load a template html file + this.ServeJson(true) +} + +func TestFlashHeader(t *testing.T) { + // create fake GET request + r, _ := http.NewRequest("GET", "/", nil) + w := httptest.NewRecorder() + + // setup the handler + handler := NewControllerRegistor() + handler.Add("/", &TestFlashController{}, "get:TestWriteFlash") + handler.ServeHTTP(w, r) + + // get the Set-Cookie value + sc := w.Header().Get("Set-Cookie") + // match for the expected header + res := strings.Contains(sc, "BEEGO_FLASH=%00notice%23BEEGOFLASH%23TestFlashString%00") + // validate the assertion + if res != true { + t.Errorf("TestFlashHeader() unable to validate flash message") + } +} From ad8418720f1adfd080ac0869871c0827373c6f49 Mon Sep 17 00:00:00 2001 From: "S.W.H" Date: Fri, 14 Mar 2014 14:47:52 +0800 Subject: [PATCH 05/22] bug fixed --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index d198d442..631e0f38 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -67,7 +67,7 @@ func (r Required) IsSatisfied(obj interface{}) bool { } func (r Required) DefaultMessage() string { - return "Required" + return fmt.Sprint(MessageTmpls["Required"]) } func (r Required) GetKey() string { From 50bc1ef757025a96e1810cc6292a9b0dd36f2089 Mon Sep 17 00:00:00 2001 From: slene Date: Mon, 17 Mar 2014 12:27:04 +0800 Subject: [PATCH 06/22] rollback: set httponly default is false. --- context/output.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/context/output.go b/context/output.go index a8a304b6..2d7814e4 100644 --- a/context/output.go +++ b/context/output.go @@ -135,12 +135,12 @@ func (output *BeegoOutput) Cookie(name string, value string, others ...interface } } - // default true - httponly := true + // default false. for session cookie default true + httponly := false if len(others) > 4 { - if v, ok := others[4].(bool); ok && !v || others[4] == nil { - // HttpOnly = false - httponly = false + if v, ok := others[4].(bool); ok && v { + // HttpOnly = true + httponly = true } } From a879e412a1986770dfc0f0e7470c7a248c20066c Mon Sep 17 00:00:00 2001 From: slene Date: Wed, 19 Mar 2014 09:46:09 +0800 Subject: [PATCH 07/22] #514 --- orm/models_test.go | 2 +- orm/models_utils.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/orm/models_test.go b/orm/models_test.go index 8564dcb8..e37e53d9 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -194,7 +194,7 @@ type User struct { UserName string `orm:"size(30);unique"` Email string `orm:"size(100)"` Password string `orm:"size(100)"` - Status int16 + Status int16 `orm:"column(Status)"` IsStaff bool IsActive bool `orm:"default(1)"` Created time.Time `orm:"auto_now_add;type(date)"` diff --git a/orm/models_utils.go b/orm/models_utils.go index f6cb14ec..d8d7cc25 100644 --- a/orm/models_utils.go +++ b/orm/models_utils.go @@ -80,7 +80,6 @@ func getTableUnique(val reflect.Value) [][]string { // get snaked column name func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string { - col = strings.ToLower(col) column := col if col == "" { column = snakeString(sf.Name) From 65631e05226e0d4238c06c7da3858afdb91660e0 Mon Sep 17 00:00:00 2001 From: slene Date: Wed, 19 Mar 2014 10:00:26 +0800 Subject: [PATCH 08/22] fix orm test --- orm/orm_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/orm/orm_test.go b/orm/orm_test.go index 0f8ad81a..cfdcc1a7 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -1417,7 +1417,7 @@ func TestRawQueryRow(t *testing.T) { ) cols = []string{ - "id", "status", "profile_id", + "id", "Status", "profile_id", } query = fmt.Sprintf("SELECT %s%s%s FROM %suser%s WHERE id = ?", Q, strings.Join(cols, sep), Q, Q, Q) err = dORM.Raw(query, 4).QueryRow(&uid, &status, &pid) @@ -1497,7 +1497,7 @@ func TestRawValues(t *testing.T) { Q := dDbBaser.TableQuote() var maps []Params - query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sstatus%s = ?", Q, Q, Q, Q, Q, Q) + query := fmt.Sprintf("SELECT %suser_name%s FROM %suser%s WHERE %sStatus%s = ?", Q, Q, Q, Q, Q, Q) num, err := dORM.Raw(query, 1).Values(&maps) throwFail(t, err) throwFail(t, AssertIs(num, 1)) From 589f97130c4088415cf05e541659f5eb1d058e82 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Fri, 21 Mar 2014 14:07:03 +0800 Subject: [PATCH 09/22] add w.Rotate --- logs/file.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logs/file.go b/logs/file.go index d0512e26..d61136c0 100644 --- a/logs/file.go +++ b/logs/file.go @@ -118,9 +118,9 @@ func (w *FileLogWriter) StartLogger() error { func (w *FileLogWriter) docheck(size int) { w.startLock.Lock() defer w.startLock.Unlock() - if (w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) || + if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) || (w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) || - (w.Daily && time.Now().Day() != w.daily_opendate) { + (w.Daily && time.Now().Day() != w.daily_opendate)) { if err := w.DoRotate(); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) return From c921b0aa5dd64295af5922866dfbdeff4e65183d Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Fri, 21 Mar 2014 14:24:00 +0800 Subject: [PATCH 10/22] fix #533 change the function name --- session/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/session.go b/session/session.go index bc8832b0..7da0d41f 100644 --- a/session/session.go +++ b/session/session.go @@ -173,7 +173,7 @@ func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) { } // Get SessionStore by its id. -func (manager *Manager) GetProvider(sid string) (sessions SessionStore, err error) { +func (manager *Manager) GetSessionStore(sid string) (sessions SessionStore, err error) { sessions, err = manager.provider.SessionRead(sid) return } From 8d1268c0a9758c91380122a88609976e33e8c6df Mon Sep 17 00:00:00 2001 From: steamonimo Date: Tue, 25 Mar 2014 12:45:23 +0100 Subject: [PATCH 11/22] session provider for postgresql This provider is based on the mysql provider: sess_mysql.go --- session/sess_postgresql.go | 227 +++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 session/sess_postgresql.go diff --git a/session/sess_postgresql.go b/session/sess_postgresql.go new file mode 100644 index 00000000..585a864d --- /dev/null +++ b/session/sess_postgresql.go @@ -0,0 +1,227 @@ +package session + +/* + +beego session provider for postgresql +------------------------------------- + +depends on github.com/lib/pq: + +go install github.com/lib/pq + + +needs this table in your database: + +CREATE TABLE session ( +session_key char(64) NOT NULL, +session_data bytea, +session_expiry timestamp NOT NULL, +CONSTRAINT session_key PRIMARY KEY(session_key) +); + + +will be activated with these settings in app.conf: + +SessionOn = true +SessionProvider = postgresql +SessionSavePath = "user=a password=b dbname=c sslmode=disable" +SessionName = session + +*/ + +import ( + "database/sql" + "net/http" + "sync" + "time" + _ "github.com/lib/pq" +) + +var postgresqlpder = &PostgresqlProvider{} + +// postgresql session store +type PostgresqlSessionStore struct { + c *sql.DB + sid string + lock sync.RWMutex + values map[interface{}]interface{} +} + +// set value in postgresql session. +// it is temp value in map. +func (st *PostgresqlSessionStore) Set(key, value interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + st.values[key] = value + return nil +} + +// get value from postgresql session +func (st *PostgresqlSessionStore) Get(key interface{}) interface{} { + st.lock.RLock() + defer st.lock.RUnlock() + if v, ok := st.values[key]; ok { + return v + } else { + return nil + } + return nil +} + +// delete value in postgresql session +func (st *PostgresqlSessionStore) Delete(key interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + delete(st.values, key) + return nil +} + +// clear all values in postgresql session +func (st *PostgresqlSessionStore) Flush() error { + st.lock.Lock() + defer st.lock.Unlock() + st.values = make(map[interface{}]interface{}) + return nil +} + +// get session id of this postgresql session store +func (st *PostgresqlSessionStore) SessionID() string { + return st.sid +} + +// save postgresql session values to database. +// must call this method to save values to database. +func (st *PostgresqlSessionStore) SessionRelease(w http.ResponseWriter) { + defer st.c.Close() + b, err := encodeGob(st.values) + if err != nil { + return + } + st.c.Exec("UPDATE session set session_data=$1, session_expiry=$2 where session_key=$3", + b, time.Now().Format(time.RFC3339), st.sid) + +} + +// postgresql session provider +type PostgresqlProvider struct { + maxlifetime int64 + savePath string +} + +// connect to postgresql +func (mp *PostgresqlProvider) connectInit() *sql.DB { + db, e := sql.Open("postgres", mp.savePath) + if e != nil { + return nil + } + return db +} + +// init postgresql session. +// savepath is the connection string of postgresql. +func (mp *PostgresqlProvider) SessionInit(maxlifetime int64, savePath string) error { + mp.maxlifetime = maxlifetime + mp.savePath = savePath + return nil +} + +// get postgresql session by sid +func (mp *PostgresqlProvider) SessionRead(sid string) (SessionStore, error) { + c := mp.connectInit() + row := c.QueryRow("select session_data from session where session_key=$1", sid) + var sessiondata []byte + err := row.Scan(&sessiondata) + if err == sql.ErrNoRows { + _, err = c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)", + sid, "", time.Now().Format(time.RFC3339)) + + if err != nil { + return nil, err + } + } else if err != nil { + return nil, err + } + + var kv map[interface{}]interface{} + if len(sessiondata) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = decodeGob(sessiondata) + if err != nil { + return nil, err + } + } + rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv} + return rs, nil +} + +// check postgresql session exist +func (mp *PostgresqlProvider) SessionExist(sid string) bool { + c := mp.connectInit() + defer c.Close() + row := c.QueryRow("select session_data from session where session_key=$1", sid) + var sessiondata []byte + err := row.Scan(&sessiondata) + + if err == sql.ErrNoRows { + return false + } else { + return true + } +} + +// generate new sid for postgresql session +func (mp *PostgresqlProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { + c := mp.connectInit() + row := c.QueryRow("select session_data from session where session_key=$1", oldsid) + var sessiondata []byte + err := row.Scan(&sessiondata) + if err == sql.ErrNoRows { + c.Exec("insert into session(session_key,session_data,session_expiry) values($1,$2,$3)", + oldsid, "", time.Now().Format(time.RFC3339)) + } + c.Exec("update session set session_key=$1 where session_key=$2", sid, oldsid) + var kv map[interface{}]interface{} + if len(sessiondata) == 0 { + kv = make(map[interface{}]interface{}) + } else { + kv, err = decodeGob(sessiondata) + if err != nil { + return nil, err + } + } + rs := &PostgresqlSessionStore{c: c, sid: sid, values: kv} + return rs, nil +} + +// delete postgresql session by sid +func (mp *PostgresqlProvider) SessionDestroy(sid string) error { + c := mp.connectInit() + c.Exec("DELETE FROM session where session_key=$1", sid) + c.Close() + return nil +} + +// delete expired values in postgresql session +func (mp *PostgresqlProvider) SessionGC() { + c := mp.connectInit() + c.Exec("DELETE from session where EXTRACT(EPOCH FROM (current_timestamp - session_expiry)) > $1", mp.maxlifetime) + c.Close() + return +} + +// count values in postgresql session +func (mp *PostgresqlProvider) SessionAll() int { + c := mp.connectInit() + defer c.Close() + var total int + err := c.QueryRow("SELECT count(*) as num from session").Scan(&total) + if err != nil { + return 0 + } + return total +} + +func init() { + Register("postgresql", postgresqlpder) +} From 3f5fee2dc627c1376614562c551cb3d447bf1fb9 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Tue, 25 Mar 2014 23:48:18 +0800 Subject: [PATCH 12/22] Logs support file & filenum --- config.go | 5 +++++ logs/console_test.go | 2 ++ logs/log.go | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index 54de325c..a054a736 100644 --- a/config.go +++ b/config.go @@ -60,6 +60,7 @@ var ( AdminHttpPort int FlashName string // name of the flash variable found in response header and cookie FlashSeperator string // used to seperate flash key:value + EnableLogFuncCallDepth bool // enable the funcCallDeppth ) func init() { @@ -133,6 +134,10 @@ func init() { // init BeeLogger BeeLogger = logs.NewLogger(10000) BeeLogger.SetLogger("console", "") + if EnableLogFuncCallDepth { + BeeLogger.EnableFuncCallDepth(true) + BeeLogger.SetLogFuncCallDepth(3) + } err := ParseConfig() if err != nil && !os.IsNotExist(err) { diff --git a/logs/console_test.go b/logs/console_test.go index f177e919..041927ca 100644 --- a/logs/console_test.go +++ b/logs/console_test.go @@ -6,6 +6,7 @@ import ( func TestConsole(t *testing.T) { log := NewLogger(10000) + log.EnableFuncCallDepth(true) log.SetLogger("console", "") log.Trace("trace") log.Info("info") @@ -23,6 +24,7 @@ func TestConsole(t *testing.T) { func BenchmarkConsole(b *testing.B) { log := NewLogger(10000) + log.EnableFuncCallDepth(true) log.SetLogger("console", "") for i := 0; i < b.N; i++ { log.Trace("trace") diff --git a/logs/log.go b/logs/log.go index b65414cb..67eb1c79 100644 --- a/logs/log.go +++ b/logs/log.go @@ -2,6 +2,8 @@ package logs import ( "fmt" + "path" + "runtime" "sync" ) @@ -43,10 +45,12 @@ func Register(name string, log loggerType) { // BeeLogger is default logger in beego application. // it can contain several providers and log message into all providers. type BeeLogger struct { - lock sync.Mutex - level int - msg chan *logMsg - outputs map[string]LoggerInterface + lock sync.Mutex + level int + enableFuncCallDepth bool + loggerFuncCallDepth int + msg chan *logMsg + outputs map[string]LoggerInterface } type logMsg struct { @@ -59,6 +63,7 @@ type logMsg struct { // if the buffering chan is full, logger adapters write to file or other way. func NewLogger(channellen int64) *BeeLogger { bl := new(BeeLogger) + bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console @@ -100,7 +105,17 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } lm := new(logMsg) lm.level = loglevel - lm.msg = msg + if bl.enableFuncCallDepth { + _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) + if ok { + _, filename := path.Split(file) + lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) + } else { + lm.msg = msg + } + } else { + lm.msg = msg + } bl.msg <- lm return nil } @@ -111,6 +126,16 @@ func (bl *BeeLogger) SetLevel(l int) { bl.level = l } +// set log funcCallDepth +func (bl *BeeLogger) SetLogFuncCallDepth(d int) { + bl.loggerFuncCallDepth = d +} + +// enable log funcCallDepth +func (bl *BeeLogger) EnableFuncCallDepth(b bool) { + bl.enableFuncCallDepth = b +} + // start logger chan reading. // when chan is full, write logs. func (bl *BeeLogger) StartLogger() { From 4f819dbd9a05b1cbbc579951daae3eaa9b59d3df Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Wed, 26 Mar 2014 00:06:25 +0800 Subject: [PATCH 13/22] Add a function SetLogFuncCall to enable --- config.go | 5 ----- log.go | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index a054a736..54de325c 100644 --- a/config.go +++ b/config.go @@ -60,7 +60,6 @@ var ( AdminHttpPort int FlashName string // name of the flash variable found in response header and cookie FlashSeperator string // used to seperate flash key:value - EnableLogFuncCallDepth bool // enable the funcCallDeppth ) func init() { @@ -134,10 +133,6 @@ func init() { // init BeeLogger BeeLogger = logs.NewLogger(10000) BeeLogger.SetLogger("console", "") - if EnableLogFuncCallDepth { - BeeLogger.EnableFuncCallDepth(true) - BeeLogger.SetLogFuncCallDepth(3) - } err := ParseConfig() if err != nil && !os.IsNotExist(err) { diff --git a/log.go b/log.go index 6af6f7be..128e998e 100644 --- a/log.go +++ b/log.go @@ -22,6 +22,11 @@ func SetLevel(l int) { BeeLogger.SetLevel(l) } +func SetLogFuncCall(b bool) { + BeeLogger.EnableFuncCallDepth(b) + BeeLogger.SetLogFuncCallDepth(3) +} + // logger references the used application logger. var BeeLogger *logs.BeeLogger From c7437d7590a6b62aba4233551f4692012c8d0211 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Wed, 26 Mar 2014 13:51:35 +0800 Subject: [PATCH 14/22] fix Cookie for session --- session/sess_cookie.go | 1 + 1 file changed, 1 insertion(+) diff --git a/session/sess_cookie.go b/session/sess_cookie.go index ddf1a902..54acba31 100644 --- a/session/sess_cookie.go +++ b/session/sess_cookie.go @@ -118,6 +118,7 @@ func (pder *CookieProvider) SessionInit(maxlifetime int64, config string) error if err != nil { return err } + pder.maxlifetime = maxlifetime return nil } From 2f4acf46c64ee3073311b0be5547cf3b80f0a1ef Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Thu, 27 Mar 2014 08:49:57 +0800 Subject: [PATCH 15/22] modify the template file --- template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.go b/template.go index 26f13cfc..eb8d0c5d 100644 --- a/template.go +++ b/template.go @@ -151,7 +151,7 @@ func getTplDeep(root, file, parent string, t *template.Template) (*template.Temp fileabspath = filepath.Join(root, file) } if e := utils.FileExists(fileabspath); !e { - panic("can't find template file" + file) + panic("can't find template file:" + file) } data, err := ioutil.ReadFile(fileabspath) if err != nil { From 5588bfc35e6df9d1fb9521db9c0a99804fb1221f Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Sat, 29 Mar 2014 14:55:34 +0800 Subject: [PATCH 16/22] support filter to get router. get runController & runMethod --- context/input.go | 13 ++++++++----- router.go | 50 ++++++++++++++++++++++++++++-------------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/context/input.go b/context/input.go index 308dd77c..0a3c8535 100644 --- a/context/input.go +++ b/context/input.go @@ -4,6 +4,7 @@ import ( "bytes" "io/ioutil" "net/http" + "reflect" "strconv" "strings" @@ -13,11 +14,13 @@ import ( // BeegoInput operates the http request header ,data ,cookie and body. // it also contains router params and current session. type BeegoInput struct { - CruSession session.SessionStore - Params map[string]string - Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. - Request *http.Request - RequestBody []byte + CruSession session.SessionStore + Params map[string]string + Data map[interface{}]interface{} // store some values in this context when calling context in filter or controller. + Request *http.Request + RequestBody []byte + RunController reflect.Type + RunMethod string } // NewInput return BeegoInput generated by http.Request. diff --git a/router.go b/router.go index 9a1ec6ff..6541c4ee 100644 --- a/router.go +++ b/router.go @@ -626,29 +626,37 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Input.Body() } + if context.Input.RunController != nil && context.Input.RunMethod { + findrouter = true + runMethod = context.Input.RunMethod + runrouter = context.Input.RunController + } + //first find path from the fixrouters to Improve Performance - for _, route := range p.fixrouters { - n := len(requestPath) - if requestPath == route.pattern { - runMethod = p.getRunMethod(r.Method, context, route) - if runMethod != "" { - runrouter = route.controllerType - findrouter = true - break + if !findrouter { + for _, route := range p.fixrouters { + n := len(requestPath) + if requestPath == route.pattern { + runMethod = p.getRunMethod(r.Method, context, route) + if runMethod != "" { + runrouter = route.controllerType + findrouter = true + break + } } - } - // pattern /admin url /admin 200 /admin/ 200 - // pattern /admin/ url /admin 301 /admin/ 200 - if requestPath[n-1] != '/' && requestPath+"/" == route.pattern { - http.Redirect(w, r, requestPath+"/", 301) - goto Admin - } - if requestPath[n-1] == '/' && route.pattern+"/" == requestPath { - runMethod = p.getRunMethod(r.Method, context, route) - if runMethod != "" { - runrouter = route.controllerType - findrouter = true - break + // pattern /admin url /admin 200 /admin/ 200 + // pattern /admin/ url /admin 301 /admin/ 200 + if requestPath[n-1] != '/' && requestPath+"/" == route.pattern { + http.Redirect(w, r, requestPath+"/", 301) + goto Admin + } + if requestPath[n-1] == '/' && route.pattern+"/" == requestPath { + runMethod = p.getRunMethod(r.Method, context, route) + if runMethod != "" { + runrouter = route.controllerType + findrouter = true + break + } } } } From 9c959fba4da7e909c38bba4cd81bf65823745736 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Sat, 29 Mar 2014 14:59:55 +0800 Subject: [PATCH 17/22] fix string --- router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router.go b/router.go index 6541c4ee..e21750f4 100644 --- a/router.go +++ b/router.go @@ -626,7 +626,7 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Input.Body() } - if context.Input.RunController != nil && context.Input.RunMethod { + if context.Input.RunController != nil && context.Input.RunMethod != "" { findrouter = true runMethod = context.Input.RunMethod runrouter = context.Input.RunController From bf0b1af64fc2f87049222cc53bf61ee4dcc5f27a Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Tue, 1 Apr 2014 18:08:00 +0800 Subject: [PATCH 18/22] add workPath don't chdir when go run or go test --- config.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index 54de325c..a963ac02 100644 --- a/config.go +++ b/config.go @@ -11,12 +11,14 @@ import ( "github.com/astaxie/beego/config" "github.com/astaxie/beego/logs" "github.com/astaxie/beego/session" + "github.com/astaxie/beego/utils" ) var ( BeeApp *App // beego application AppName string AppPath string + workPath string AppConfigPath string StaticDir map[string]string TemplateCache map[string]*template.Template // template caching map @@ -66,9 +68,20 @@ func init() { // create beego application BeeApp = NewApp() + workPath, _ = os.Getwd() + workPath, _ = filepath.Abs(workPath) // initialize default configurations AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0])) - os.Chdir(AppPath) + + AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") + + if workPath != AppPath { + if utils.FileExists(AppConfigPath) { + os.Chdir(AppPath) + } else { + AppConfigPath = filepath.Join(workPath, "conf", "app.conf") + } + } StaticDir = make(map[string]string) StaticDir["/static"] = "static" @@ -107,8 +120,6 @@ func init() { EnableGzip = false - AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") - HttpServerTimeOut = 0 ErrorsShow = true From 9c5ceb70ccf5dd004e3a8dfe5602504d75a6f5d9 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Wed, 2 Apr 2014 23:43:37 +0800 Subject: [PATCH 19/22] Logs: modify StartLogger to private --- logs/file.go | 6 +++--- logs/log.go | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/logs/file.go b/logs/file.go index d61136c0..9400e04d 100644 --- a/logs/file.go +++ b/logs/file.go @@ -97,12 +97,12 @@ func (w *FileLogWriter) Init(jsonconfig string) error { if len(w.Filename) == 0 { return errors.New("jsonconfig must have filename") } - err = w.StartLogger() + err = w.startLogger() return err } // start file logger. create log file and set to locker-inside file writer. -func (w *FileLogWriter) StartLogger() error { +func (w *FileLogWriter) startLogger() error { fd, err := w.createLogFile() if err != nil { return err @@ -199,7 +199,7 @@ func (w *FileLogWriter) DoRotate() error { } // re-start logger - err = w.StartLogger() + err = w.startLogger() if err != nil { return fmt.Errorf("Rotate StartLogger: %s\n", err) } diff --git a/logs/log.go b/logs/log.go index 67eb1c79..c859c1e6 100644 --- a/logs/log.go +++ b/logs/log.go @@ -67,7 +67,7 @@ func NewLogger(channellen int64) *BeeLogger { bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) //bl.SetLogger("console", "") // default output to console - go bl.StartLogger() + go bl.startLogger() return bl } @@ -78,7 +78,10 @@ func (bl *BeeLogger) SetLogger(adaptername string, config string) error { defer bl.lock.Unlock() if log, ok := adapters[adaptername]; ok { lg := log() - lg.Init(config) + err := lg.Init(config) + if err != nil { + return err + } bl.outputs[adaptername] = lg return nil } else { @@ -138,7 +141,7 @@ func (bl *BeeLogger) EnableFuncCallDepth(b bool) { // start logger chan reading. // when chan is full, write logs. -func (bl *BeeLogger) StartLogger() { +func (bl *BeeLogger) startLogger() { for { select { case bm := <-bl.msg: From 12e1ab0f80f400fd43d397d55572b51cade194a5 Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Wed, 2 Apr 2014 23:45:44 +0800 Subject: [PATCH 20/22] beego: setLogger return error --- log.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/log.go b/log.go index 128e998e..f374085e 100644 --- a/log.go +++ b/log.go @@ -31,8 +31,12 @@ func SetLogFuncCall(b bool) { var BeeLogger *logs.BeeLogger // SetLogger sets a new logger. -func SetLogger(adaptername string, config string) { - BeeLogger.SetLogger(adaptername, config) +func SetLogger(adaptername string, config string) error { + err := BeeLogger.SetLogger(adaptername, config) + if err != nil { + return err + } + return nil } // Trace logs a message at trace level. From 5505cc09edb187f54ff8ca4dbde4192c70d1137c Mon Sep 17 00:00:00 2001 From: "asta.xie" Date: Thu, 3 Apr 2014 15:07:20 +0800 Subject: [PATCH 21/22] beego: move init to a fund & add a new fund TestBeegoInit support Test with everything init --- beego.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/beego.go b/beego.go index e658c3b5..5efd2d6a 100644 --- a/beego.go +++ b/beego.go @@ -2,6 +2,7 @@ package beego import ( "net/http" + "os" "path" "path/filepath" "strconv" @@ -174,6 +175,16 @@ func AddAPPStartHook(hf hookfunc) { // Run beego application. // it's alias of App.Run. func Run() { + initBeforeHttpRun() + + if EnableAdmin { + go BeeAdminApp.Run() + } + + BeeApp.Run() +} + +func initBeforeHttpRun() { // if AppConfigPath not In the conf/app.conf reParse config if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { err := ParseConfig() @@ -222,12 +233,13 @@ func Run() { middleware.VERSION = VERSION middleware.AppName = AppName middleware.RegisterErrorHandler() +} - if EnableAdmin { - go BeeAdminApp.Run() - } - - BeeApp.Run() +func TestBeegoInit(apppath string) { + AppPath = apppath + AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") + os.Chdir(AppPath) + initBeforeHttpRun() } func init() { From 1705b42546a7bd3f8c998470fceb556005f6f87d Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 3 Apr 2014 15:54:37 +0800 Subject: [PATCH 22/22] beego: change version from 1.1.1 to 1.1.2 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 5efd2d6a..52b2ef92 100644 --- a/beego.go +++ b/beego.go @@ -13,7 +13,7 @@ import ( ) // beego web framework version. -const VERSION = "1.1.1" +const VERSION = "1.1.2" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc