From 35e340b9375bb76fa64d912d488fef082f132351 Mon Sep 17 00:00:00 2001 From: Henrique Bejgel Date: Mon, 18 Jan 2016 21:35:14 -0200 Subject: [PATCH 01/29] Checks if index is greater than the length of the wildcards. #1580 --- tree.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 72ef3dda..ffd444d4 100644 --- a/tree.go +++ b/tree.go @@ -420,7 +420,11 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b if len(strs) == 2 { ctx.Input.SetParam(":ext", strs[1]) } - ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0])) + if index > (len(wildcardValues) - 1) { + ctx.Input.SetParam(":path", "") + } else { + ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0])) + } return true } // match :id From be544f963e4e39e884b36500a23d219eb6c61716 Mon Sep 17 00:00:00 2001 From: ysqi Date: Sat, 23 Jan 2016 11:02:40 +0800 Subject: [PATCH 02/29] Support Parse Bool with more diffrent values ParseBool returns the boolean value represented by the string. It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On, 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off. Any other value returns an error. --- config/config.go | 38 +++++++++++ config/fake.go | 2 +- config/ini.go | 2 +- config/ini_test.go | 104 ++++++++++++++++++------------ config/json.go | 8 +-- config/json_test.go | 153 +++++++++++++++++++++++++------------------- config/xml/xml.go | 6 +- config/yaml/yaml.go | 7 +- 8 files changed, 202 insertions(+), 118 deletions(-) diff --git a/config/config.go b/config/config.go index da5d358b..e9df0f0e 100644 --- a/config/config.go +++ b/config/config.go @@ -106,3 +106,41 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) { } return adapter.ParseData(data) } + +// ParseBool returns the boolean value represented by the string. +// +// It accepts 1, 1.0, t, T, TRUE, true, True, YES, yes, Yes,Y, y, ON, on, On, +// 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off. +// Any other value returns an error. +func ParseBool(val interface{}) (value bool, err error) { + + if val != nil { + switch v := val.(type) { + case bool: + return v, nil + case string: + switch v { + case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "Y", "y", "ON", "on", "On": + return true, nil + case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "N", "n", "OFF", "off", "Off": + return false, nil + } + case int8, int32, int64: + strV := fmt.Sprintf("%s", v) + if strV == "1" { + return true, nil + } else if strV == "0" { + return false, nil + } + case float64: + if v == 1 { + return true, nil + } else if v == 0 { + return false, nil + } + } + return false, fmt.Errorf("parsing %q: invalid syntax", val) + } else { + return false, fmt.Errorf("parsing : invalid syntax") + } +} diff --git a/config/fake.go b/config/fake.go index 50aa5d4a..6daaca2c 100644 --- a/config/fake.go +++ b/config/fake.go @@ -82,7 +82,7 @@ func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 { } func (c *fakeConfigContainer) Bool(key string) (bool, error) { - return strconv.ParseBool(c.getData(key)) + return ParseBool(c.getData(key)) } func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool { diff --git a/config/ini.go b/config/ini.go index 59e84e1e..6154b217 100644 --- a/config/ini.go +++ b/config/ini.go @@ -194,7 +194,7 @@ type IniConfigContainer struct { // Bool returns the boolean value for a given key. func (c *IniConfigContainer) Bool(key string) (bool, error) { - return strconv.ParseBool(c.getdata(key)) + return ParseBool(c.getdata(key)) } // DefaultBool returns the boolean value for a given key. diff --git a/config/ini_test.go b/config/ini_test.go index 7599ab8b..17643b6d 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -15,11 +15,15 @@ package config import ( + "fmt" "os" "testing" ) -var inicontext = ` +func TestIni(t *testing.T) { + + var ( + inicontext = ` ;comment one #comment two appname = beeapi @@ -29,6 +33,13 @@ PI = 3.1415976 runmode = "dev" autorender = false copyrequestbody = true +session= on +cookieon= off +newreg = OFF +needlogin = ON +enableSession = Y +enableCookie = N +flag = 1 [demo] key1="asta" key2 = "xie" @@ -36,7 +47,31 @@ CaseInsensitive = true peers = one;two;three ` -func TestIni(t *testing.T) { + keyValue = map[string]interface{}{ + "appname": "beeapi", + "httpport": 8080, + "mysqlport": int64(3600), + "pi": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "session": true, + "cookieon": false, + "newreg": false, + "needlogin": true, + "enableSession": true, + "enableCookie": false, + "flag": true, + "demo::key1": "asta", + "demo::key2": "xie", + "demo::CaseInsensitive": true, + "demo::peers": []string{"one", "two", "three"}, + "null": "", + "demo2::key1": "", + "error": "", + } + ) + f, err := os.Create("testini.conf") if err != nil { t.Fatal(err) @@ -52,31 +87,31 @@ func TestIni(t *testing.T) { if err != nil { t.Fatal(err) } - if iniconf.String("appname") != "beeapi" { - t.Fatal("appname not equal to beeapi") - } - if port, err := iniconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) - t.Fatal(err) - } - if port, err := iniconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) - } - if pi, err := iniconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if iniconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v, err := iniconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := iniconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + for k, v := range keyValue { + var err error + var value interface{} + switch v.(type) { + case int: + value, err = iniconf.Int(k) + case int64: + value, err = iniconf.Int64(k) + case float64: + value, err = iniconf.Float(k) + case bool: + value, err = iniconf.Bool(k) + case []string: + value = iniconf.Strings(k) + case string: + value = iniconf.String(k) + default: + value, err = iniconf.DIY(k) + } + if err != nil { + t.Fatalf("get key %q value fail,err %s", k, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Fatalf("get key %q value, want %v got %v .", k, v, value) + } + } if err = iniconf.Set("name", "astaxie"); err != nil { t.Fatal(err) @@ -84,20 +119,5 @@ func TestIni(t *testing.T) { if iniconf.String("name") != "astaxie" { t.Fatal("get name error") } - if iniconf.String("demo::key1") != "asta" { - t.Fatal("get demo.key1 error") - } - if iniconf.String("demo::key2") != "xie" { - t.Fatal("get demo.key2 error") - } - if v, err := iniconf.Bool("demo::caseinsensitive"); err != nil || v != true { - t.Fatal("get demo.caseinsensitive error") - } - - if data := iniconf.Strings("demo::peers"); len(data) != 3 { - t.Fatal("get strings error", data) - } else if data[0] != "one" { - t.Fatal("get first params error not equat to one") - } } diff --git a/config/json.go b/config/json.go index 65b4ac48..0bc1d456 100644 --- a/config/json.go +++ b/config/json.go @@ -17,6 +17,7 @@ package config import ( "encoding/json" "errors" + "fmt" "io/ioutil" "os" "strings" @@ -70,12 +71,9 @@ type JSONConfigContainer struct { func (c *JSONConfigContainer) Bool(key string) (bool, error) { val := c.getData(key) if val != nil { - if v, ok := val.(bool); ok { - return v, nil - } - return false, errors.New("not bool value") + return ParseBool(val) } - return false, errors.New("not exist key:" + key) + return false, fmt.Errorf("not exist key: %q", key) } // DefaultBool return the bool value if has no error diff --git a/config/json_test.go b/config/json_test.go index 5aedae36..9c06143e 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -15,34 +15,14 @@ package config import ( + "fmt" "os" "testing" ) -var jsoncontext = `{ -"appname": "beeapi", -"testnames": "foo;bar", -"httpport": 8080, -"mysqlport": 3600, -"PI": 3.1415976, -"runmode": "dev", -"autorender": false, -"copyrequestbody": true, -"database": { - "host": "host", - "port": "port", - "database": "database", - "username": "username", - "password": "password", - "conns":{ - "maxconnection":12, - "autoconnect":true, - "connectioninfo":"info" - } - } -}` +func TestJsonStartsWithArray(t *testing.T) { -var jsoncontextwitharray = `[ + const jsoncontextwitharray = `[ { "url": "user", "serviceAPI": "http://www.test.com/user" @@ -52,8 +32,6 @@ var jsoncontextwitharray = `[ "serviceAPI": "http://www.test.com/employee" } ]` - -func TestJsonStartsWithArray(t *testing.T) { f, err := os.Create("testjsonWithArray.conf") if err != nil { t.Fatal(err) @@ -90,6 +68,64 @@ func TestJsonStartsWithArray(t *testing.T) { } func TestJson(t *testing.T) { + + var ( + jsoncontext = `{ +"appname": "beeapi", +"testnames": "foo;bar", +"httpport": 8080, +"mysqlport": 3600, +"PI": 3.1415976, +"runmode": "dev", +"autorender": false, +"copyrequestbody": true, +"session": "on", +"cookieon": "off", +"newreg": "OFF", +"needlogin": "ON", +"enableSession": "Y", +"enableCookie": "N", +"flag": 1, +"database": { + "host": "host", + "port": "port", + "database": "database", + "username": "username", + "password": "password", + "conns":{ + "maxconnection":12, + "autoconnect":true, + "connectioninfo":"info" + } + } +}` + keyValue = map[string]interface{}{ + "appname": "beeapi", + "testnames": []string{"foo", "bar"}, + "httpport": 8080, + "mysqlport": int64(3600), + "PI": 3.1415976, + "runmode": "dev", + "autorender": false, + "copyrequestbody": true, + "session": true, + "cookieon": false, + "newreg": false, + "needlogin": true, + "enableSession": true, + "enableCookie": false, + "flag": true, + "database::host": "host", + "database::port": "port", + "database::database": "database", + "database::password": "password", + "database::conns::maxconnection": 12, + "database::conns::autoconnect": true, + "database::conns::connectioninfo": "info", + "unknown": "", + } + ) + f, err := os.Create("testjson.conf") if err != nil { t.Fatal(err) @@ -105,37 +141,32 @@ func TestJson(t *testing.T) { if err != nil { t.Fatal(err) } - if jsonconf.String("appname") != "beeapi" { - t.Fatal("appname not equal to beeapi") - } - if port, err := jsonconf.Int("httpport"); err != nil || port != 8080 { - t.Error(port) - t.Fatal(err) - } - if port, err := jsonconf.Int64("mysqlport"); err != nil || port != 3600 { - t.Error(port) - t.Fatal(err) - } - if pi, err := jsonconf.Float("PI"); err != nil || pi != 3.1415976 { - t.Error(pi) - t.Fatal(err) - } - if jsonconf.String("runmode") != "dev" { - t.Fatal("runmode not equal to dev") - } - if v := jsonconf.Strings("unknown"); len(v) > 0 { - t.Fatal("unknown strings, the length should be 0") - } - if v := jsonconf.Strings("testnames"); len(v) != 2 { - t.Fatal("testnames length should be 2") - } - if v, err := jsonconf.Bool("autorender"); err != nil || v != false { - t.Error(v) - t.Fatal(err) - } - if v, err := jsonconf.Bool("copyrequestbody"); err != nil || v != true { - t.Error(v) - t.Fatal(err) + + for k, v := range keyValue { + var err error + var value interface{} + switch v.(type) { + case int: + value, err = jsonconf.Int(k) + case int64: + value, err = jsonconf.Int64(k) + case float64: + value, err = jsonconf.Float(k) + case bool: + value, err = jsonconf.Bool(k) + case []string: + value = jsonconf.Strings(k) + case string: + value = jsonconf.String(k) + default: + value, err = jsonconf.DIY(k) + } + if err != nil { + t.Fatalf("get key %q value fatal,%V err %s", k, v, err) + } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { + t.Fatalf("get key %q value, want %v got %v .", k, v, value) + } + } if err = jsonconf.Set("name", "astaxie"); err != nil { t.Fatal(err) @@ -143,15 +174,7 @@ func TestJson(t *testing.T) { if jsonconf.String("name") != "astaxie" { t.Fatal("get name error") } - if jsonconf.String("database::host") != "host" { - t.Fatal("get database::host error") - } - if jsonconf.String("database::conns::connectioninfo") != "info" { - t.Fatal("get database::conns::connectioninfo error") - } - if maxconnection, err := jsonconf.Int("database::conns::maxconnection"); err != nil || maxconnection != 12 { - t.Fatal("get database::conns::maxconnection error") - } + if db, err := jsonconf.DIY("database"); err != nil { t.Fatal(err) } else if m, ok := db.(map[string]interface{}); !ok { diff --git a/config/xml/xml.go b/config/xml/xml.go index 4d48f7d2..662ef3d9 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -92,7 +92,11 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - return strconv.ParseBool(c.data[key].(string)) + if v, ok := c.data[key]; ok { + return config.ParseBool(v) + } else { + return false, fmt.Errorf("not exist key: %q", key) + } } // DefaultBool return the bool value if has no error diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index f034d3ba..ccfd7b8b 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -121,10 +121,11 @@ type ConfigContainer struct { // Bool returns the boolean value for a given key. func (c *ConfigContainer) Bool(key string) (bool, error) { - if v, ok := c.data[key].(bool); ok { - return v, nil + if v, ok := c.data[key]; ok { + return config.ParseBool(v) + } else { + return false, fmt.Errorf("not exist key: %q", key) } - return false, errors.New("not bool value") } // DefaultBool return the bool value if has no error From 51ae45a7995db54356f6975499e4624617442f9b Mon Sep 17 00:00:00 2001 From: ysqi Date: Sat, 23 Jan 2016 14:53:52 +0800 Subject: [PATCH 03/29] Fixed #1607 --- config/ini.go | 40 +++++++++++++++++++++++++++-------- config/ini_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/config/ini.go b/config/ini.go index 59e84e1e..5a700a0b 100644 --- a/config/ini.go +++ b/config/ini.go @@ -27,7 +27,6 @@ import ( "strings" "sync" "time" - "unicode" ) var ( @@ -97,9 +96,11 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) { } if bComment != nil { line = bytes.TrimLeft(line, string(bComment)) - line = bytes.TrimLeftFunc(line, unicode.IsSpace) + // Need append to a new line if multi-line comments. + if comment.Len() > 0 { + comment.WriteByte('\n') + } comment.Write(line) - comment.WriteByte('\n') continue } @@ -299,14 +300,35 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { } defer f.Close() + // Get section or key comments. Fixed #1607 + getCommentStr := func(section, key string) string { + comment, ok := "", false + if len(key) == 0 { + comment, ok = c.sectionComment[section] + } else { + comment, ok = c.keyComment[section+"."+key] + } + + if ok { + // Empty comment + if len(comment) == 0 || len(strings.TrimSpace(comment)) == 0 { + return string(bNumComment) + } + prefix := string(bNumComment) + // Add the line head character "#" + return prefix + strings.Replace(comment, lineBreak, lineBreak+prefix, -1) + } + return "" + } + buf := bytes.NewBuffer(nil) // Save default section at first place if dt, ok := c.data[defaultSection]; ok { for key, val := range dt { if key != " " { // Write key comments. - if v, ok := c.keyComment[key]; ok { - if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + if v := getCommentStr(defaultSection, key); len(v) > 0 { + if _, err = buf.WriteString(v + lineBreak); err != nil { return err } } @@ -327,8 +349,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { for section, dt := range c.data { if section != defaultSection { // Write section comments. - if v, ok := c.sectionComment[section]; ok { - if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + if v := getCommentStr(section, ""); len(v) > 0 { + if _, err = buf.WriteString(v + lineBreak); err != nil { return err } } @@ -341,8 +363,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { for key, val := range dt { if key != " " { // Write key comments. - if v, ok := c.keyComment[key]; ok { - if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + if v := getCommentStr(section, key); len(v) > 0 { + if _, err = buf.WriteString(v + lineBreak); err != nil { return err } } diff --git a/config/ini_test.go b/config/ini_test.go index 7599ab8b..9f0bb8af 100644 --- a/config/ini_test.go +++ b/config/ini_test.go @@ -15,6 +15,7 @@ package config import ( + "io/ioutil" "os" "testing" ) @@ -101,3 +102,54 @@ func TestIni(t *testing.T) { } } + +func TestIniSave(t *testing.T) { + + const ( + inicontext = ` +app = app +;comment one +#comment two +# comment three +appname = beeapi +httpport = 8080 +# DB Info +# enable db +[dbinfo] +# db type name +# suport mysql,sqlserver +name = mysql +` + + saveResult = `app=app +#comment one +#comment two +# comment three +appname=beeapi +httpport=8080 + +# DB Info +# enable db +[dbinfo] +# db type name +# suport mysql,sqlserver +name=mysql + +` + ) + cfg, err := NewConfigData("ini", []byte(inicontext)) + if err != nil { + t.Fatal(err) + } + name := "newIniConfig.ini" + if err := cfg.SaveConfigFile(name); err != nil { + t.Fatal(err) + } + defer os.Remove(name) + + if data, err := ioutil.ReadFile(name); err != nil { + t.Fatal(err) + } else if string(data) != saveResult { + t.Fatal("different after save ini config file.") + } +} From ecf24640fd0f62c69bcba8e2099903f2e06dd7e5 Mon Sep 17 00:00:00 2001 From: youngsterxyf Date: Sat, 23 Jan 2016 16:24:58 +0800 Subject: [PATCH 04/29] fix issue #1566 --- logs/conn.go | 9 ++++-- logs/console.go | 9 ++++-- logs/es/es.go | 8 ++--- logs/file.go | 45 +++------------------------ logs/log.go | 81 ++++++++++++++++++++++++++++++++++++++----------- logs/smtp.go | 4 +-- 6 files changed, 88 insertions(+), 68 deletions(-) diff --git a/logs/conn.go b/logs/conn.go index 3655bf51..2a29688a 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -19,6 +19,7 @@ import ( "io" "log" "net" + "time" ) // connWriter implements LoggerInterface. @@ -48,7 +49,7 @@ func (c *connWriter) Init(jsonconfig string) error { // WriteMsg write message in connection. // if connection is down, try to re-connect. -func (c *connWriter) WriteMsg(msg string, level int) error { +func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { if level > c.Level { return nil } @@ -62,6 +63,10 @@ func (c *connWriter) WriteMsg(msg string, level int) error { if c.ReconnectOnMsg { defer c.innerWriter.Close() } + + logTimeStr := formatLogTime(when) + msg = logTimeStr + msg + c.lg.Println(msg) return nil } @@ -94,7 +99,7 @@ func (c *connWriter) connect() error { } c.innerWriter = conn - c.lg = log.New(conn, "", log.Ldate|log.Ltime) + c.lg = log.New(conn, "", 0) return nil } diff --git a/logs/console.go b/logs/console.go index 23e8ebca..48d1e5b2 100644 --- a/logs/console.go +++ b/logs/console.go @@ -19,6 +19,7 @@ import ( "log" "os" "runtime" + "time" ) // brush is a color join function @@ -53,7 +54,7 @@ type consoleWriter struct { // NewConsole create ConsoleWriter returning as LoggerInterface. func NewConsole() Logger { cw := &consoleWriter{ - lg: log.New(os.Stdout, "", log.Ldate|log.Ltime), + lg: log.New(os.Stdout, "", 0), Level: LevelDebug, } return cw @@ -69,10 +70,14 @@ func (c *consoleWriter) Init(jsonconfig string) error { } // WriteMsg write message in console. -func (c *consoleWriter) WriteMsg(msg string, level int) error { +func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { if level > c.Level { return nil } + + logTimeStr := formatLogTime(when) + msg = logTimeStr + msg + if goos := runtime.GOOS; goos == "windows" { c.lg.Println(msg) return nil diff --git a/logs/es/es.go b/logs/es/es.go index f8dc5f65..397ca2ef 100644 --- a/logs/es/es.go +++ b/logs/es/es.go @@ -48,16 +48,16 @@ func (el *esLogger) Init(jsonconfig string) error { } // WriteMsg will write the msg and level into es -func (el *esLogger) WriteMsg(msg string, level int) error { +func (el *esLogger) WriteMsg(when time.Time, msg string, level int) error { if level > el.Level { return nil } - t := time.Now() + vals := make(map[string]interface{}) - vals["@timestamp"] = t.Format(time.RFC3339) + vals["@timestamp"] = when.Format(time.RFC3339) vals["@msg"] = msg d := goes.Document{ - Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()), + Index: fmt.Sprintf("%04d.%02d.%02d", when.Year(), when.Month(), when.Day()), Type: "logs", Fields: vals, } diff --git a/logs/file.go b/logs/file.go index 0eae734a..16bf29c2 100644 --- a/logs/file.go +++ b/logs/file.go @@ -114,50 +114,15 @@ func (w *fileLogWriter) needRotate(size int, day int) bool { } // WriteMsg write logger message into file. -func (w *fileLogWriter) WriteMsg(msg string, level int) error { +func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { if level > w.Level { return nil } //2016/01/12 21:34:33 - now := time.Now() - y, mo, d := now.Date() - h, mi, s := now.Clock() - //len(2006/01/02 15:03:04)==19 - var buf [20]byte - t := 3 - for y >= 10 { - p := y / 10 - buf[t] = byte('0' + y - p*10) - y = p - t-- - } - buf[0] = byte('0' + y) - buf[4] = '/' - if mo > 9 { - buf[5] = '1' - buf[6] = byte('0' + mo - 9) - } else { - buf[5] = '0' - buf[6] = byte('0' + mo) - } - buf[7] = '/' - t = d / 10 - buf[8] = byte('0' + t) - buf[9] = byte('0' + d - t*10) - buf[10] = ' ' - t = h / 10 - buf[11] = byte('0' + t) - buf[12] = byte('0' + h - t*10) - buf[13] = ':' - t = mi / 10 - buf[14] = byte('0' + t) - buf[15] = byte('0' + mi - t*10) - buf[16] = ':' - t = s / 10 - buf[17] = byte('0' + t) - buf[18] = byte('0' + s - t*10) - buf[19] = ' ' - msg = string(buf[0:]) + msg + "\n" + // now := time.Now() + d := when.Day() + logTimeStr := formatLogTime(when) + msg = logTimeStr + msg + "\n" if w.Rotate { if w.needRotate(len(msg), d) { diff --git a/logs/log.go b/logs/log.go index ccaaa3ad..0c4ac13a 100644 --- a/logs/log.go +++ b/logs/log.go @@ -40,6 +40,7 @@ import ( "runtime" "strconv" "sync" + "time" ) // RFC5424 log message levels. @@ -68,7 +69,7 @@ type loggerType func() Logger // Logger defines the behavior of a log provider. type Logger interface { Init(config string) error - WriteMsg(msg string, level int) error + WriteMsg(when time.Time, msg string, level int) error Destroy() Flush() } @@ -108,6 +109,7 @@ type nameLogger struct { type logMsg struct { level int msg string + when time.Time } var logMsgPool *sync.Pool @@ -173,16 +175,16 @@ func (bl *BeeLogger) DelLogger(adapterName string) error { return nil } -func (bl *BeeLogger) writeToLoggers(msg string, level int) { +func (bl *BeeLogger) writeToLoggers(when time.Time, msg string, level int) { for _, l := range bl.outputs { - err := l.WriteMsg(msg, level) + err := l.WriteMsg(when, msg, level) if err != nil { fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err) } } } -func (bl *BeeLogger) writeMsg(logLevel int, msg string) error { +func (bl *BeeLogger) writeMsg(logLevel int, msg string, when time.Time) error { if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if !ok { @@ -196,9 +198,10 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string) error { lm := logMsgPool.Get().(*logMsg) lm.level = logLevel lm.msg = msg + lm.when = when bl.msgChan <- lm } else { - bl.writeToLoggers(msg, logLevel) + bl.writeToLoggers(when, msg, logLevel) } return nil } @@ -231,7 +234,7 @@ func (bl *BeeLogger) startLogger() { for { select { case bm := <-bl.msgChan: - bl.writeToLoggers(bm.msg, bm.level) + bl.writeToLoggers(bm.when, bm.msg, bm.level) logMsgPool.Put(bm) } } @@ -243,7 +246,7 @@ func (bl *BeeLogger) Emergency(format string, v ...interface{}) { return } msg := fmt.Sprintf("[M] "+format, v...) - bl.writeMsg(LevelEmergency, msg) + bl.writeMsg(LevelEmergency, msg, time.Now()) } // Alert Log ALERT level message. @@ -252,7 +255,7 @@ func (bl *BeeLogger) Alert(format string, v ...interface{}) { return } msg := fmt.Sprintf("[A] "+format, v...) - bl.writeMsg(LevelAlert, msg) + bl.writeMsg(LevelAlert, msg, time.Now()) } // Critical Log CRITICAL level message. @@ -261,7 +264,7 @@ func (bl *BeeLogger) Critical(format string, v ...interface{}) { return } msg := fmt.Sprintf("[C] "+format, v...) - bl.writeMsg(LevelCritical, msg) + bl.writeMsg(LevelCritical, msg, time.Now()) } // Error Log ERROR level message. @@ -270,7 +273,7 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) { return } msg := fmt.Sprintf("[E] "+format, v...) - bl.writeMsg(LevelError, msg) + bl.writeMsg(LevelError, msg, time.Now()) } // Warning Log WARNING level message. @@ -279,7 +282,7 @@ func (bl *BeeLogger) Warning(format string, v ...interface{}) { return } msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg) + bl.writeMsg(LevelWarning, msg, time.Now()) } // Notice Log NOTICE level message. @@ -288,7 +291,7 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) { return } msg := fmt.Sprintf("[N] "+format, v...) - bl.writeMsg(LevelNotice, msg) + bl.writeMsg(LevelNotice, msg, time.Now()) } // Informational Log INFORMATIONAL level message. @@ -297,7 +300,7 @@ func (bl *BeeLogger) Informational(format string, v ...interface{}) { return } msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg) + bl.writeMsg(LevelInformational, msg, time.Now()) } // Debug Log DEBUG level message. @@ -306,7 +309,7 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { return } msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg) + bl.writeMsg(LevelDebug, msg, time.Now()) } // Warn Log WARN level message. @@ -316,7 +319,7 @@ func (bl *BeeLogger) Warn(format string, v ...interface{}) { return } msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg) + bl.writeMsg(LevelWarning, msg, time.Now()) } // Info Log INFO level message. @@ -326,7 +329,7 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) { return } msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg) + bl.writeMsg(LevelInformational, msg, time.Now()) } // Trace Log TRACE level message. @@ -336,7 +339,7 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { return } msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg) + bl.writeMsg(LevelDebug, msg, time.Now()) } // Flush flush all chan data. @@ -351,7 +354,7 @@ func (bl *BeeLogger) Close() { for { if len(bl.msgChan) > 0 { bm := <-bl.msgChan - bl.writeToLoggers(bm.msg, bm.level) + bl.writeToLoggers(bm.when, bm.msg, bm.level) logMsgPool.Put(bm) continue } @@ -362,3 +365,45 @@ func (bl *BeeLogger) Close() { l.Destroy() } } + +func formatLogTime(when time.Time) string { + y, mo, d := when.Date() + h, mi, s := when.Clock() + //len(2006/01/02 15:03:04)==19 + var buf [20]byte + t := 3 + for y >= 10 { + p := y / 10 + buf[t] = byte('0' + y - p*10) + y = p + t-- + } + buf[0] = byte('0' + y) + buf[4] = '/' + if mo > 9 { + buf[5] = '1' + buf[6] = byte('0' + mo - 9) + } else { + buf[5] = '0' + buf[6] = byte('0' + mo) + } + buf[7] = '/' + t = d / 10 + buf[8] = byte('0' + t) + buf[9] = byte('0' + d - t*10) + buf[10] = ' ' + t = h / 10 + buf[11] = byte('0' + t) + buf[12] = byte('0' + h - t*10) + buf[13] = ':' + t = mi / 10 + buf[14] = byte('0' + t) + buf[15] = byte('0' + mi - t*10) + buf[16] = ':' + t = s / 10 + buf[17] = byte('0' + t) + buf[18] = byte('0' + s - t*10) + buf[19] = ' ' + + return string(buf[0:]) +} diff --git a/logs/smtp.go b/logs/smtp.go index 748462f9..47f5a0c6 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -126,7 +126,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd // WriteMsg write message in smtp writer. // it will send an email with subject and only this message. -func (s *SMTPWriter) WriteMsg(msg string, level int) error { +func (s *SMTPWriter) WriteMsg(when time.Time, msg string, level int) error { if level > s.Level { return nil } @@ -140,7 +140,7 @@ func (s *SMTPWriter) WriteMsg(msg string, level int) error { // and send the email all in one step. contentType := "Content-Type: text/plain" + "; charset=UTF-8" mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress + - ">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", time.Now().Format("2006-01-02 15:04:05")) + msg) + ">\r\nSubject: " + s.Subject + "\r\n" + contentType + "\r\n\r\n" + fmt.Sprintf(".%s", when.Format("2006-01-02 15:04:05")) + msg) return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg) } From 007af6224eba5a61bd9d71124f3cbf84ca61ed29 Mon Sep 17 00:00:00 2001 From: ysqi Date: Sat, 23 Jan 2016 19:13:19 +0800 Subject: [PATCH 05/29] Fixed #1586 --- tree.go | 19 +++++++++----- tree_test.go | 74 +++++++++++++++++++++------------------------------- 2 files changed, 43 insertions(+), 50 deletions(-) diff --git a/tree.go b/tree.go index 72ef3dda..a151b15d 100644 --- a/tree.go +++ b/tree.go @@ -15,12 +15,11 @@ package beego import ( + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/utils" "path" "regexp" "strings" - - "github.com/astaxie/beego/context" - "github.com/astaxie/beego/utils" ) var ( @@ -438,7 +437,9 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b } matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) for i, match := range matches[1:] { - ctx.Input.SetParam(leaf.wildcards[i], match) + if i < len(leaf.wildcards) { + ctx.Input.SetParam(leaf.wildcards[i], match) + } } return true } @@ -536,13 +537,19 @@ func splitSegment(key string) (bool, []string, string) { continue } } - if v == ':' { + // Escape Sequence '\' + if i > 0 && key[i-1] == '\\' { + out = append(out, v) + } else if v == ':' { param = make([]rune, 0) start = true } else if v == '(' { startexp = true start = false - params = append(params, ":"+string(param)) + if len(param) > 0 { + params = append(params, ":"+string(param)) + param = make([]rune, 0) + } paramsNum++ expt = make([]rune, 0) expt = append(expt, '(') diff --git a/tree_test.go b/tree_test.go index 15ab7ce4..168c9020 100644 --- a/tree_test.go +++ b/tree_test.go @@ -15,9 +15,9 @@ package beego import ( - "testing" - "github.com/astaxie/beego/context" + "strings" + "testing" ) type testinfo struct { @@ -57,6 +57,9 @@ func init() { "/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg", map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}}) routers = append(routers, testinfo{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}}) + routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(a)", map[string]string{":id": "123"}}) + routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(b)", map[string]string{":id": "123"}}) + routers = append(routers, testinfo{"/v1/shop/:id\\((a|b|c)\\)", "/v1/shop/123(c)", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}}) routers = append(routers, testinfo{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}}) routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}}) @@ -245,48 +248,31 @@ func TestSplitPath(t *testing.T) { } func TestSplitSegment(t *testing.T) { - b, w, r := splitSegment("admin") - if b || len(w) != 0 || r != "" { - t.Fatal("admin should return false, nil, ''") + + items := map[string]struct { + isReg bool + params []string + regStr string + }{ + "admin": {false, nil, ""}, + "*": {true, []string{":splat"}, ""}, + "*.*": {true, []string{".", ":path", ":ext"}, ""}, + ":id": {true, []string{":id"}, ""}, + "?:id": {true, []string{":", ":id"}, ""}, + ":id:int": {true, []string{":id"}, "([0-9]+)"}, + ":name:string": {true, []string{":name"}, `([\w]+)`}, + ":id([0-9]+)": {true, []string{":id"}, `([0-9]+)`}, + ":id([0-9]+)_:name": {true, []string{":id", ":name"}, `([0-9]+)_(.+)`}, + ":id(.+)_cms.html": {true, []string{":id"}, `(.+)_cms.html`}, + "cms_:id(.+)_:page(.+).html": {true, []string{":id", ":page"}, `cms_(.+)_(.+).html`}, + `:app(a|b|c)`: {true, []string{":app"}, `(a|b|c)`}, + `:app\((a|b|c)\)`: {true, []string{":app"}, `(.+)\((a|b|c)\)`}, } - b, w, r = splitSegment("*") - if !b || len(w) != 1 || w[0] != ":splat" || r != "" { - t.Fatal("* should return true, [:splat], ''") - } - b, w, r = splitSegment("*.*") - if !b || len(w) != 3 || w[1] != ":path" || w[2] != ":ext" || w[0] != "." || r != "" { - t.Fatal("admin should return true,[. :path :ext], ''") - } - b, w, r = splitSegment(":id") - if !b || len(w) != 1 || w[0] != ":id" || r != "" { - t.Fatal(":id should return true, [:id], ''") - } - b, w, r = splitSegment("?:id") - if !b || len(w) != 2 || w[0] != ":" || w[1] != ":id" || r != "" { - t.Fatal("?:id should return true, [: :id], ''") - } - b, w, r = splitSegment(":id:int") - if !b || len(w) != 1 || w[0] != ":id" || r != "([0-9]+)" { - t.Fatal(":id:int should return true, [:id], '([0-9]+)'") - } - b, w, r = splitSegment(":name:string") - if !b || len(w) != 1 || w[0] != ":name" || r != `([\w]+)` { - t.Fatal(`:name:string should return true, [:name], '([\w]+)'`) - } - b, w, r = splitSegment(":id([0-9]+)") - if !b || len(w) != 1 || w[0] != ":id" || r != `([0-9]+)` { - t.Fatal(`:id([0-9]+) should return true, [:id], '([0-9]+)'`) - } - b, w, r = splitSegment(":id([0-9]+)_:name") - if !b || len(w) != 2 || w[0] != ":id" || w[1] != ":name" || r != `([0-9]+)_(.+)` { - t.Fatal(`:id([0-9]+)_:name should return true, [:id :name], '([0-9]+)_(.+)'`) - } - b, w, r = splitSegment(":id(.+)_cms.html") - if !b || len(w) != 1 || w[0] != ":id" || r != `(.+)_cms.html` { - t.Fatal(":id_cms.html should return true, [:id], '(.+)_cms.html'") - } - b, w, r = splitSegment("cms_:id(.+)_:page(.+).html") - if !b || len(w) != 2 || w[0] != ":id" || w[1] != ":page" || r != `cms_(.+)_(.+).html` { - t.Fatal(":id_cms.html should return true, [:id :page], cms_(.+)_(.+).html") + + for pattern, v := range items { + b, w, r := splitSegment(pattern) + if b != v.isReg || r != v.regStr || strings.Join(w, ",") != strings.Join(v.params, ",") { + t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r) + } } } From 4c1cfc13868e7779570dc7b3c6494c428160308d Mon Sep 17 00:00:00 2001 From: coseyo Date: Sun, 24 Jan 2016 00:18:16 +0800 Subject: [PATCH 06/29] fix path issue --- config.go | 11 ++++++++++- parser.go | 6 +++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index e91ca28b..d090d247 100644 --- a/config.go +++ b/config.go @@ -103,6 +103,8 @@ var ( BConfig *Config // AppConfig is the instance of Config, store the config information from file AppConfig *beegoAppConfig + // AppPath is the absolute path to the app + AppPath string // AppConfigPath is the path to the config files AppConfigPath string // AppConfigProvider is the provider for the config, default is ini @@ -179,9 +181,16 @@ func init() { // ParseConfig parsed default config file. // now only support ini, next will support json. func ParseConfig() (err error) { + AppPath, _ := filepath.Abs(filepath.Dir(os.Args[0])) + workPath, _ := os.Getwd() + workPath, _ = filepath.Abs(workPath) + + if workPath != AppPath { + os.Chdir(AppPath) + } + if AppConfigPath == "" { // initialize default configurations - AppPath, _ := filepath.Abs(filepath.Dir(os.Args[0])) AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") if !utils.FileExists(AppConfigPath) { AppConfig = &beegoAppConfig{config.NewFakeConfig()} diff --git a/parser.go b/parser.go index b14d74b9..f23f4720 100644 --- a/parser.go +++ b/parser.go @@ -130,7 +130,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat } func genRouterCode() { - os.Mkdir("routers", 0755) + os.Mkdir(path.Join(AppPath, "routers"), 0755) Info("generate router from comments") var ( globalinfo string @@ -172,7 +172,7 @@ func genRouterCode() { } } if globalinfo != "" { - f, err := os.Create(path.Join("routers", commentFilename)) + f, err := os.Create(path.Join(AppPath, "routers", commentFilename)) if err != nil { panic(err) } @@ -182,7 +182,7 @@ func genRouterCode() { } func compareFile(pkgRealpath string) bool { - if !utils.FileExists(path.Join("routers", commentFilename)) { + if !utils.FileExists(path.Join(AppPath, "routers", commentFilename)) { return true } if utils.FileExists(lastupdateFilename) { From 3031bdd1762bfb4a04fb36ea764e4c1209a5c9d8 Mon Sep 17 00:00:00 2001 From: coseyo Date: Sun, 24 Jan 2016 00:40:03 +0800 Subject: [PATCH 07/29] fix test error --- config.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config.go b/config.go index d090d247..ea329596 100644 --- a/config.go +++ b/config.go @@ -185,18 +185,17 @@ func ParseConfig() (err error) { workPath, _ := os.Getwd() workPath, _ = filepath.Abs(workPath) - if workPath != AppPath { - os.Chdir(AppPath) - } - if AppConfigPath == "" { // initialize default configurations AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") - if !utils.FileExists(AppConfigPath) { + if utils.FileExists(AppConfigPath) && workPath != AppPath { + os.Chdir(AppPath) + } else { AppConfig = &beegoAppConfig{config.NewFakeConfig()} return } } + AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath) if err != nil { return err From 09d3d89c6f0444f92c03a382bc2cdade17363af1 Mon Sep 17 00:00:00 2001 From: coseyo Date: Sun, 24 Jan 2016 00:47:37 +0800 Subject: [PATCH 08/29] fix test error again --- config.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index ea329596..3ceae12e 100644 --- a/config.go +++ b/config.go @@ -188,14 +188,16 @@ func ParseConfig() (err error) { if AppConfigPath == "" { // initialize default configurations AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") - if utils.FileExists(AppConfigPath) && workPath != AppPath { - os.Chdir(AppPath) - } else { + if !utils.FileExists(AppConfigPath) { AppConfig = &beegoAppConfig{config.NewFakeConfig()} return } } + if workPath != AppPath { + os.Chdir(AppPath) + } + AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath) if err != nil { return err From 3d7354b9d286eb75cbcc1b760b20741a2f2f6394 Mon Sep 17 00:00:00 2001 From: ysqi Date: Sun, 24 Jan 2016 11:10:04 +0800 Subject: [PATCH 09/29] import reset --- tree.go | 5 +++-- tree_test.go | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tree.go b/tree.go index a151b15d..3f413325 100644 --- a/tree.go +++ b/tree.go @@ -15,11 +15,12 @@ package beego import ( - "github.com/astaxie/beego/context" - "github.com/astaxie/beego/utils" "path" "regexp" "strings" + + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/utils" ) var ( diff --git a/tree_test.go b/tree_test.go index 168c9020..9f21c18c 100644 --- a/tree_test.go +++ b/tree_test.go @@ -15,9 +15,10 @@ package beego import ( - "github.com/astaxie/beego/context" "strings" "testing" + + "github.com/astaxie/beego/context" ) type testinfo struct { From a80feb00b82ff6d7c2e7dbe2914564dc13e05e5f Mon Sep 17 00:00:00 2001 From: miraclesu Date: Mon, 25 Jan 2016 18:11:50 +0800 Subject: [PATCH 10/29] Fix utils mail from field can't including Chinese bug --- utils/mail.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 1d80a039..bebce610 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -231,11 +231,10 @@ func (e *Email) Send() error { if e.From == "" || len(to) == 0 { return errors.New("Must specify at least one From address and one To address") } - from, err := mail.ParseAddress(e.From) + from, err := mail.ParseAddress(e.Username) if err != nil { return err } - e.From = from.String() raw, err := e.Bytes() if err != nil { return err From 0e17e2a3d28b9d68b8ce5e1e41a476e9cb52c09d Mon Sep 17 00:00:00 2001 From: youngsterxyf Date: Mon, 25 Jan 2016 20:20:53 +0800 Subject: [PATCH 11/29] accept @JessonChan suggestion --- logs/log.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/logs/log.go b/logs/log.go index 0c4ac13a..076a9766 100644 --- a/logs/log.go +++ b/logs/log.go @@ -184,7 +184,8 @@ func (bl *BeeLogger) writeToLoggers(when time.Time, msg string, level int) { } } -func (bl *BeeLogger) writeMsg(logLevel int, msg string, when time.Time) error { +func (bl *BeeLogger) writeMsg(logLevel int, msg string) error { + when := time.Now() if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) if !ok { @@ -246,7 +247,7 @@ func (bl *BeeLogger) Emergency(format string, v ...interface{}) { return } msg := fmt.Sprintf("[M] "+format, v...) - bl.writeMsg(LevelEmergency, msg, time.Now()) + bl.writeMsg(LevelEmergency, msg) } // Alert Log ALERT level message. @@ -255,7 +256,7 @@ func (bl *BeeLogger) Alert(format string, v ...interface{}) { return } msg := fmt.Sprintf("[A] "+format, v...) - bl.writeMsg(LevelAlert, msg, time.Now()) + bl.writeMsg(LevelAlert, msg) } // Critical Log CRITICAL level message. @@ -264,7 +265,7 @@ func (bl *BeeLogger) Critical(format string, v ...interface{}) { return } msg := fmt.Sprintf("[C] "+format, v...) - bl.writeMsg(LevelCritical, msg, time.Now()) + bl.writeMsg(LevelCritical, msg) } // Error Log ERROR level message. @@ -273,7 +274,7 @@ func (bl *BeeLogger) Error(format string, v ...interface{}) { return } msg := fmt.Sprintf("[E] "+format, v...) - bl.writeMsg(LevelError, msg, time.Now()) + bl.writeMsg(LevelError, msg) } // Warning Log WARNING level message. @@ -282,7 +283,7 @@ func (bl *BeeLogger) Warning(format string, v ...interface{}) { return } msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg, time.Now()) + bl.writeMsg(LevelWarning, msg) } // Notice Log NOTICE level message. @@ -291,7 +292,7 @@ func (bl *BeeLogger) Notice(format string, v ...interface{}) { return } msg := fmt.Sprintf("[N] "+format, v...) - bl.writeMsg(LevelNotice, msg, time.Now()) + bl.writeMsg(LevelNotice, msg) } // Informational Log INFORMATIONAL level message. @@ -300,7 +301,7 @@ func (bl *BeeLogger) Informational(format string, v ...interface{}) { return } msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg, time.Now()) + bl.writeMsg(LevelInformational, msg) } // Debug Log DEBUG level message. @@ -309,7 +310,7 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { return } msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg, time.Now()) + bl.writeMsg(LevelDebug, msg) } // Warn Log WARN level message. @@ -319,7 +320,7 @@ func (bl *BeeLogger) Warn(format string, v ...interface{}) { return } msg := fmt.Sprintf("[W] "+format, v...) - bl.writeMsg(LevelWarning, msg, time.Now()) + bl.writeMsg(LevelWarning, msg) } // Info Log INFO level message. @@ -329,7 +330,7 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) { return } msg := fmt.Sprintf("[I] "+format, v...) - bl.writeMsg(LevelInformational, msg, time.Now()) + bl.writeMsg(LevelInformational, msg) } // Trace Log TRACE level message. @@ -339,7 +340,7 @@ func (bl *BeeLogger) Trace(format string, v ...interface{}) { return } msg := fmt.Sprintf("[D] "+format, v...) - bl.writeMsg(LevelDebug, msg, time.Now()) + bl.writeMsg(LevelDebug, msg) } // Flush flush all chan data. From bcac4bb8e3631e77fe1a0e0d6fd312a4f3c1b099 Mon Sep 17 00:00:00 2001 From: youngsterxyf Date: Mon, 25 Jan 2016 20:53:25 +0800 Subject: [PATCH 12/29] accept @JessonChan suggestion --- logs/conn.go | 3 +-- logs/console.go | 5 +---- logs/file.go | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/logs/conn.go b/logs/conn.go index 2a29688a..5d78467b 100644 --- a/logs/conn.go +++ b/logs/conn.go @@ -64,8 +64,7 @@ func (c *connWriter) WriteMsg(when time.Time, msg string, level int) error { defer c.innerWriter.Close() } - logTimeStr := formatLogTime(when) - msg = logTimeStr + msg + msg = formatLogTime(when) + msg c.lg.Println(msg) return nil diff --git a/logs/console.go b/logs/console.go index 48d1e5b2..e6da0df9 100644 --- a/logs/console.go +++ b/logs/console.go @@ -74,10 +74,7 @@ func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { if level > c.Level { return nil } - - logTimeStr := formatLogTime(when) - msg = logTimeStr + msg - + msg = formatLogTime(when) + msg if goos := runtime.GOOS; goos == "windows" { c.lg.Println(msg) return nil diff --git a/logs/file.go b/logs/file.go index 16bf29c2..f277317c 100644 --- a/logs/file.go +++ b/logs/file.go @@ -121,8 +121,7 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { //2016/01/12 21:34:33 // now := time.Now() d := when.Day() - logTimeStr := formatLogTime(when) - msg = logTimeStr + msg + "\n" + msg = formatLogTime(when) + msg + "\n" if w.Rotate { if w.needRotate(len(msg), d) { From fdce4af9c824d8064436820fbe74fa5fe73e68c2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 20:53:52 +0800 Subject: [PATCH 13/29] fix #1619 --- orm/types.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/orm/types.go b/orm/types.go index 5fac5fed..1c8460b2 100644 --- a/orm/types.go +++ b/orm/types.go @@ -162,6 +162,12 @@ type QuerySeter interface { // qs.RelatedSel("profile").One(&user) // user.Profile.Age = 32 RelatedSel(params ...interface{}) QuerySeter + // Set Distinct + // for example: + // o.QueryTable("policy").Filter("Groups__Group__Users__User", user). + // Distinct(). + // All(&permissions) + Distinct() QuerySeter // return QuerySeter execution result number // for example: // num, err = qs.Filter("profile__age__gt", 28).Count() From 87650ce8bcad80f103e67787651a0059b3054aaa Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 20:57:41 +0800 Subject: [PATCH 14/29] make golint happy --- config/config.go | 4 +--- config/xml/xml.go | 3 +-- config/yaml/yaml.go | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/config/config.go b/config/config.go index e9df0f0e..c0afec05 100644 --- a/config/config.go +++ b/config/config.go @@ -113,7 +113,6 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) { // 0, 0.0, f, F, FALSE, false, False, NO, no, No, N,n, OFF, off, Off. // Any other value returns an error. func ParseBool(val interface{}) (value bool, err error) { - if val != nil { switch v := val.(type) { case bool: @@ -140,7 +139,6 @@ func ParseBool(val interface{}) (value bool, err error) { } } return false, fmt.Errorf("parsing %q: invalid syntax", val) - } else { - return false, fmt.Errorf("parsing : invalid syntax") } + return false, fmt.Errorf("parsing : invalid syntax") } diff --git a/config/xml/xml.go b/config/xml/xml.go index 662ef3d9..ffb32862 100644 --- a/config/xml/xml.go +++ b/config/xml/xml.go @@ -94,9 +94,8 @@ type ConfigContainer struct { func (c *ConfigContainer) Bool(key string) (bool, error) { if v, ok := c.data[key]; ok { return config.ParseBool(v) - } else { - return false, fmt.Errorf("not exist key: %q", key) } + return false, fmt.Errorf("not exist key: %q", key) } // DefaultBool return the bool value if has no error diff --git a/config/yaml/yaml.go b/config/yaml/yaml.go index ccfd7b8b..9a96ac92 100644 --- a/config/yaml/yaml.go +++ b/config/yaml/yaml.go @@ -123,9 +123,8 @@ type ConfigContainer struct { func (c *ConfigContainer) Bool(key string) (bool, error) { if v, ok := c.data[key]; ok { return config.ParseBool(v) - } else { - return false, fmt.Errorf("not exist key: %q", key) } + return false, fmt.Errorf("not exist key: %q", key) } // DefaultBool return the bool value if has no error From 7e3ad5bcb010beb9c8a01fbdb475c655f9f0ebe2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 21:08:29 +0800 Subject: [PATCH 15/29] fix #1585 --- hooks.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/hooks.go b/hooks.go index 78abf8ef..59b10b32 100644 --- a/hooks.go +++ b/hooks.go @@ -68,13 +68,11 @@ func registerSession() error { } func registerTemplate() error { - if BConfig.WebConfig.AutoRender { - if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil { - if BConfig.RunMode == DEV { - Warn(err) - } - return err + if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil { + if BConfig.RunMode == DEV { + Warn(err) } + return err } return nil } From e7d4452af0c8f51007c60a379a55dce39a9d3da3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 21:13:56 +0800 Subject: [PATCH 16/29] add golint and go test --- .travis.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c59cef61..bb98eebb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,9 @@ language: go go: - - 1.5.1 - + - 1.5.3 + - 1.4.3 + - 1.3.3 services: - redis-server - mysql @@ -24,7 +25,14 @@ install: - go get github.com/couchbase/go-couchbase - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis + - go get code.google.com/p/go.tools/cmd/vet + - go get code.google.com/p/go.tools/cmd/cover + - go get github.com/golang/lint/golint before_script: - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" +script: + - go vet -x ./... + - $HOME/gopath/bin/golint ./... + - go test -v ./... From 3dac344ff690ba7db854b33f789e88af268206fe Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 21:20:10 +0800 Subject: [PATCH 17/29] fix the vet url --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bb98eebb..30ec5d4b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,8 +25,7 @@ install: - go get github.com/couchbase/go-couchbase - go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/ledis - - go get code.google.com/p/go.tools/cmd/vet - - go get code.google.com/p/go.tools/cmd/cover + - go get golang.org/x/tools/cmd/vet - go get github.com/golang/lint/golint before_script: - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" From f8004b69adb338fd945409f1fa96c181433b9fa4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 21:33:57 +0800 Subject: [PATCH 18/29] fix the go vet --- config/json_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/json_test.go b/config/json_test.go index 9c06143e..df663461 100644 --- a/config/json_test.go +++ b/config/json_test.go @@ -162,7 +162,7 @@ func TestJson(t *testing.T) { value, err = jsonconf.DIY(k) } if err != nil { - t.Fatalf("get key %q value fatal,%V err %s", k, v, err) + t.Fatalf("get key %q value fatal,%v err %s", k, v, err) } else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) { t.Fatalf("get key %q value, want %v got %v .", k, v, value) } From 15e9ba19c0ea4342f79aa09a3fd404a8b9ae3369 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 25 Jan 2016 21:39:44 +0800 Subject: [PATCH 19/29] fix the range only used in Go 1.4 fix #1623 --- tree.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tree.go b/tree.go index ffd444d4..07034bb4 100644 --- a/tree.go +++ b/tree.go @@ -141,7 +141,7 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st regexpStr = "([^.]+).(.+)" params = params[1:] } else { - for range params { + for _ = range params { regexpStr = "([^/]+)/" + regexpStr } } @@ -254,7 +254,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, regexpStr = "/([^.]+).(.+)" params = params[1:] } else { - for range params { + for _ = range params { regexpStr = "/([^/]+)" + regexpStr } } From 4de91f675da1c4a68360e1298ab362c91b8c4068 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Mon, 25 Jan 2016 22:29:45 +0800 Subject: [PATCH 20/29] show from when Config from is empty --- utils/mail.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index bebce610..07043149 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -74,9 +74,6 @@ func NewEMail(config string) *Email { if err != nil { return nil } - if e.From == "" { - e.From = e.Username - } return e } @@ -228,13 +225,19 @@ func (e *Email) Send() error { to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc)) to = append(append(append(to, e.To...), e.Cc...), e.Bcc...) // Check to make sure there is at least one recipient and one "From" address - if e.From == "" || len(to) == 0 { - return errors.New("Must specify at least one From address and one To address") + if len(to) == 0 { + return errors.New("Must specify at least one To address") } + from, err := mail.ParseAddress(e.Username) if err != nil { return err } + + if len(e.From) == 0 { + e.From = from.String() + } + raw, err := e.Bytes() if err != nil { return err From 5930f27da7d776c2ddf23b53287a1967dbb85a4e Mon Sep 17 00:00:00 2001 From: miraclesu Date: Mon, 25 Jan 2016 22:55:40 +0800 Subject: [PATCH 21/29] Fix mail Chinese subject garbled bug --- utils/mail.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/utils/mail.go b/utils/mail.go index 07043149..125fddd5 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -237,6 +237,9 @@ func (e *Email) Send() error { if len(e.From) == 0 { e.From = from.String() } + // use mail's RFC 5322 to encode any string + sub := mail.Address{e.Subject, ""} + e.Subject = strings.Trim(sub.String(), " <@>") raw, err := e.Bytes() if err != nil { From d1481ea659253f6f66bae053fd358f66c68f384c Mon Sep 17 00:00:00 2001 From: coseyo Date: Mon, 25 Jan 2016 23:00:09 +0800 Subject: [PATCH 22/29] move assignment to init --- config.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/config.go b/config.go index 3ceae12e..bf9077a7 100644 --- a/config.go +++ b/config.go @@ -113,9 +113,15 @@ var ( TemplateCache map[string]*template.Template // GlobalSessions is the instance for the session manager GlobalSessions *session.Manager + + workPath string ) func init() { + AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0])) + workPath, _ = os.Getwd() + workPath, _ = filepath.Abs(workPath) + BConfig = &Config{ AppName: "beego", RunMode: DEV, @@ -181,10 +187,6 @@ func init() { // ParseConfig parsed default config file. // now only support ini, next will support json. func ParseConfig() (err error) { - AppPath, _ := filepath.Abs(filepath.Dir(os.Args[0])) - workPath, _ := os.Getwd() - workPath, _ = filepath.Abs(workPath) - if AppConfigPath == "" { // initialize default configurations AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") From 61e9dc74c9099dfc6f5bf9cbe10ecbd3987be2db Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 26 Jan 2016 00:27:02 +0800 Subject: [PATCH 23/29] make sure the memcache testing success --- cache/memcache/memcache_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go index 8d98c177..0c8c57f2 100644 --- a/cache/memcache/memcache_test.go +++ b/cache/memcache/memcache_test.go @@ -37,7 +37,7 @@ func TestMemcacheCache(t *testing.T) { t.Error("check err") } - time.Sleep(10 * time.Second) + time.Sleep(11 * time.Second) if bm.IsExist("astaxie") { t.Error("check err") From b5a07c6ba89dd7aa0903ba09fd3a5059a3318f3b Mon Sep 17 00:00:00 2001 From: JessonChan Date: Tue, 26 Jan 2016 09:20:49 +0800 Subject: [PATCH 24/29] log file name bug fixed this bug happens when daily rotate. ex,when it is 2016-01-22 23:59:59 and need a rotate,the file name should named with 2016-01-22 but named with 2016-01-23(next day) --- logs/file.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logs/file.go b/logs/file.go index f277317c..e7401922 100644 --- a/logs/file.go +++ b/logs/file.go @@ -127,7 +127,7 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { if w.needRotate(len(msg), d) { w.Lock() if w.needRotate(len(msg), d) { - if err := w.doRotate(); err != nil { + if err := w.doRotate(when); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } @@ -200,7 +200,7 @@ func (w *fileLogWriter) lines() (int, error) { // DoRotate means it need to write file in new file. // new file name like xx.2013-01-01.2.log -func (w *fileLogWriter) doRotate() error { +func (w *fileLogWriter) doRotate(logTime time.Time) error { _, err := os.Lstat(w.Filename) if err != nil { return err @@ -215,7 +215,7 @@ func (w *fileLogWriter) doRotate() error { suffix = ".log" } for ; err == nil && num <= 999; num++ { - fName = filenameOnly + fmt.Sprintf(".%s.%03d%s", time.Now().Format("2006-01-02"), num, suffix) + fName = filenameOnly + fmt.Sprintf(".%s.%03d%s", logTime.Format("2006-01-02"), num, suffix) _, err = os.Lstat(fName) } // return error if the last file checked still existed From f2567bc114f8f01bc2e8493bf13a1a1bcfc72c64 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Tue, 26 Jan 2016 09:29:04 +0800 Subject: [PATCH 25/29] some typo fixed --- logs/console.go | 24 ++++++++++++------------ logs/file.go | 5 +---- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/logs/console.go b/logs/console.go index e6da0df9..b717a12e 100644 --- a/logs/console.go +++ b/logs/console.go @@ -35,14 +35,14 @@ func newBrush(color string) brush { } var colors = []brush{ - newBrush("1;37"), // Emergency white - newBrush("1;36"), // Alert cyan - newBrush("1;35"), // Critical magenta - newBrush("1;31"), // Error red - newBrush("1;33"), // Warning yellow - newBrush("1;32"), // Notice green + newBrush("1;37"), // Emergency white + newBrush("1;36"), // Alert cyan + newBrush("1;35"), // Critical magenta + newBrush("1;31"), // Error red + newBrush("1;33"), // Warning yellow + newBrush("1;32"), // Notice green newBrush("1;34"), // Informational blue - newBrush("1;34"), // Debug blue + newBrush("1;34"), // Debug blue } // consoleWriter implements LoggerInterface and writes messages to terminal. @@ -61,12 +61,12 @@ func NewConsole() Logger { } // Init init console logger. -// jsonconfig like '{"level":LevelTrace}'. -func (c *consoleWriter) Init(jsonconfig string) error { - if len(jsonconfig) == 0 { +// jsonConfig like '{"level":LevelTrace}'. +func (c *consoleWriter) Init(jsonConfig string) error { + if len(jsonConfig) == 0 { return nil } - return json.Unmarshal([]byte(jsonconfig), c) + return json.Unmarshal([]byte(jsonConfig), c) } // WriteMsg write message in console. @@ -75,7 +75,7 @@ func (c *consoleWriter) WriteMsg(when time.Time, msg string, level int) error { return nil } msg = formatLogTime(when) + msg - if goos := runtime.GOOS; goos == "windows" { + if runtime.GOOS == "windows" { c.lg.Println(msg) return nil } diff --git a/logs/file.go b/logs/file.go index e7401922..3a042164 100644 --- a/logs/file.go +++ b/logs/file.go @@ -118,19 +118,16 @@ func (w *fileLogWriter) WriteMsg(when time.Time, msg string, level int) error { if level > w.Level { return nil } - //2016/01/12 21:34:33 - // now := time.Now() - d := when.Day() msg = formatLogTime(when) + msg + "\n" if w.Rotate { + d := when.Day() if w.needRotate(len(msg), d) { w.Lock() if w.needRotate(len(msg), d) { if err := w.doRotate(when); err != nil { fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err) } - } w.Unlock() } From e11d150e8b989a258c0a99576f7485b879c8a151 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Tue, 26 Jan 2016 09:35:39 +0800 Subject: [PATCH 26/29] replace \t with space --- logs/console.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/logs/console.go b/logs/console.go index b717a12e..10d8e8b2 100644 --- a/logs/console.go +++ b/logs/console.go @@ -35,14 +35,14 @@ func newBrush(color string) brush { } var colors = []brush{ - newBrush("1;37"), // Emergency white - newBrush("1;36"), // Alert cyan - newBrush("1;35"), // Critical magenta - newBrush("1;31"), // Error red - newBrush("1;33"), // Warning yellow - newBrush("1;32"), // Notice green - newBrush("1;34"), // Informational blue - newBrush("1;34"), // Debug blue + newBrush("1;37"), // Emergency white + newBrush("1;36"), // Alert cyan + newBrush("1;35"), // Critical magenta + newBrush("1;31"), // Error red + newBrush("1;33"), // Warning yellow + newBrush("1;32"), // Notice green + newBrush("1;34"), // Informational blue + newBrush("1;34"), // Debug blue } // consoleWriter implements LoggerInterface and writes messages to terminal. From f26d360ec9b82d58389799dfa3ee1d2383894c6a Mon Sep 17 00:00:00 2001 From: miraclesu Date: Tue, 26 Jan 2016 10:33:48 +0800 Subject: [PATCH 27/29] Fix vet fail --- utils/mail.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 125fddd5..0a961204 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -235,11 +235,11 @@ func (e *Email) Send() error { } if len(e.From) == 0 { - e.From = from.String() + e.From = e.Username } - // use mail's RFC 5322 to encode any string - sub := mail.Address{e.Subject, ""} - e.Subject = strings.Trim(sub.String(), " <@>") + // use mail's RFC 2047 to encode any string + sub := mail.Address{Name: e.Subject, Address: ""} + e.Subject = strings.TrimRight(sub.String(), " <@>") raw, err := e.Bytes() if err != nil { From bf870eb9a26af47b5bdfee9e3cb2ded180e80247 Mon Sep 17 00:00:00 2001 From: miraclesu Date: Tue, 26 Jan 2016 20:50:03 +0800 Subject: [PATCH 28/29] mv mime.QEncoding.Encode logic to mail it is named qEncode --- utils/mail.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/utils/mail.go b/utils/mail.go index 0a961204..10555a0a 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -31,10 +31,13 @@ import ( "path/filepath" "strconv" "strings" + "sync" ) const ( maxLineLength = 76 + + upperhex = "0123456789ABCDEF" ) // Email is the type used for email messages @@ -238,8 +241,7 @@ func (e *Email) Send() error { e.From = e.Username } // use mail's RFC 2047 to encode any string - sub := mail.Address{Name: e.Subject, Address: ""} - e.Subject = strings.TrimRight(sub.String(), " <@>") + e.Subject = qEncode("utf-8", e.Subject) raw, err := e.Bytes() if err != nil { @@ -347,3 +349,73 @@ func base64Wrap(w io.Writer, b []byte) { w.Write(out) } } + +// Encode returns the encoded-word form of s. If s is ASCII without special +// characters, it is returned unchanged. The provided charset is the IANA +// charset name of s. It is case insensitive. +// RFC 2047 encoded-word +func qEncode(charset, s string) string { + if !needsEncoding(s) { + return s + } + return encodeWord(charset, s) +} + +func needsEncoding(s string) bool { + for _, b := range s { + if (b < ' ' || b > '~') && b != '\t' { + return true + } + } + return false +} + +// encodeWord encodes a string into an encoded-word. +func encodeWord(charset, s string) string { + buf := getBuffer() + + buf.WriteString("=?") + buf.WriteString(charset) + buf.WriteByte('?') + buf.WriteByte('q') + buf.WriteByte('?') + + enc := make([]byte, 3) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == ' ': + buf.WriteByte('_') + case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_': + buf.WriteByte(b) + default: + enc[0] = '=' + enc[1] = upperhex[b>>4] + enc[2] = upperhex[b&0x0f] + buf.Write(enc) + } + } + buf.WriteString("?=") + + es := buf.String() + putBuffer(buf) + return es +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +func getBuffer() *bytes.Buffer { + return bufPool.Get().(*bytes.Buffer) +} + +func putBuffer(buf *bytes.Buffer) { + if buf.Len() > 1024 { + return + } + buf.Reset() + bufPool.Put(buf) +} From e3810b599d10946613001bc5a498e606c911018f Mon Sep 17 00:00:00 2001 From: Yunkai Zhang Date: Tue, 26 Jan 2016 23:27:26 +0800 Subject: [PATCH 29/29] Fix regression caused by commit ad65479 Commit ad65479 will cause "Method Not Allow" in preflight response when enable CORS plugin. The root cause is that CORS plugin didn't generate http output after applied commit ad65479, so the value of `ctx.ResponseWriter.Started` will be keep `false`, and then later filter chains will be go on to run when CORS filter finished. This path will both fix "Method Not Allow" and the original bug "multiple response.WriteHeader calls". Signed-off-by: Yunkai Zhang --- plugins/cors/cors.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/cors/cors.go b/plugins/cors/cors.go index 1e973a40..45c327ab 100644 --- a/plugins/cors/cors.go +++ b/plugins/cors/cors.go @@ -36,6 +36,7 @@ package cors import ( + "net/http" "regexp" "strconv" "strings" @@ -215,6 +216,7 @@ func Allow(opts *Options) beego.FilterFunc { for key, value := range headers { ctx.Output.Header(key, value) } + ctx.ResponseWriter.WriteHeader(http.StatusOK) return } headers = opts.Header(origin)