mirror of
https://github.com/astaxie/beego.git
synced 2024-11-22 13:00:54 +00:00
Merge branches 'master' and 'develop' of lcbluestorm.github.com:lcbluestorm/beego into develop
This commit is contained in:
commit
be23c42674
14
.travis.yml
14
.travis.yml
@ -1,8 +1,10 @@
|
|||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.5.1
|
- tip
|
||||||
|
- 1.5.3
|
||||||
|
- 1.4.3
|
||||||
|
- 1.3.3
|
||||||
services:
|
services:
|
||||||
- redis-server
|
- redis-server
|
||||||
- mysql
|
- mysql
|
||||||
@ -24,7 +26,15 @@ install:
|
|||||||
- go get github.com/couchbase/go-couchbase
|
- go get github.com/couchbase/go-couchbase
|
||||||
- go get github.com/siddontang/ledisdb/config
|
- go get github.com/siddontang/ledisdb/config
|
||||||
- go get github.com/siddontang/ledisdb/ledis
|
- go get github.com/siddontang/ledisdb/ledis
|
||||||
|
- go get golang.org/x/tools/cmd/vet
|
||||||
|
- go get github.com/golang/lint/golint
|
||||||
before_script:
|
before_script:
|
||||||
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
|
- 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' = '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"
|
- 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 ./...
|
||||||
|
notifications:
|
||||||
|
webhooks: https://hooks.pubu.im/services/z7m9bvybl3rgtg9
|
||||||
|
@ -53,6 +53,7 @@ Please see [Documentation](http://beego.me/docs) for more.
|
|||||||
## Community
|
## Community
|
||||||
|
|
||||||
* [http://beego.me/community](http://beego.me/community)
|
* [http://beego.me/community](http://beego.me/community)
|
||||||
|
* Welcome to join us in Slack: [https://beego.slack.com](https://beego.slack.com), you can get invited from [here](https://github.com/beego/beedoc/issues/232)
|
||||||
|
|
||||||
## LICENSE
|
## LICENSE
|
||||||
|
|
||||||
|
4
admin.go
4
admin.go
@ -90,8 +90,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
|
|||||||
switch command {
|
switch command {
|
||||||
case "conf":
|
case "conf":
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
m["AppConfigPath"] = AppConfigPath
|
m["AppConfigPath"] = appConfigPath
|
||||||
m["AppConfigProvider"] = AppConfigProvider
|
m["AppConfigProvider"] = appConfigProvider
|
||||||
m["BConfig.AppName"] = BConfig.AppName
|
m["BConfig.AppName"] = BConfig.AppName
|
||||||
m["BConfig.RunMode"] = BConfig.RunMode
|
m["BConfig.RunMode"] = BConfig.RunMode
|
||||||
m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive
|
m["BConfig.RouterCaseSensitive"] = BConfig.RouterCaseSensitive
|
||||||
|
18
beego.go
18
beego.go
@ -15,7 +15,6 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -68,21 +67,6 @@ func Run(params ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initBeforeHTTPRun() {
|
func initBeforeHTTPRun() {
|
||||||
// if AppConfigPath is setted or conf/app.conf exist
|
|
||||||
err := ParseConfig()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
//init log
|
|
||||||
for adaptor, config := range BConfig.Log.Outputs {
|
|
||||||
err = BeeLogger.SetLogger(adaptor, config)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%s with the config `%s` got err:%s\n", adaptor, config, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SetLogFuncCall(BConfig.Log.FileLineNum)
|
|
||||||
|
|
||||||
//init hooks
|
//init hooks
|
||||||
AddAPPStartHook(registerMime)
|
AddAPPStartHook(registerMime)
|
||||||
AddAPPStartHook(registerDefaultErrorHandler)
|
AddAPPStartHook(registerDefaultErrorHandler)
|
||||||
@ -101,7 +85,7 @@ func initBeforeHTTPRun() {
|
|||||||
// TestBeegoInit is for test package init
|
// TestBeegoInit is for test package init
|
||||||
func TestBeegoInit(ap string) {
|
func TestBeegoInit(ap string) {
|
||||||
os.Setenv("BEEGO_RUNMODE", "test")
|
os.Setenv("BEEGO_RUNMODE", "test")
|
||||||
AppConfigPath = filepath.Join(ap, "conf", "app.conf")
|
appConfigPath = filepath.Join(ap, "conf", "app.conf")
|
||||||
os.Chdir(ap)
|
os.Chdir(ap)
|
||||||
initBeforeHTTPRun()
|
initBeforeHTTPRun()
|
||||||
}
|
}
|
||||||
|
2
cache/memcache/memcache_test.go
vendored
2
cache/memcache/memcache_test.go
vendored
@ -37,7 +37,7 @@ func TestMemcacheCache(t *testing.T) {
|
|||||||
t.Error("check err")
|
t.Error("check err")
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(10 * time.Second)
|
time.Sleep(11 * time.Second)
|
||||||
|
|
||||||
if bm.IsExist("astaxie") {
|
if bm.IsExist("astaxie") {
|
||||||
t.Error("check err")
|
t.Error("check err")
|
||||||
|
92
config.go
92
config.go
@ -19,6 +19,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/astaxie/beego/config"
|
"github.com/astaxie/beego/config"
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
@ -103,17 +104,29 @@ var (
|
|||||||
BConfig *Config
|
BConfig *Config
|
||||||
// AppConfig is the instance of Config, store the config information from file
|
// AppConfig is the instance of Config, store the config information from file
|
||||||
AppConfig *beegoAppConfig
|
AppConfig *beegoAppConfig
|
||||||
// AppConfigPath is the path to the config files
|
// AppPath is the absolute path to the app
|
||||||
AppConfigPath string
|
AppPath string
|
||||||
// AppConfigProvider is the provider for the config, default is ini
|
|
||||||
AppConfigProvider = "ini"
|
|
||||||
// TemplateCache stores template caching
|
// TemplateCache stores template caching
|
||||||
TemplateCache map[string]*template.Template
|
TemplateCache map[string]*template.Template
|
||||||
// GlobalSessions is the instance for the session manager
|
// GlobalSessions is the instance for the session manager
|
||||||
GlobalSessions *session.Manager
|
GlobalSessions *session.Manager
|
||||||
|
|
||||||
|
workPath string
|
||||||
|
// appConfigPath is the path to the config files
|
||||||
|
appConfigPath string
|
||||||
|
// appConfigProvider is the provider for the config, default is ini
|
||||||
|
appConfigProvider = "ini"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
AppPath, _ = filepath.Abs(filepath.Dir(os.Args[0]))
|
||||||
|
workPath, _ = os.Getwd()
|
||||||
|
workPath, _ = filepath.Abs(workPath)
|
||||||
|
|
||||||
|
if workPath != AppPath {
|
||||||
|
os.Chdir(AppPath)
|
||||||
|
}
|
||||||
|
|
||||||
BConfig = &Config{
|
BConfig = &Config{
|
||||||
AppName: "beego",
|
AppName: "beego",
|
||||||
RunMode: DEV,
|
RunMode: DEV,
|
||||||
@ -173,21 +186,19 @@ func init() {
|
|||||||
Outputs: map[string]string{"console": ""},
|
Outputs: map[string]string{"console": ""},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ParseConfig()
|
|
||||||
|
appConfigPath = filepath.Join(AppPath, "conf", "app.conf")
|
||||||
|
if !utils.FileExists(appConfigPath) {
|
||||||
|
AppConfig = &beegoAppConfig{config.NewFakeConfig()}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parseConfig(appConfigPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseConfig parsed default config file.
|
|
||||||
// now only support ini, next will support json.
|
// now only support ini, next will support json.
|
||||||
func ParseConfig() (err error) {
|
func parseConfig(appConfigPath string) (err error) {
|
||||||
if AppConfigPath == "" {
|
AppConfig, err = newAppConfig(appConfigProvider, appConfigPath)
|
||||||
if utils.FileExists(filepath.Join("conf", "app.conf")) {
|
|
||||||
AppConfigPath = filepath.Join("conf", "app.conf")
|
|
||||||
} else {
|
|
||||||
AppConfig = &beegoAppConfig{config.NewFakeConfig()}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AppConfig, err = newAppConfig(AppConfigProvider, AppConfigPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -241,6 +252,8 @@ func ParseConfig() (err error) {
|
|||||||
BConfig.WebConfig.Session.SessionCookieLifeTime = AppConfig.DefaultInt("SessionCookieLifeTime", BConfig.WebConfig.Session.SessionCookieLifeTime)
|
BConfig.WebConfig.Session.SessionCookieLifeTime = AppConfig.DefaultInt("SessionCookieLifeTime", BConfig.WebConfig.Session.SessionCookieLifeTime)
|
||||||
BConfig.WebConfig.Session.SessionAutoSetCookie = AppConfig.DefaultBool("SessionAutoSetCookie", BConfig.WebConfig.Session.SessionAutoSetCookie)
|
BConfig.WebConfig.Session.SessionAutoSetCookie = AppConfig.DefaultBool("SessionAutoSetCookie", BConfig.WebConfig.Session.SessionAutoSetCookie)
|
||||||
BConfig.WebConfig.Session.SessionDomain = AppConfig.DefaultString("SessionDomain", BConfig.WebConfig.Session.SessionDomain)
|
BConfig.WebConfig.Session.SessionDomain = AppConfig.DefaultString("SessionDomain", BConfig.WebConfig.Session.SessionDomain)
|
||||||
|
BConfig.Log.AccessLogs = AppConfig.DefaultBool("LogAccessLogs", BConfig.Log.AccessLogs)
|
||||||
|
BConfig.Log.FileLineNum = AppConfig.DefaultBool("LogFileLineNum", BConfig.Log.FileLineNum)
|
||||||
|
|
||||||
if sd := AppConfig.String("StaticDir"); sd != "" {
|
if sd := AppConfig.String("StaticDir"); sd != "" {
|
||||||
for k := range BConfig.WebConfig.StaticDir {
|
for k := range BConfig.WebConfig.StaticDir {
|
||||||
@ -273,15 +286,58 @@ func ParseConfig() (err error) {
|
|||||||
BConfig.WebConfig.StaticExtensionsToGzip = fileExts
|
BConfig.WebConfig.StaticExtensionsToGzip = fileExts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if lo := AppConfig.String("LogOutputs"); lo != "" {
|
||||||
|
los := strings.Split(lo, ";")
|
||||||
|
for _, v := range los {
|
||||||
|
if logType2Config := strings.SplitN(v, ",", 2); len(logType2Config) == 2 {
|
||||||
|
BConfig.Log.Outputs[logType2Config[0]] = logType2Config[1]
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//init log
|
||||||
|
BeeLogger.Close()
|
||||||
|
for adaptor, config := range BConfig.Log.Outputs {
|
||||||
|
err = BeeLogger.SetLogger(adaptor, config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s with the config `%s` got err:%s\n", adaptor, config, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetLogFuncCall(BConfig.Log.FileLineNum)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadAppConfig allow developer to apply a config file
|
||||||
|
func LoadAppConfig(adapterName, configPath string) error {
|
||||||
|
absConfigPath, err := filepath.Abs(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.FileExists(absConfigPath) {
|
||||||
|
return fmt.Errorf("the target config file: %s don't exist", configPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if absConfigPath == appConfigPath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfigPath = absConfigPath
|
||||||
|
appConfigProvider = adapterName
|
||||||
|
|
||||||
|
return parseConfig(appConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
type beegoAppConfig struct {
|
type beegoAppConfig struct {
|
||||||
innerConfig config.Configer
|
innerConfig config.Configer
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, error) {
|
func newAppConfig(appConfigProvider, appConfigPath string) (*beegoAppConfig, error) {
|
||||||
ac, err := config.NewConfig(AppConfigProvider, AppConfigPath)
|
ac, err := config.NewConfig(appConfigProvider, appConfigPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -106,3 +106,39 @@ func NewConfigData(adapterName string, data []byte) (Configer, error) {
|
|||||||
}
|
}
|
||||||
return adapter.ParseData(data)
|
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)
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("parsing <nil>: invalid syntax")
|
||||||
|
}
|
||||||
|
@ -82,7 +82,7 @@ func (c *fakeConfigContainer) DefaultInt64(key string, defaultval int64) int64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *fakeConfigContainer) Bool(key string) (bool, error) {
|
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 {
|
func (c *fakeConfigContainer) DefaultBool(key string, defaultval bool) bool {
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -97,9 +96,11 @@ func (ini *IniConfig) parseFile(name string) (*IniConfigContainer, error) {
|
|||||||
}
|
}
|
||||||
if bComment != nil {
|
if bComment != nil {
|
||||||
line = bytes.TrimLeft(line, string(bComment))
|
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.Write(line)
|
||||||
comment.WriteByte('\n')
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +195,7 @@ type IniConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *IniConfigContainer) Bool(key string) (bool, error) {
|
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.
|
// DefaultBool returns the boolean value for a given key.
|
||||||
@ -299,14 +300,35 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
|||||||
}
|
}
|
||||||
defer f.Close()
|
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)
|
buf := bytes.NewBuffer(nil)
|
||||||
// Save default section at first place
|
// Save default section at first place
|
||||||
if dt, ok := c.data[defaultSection]; ok {
|
if dt, ok := c.data[defaultSection]; ok {
|
||||||
for key, val := range dt {
|
for key, val := range dt {
|
||||||
if key != " " {
|
if key != " " {
|
||||||
// Write key comments.
|
// Write key comments.
|
||||||
if v, ok := c.keyComment[key]; ok {
|
if v := getCommentStr(defaultSection, key); len(v) > 0 {
|
||||||
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
|
if _, err = buf.WriteString(v + lineBreak); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,8 +349,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
|||||||
for section, dt := range c.data {
|
for section, dt := range c.data {
|
||||||
if section != defaultSection {
|
if section != defaultSection {
|
||||||
// Write section comments.
|
// Write section comments.
|
||||||
if v, ok := c.sectionComment[section]; ok {
|
if v := getCommentStr(section, ""); len(v) > 0 {
|
||||||
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
|
if _, err = buf.WriteString(v + lineBreak); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -341,8 +363,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) {
|
|||||||
for key, val := range dt {
|
for key, val := range dt {
|
||||||
if key != " " {
|
if key != " " {
|
||||||
// Write key comments.
|
// Write key comments.
|
||||||
if v, ok := c.keyComment[key]; ok {
|
if v := getCommentStr(section, key); len(v) > 0 {
|
||||||
if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil {
|
if _, err = buf.WriteString(v + lineBreak); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,17 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var inicontext = `
|
func TestIni(t *testing.T) {
|
||||||
|
|
||||||
|
var (
|
||||||
|
inicontext = `
|
||||||
;comment one
|
;comment one
|
||||||
#comment two
|
#comment two
|
||||||
appname = beeapi
|
appname = beeapi
|
||||||
@ -29,6 +35,13 @@ PI = 3.1415976
|
|||||||
runmode = "dev"
|
runmode = "dev"
|
||||||
autorender = false
|
autorender = false
|
||||||
copyrequestbody = true
|
copyrequestbody = true
|
||||||
|
session= on
|
||||||
|
cookieon= off
|
||||||
|
newreg = OFF
|
||||||
|
needlogin = ON
|
||||||
|
enableSession = Y
|
||||||
|
enableCookie = N
|
||||||
|
flag = 1
|
||||||
[demo]
|
[demo]
|
||||||
key1="asta"
|
key1="asta"
|
||||||
key2 = "xie"
|
key2 = "xie"
|
||||||
@ -36,7 +49,31 @@ CaseInsensitive = true
|
|||||||
peers = one;two;three
|
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")
|
f, err := os.Create("testini.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -52,31 +89,31 @@ func TestIni(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if iniconf.String("appname") != "beeapi" {
|
for k, v := range keyValue {
|
||||||
t.Fatal("appname not equal to beeapi")
|
var err error
|
||||||
}
|
var value interface{}
|
||||||
if port, err := iniconf.Int("httpport"); err != nil || port != 8080 {
|
switch v.(type) {
|
||||||
t.Error(port)
|
case int:
|
||||||
t.Fatal(err)
|
value, err = iniconf.Int(k)
|
||||||
}
|
case int64:
|
||||||
if port, err := iniconf.Int64("mysqlport"); err != nil || port != 3600 {
|
value, err = iniconf.Int64(k)
|
||||||
t.Error(port)
|
case float64:
|
||||||
t.Fatal(err)
|
value, err = iniconf.Float(k)
|
||||||
}
|
case bool:
|
||||||
if pi, err := iniconf.Float("PI"); err != nil || pi != 3.1415976 {
|
value, err = iniconf.Bool(k)
|
||||||
t.Error(pi)
|
case []string:
|
||||||
t.Fatal(err)
|
value = iniconf.Strings(k)
|
||||||
}
|
case string:
|
||||||
if iniconf.String("runmode") != "dev" {
|
value = iniconf.String(k)
|
||||||
t.Fatal("runmode not equal to dev")
|
default:
|
||||||
}
|
value, err = iniconf.DIY(k)
|
||||||
if v, err := iniconf.Bool("autorender"); err != nil || v != false {
|
}
|
||||||
t.Error(v)
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatalf("get key %q value fail,err %s", k, err)
|
||||||
}
|
} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
|
||||||
if v, err := iniconf.Bool("copyrequestbody"); err != nil || v != true {
|
t.Fatalf("get key %q value, want %v got %v .", k, v, value)
|
||||||
t.Error(v)
|
}
|
||||||
t.Fatal(err)
|
|
||||||
}
|
}
|
||||||
if err = iniconf.Set("name", "astaxie"); err != nil {
|
if err = iniconf.Set("name", "astaxie"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -84,20 +121,63 @@ func TestIni(t *testing.T) {
|
|||||||
if iniconf.String("name") != "astaxie" {
|
if iniconf.String("name") != "astaxie" {
|
||||||
t.Fatal("get name error")
|
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
cfgData := string(data)
|
||||||
|
datas := strings.Split(saveResult, "\n")
|
||||||
|
for _, line := range datas {
|
||||||
|
if strings.Contains(cfgData, line+"\n") == false {
|
||||||
|
t.Fatalf("different after save ini config file. need contains %q", line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -17,6 +17,7 @@ package config
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -70,12 +71,9 @@ type JSONConfigContainer struct {
|
|||||||
func (c *JSONConfigContainer) Bool(key string) (bool, error) {
|
func (c *JSONConfigContainer) Bool(key string) (bool, error) {
|
||||||
val := c.getData(key)
|
val := c.getData(key)
|
||||||
if val != nil {
|
if val != nil {
|
||||||
if v, ok := val.(bool); ok {
|
return ParseBool(val)
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
return false, errors.New("not bool value")
|
|
||||||
}
|
}
|
||||||
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
|
// DefaultBool return the bool value if has no error
|
||||||
|
@ -15,34 +15,14 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var jsoncontext = `{
|
func TestJsonStartsWithArray(t *testing.T) {
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}`
|
|
||||||
|
|
||||||
var jsoncontextwitharray = `[
|
const jsoncontextwitharray = `[
|
||||||
{
|
{
|
||||||
"url": "user",
|
"url": "user",
|
||||||
"serviceAPI": "http://www.test.com/user"
|
"serviceAPI": "http://www.test.com/user"
|
||||||
@ -52,8 +32,6 @@ var jsoncontextwitharray = `[
|
|||||||
"serviceAPI": "http://www.test.com/employee"
|
"serviceAPI": "http://www.test.com/employee"
|
||||||
}
|
}
|
||||||
]`
|
]`
|
||||||
|
|
||||||
func TestJsonStartsWithArray(t *testing.T) {
|
|
||||||
f, err := os.Create("testjsonWithArray.conf")
|
f, err := os.Create("testjsonWithArray.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -90,6 +68,64 @@ func TestJsonStartsWithArray(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestJson(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")
|
f, err := os.Create("testjson.conf")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -105,37 +141,32 @@ func TestJson(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if jsonconf.String("appname") != "beeapi" {
|
|
||||||
t.Fatal("appname not equal to beeapi")
|
for k, v := range keyValue {
|
||||||
}
|
var err error
|
||||||
if port, err := jsonconf.Int("httpport"); err != nil || port != 8080 {
|
var value interface{}
|
||||||
t.Error(port)
|
switch v.(type) {
|
||||||
t.Fatal(err)
|
case int:
|
||||||
}
|
value, err = jsonconf.Int(k)
|
||||||
if port, err := jsonconf.Int64("mysqlport"); err != nil || port != 3600 {
|
case int64:
|
||||||
t.Error(port)
|
value, err = jsonconf.Int64(k)
|
||||||
t.Fatal(err)
|
case float64:
|
||||||
}
|
value, err = jsonconf.Float(k)
|
||||||
if pi, err := jsonconf.Float("PI"); err != nil || pi != 3.1415976 {
|
case bool:
|
||||||
t.Error(pi)
|
value, err = jsonconf.Bool(k)
|
||||||
t.Fatal(err)
|
case []string:
|
||||||
}
|
value = jsonconf.Strings(k)
|
||||||
if jsonconf.String("runmode") != "dev" {
|
case string:
|
||||||
t.Fatal("runmode not equal to dev")
|
value = jsonconf.String(k)
|
||||||
}
|
default:
|
||||||
if v := jsonconf.Strings("unknown"); len(v) > 0 {
|
value, err = jsonconf.DIY(k)
|
||||||
t.Fatal("unknown strings, the length should be 0")
|
}
|
||||||
}
|
if err != nil {
|
||||||
if v := jsonconf.Strings("testnames"); len(v) != 2 {
|
t.Fatalf("get key %q value fatal,%v err %s", k, v, err)
|
||||||
t.Fatal("testnames length should be 2")
|
} else if fmt.Sprintf("%v", v) != fmt.Sprintf("%v", value) {
|
||||||
}
|
t.Fatalf("get key %q value, want %v got %v .", k, v, value)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
if err = jsonconf.Set("name", "astaxie"); err != nil {
|
if err = jsonconf.Set("name", "astaxie"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -143,15 +174,7 @@ func TestJson(t *testing.T) {
|
|||||||
if jsonconf.String("name") != "astaxie" {
|
if jsonconf.String("name") != "astaxie" {
|
||||||
t.Fatal("get name error")
|
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 {
|
if db, err := jsonconf.DIY("database"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if m, ok := db.(map[string]interface{}); !ok {
|
} else if m, ok := db.(map[string]interface{}); !ok {
|
||||||
|
@ -92,7 +92,10 @@ type ConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
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)
|
||||||
|
}
|
||||||
|
return false, fmt.Errorf("not exist key: %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool return the bool value if has no error
|
// DefaultBool return the bool value if has no error
|
||||||
|
@ -121,10 +121,10 @@ type ConfigContainer struct {
|
|||||||
|
|
||||||
// Bool returns the boolean value for a given key.
|
// Bool returns the boolean value for a given key.
|
||||||
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
func (c *ConfigContainer) Bool(key string) (bool, error) {
|
||||||
if v, ok := c.data[key].(bool); ok {
|
if v, ok := c.data[key]; ok {
|
||||||
return v, nil
|
return config.ParseBool(v)
|
||||||
}
|
}
|
||||||
return false, errors.New("not bool value")
|
return false, fmt.Errorf("not exist key: %q", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultBool return the bool value if has no error
|
// DefaultBool return the bool value if has no error
|
||||||
|
@ -287,6 +287,13 @@ func (input *BeegoInput) Params() map[string]string {
|
|||||||
|
|
||||||
// SetParam will set the param with key and value
|
// SetParam will set the param with key and value
|
||||||
func (input *BeegoInput) SetParam(key, val string) {
|
func (input *BeegoInput) SetParam(key, val string) {
|
||||||
|
// check if already exists
|
||||||
|
for i, v := range input.pnames {
|
||||||
|
if v == key && i <= len(input.pvalues) {
|
||||||
|
input.pvalues[i] = val
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
input.pvalues = append(input.pvalues, val)
|
input.pvalues = append(input.pvalues, val)
|
||||||
input.pnames = append(input.pnames, key)
|
input.pnames = append(input.pnames, key)
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -117,3 +118,56 @@ func TestSubDomain(t *testing.T) {
|
|||||||
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
t.Fatal("Subdomain parse error, got " + beegoInput.SubDomains())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParams(t *testing.T) {
|
||||||
|
inp := NewInput()
|
||||||
|
|
||||||
|
inp.SetParam("p1", "val1_ver1")
|
||||||
|
inp.SetParam("p2", "val2_ver1")
|
||||||
|
inp.SetParam("p3", "val3_ver1")
|
||||||
|
if l := inp.ParamsLen(); l != 3 {
|
||||||
|
t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := inp.Param("p1"); val != "val1_ver1" {
|
||||||
|
t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver1")
|
||||||
|
}
|
||||||
|
if val := inp.Param("p3"); val != "val3_ver1" {
|
||||||
|
t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val3_ver1")
|
||||||
|
}
|
||||||
|
vals := inp.Params()
|
||||||
|
expected := map[string]string{
|
||||||
|
"p1": "val1_ver1",
|
||||||
|
"p2": "val2_ver1",
|
||||||
|
"p3": "val3_ver1",
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(vals, expected) {
|
||||||
|
t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
// overwriting existing params
|
||||||
|
inp.SetParam("p1", "val1_ver2")
|
||||||
|
inp.SetParam("p2", "val2_ver2")
|
||||||
|
expected = map[string]string{
|
||||||
|
"p1": "val1_ver2",
|
||||||
|
"p2": "val2_ver2",
|
||||||
|
"p3": "val3_ver1",
|
||||||
|
}
|
||||||
|
vals = inp.Params()
|
||||||
|
if !reflect.DeepEqual(vals, expected) {
|
||||||
|
t.Fatalf("Input.Params wrong value: %s, expected %s", vals, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
if l := inp.ParamsLen(); l != 3 {
|
||||||
|
t.Fatalf("Input.ParamsLen wrong value: %d, expected %d", l, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := inp.Param("p1"); val != "val1_ver2" {
|
||||||
|
t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
|
||||||
|
}
|
||||||
|
|
||||||
|
if val := inp.Param("p2"); val != "val2_ver2" {
|
||||||
|
t.Fatalf("Input.Param wrong value: %s, expected %s", val, "val1_ver2")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
1
error.go
1
error.go
@ -424,6 +424,7 @@ func exception(errCode string, ctx *context.Context) {
|
|||||||
|
|
||||||
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
func executeError(err *errorInfo, ctx *context.Context, code int) {
|
||||||
if err.errorType == errorTypeHandler {
|
if err.errorType == errorTypeHandler {
|
||||||
|
ctx.ResponseWriter.WriteHeader(code)
|
||||||
err.handler(ctx.ResponseWriter, ctx.Request)
|
err.handler(ctx.ResponseWriter, ctx.Request)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -90,16 +90,15 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
|
|||||||
addr = ":https"
|
addr = ":https"
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &tls.Config{}
|
if srv.TLSConfig == nil {
|
||||||
if srv.TLSConfig != nil {
|
srv.TLSConfig = &tls.Config{}
|
||||||
*config = *srv.TLSConfig
|
|
||||||
}
|
}
|
||||||
if config.NextProtos == nil {
|
if srv.TLSConfig.NextProtos == nil {
|
||||||
config.NextProtos = []string{"http/1.1"}
|
srv.TLSConfig.NextProtos = []string{"http/1.1"}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.Certificates = make([]tls.Certificate, 1)
|
srv.TLSConfig.Certificates = make([]tls.Certificate, 1)
|
||||||
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -113,7 +112,7 @@ func (srv *Server) ListenAndServeTLS(certFile, keyFile string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
srv.tlsInnerListener = newGraceListener(l, srv)
|
srv.tlsInnerListener = newGraceListener(l, srv)
|
||||||
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config)
|
srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig)
|
||||||
|
|
||||||
if srv.isChild {
|
if srv.isChild {
|
||||||
process, err := os.FindProcess(os.Getppid())
|
process, err := os.FindProcess(os.Getppid())
|
||||||
|
10
hooks.go
10
hooks.go
@ -68,13 +68,11 @@ func registerSession() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func registerTemplate() error {
|
func registerTemplate() error {
|
||||||
if BConfig.WebConfig.AutoRender {
|
if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil {
|
||||||
if err := BuildTemplate(BConfig.WebConfig.ViewsPath); err != nil {
|
if BConfig.RunMode == DEV {
|
||||||
if BConfig.RunMode == DEV {
|
Warn(err)
|
||||||
Warn(err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// connWriter implements LoggerInterface.
|
// connWriter implements LoggerInterface.
|
||||||
@ -48,7 +49,7 @@ func (c *connWriter) Init(jsonconfig string) error {
|
|||||||
|
|
||||||
// WriteMsg write message in connection.
|
// WriteMsg write message in connection.
|
||||||
// if connection is down, try to re-connect.
|
// 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 {
|
if level > c.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -62,6 +63,9 @@ func (c *connWriter) WriteMsg(msg string, level int) error {
|
|||||||
if c.ReconnectOnMsg {
|
if c.ReconnectOnMsg {
|
||||||
defer c.innerWriter.Close()
|
defer c.innerWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg = formatLogTime(when) + msg
|
||||||
|
|
||||||
c.lg.Println(msg)
|
c.lg.Println(msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -94,7 +98,7 @@ func (c *connWriter) connect() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.innerWriter = conn
|
c.innerWriter = conn
|
||||||
c.lg = log.New(conn, "", log.Ldate|log.Ltime)
|
c.lg = log.New(conn, "", 0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// brush is a color join function
|
// brush is a color join function
|
||||||
@ -34,46 +35,49 @@ func newBrush(color string) brush {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var colors = []brush{
|
var colors = []brush{
|
||||||
newBrush("1;37"), // Emergency white
|
newBrush("1;37"), // Emergency white
|
||||||
newBrush("1;36"), // Alert cyan
|
newBrush("1;36"), // Alert cyan
|
||||||
newBrush("1;35"), // Critical magenta
|
newBrush("1;35"), // Critical magenta
|
||||||
newBrush("1;31"), // Error red
|
newBrush("1;31"), // Error red
|
||||||
newBrush("1;33"), // Warning yellow
|
newBrush("1;33"), // Warning yellow
|
||||||
newBrush("1;32"), // Notice green
|
newBrush("1;32"), // Notice green
|
||||||
newBrush("1;34"), // Informational blue
|
newBrush("1;34"), // Informational blue
|
||||||
newBrush("1;34"), // Debug blue
|
newBrush("1;34"), // Debug blue
|
||||||
}
|
}
|
||||||
|
|
||||||
// consoleWriter implements LoggerInterface and writes messages to terminal.
|
// consoleWriter implements LoggerInterface and writes messages to terminal.
|
||||||
type consoleWriter struct {
|
type consoleWriter struct {
|
||||||
lg *log.Logger
|
lg *log.Logger
|
||||||
Level int `json:"level"`
|
Level int `json:"level"`
|
||||||
|
Color bool `json:"color"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConsole create ConsoleWriter returning as LoggerInterface.
|
// NewConsole create ConsoleWriter returning as LoggerInterface.
|
||||||
func NewConsole() Logger {
|
func NewConsole() Logger {
|
||||||
cw := &consoleWriter{
|
cw := &consoleWriter{
|
||||||
lg: log.New(os.Stdout, "", log.Ldate|log.Ltime),
|
lg: log.New(os.Stdout, "", 0),
|
||||||
Level: LevelDebug,
|
Level: LevelDebug,
|
||||||
|
Color: true,
|
||||||
}
|
}
|
||||||
return cw
|
return cw
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init init console logger.
|
// Init init console logger.
|
||||||
// jsonconfig like '{"level":LevelTrace}'.
|
// jsonConfig like '{"level":LevelTrace}'.
|
||||||
func (c *consoleWriter) Init(jsonconfig string) error {
|
func (c *consoleWriter) Init(jsonConfig string) error {
|
||||||
if len(jsonconfig) == 0 {
|
if len(jsonConfig) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return json.Unmarshal([]byte(jsonconfig), c)
|
return json.Unmarshal([]byte(jsonConfig), c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg write message in console.
|
// 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 {
|
if level > c.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if goos := runtime.GOOS; goos == "windows" {
|
msg = formatLogTime(when) + msg
|
||||||
|
if runtime.GOOS == "windows" || !c.Color {
|
||||||
c.lg.Println(msg)
|
c.lg.Println(msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -42,3 +42,10 @@ func TestConsole(t *testing.T) {
|
|||||||
log2.SetLogger("console", `{"level":3}`)
|
log2.SetLogger("console", `{"level":3}`)
|
||||||
testConsoleCalls(log2)
|
testConsoleCalls(log2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test console without color
|
||||||
|
func TestConsoleNoColor(t *testing.T) {
|
||||||
|
log := NewLogger(100)
|
||||||
|
log.SetLogger("console", `{"color":false}`)
|
||||||
|
testConsoleCalls(log)
|
||||||
|
}
|
||||||
|
@ -48,16 +48,16 @@ func (el *esLogger) Init(jsonconfig string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg will write the msg and level into es
|
// 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 {
|
if level > el.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
t := time.Now()
|
|
||||||
vals := make(map[string]interface{})
|
vals := make(map[string]interface{})
|
||||||
vals["@timestamp"] = t.Format(time.RFC3339)
|
vals["@timestamp"] = when.Format(time.RFC3339)
|
||||||
vals["@msg"] = msg
|
vals["@msg"] = msg
|
||||||
d := goes.Document{
|
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",
|
Type: "logs",
|
||||||
Fields: vals,
|
Fields: vals,
|
||||||
}
|
}
|
||||||
|
51
logs/file.go
51
logs/file.go
@ -114,59 +114,20 @@ func (w *fileLogWriter) needRotate(size int, day int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteMsg write logger message into file.
|
// 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 {
|
if level > w.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
//2016/01/12 21:34:33
|
msg = formatLogTime(when) + msg + "\n"
|
||||||
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"
|
|
||||||
|
|
||||||
if w.Rotate {
|
if w.Rotate {
|
||||||
|
d := when.Day()
|
||||||
if w.needRotate(len(msg), d) {
|
if w.needRotate(len(msg), d) {
|
||||||
w.Lock()
|
w.Lock()
|
||||||
if w.needRotate(len(msg), d) {
|
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)
|
fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
w.Unlock()
|
w.Unlock()
|
||||||
}
|
}
|
||||||
@ -236,7 +197,7 @@ func (w *fileLogWriter) lines() (int, error) {
|
|||||||
|
|
||||||
// DoRotate means it need to write file in new file.
|
// DoRotate means it need to write file in new file.
|
||||||
// new file name like xx.2013-01-01.2.log
|
// 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)
|
_, err := os.Lstat(w.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -251,7 +212,7 @@ func (w *fileLogWriter) doRotate() error {
|
|||||||
suffix = ".log"
|
suffix = ".log"
|
||||||
}
|
}
|
||||||
for ; err == nil && num <= 999; num++ {
|
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)
|
_, err = os.Lstat(fName)
|
||||||
}
|
}
|
||||||
// return error if the last file checked still existed
|
// return error if the last file checked still existed
|
||||||
|
59
logs/log.go
59
logs/log.go
@ -40,6 +40,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RFC5424 log message levels.
|
// RFC5424 log message levels.
|
||||||
@ -68,7 +69,7 @@ type loggerType func() Logger
|
|||||||
// Logger defines the behavior of a log provider.
|
// Logger defines the behavior of a log provider.
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
Init(config string) error
|
Init(config string) error
|
||||||
WriteMsg(msg string, level int) error
|
WriteMsg(when time.Time, msg string, level int) error
|
||||||
Destroy()
|
Destroy()
|
||||||
Flush()
|
Flush()
|
||||||
}
|
}
|
||||||
@ -108,6 +109,7 @@ type nameLogger struct {
|
|||||||
type logMsg struct {
|
type logMsg struct {
|
||||||
level int
|
level int
|
||||||
msg string
|
msg string
|
||||||
|
when time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
var logMsgPool *sync.Pool
|
var logMsgPool *sync.Pool
|
||||||
@ -173,9 +175,9 @@ func (bl *BeeLogger) DelLogger(adapterName string) error {
|
|||||||
return nil
|
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 {
|
for _, l := range bl.outputs {
|
||||||
err := l.WriteMsg(msg, level)
|
err := l.WriteMsg(when, msg, level)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
|
fmt.Fprintf(os.Stderr, "unable to WriteMsg to adapter:%v,error:%v\n", l.name, err)
|
||||||
}
|
}
|
||||||
@ -183,6 +185,7 @@ func (bl *BeeLogger) writeToLoggers(msg string, level int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bl *BeeLogger) writeMsg(logLevel int, msg string) error {
|
func (bl *BeeLogger) writeMsg(logLevel int, msg string) error {
|
||||||
|
when := time.Now()
|
||||||
if bl.enableFuncCallDepth {
|
if bl.enableFuncCallDepth {
|
||||||
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
_, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -196,9 +199,10 @@ func (bl *BeeLogger) writeMsg(logLevel int, msg string) error {
|
|||||||
lm := logMsgPool.Get().(*logMsg)
|
lm := logMsgPool.Get().(*logMsg)
|
||||||
lm.level = logLevel
|
lm.level = logLevel
|
||||||
lm.msg = msg
|
lm.msg = msg
|
||||||
|
lm.when = when
|
||||||
bl.msgChan <- lm
|
bl.msgChan <- lm
|
||||||
} else {
|
} else {
|
||||||
bl.writeToLoggers(msg, logLevel)
|
bl.writeToLoggers(when, msg, logLevel)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -231,7 +235,7 @@ func (bl *BeeLogger) startLogger() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case bm := <-bl.msgChan:
|
case bm := <-bl.msgChan:
|
||||||
bl.writeToLoggers(bm.msg, bm.level)
|
bl.writeToLoggers(bm.when, bm.msg, bm.level)
|
||||||
logMsgPool.Put(bm)
|
logMsgPool.Put(bm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -351,7 +355,7 @@ func (bl *BeeLogger) Close() {
|
|||||||
for {
|
for {
|
||||||
if len(bl.msgChan) > 0 {
|
if len(bl.msgChan) > 0 {
|
||||||
bm := <-bl.msgChan
|
bm := <-bl.msgChan
|
||||||
bl.writeToLoggers(bm.msg, bm.level)
|
bl.writeToLoggers(bm.when, bm.msg, bm.level)
|
||||||
logMsgPool.Put(bm)
|
logMsgPool.Put(bm)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -361,4 +365,47 @@ func (bl *BeeLogger) Close() {
|
|||||||
l.Flush()
|
l.Flush()
|
||||||
l.Destroy()
|
l.Destroy()
|
||||||
}
|
}
|
||||||
|
bl.outputs = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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:])
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ func (s *SMTPWriter) sendMail(hostAddressWithPort string, auth smtp.Auth, fromAd
|
|||||||
|
|
||||||
// WriteMsg write message in smtp writer.
|
// WriteMsg write message in smtp writer.
|
||||||
// it will send an email with subject and only this message.
|
// 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 {
|
if level > s.Level {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -140,7 +140,7 @@ func (s *SMTPWriter) WriteMsg(msg string, level int) error {
|
|||||||
// and send the email all in one step.
|
// and send the email all in one step.
|
||||||
contentType := "Content-Type: text/plain" + "; charset=UTF-8"
|
contentType := "Content-Type: text/plain" + "; charset=UTF-8"
|
||||||
mailmsg := []byte("To: " + strings.Join(s.RecipientAddresses, ";") + "\r\nFrom: " + s.FromAddress + "<" + s.FromAddress +
|
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)
|
return s.sendMail(s.Host, auth, s.FromAddress, s.RecipientAddresses, mailmsg)
|
||||||
}
|
}
|
||||||
|
10
orm/types.go
10
orm/types.go
@ -148,6 +148,10 @@ type QuerySeter interface {
|
|||||||
// add OFFSET value
|
// add OFFSET value
|
||||||
// same as Limit function's args[0]
|
// same as Limit function's args[0]
|
||||||
Offset(offset interface{}) QuerySeter
|
Offset(offset interface{}) QuerySeter
|
||||||
|
// add GROUP BY expression
|
||||||
|
// for example:
|
||||||
|
// qs.GroupBy("id")
|
||||||
|
GroupBy(exprs ...string) QuerySeter
|
||||||
// add ORDER expression.
|
// add ORDER expression.
|
||||||
// "column" means ASC, "-column" means DESC.
|
// "column" means ASC, "-column" means DESC.
|
||||||
// for example:
|
// for example:
|
||||||
@ -162,6 +166,12 @@ type QuerySeter interface {
|
|||||||
// qs.RelatedSel("profile").One(&user)
|
// qs.RelatedSel("profile").One(&user)
|
||||||
// user.Profile.Age = 32
|
// user.Profile.Age = 32
|
||||||
RelatedSel(params ...interface{}) QuerySeter
|
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
|
// return QuerySeter execution result number
|
||||||
// for example:
|
// for example:
|
||||||
// num, err = qs.Filter("profile__age__gt", 28).Count()
|
// num, err = qs.Filter("profile__age__gt", 28).Count()
|
||||||
|
@ -130,7 +130,7 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genRouterCode() {
|
func genRouterCode() {
|
||||||
os.Mkdir("routers", 0755)
|
os.Mkdir(path.Join(AppPath, "routers"), 0755)
|
||||||
Info("generate router from comments")
|
Info("generate router from comments")
|
||||||
var (
|
var (
|
||||||
globalinfo string
|
globalinfo string
|
||||||
@ -172,7 +172,7 @@ func genRouterCode() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if globalinfo != "" {
|
if globalinfo != "" {
|
||||||
f, err := os.Create(path.Join("routers", commentFilename))
|
f, err := os.Create(path.Join(AppPath, "routers", commentFilename))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -182,7 +182,7 @@ func genRouterCode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func compareFile(pkgRealpath string) bool {
|
func compareFile(pkgRealpath string) bool {
|
||||||
if !utils.FileExists(path.Join("routers", commentFilename)) {
|
if !utils.FileExists(path.Join(AppPath, "routers", commentFilename)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if utils.FileExists(lastupdateFilename) {
|
if utils.FileExists(lastupdateFilename) {
|
||||||
|
@ -62,12 +62,12 @@ var (
|
|||||||
}
|
}
|
||||||
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
// these beego.Controller's methods shouldn't reflect to AutoRouter
|
||||||
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
exceptMethod = []string{"Init", "Prepare", "Finish", "Render", "RenderString",
|
||||||
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJson", "ServeJsonp",
|
"RenderBytes", "Redirect", "Abort", "StopRun", "UrlFor", "ServeJSON", "ServeJSONP",
|
||||||
"ServeXml", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
|
"ServeXML", "Input", "ParseForm", "GetString", "GetStrings", "GetInt", "GetBool",
|
||||||
"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
|
"GetFloat", "GetFile", "SaveToFile", "StartSession", "SetSession", "GetSession",
|
||||||
"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
|
"DelSession", "SessionRegenerateID", "DestroySession", "IsAjax", "GetSecureCookie",
|
||||||
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
"SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml",
|
||||||
"GetControllerAndAction"}
|
"GetControllerAndAction", "ServeFormatted"}
|
||||||
|
|
||||||
urlPlaceholder = "{{placeholder}}"
|
urlPlaceholder = "{{placeholder}}"
|
||||||
// DefaultAccessLogFilter will skip the accesslog if return true
|
// DefaultAccessLogFilter will skip the accesslog if return true
|
||||||
|
@ -8,9 +8,11 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const licenseFile = "./LICENSE"
|
var currentWorkDir, _ = os.Getwd()
|
||||||
|
var licenseFile = filepath.Join(currentWorkDir, "LICENSE")
|
||||||
|
|
||||||
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
func testOpenFile(encoding string, content []byte, t *testing.T) {
|
||||||
fi, _ := os.Stat(licenseFile)
|
fi, _ := os.Stat(licenseFile)
|
||||||
|
10
template.go
10
template.go
@ -62,7 +62,7 @@ func init() {
|
|||||||
beegoTplFuncMap["lt"] = lt // <
|
beegoTplFuncMap["lt"] = lt // <
|
||||||
beegoTplFuncMap["ne"] = ne // !=
|
beegoTplFuncMap["ne"] = ne // !=
|
||||||
|
|
||||||
beegoTplFuncMap["urlfor"] = URLFor // !=
|
beegoTplFuncMap["urlfor"] = URLFor // build a URL to match a Controller and it's method
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFuncMap let user to register a func in the template.
|
// AddFuncMap let user to register a func in the template.
|
||||||
@ -272,7 +272,9 @@ func SetStaticPath(url string, path string) *App {
|
|||||||
if !strings.HasPrefix(url, "/") {
|
if !strings.HasPrefix(url, "/") {
|
||||||
url = "/" + url
|
url = "/" + url
|
||||||
}
|
}
|
||||||
url = strings.TrimRight(url, "/")
|
if url != "/" {
|
||||||
|
url = strings.TrimRight(url, "/")
|
||||||
|
}
|
||||||
BConfig.WebConfig.StaticDir[url] = path
|
BConfig.WebConfig.StaticDir[url] = path
|
||||||
return BeeApp
|
return BeeApp
|
||||||
}
|
}
|
||||||
@ -282,7 +284,9 @@ func DelStaticPath(url string) *App {
|
|||||||
if !strings.HasPrefix(url, "/") {
|
if !strings.HasPrefix(url, "/") {
|
||||||
url = "/" + url
|
url = "/" + url
|
||||||
}
|
}
|
||||||
url = strings.TrimRight(url, "/")
|
if url != "/" {
|
||||||
|
url = strings.TrimRight(url, "/")
|
||||||
|
}
|
||||||
delete(BConfig.WebConfig.StaticDir, url)
|
delete(BConfig.WebConfig.StaticDir, url)
|
||||||
return BeeApp
|
return BeeApp
|
||||||
}
|
}
|
||||||
|
34
tree.go
34
tree.go
@ -141,7 +141,7 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st
|
|||||||
regexpStr = "([^.]+).(.+)"
|
regexpStr = "([^.]+).(.+)"
|
||||||
params = params[1:]
|
params = params[1:]
|
||||||
} else {
|
} else {
|
||||||
for range params {
|
for _ = range params {
|
||||||
regexpStr = "([^/]+)/" + regexpStr
|
regexpStr = "([^/]+)/" + regexpStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -254,7 +254,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string,
|
|||||||
regexpStr = "/([^.]+).(.+)"
|
regexpStr = "/([^.]+).(.+)"
|
||||||
params = params[1:]
|
params = params[1:]
|
||||||
} else {
|
} else {
|
||||||
for range params {
|
for _ = range params {
|
||||||
regexpStr = "/([^/]+)" + regexpStr
|
regexpStr = "/([^/]+)" + regexpStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,7 +420,11 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b
|
|||||||
if len(strs) == 2 {
|
if len(strs) == 2 {
|
||||||
ctx.Input.SetParam(":ext", strs[1])
|
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
|
return true
|
||||||
}
|
}
|
||||||
// match :id
|
// match :id
|
||||||
@ -438,7 +442,9 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b
|
|||||||
}
|
}
|
||||||
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
|
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
|
||||||
for i, match := range matches[1:] {
|
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
|
return true
|
||||||
}
|
}
|
||||||
@ -448,17 +454,11 @@ func (leaf *leafInfo) match(wildcardValues []string, ctx *context.Context) (ok b
|
|||||||
// "/admin/" -> ["admin"]
|
// "/admin/" -> ["admin"]
|
||||||
// "/admin/users" -> ["admin", "users"]
|
// "/admin/users" -> ["admin", "users"]
|
||||||
func splitPath(key string) []string {
|
func splitPath(key string) []string {
|
||||||
|
key = strings.Trim(key, "/ ")
|
||||||
if key == "" {
|
if key == "" {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
elements := strings.Split(key, "/")
|
return strings.Split(key, "/")
|
||||||
if elements[0] == "" {
|
|
||||||
elements = elements[1:]
|
|
||||||
}
|
|
||||||
if elements[len(elements)-1] == "" {
|
|
||||||
elements = elements[:len(elements)-1]
|
|
||||||
}
|
|
||||||
return elements
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// "admin" -> false, nil, ""
|
// "admin" -> false, nil, ""
|
||||||
@ -542,13 +542,19 @@ func splitSegment(key string) (bool, []string, string) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if v == ':' {
|
// Escape Sequence '\'
|
||||||
|
if i > 0 && key[i-1] == '\\' {
|
||||||
|
out = append(out, v)
|
||||||
|
} else if v == ':' {
|
||||||
param = make([]rune, 0)
|
param = make([]rune, 0)
|
||||||
start = true
|
start = true
|
||||||
} else if v == '(' {
|
} else if v == '(' {
|
||||||
startexp = true
|
startexp = true
|
||||||
start = false
|
start = false
|
||||||
params = append(params, ":"+string(param))
|
if len(param) > 0 {
|
||||||
|
params = append(params, ":"+string(param))
|
||||||
|
param = make([]rune, 0)
|
||||||
|
}
|
||||||
paramsNum++
|
paramsNum++
|
||||||
expt = make([]rune, 0)
|
expt = make([]rune, 0)
|
||||||
expt = append(expt, '(')
|
expt = append(expt, '(')
|
||||||
|
71
tree_test.go
71
tree_test.go
@ -15,6 +15,7 @@
|
|||||||
package beego
|
package beego
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
"github.com/astaxie/beego/context"
|
||||||
@ -57,6 +58,9 @@ func init() {
|
|||||||
"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
|
"/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
|
||||||
map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}})
|
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: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{"/: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/: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"}})
|
routers = append(routers, testinfo{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}})
|
||||||
@ -245,48 +249,31 @@ func TestSplitPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSplitSegment(t *testing.T) {
|
func TestSplitSegment(t *testing.T) {
|
||||||
b, w, r := splitSegment("admin")
|
|
||||||
if b || len(w) != 0 || r != "" {
|
items := map[string]struct {
|
||||||
t.Fatal("admin should return false, nil, ''")
|
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 != "" {
|
for pattern, v := range items {
|
||||||
t.Fatal("* should return true, [:splat], ''")
|
b, w, r := splitSegment(pattern)
|
||||||
}
|
if b != v.isReg || r != v.regStr || strings.Join(w, ",") != strings.Join(v.params, ",") {
|
||||||
b, w, r = splitSegment("*.*")
|
t.Fatalf("%s should return %t,%s,%q, got %t,%s,%q", pattern, v.isReg, v.params, v.regStr, b, w, r)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,13 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
maxLineLength = 76
|
maxLineLength = 76
|
||||||
|
|
||||||
|
upperhex = "0123456789ABCDEF"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Email is the type used for email messages
|
// Email is the type used for email messages
|
||||||
@ -74,9 +77,6 @@ func NewEMail(config string) *Email {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if e.From == "" {
|
|
||||||
e.From = e.Username
|
|
||||||
}
|
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,14 +228,21 @@ func (e *Email) Send() error {
|
|||||||
to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
|
to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
|
||||||
to = append(append(append(to, e.To...), e.Cc...), 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
|
// Check to make sure there is at least one recipient and one "From" address
|
||||||
if e.From == "" || len(to) == 0 {
|
if len(to) == 0 {
|
||||||
return errors.New("Must specify at least one From address and one To address")
|
return errors.New("Must specify at least one To address")
|
||||||
}
|
}
|
||||||
from, err := mail.ParseAddress(e.From)
|
|
||||||
|
from, err := mail.ParseAddress(e.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.From = from.String()
|
|
||||||
|
if len(e.From) == 0 {
|
||||||
|
e.From = e.Username
|
||||||
|
}
|
||||||
|
// use mail's RFC 2047 to encode any string
|
||||||
|
e.Subject = qEncode("utf-8", e.Subject)
|
||||||
|
|
||||||
raw, err := e.Bytes()
|
raw, err := e.Bytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -342,3 +349,73 @@ func base64Wrap(w io.Writer, b []byte) {
|
|||||||
w.Write(out)
|
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)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user