commit
						bf5c5626ab
					
				| @ -1,9 +1,9 @@ | |||||||
| language: go | language: go | ||||||
| 
 | 
 | ||||||
| go: | go: | ||||||
|   - 1.6.4 |  | ||||||
|   - 1.7.5 |   - 1.7.5 | ||||||
|   - 1.8.1 |   - 1.8.5 | ||||||
|  |   - 1.9.2 | ||||||
| services: | services: | ||||||
|   - redis-server |   - redis-server | ||||||
|   - mysql |   - mysql | ||||||
| @ -11,7 +11,6 @@ services: | |||||||
|   - memcached |   - memcached | ||||||
| env: | env: | ||||||
|   - ORM_DRIVER=sqlite3   ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db |   - ORM_DRIVER=sqlite3   ORM_SOURCE=$TRAVIS_BUILD_DIR/orm_test.db | ||||||
|   - ORM_DRIVER=mysql    ORM_SOURCE="root:@/orm_test?charset=utf8" |  | ||||||
|   - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable" |   - ORM_DRIVER=postgres ORM_SOURCE="user=postgres dbname=orm_test sslmode=disable" | ||||||
| before_install: | before_install: | ||||||
|  - git clone git://github.com/ideawu/ssdb.git |  - git clone git://github.com/ideawu/ssdb.git | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| # Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/astaxie/beego) [](http://golangfoundation.org) | # Beego [](https://travis-ci.org/astaxie/beego) [](http://godoc.org/github.com/astaxie/beego) [](http://golangfoundation.org) [](https://goreportcard.com/report/github.com/astaxie/beego) | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| beego is used for rapid development of RESTful APIs, web apps and backend services in Go. | beego is used for rapid development of RESTful APIs, web apps and backend services in Go. | ||||||
| It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. | It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. | ||||||
|  | |||||||
| @ -67,6 +67,7 @@ func oldMap() map[string]interface{} { | |||||||
| 	m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain | 	m["BConfig.WebConfig.Session.SessionDomain"] = BConfig.WebConfig.Session.SessionDomain | ||||||
| 	m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly | 	m["BConfig.WebConfig.Session.SessionDisableHTTPOnly"] = BConfig.WebConfig.Session.SessionDisableHTTPOnly | ||||||
| 	m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs | 	m["BConfig.Log.AccessLogs"] = BConfig.Log.AccessLogs | ||||||
|  | 	m["BConfig.Log.AccessLogsFormat"] = BConfig.Log.AccessLogsFormat | ||||||
| 	m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum | 	m["BConfig.Log.FileLineNum"] = BConfig.Log.FileLineNum | ||||||
| 	m["BConfig.Log.Outputs"] = BConfig.Log.Outputs | 	m["BConfig.Log.Outputs"] = BConfig.Log.Outputs | ||||||
| 	return m | 	return m | ||||||
|  | |||||||
							
								
								
									
										119
									
								
								app.go
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								app.go
									
									
									
									
									
								
							| @ -15,13 +15,17 @@ | |||||||
| package beego | package beego | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"crypto/x509" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/fcgi" | 	"net/http/fcgi" | ||||||
| 	"os" | 	"os" | ||||||
| 	"path" | 	"path" | ||||||
| 	"time" | 	"time" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/astaxie/beego/grace" | 	"github.com/astaxie/beego/grace" | ||||||
| 	"github.com/astaxie/beego/logs" | 	"github.com/astaxie/beego/logs" | ||||||
| @ -51,8 +55,11 @@ func NewApp() *App { | |||||||
| 	return app | 	return app | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // MiddleWare function for http.Handler | ||||||
|  | type MiddleWare func(http.Handler) http.Handler | ||||||
|  | 
 | ||||||
| // Run beego application. | // Run beego application. | ||||||
| func (app *App) Run() { | func (app *App) Run(mws ...MiddleWare) { | ||||||
| 	addr := BConfig.Listen.HTTPAddr | 	addr := BConfig.Listen.HTTPAddr | ||||||
| 
 | 
 | ||||||
| 	if BConfig.Listen.HTTPPort != 0 { | 	if BConfig.Listen.HTTPPort != 0 { | ||||||
| @ -94,6 +101,12 @@ func (app *App) Run() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	app.Server.Handler = app.Handlers | 	app.Server.Handler = app.Handlers | ||||||
|  | 	for i:=len(mws)-1;i>=0;i-- { | ||||||
|  | 		if mws[i] == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		app.Server.Handler = mws[i](app.Server.Handler) | ||||||
|  | 	} | ||||||
| 	app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | 	app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | ||||||
| 	app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | 	app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second | ||||||
| 	app.Server.ErrorLog = logs.GetLogger("HTTP") | 	app.Server.ErrorLog = logs.GetLogger("HTTP") | ||||||
| @ -102,7 +115,7 @@ func (app *App) Run() { | |||||||
| 	if BConfig.Listen.Graceful { | 	if BConfig.Listen.Graceful { | ||||||
| 		httpsAddr := BConfig.Listen.HTTPSAddr | 		httpsAddr := BConfig.Listen.HTTPSAddr | ||||||
| 		app.Server.Addr = httpsAddr | 		app.Server.Addr = httpsAddr | ||||||
| 		if BConfig.Listen.EnableHTTPS { | 		if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { | ||||||
| 			go func() { | 			go func() { | ||||||
| 				time.Sleep(20 * time.Microsecond) | 				time.Sleep(20 * time.Microsecond) | ||||||
| 				if BConfig.Listen.HTTPSPort != 0 { | 				if BConfig.Listen.HTTPSPort != 0 { | ||||||
| @ -112,11 +125,20 @@ func (app *App) Run() { | |||||||
| 				server := grace.NewServer(httpsAddr, app.Handlers) | 				server := grace.NewServer(httpsAddr, app.Handlers) | ||||||
| 				server.Server.ReadTimeout = app.Server.ReadTimeout | 				server.Server.ReadTimeout = app.Server.ReadTimeout | ||||||
| 				server.Server.WriteTimeout = app.Server.WriteTimeout | 				server.Server.WriteTimeout = app.Server.WriteTimeout | ||||||
|  | 				if BConfig.Listen.EnableMutualHTTPS { | ||||||
|  | 
 | ||||||
|  | 					if err := server.ListenAndServeMutualTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile, BConfig.Listen.TrustCaFile); err != nil { | ||||||
|  | 						logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) | ||||||
|  | 						time.Sleep(100 * time.Microsecond) | ||||||
|  | 						endRunning <- true | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
| 					if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | 					if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | ||||||
| 						logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) | 						logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) | ||||||
| 						time.Sleep(100 * time.Microsecond) | 						time.Sleep(100 * time.Microsecond) | ||||||
| 						endRunning <- true | 						endRunning <- true | ||||||
| 					} | 					} | ||||||
|  | 				} | ||||||
| 			}() | 			}() | ||||||
| 		} | 		} | ||||||
| 		if BConfig.Listen.EnableHTTP { | 		if BConfig.Listen.EnableHTTP { | ||||||
| @ -139,7 +161,7 @@ func (app *App) Run() { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// run normal mode | 	// run normal mode | ||||||
| 	if BConfig.Listen.EnableHTTPS { | 	if BConfig.Listen.EnableHTTPS || BConfig.Listen.EnableMutualHTTPS { | ||||||
| 		go func() { | 		go func() { | ||||||
| 			time.Sleep(20 * time.Microsecond) | 			time.Sleep(20 * time.Microsecond) | ||||||
| 			if BConfig.Listen.HTTPSPort != 0 { | 			if BConfig.Listen.HTTPSPort != 0 { | ||||||
| @ -149,6 +171,19 @@ func (app *App) Run() { | |||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
| 			logs.Info("https server Running on https://%s", app.Server.Addr) | 			logs.Info("https server Running on https://%s", app.Server.Addr) | ||||||
|  | 			if BConfig.Listen.EnableMutualHTTPS { | ||||||
|  | 				pool := x509.NewCertPool() | ||||||
|  | 				data, err := ioutil.ReadFile(BConfig.Listen.TrustCaFile) | ||||||
|  | 				if err != nil { | ||||||
|  | 					BeeLogger.Info("MutualHTTPS should provide TrustCaFile") | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				pool.AppendCertsFromPEM(data) | ||||||
|  | 				app.Server.TLSConfig = &tls.Config{ | ||||||
|  | 					ClientCAs:  pool, | ||||||
|  | 					ClientAuth: tls.RequireAndVerifyClientCert, | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | 			if err := app.Server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil { | ||||||
| 				logs.Critical("ListenAndServeTLS: ", err) | 				logs.Critical("ListenAndServeTLS: ", err) | ||||||
| 				time.Sleep(100 * time.Microsecond) | 				time.Sleep(100 * time.Microsecond) | ||||||
| @ -207,6 +242,84 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *A | |||||||
| 	return BeeApp | 	return BeeApp | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // UnregisterFixedRoute unregisters the route with the specified fixedRoute. It is particularly useful | ||||||
|  | // in web applications that inherit most routes from a base webapp via the underscore | ||||||
|  | // import, and aim to overwrite only certain paths. | ||||||
|  | // The method parameter can be empty or "*" for all HTTP methods, or a particular | ||||||
|  | // method type (e.g. "GET" or "POST") for selective removal. | ||||||
|  | // | ||||||
|  | // Usage (replace "GET" with "*" for all methods): | ||||||
|  | //  beego.UnregisterFixedRoute("/yourpreviouspath", "GET") | ||||||
|  | //  beego.Router("/yourpreviouspath", yourControllerAddress, "get:GetNewPage") | ||||||
|  | func UnregisterFixedRoute(fixedRoute string, method string) *App { | ||||||
|  | 	subPaths := splitPath(fixedRoute) | ||||||
|  | 	if method == "" || method == "*" { | ||||||
|  | 		for m := range HTTPMETHOD { | ||||||
|  | 			if _, ok := BeeApp.Handlers.routers[m]; !ok { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			if BeeApp.Handlers.routers[m].prefix == strings.Trim(fixedRoute, "/ ") { | ||||||
|  | 				findAndRemoveSingleTree(BeeApp.Handlers.routers[m]) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			findAndRemoveTree(subPaths, BeeApp.Handlers.routers[m], m) | ||||||
|  | 		} | ||||||
|  | 		return BeeApp | ||||||
|  | 	} | ||||||
|  | 	// Single HTTP method | ||||||
|  | 	um := strings.ToUpper(method) | ||||||
|  | 	if _, ok := BeeApp.Handlers.routers[um]; ok { | ||||||
|  | 		if BeeApp.Handlers.routers[um].prefix == strings.Trim(fixedRoute, "/ ") { | ||||||
|  | 			findAndRemoveSingleTree(BeeApp.Handlers.routers[um]) | ||||||
|  | 			return BeeApp | ||||||
|  | 		} | ||||||
|  | 		findAndRemoveTree(subPaths, BeeApp.Handlers.routers[um], um) | ||||||
|  | 	} | ||||||
|  | 	return BeeApp | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func findAndRemoveTree(paths []string, entryPointTree *Tree, method string) { | ||||||
|  | 	for i := range entryPointTree.fixrouters { | ||||||
|  | 		if entryPointTree.fixrouters[i].prefix == paths[0] { | ||||||
|  | 			if len(paths) == 1 { | ||||||
|  | 				if len(entryPointTree.fixrouters[i].fixrouters) > 0 { | ||||||
|  | 					// If the route had children subtrees, remove just the functional leaf, | ||||||
|  | 					// to allow children to function as before | ||||||
|  | 					if len(entryPointTree.fixrouters[i].leaves) > 0 { | ||||||
|  | 						entryPointTree.fixrouters[i].leaves[0] = nil | ||||||
|  | 						entryPointTree.fixrouters[i].leaves = entryPointTree.fixrouters[i].leaves[1:] | ||||||
|  | 					} | ||||||
|  | 				} else { | ||||||
|  | 					// Remove the *Tree from the fixrouters slice | ||||||
|  | 					entryPointTree.fixrouters[i] = nil | ||||||
|  | 
 | ||||||
|  | 					if i == len(entryPointTree.fixrouters)-1 { | ||||||
|  | 						entryPointTree.fixrouters = entryPointTree.fixrouters[:i] | ||||||
|  | 					} else { | ||||||
|  | 						entryPointTree.fixrouters = append(entryPointTree.fixrouters[:i], entryPointTree.fixrouters[i+1:len(entryPointTree.fixrouters)]...) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			findAndRemoveTree(paths[1:], entryPointTree.fixrouters[i], method) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func findAndRemoveSingleTree(entryPointTree *Tree) { | ||||||
|  | 	if entryPointTree == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if len(entryPointTree.fixrouters) > 0 { | ||||||
|  | 		// If the route had children subtrees, remove just the functional leaf, | ||||||
|  | 		// to allow children to function as before | ||||||
|  | 		if len(entryPointTree.leaves) > 0 { | ||||||
|  | 			entryPointTree.leaves[0] = nil | ||||||
|  | 			entryPointTree.leaves = entryPointTree.leaves[1:] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Include will generate router file in the router/xxx.go from the controller's comments | // Include will generate router file in the router/xxx.go from the controller's comments | ||||||
| // usage: | // usage: | ||||||
| // beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) | // beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								beego.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								beego.go
									
									
									
									
									
								
							| @ -23,7 +23,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
| 	// VERSION represent beego web framework version. | 	// VERSION represent beego web framework version. | ||||||
| 	VERSION = "1.9.0" | 	VERSION = "1.9.2" | ||||||
| 
 | 
 | ||||||
| 	// DEV is for develop | 	// DEV is for develop | ||||||
| 	DEV = "dev" | 	DEV = "dev" | ||||||
| @ -67,6 +67,21 @@ func Run(params ...string) { | |||||||
| 	BeeApp.Run() | 	BeeApp.Run() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // RunWithMiddleWares Run beego application with middlewares. | ||||||
|  | func RunWithMiddleWares(addr string, mws ...MiddleWare) { | ||||||
|  | 	initBeforeHTTPRun() | ||||||
|  | 
 | ||||||
|  | 	strs := strings.Split(addr, ":") | ||||||
|  | 	if len(strs) > 0 && strs[0] != "" { | ||||||
|  | 		BConfig.Listen.HTTPAddr = strs[0] | ||||||
|  | 	} | ||||||
|  | 	if len(strs) > 1 && strs[1] != "" { | ||||||
|  | 		BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1]) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	BeeApp.Run(mws...) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func initBeforeHTTPRun() { | func initBeforeHTTPRun() { | ||||||
| 	//init hooks | 	//init hooks | ||||||
| 	AddAPPStartHook( | 	AddAPPStartHook( | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								cache/cache.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								cache/cache.go
									
									
									
									
										vendored
									
									
								
							| @ -12,7 +12,7 @@ | |||||||
| // See the License for the specific language governing permissions and | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| // Package cache provide a Cache interface and some implemetn engine | // Package cache provide a Cache interface and some implement engine | ||||||
| // Usage: | // Usage: | ||||||
| // | // | ||||||
| // import( | // import( | ||||||
|  | |||||||
							
								
								
									
										69
									
								
								cache/redis/redis.go
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										69
									
								
								cache/redis/redis.go
									
									
									
									
										vendored
									
									
								
							| @ -32,6 +32,7 @@ package redis | |||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| @ -59,14 +60,23 @@ func NewRedisCache() cache.Cache { | |||||||
| 	return &Cache{key: DefaultKey} | 	return &Cache{key: DefaultKey} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // actually do the redis cmds | // actually do the redis cmds, args[0] must be the key name. | ||||||
| func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) { | func (rc *Cache) do(commandName string, args ...interface{}) (reply interface{}, err error) { | ||||||
|  | 	if len(args) < 1 { | ||||||
|  | 		return nil, errors.New("missing required arguments") | ||||||
|  | 	} | ||||||
|  | 	args[0] = rc.associate(args[0]) | ||||||
| 	c := rc.p.Get() | 	c := rc.p.Get() | ||||||
| 	defer c.Close() | 	defer c.Close() | ||||||
| 
 | 
 | ||||||
| 	return c.Do(commandName, args...) | 	return c.Do(commandName, args...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // associate with config key. | ||||||
|  | func (rc *Cache) associate(originKey interface{}) string { | ||||||
|  | 	return fmt.Sprintf("%s:%s", rc.key, originKey) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Get cache from redis. | // Get cache from redis. | ||||||
| func (rc *Cache) Get(key string) interface{} { | func (rc *Cache) Get(key string) interface{} { | ||||||
| 	if v, err := rc.do("GET", key); err == nil { | 	if v, err := rc.do("GET", key); err == nil { | ||||||
| @ -77,57 +87,28 @@ func (rc *Cache) Get(key string) interface{} { | |||||||
| 
 | 
 | ||||||
| // GetMulti get cache from redis. | // GetMulti get cache from redis. | ||||||
| func (rc *Cache) GetMulti(keys []string) []interface{} { | func (rc *Cache) GetMulti(keys []string) []interface{} { | ||||||
| 	size := len(keys) |  | ||||||
| 	var rv []interface{} |  | ||||||
| 	c := rc.p.Get() | 	c := rc.p.Get() | ||||||
| 	defer c.Close() | 	defer c.Close() | ||||||
| 	var err error | 	var args []interface{} | ||||||
| 	for _, key := range keys { | 	for _, key := range keys { | ||||||
| 		err = c.Send("GET", key) | 		args = append(args, rc.associate(key)) | ||||||
|  | 	} | ||||||
|  | 	values, err := redis.Values(c.Do("MGET", args...)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 			goto ERROR | 		return nil | ||||||
| 	} | 	} | ||||||
| 	} | 	return values | ||||||
| 	if err = c.Flush(); err != nil { |  | ||||||
| 		goto ERROR |  | ||||||
| 	} |  | ||||||
| 	for i := 0; i < size; i++ { |  | ||||||
| 		if v, err := c.Receive(); err == nil { |  | ||||||
| 			rv = append(rv, v.([]byte)) |  | ||||||
| 		} else { |  | ||||||
| 			rv = append(rv, err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return rv |  | ||||||
| ERROR: |  | ||||||
| 	rv = rv[0:0] |  | ||||||
| 	for i := 0; i < size; i++ { |  | ||||||
| 		rv = append(rv, nil) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return rv |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Put put cache to redis. | // Put put cache to redis. | ||||||
| func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { | func (rc *Cache) Put(key string, val interface{}, timeout time.Duration) error { | ||||||
| 	var err error | 	_, err := rc.do("SETEX", key, int64(timeout/time.Second), val) | ||||||
| 	if _, err = rc.do("SETEX", key, int64(timeout/time.Second), val); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if _, err = rc.do("HSET", rc.key, key, true); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Delete delete cache in redis. | // Delete delete cache in redis. | ||||||
| func (rc *Cache) Delete(key string) error { | func (rc *Cache) Delete(key string) error { | ||||||
| 	var err error | 	_, err := rc.do("DEL", key) | ||||||
| 	if _, err = rc.do("DEL", key); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	_, err = rc.do("HDEL", rc.key, key) |  | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -137,11 +118,6 @@ func (rc *Cache) IsExist(key string) bool { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return false | 		return false | ||||||
| 	} | 	} | ||||||
| 	if !v { |  | ||||||
| 		if _, err = rc.do("HDEL", rc.key, key); err != nil { |  | ||||||
| 			return false |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return v | 	return v | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -159,16 +135,17 @@ func (rc *Cache) Decr(key string) error { | |||||||
| 
 | 
 | ||||||
| // ClearAll clean all cache in redis. delete this redis collection. | // ClearAll clean all cache in redis. delete this redis collection. | ||||||
| func (rc *Cache) ClearAll() error { | func (rc *Cache) ClearAll() error { | ||||||
| 	cachedKeys, err := redis.Strings(rc.do("HKEYS", rc.key)) | 	c := rc.p.Get() | ||||||
|  | 	defer c.Close() | ||||||
|  | 	cachedKeys, err := redis.Strings(c.Do("KEYS", rc.key+":*")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	for _, str := range cachedKeys { | 	for _, str := range cachedKeys { | ||||||
| 		if _, err = rc.do("DEL", str); err != nil { | 		if _, err = c.Do("DEL", str); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	_, err = rc.do("DEL", rc.key) |  | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								config.go
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								config.go
									
									
									
									
									
								
							| @ -56,10 +56,12 @@ type Listen struct { | |||||||
| 	HTTPAddr          string | 	HTTPAddr          string | ||||||
| 	HTTPPort          int | 	HTTPPort          int | ||||||
| 	EnableHTTPS       bool | 	EnableHTTPS       bool | ||||||
|  | 	EnableMutualHTTPS bool | ||||||
| 	HTTPSAddr         string | 	HTTPSAddr         string | ||||||
| 	HTTPSPort         int | 	HTTPSPort         int | ||||||
| 	HTTPSCertFile     string | 	HTTPSCertFile     string | ||||||
| 	HTTPSKeyFile      string | 	HTTPSKeyFile      string | ||||||
|  | 	TrustCaFile       string | ||||||
| 	EnableAdmin       bool | 	EnableAdmin       bool | ||||||
| 	AdminAddr         string | 	AdminAddr         string | ||||||
| 	AdminPort         int | 	AdminPort         int | ||||||
| @ -104,6 +106,7 @@ type SessionConfig struct { | |||||||
| // LogConfig holds Log related config | // LogConfig holds Log related config | ||||||
| type LogConfig struct { | type LogConfig struct { | ||||||
| 	AccessLogs       bool | 	AccessLogs       bool | ||||||
|  | 	AccessLogsFormat string //access log format: JSON_FORMAT, APACHE_FORMAT or empty string | ||||||
| 	FileLineNum      bool | 	FileLineNum      bool | ||||||
| 	Outputs          map[string]string // Store Adaptor : config | 	Outputs          map[string]string // Store Adaptor : config | ||||||
| } | } | ||||||
| @ -134,9 +137,13 @@ func init() { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		panic(err) | 		panic(err) | ||||||
| 	} | 	} | ||||||
| 	appConfigPath = filepath.Join(workPath, "conf", "app.conf") | 	var filename = "app.conf" | ||||||
|  | 	if os.Getenv("BEEGO_MODE") != "" { | ||||||
|  | 		filename = os.Getenv("BEEGO_MODE") + ".app.conf" | ||||||
|  | 	} | ||||||
|  | 	appConfigPath = filepath.Join(workPath, "conf", filename) | ||||||
| 	if !utils.FileExists(appConfigPath) { | 	if !utils.FileExists(appConfigPath) { | ||||||
| 		appConfigPath = filepath.Join(AppPath, "conf", "app.conf") | 		appConfigPath = filepath.Join(AppPath, "conf", filename) | ||||||
| 		if !utils.FileExists(appConfigPath) { | 		if !utils.FileExists(appConfigPath) { | ||||||
| 			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} | 			AppConfig = &beegoAppConfig{innerConfig: config.NewFakeConfig()} | ||||||
| 			return | 			return | ||||||
| @ -240,6 +247,7 @@ func newBConfig() *Config { | |||||||
| 		}, | 		}, | ||||||
| 		Log: LogConfig{ | 		Log: LogConfig{ | ||||||
| 			AccessLogs:       false, | 			AccessLogs:       false, | ||||||
|  | 			AccessLogsFormat: "APACHE_FORMAT", | ||||||
| 			FileLineNum:      true, | 			FileLineNum:      true, | ||||||
| 			Outputs:          map[string]string{"console": ""}, | 			Outputs:          map[string]string{"console": ""}, | ||||||
| 		}, | 		}, | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import ( | |||||||
| 	"errors" | 	"errors" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" | 	"io/ioutil" | ||||||
|  | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| @ -115,9 +116,8 @@ func (input *BeegoInput) Domain() string { | |||||||
| // if no host info in request, return localhost. | // if no host info in request, return localhost. | ||||||
| func (input *BeegoInput) Host() string { | func (input *BeegoInput) Host() string { | ||||||
| 	if input.Context.Request.Host != "" { | 	if input.Context.Request.Host != "" { | ||||||
| 		hostParts := strings.Split(input.Context.Request.Host, ":") | 		if hostPart, _, err := net.SplitHostPort(input.Context.Request.Host); err == nil { | ||||||
| 		if len(hostParts) > 0 { | 			return hostPart | ||||||
| 			return hostParts[0] |  | ||||||
| 		} | 		} | ||||||
| 		return input.Context.Request.Host | 		return input.Context.Request.Host | ||||||
| 	} | 	} | ||||||
| @ -206,20 +206,20 @@ func (input *BeegoInput) AcceptsJSON() bool { | |||||||
| 
 | 
 | ||||||
| // IP returns request client ip. | // IP returns request client ip. | ||||||
| // if in proxy, return first proxy id. | // if in proxy, return first proxy id. | ||||||
| // if error, return 127.0.0.1. | // if error, return RemoteAddr. | ||||||
| func (input *BeegoInput) IP() string { | func (input *BeegoInput) IP() string { | ||||||
| 	ips := input.Proxy() | 	ips := input.Proxy() | ||||||
| 	if len(ips) > 0 && ips[0] != "" { | 	if len(ips) > 0 && ips[0] != "" { | ||||||
| 		rip := strings.Split(ips[0], ":") | 		rip, _, err := net.SplitHostPort(ips[0]) | ||||||
| 		return rip[0] | 		if err != nil { | ||||||
|  | 			rip = ips[0] | ||||||
| 		} | 		} | ||||||
| 	ip := strings.Split(input.Context.Request.RemoteAddr, ":") | 		return rip | ||||||
| 	if len(ip) > 0 { |  | ||||||
| 		if ip[0] != "[" { |  | ||||||
| 			return ip[0] |  | ||||||
| 	} | 	} | ||||||
|  | 	if ip, _, err := net.SplitHostPort(input.Context.Request.RemoteAddr); err == nil { | ||||||
|  | 		return ip | ||||||
| 	} | 	} | ||||||
| 	return "127.0.0.1" | 	return input.Context.Request.RemoteAddr | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Proxy returns proxy client ips slice. | // Proxy returns proxy client ips slice. | ||||||
| @ -253,9 +253,8 @@ func (input *BeegoInput) SubDomains() string { | |||||||
| // Port returns request client port. | // Port returns request client port. | ||||||
| // when error or empty, return 80. | // when error or empty, return 80. | ||||||
| func (input *BeegoInput) Port() int { | func (input *BeegoInput) Port() int { | ||||||
| 	parts := strings.Split(input.Context.Request.Host, ":") | 	if _, portPart, err := net.SplitHostPort(input.Context.Request.Host); err == nil { | ||||||
| 	if len(parts) == 2 { | 		port, _ := strconv.Atoi(portPart) | ||||||
| 		port, _ := strconv.Atoi(parts[1]) |  | ||||||
| 		return port | 		return port | ||||||
| 	} | 	} | ||||||
| 	return 80 | 	return 80 | ||||||
|  | |||||||
| @ -2,7 +2,9 @@ package grace | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"crypto/tls" | 	"crypto/tls" | ||||||
|  | 	"crypto/x509" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
| 	"log" | 	"log" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| @ -65,7 +67,7 @@ func (srv *Server) ListenAndServe() (err error) { | |||||||
| 			log.Println(err) | 			log.Println(err) | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| 		err = process.Kill() | 		err = process.Signal(syscall.SIGTERM) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @ -114,6 +116,62 @@ 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, srv.TLSConfig) | 	srv.GraceListener = tls.NewListener(srv.tlsInnerListener, srv.TLSConfig) | ||||||
| 
 | 
 | ||||||
|  | 	if srv.isChild { | ||||||
|  | 		process, err := os.FindProcess(os.Getppid()) | ||||||
|  | 		if err != nil { | ||||||
|  | 			log.Println(err) | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		err = process.Signal(syscall.SIGTERM) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	log.Println(os.Getpid(), srv.Addr) | ||||||
|  | 	return srv.Serve() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ListenAndServeMutualTLS listens on the TCP network address srv.Addr and then calls | ||||||
|  | // Serve to handle requests on incoming mutual TLS connections. | ||||||
|  | func (srv *Server) ListenAndServeMutualTLS(certFile, keyFile, trustFile string) (err error) { | ||||||
|  | 	addr := srv.Addr | ||||||
|  | 	if addr == "" { | ||||||
|  | 		addr = ":https" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if srv.TLSConfig == nil { | ||||||
|  | 		srv.TLSConfig = &tls.Config{} | ||||||
|  | 	} | ||||||
|  | 	if srv.TLSConfig.NextProtos == nil { | ||||||
|  | 		srv.TLSConfig.NextProtos = []string{"http/1.1"} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	srv.TLSConfig.Certificates = make([]tls.Certificate, 1) | ||||||
|  | 	srv.TLSConfig.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	srv.TLSConfig.ClientAuth = tls.RequireAndVerifyClientCert | ||||||
|  | 	pool := x509.NewCertPool() | ||||||
|  | 	data, err := ioutil.ReadFile(trustFile) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Println(err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	pool.AppendCertsFromPEM(data) | ||||||
|  | 	srv.TLSConfig.ClientCAs = pool | ||||||
|  | 	log.Println("Mutual HTTPS") | ||||||
|  | 	go srv.handleSignals() | ||||||
|  | 
 | ||||||
|  | 	l, err := srv.getListener(addr) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Println(err) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	srv.tlsInnerListener = newGraceListener(l, srv) | ||||||
|  | 	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()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
|  | |||||||
| @ -317,7 +317,19 @@ func (b *BeegoHTTPRequest) Body(data interface{}) *BeegoHTTPRequest { | |||||||
| 	} | 	} | ||||||
| 	return b | 	return b | ||||||
| } | } | ||||||
| 
 | // XMLBody adds request raw body encoding by XML. | ||||||
|  | func (b *BeegoHTTPRequest) XMLBody(obj interface{}) (*BeegoHTTPRequest, error) { | ||||||
|  | 	if b.req.Body == nil && obj != nil { | ||||||
|  | 		byts, err := xml.Marshal(obj) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return b, err | ||||||
|  | 		} | ||||||
|  | 		b.req.Body = ioutil.NopCloser(bytes.NewReader(byts)) | ||||||
|  | 		b.req.ContentLength = int64(len(byts)) | ||||||
|  | 		b.req.Header.Set("Content-Type", "application/xml") | ||||||
|  | 	} | ||||||
|  | 	return b, nil | ||||||
|  | } | ||||||
| // JSONBody adds request raw body encoding by JSON. | // JSONBody adds request raw body encoding by JSON. | ||||||
| func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) { | func (b *BeegoHTTPRequest) JSONBody(obj interface{}) (*BeegoHTTPRequest, error) { | ||||||
| 	if b.req.Body == nil && obj != nil { | 	if b.req.Body == nil && obj != nil { | ||||||
|  | |||||||
							
								
								
									
										86
									
								
								logs/accesslog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								logs/accesslog.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,86 @@ | |||||||
|  | // Copyright 2014 beego Author. All Rights Reserved. | ||||||
|  | // | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | // you may not use this file except in compliance with the License. | ||||||
|  | // You may obtain a copy of the License at | ||||||
|  | // | ||||||
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | // | ||||||
|  | // Unless required by applicable law or agreed to in writing, software | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | // See the License for the specific language governing permissions and | ||||||
|  | // limitations under the License. | ||||||
|  | 
 | ||||||
|  | package logs | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"time" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	apacheFormatPattern = "%s - - [%s] \"%s %d %d\" %f %s %s\n" | ||||||
|  | 	apacheFormat        = "APACHE_FORMAT" | ||||||
|  | 	jsonFormat          = "JSON_FORMAT" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // AccessLogRecord struct for holding access log data. | ||||||
|  | type AccessLogRecord struct { | ||||||
|  | 	RemoteAddr     string        `json:"remote_addr"` | ||||||
|  | 	RequestTime    time.Time     `json:"request_time"` | ||||||
|  | 	RequestMethod  string        `json:"request_method"` | ||||||
|  | 	Request        string        `json:"request"` | ||||||
|  | 	ServerProtocol string        `json:"server_protocol"` | ||||||
|  | 	Host           string        `json:"host"` | ||||||
|  | 	Status         int           `json:"status"` | ||||||
|  | 	BodyBytesSent  int64         `json:"body_bytes_sent"` | ||||||
|  | 	ElapsedTime    time.Duration `json:"elapsed_time"` | ||||||
|  | 	HTTPReferrer   string        `json:"http_referrer"` | ||||||
|  | 	HTTPUserAgent  string        `json:"http_user_agent"` | ||||||
|  | 	RemoteUser     string        `json:"remote_user"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (r *AccessLogRecord) json() ([]byte, error) { | ||||||
|  | 	buffer := &bytes.Buffer{} | ||||||
|  | 	encoder := json.NewEncoder(buffer) | ||||||
|  | 	disableEscapeHTML(encoder) | ||||||
|  | 
 | ||||||
|  | 	err := encoder.Encode(r) | ||||||
|  | 	return buffer.Bytes(), err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func disableEscapeHTML(i interface{}) { | ||||||
|  | 	e, ok := i.(interface { | ||||||
|  | 		SetEscapeHTML(bool) | ||||||
|  | 	}); | ||||||
|  | 	if ok { | ||||||
|  | 		e.SetEscapeHTML(false) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // AccessLog - Format and print access log. | ||||||
|  | func AccessLog(r *AccessLogRecord, format string) { | ||||||
|  | 	var msg string | ||||||
|  | 
 | ||||||
|  | 	switch format { | ||||||
|  | 
 | ||||||
|  | 	case apacheFormat: | ||||||
|  | 		timeFormatted := r.RequestTime.Format("02/Jan/2006 03:04:05") | ||||||
|  | 		msg = fmt.Sprintf(apacheFormatPattern, r.RemoteAddr, timeFormatted, r.Request, r.Status, r.BodyBytesSent, | ||||||
|  | 			r.ElapsedTime.Seconds(), r.HTTPReferrer, r.HTTPUserAgent) | ||||||
|  | 	case jsonFormat: | ||||||
|  | 		fallthrough | ||||||
|  | 	default: | ||||||
|  | 		jsonData, err := r.json() | ||||||
|  | 		if err != nil { | ||||||
|  | 			msg = fmt.Sprintf(`{"Error": "%s"}`, err) | ||||||
|  | 		} else { | ||||||
|  | 			msg = string(jsonData) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	beeLogger.Debug(msg) | ||||||
|  | } | ||||||
| @ -182,7 +182,7 @@ func (w *fileLogWriter) initFd() error { | |||||||
| 	if w.Daily { | 	if w.Daily { | ||||||
| 		go w.dailyRotate(w.dailyOpenTime) | 		go w.dailyRotate(w.dailyOpenTime) | ||||||
| 	} | 	} | ||||||
| 	if fInfo.Size() > 0 { | 	if fInfo.Size() > 0 && w.MaxLines > 0 { | ||||||
| 		count, err := w.lines() | 		count, err := w.lines() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return err | 			return err | ||||||
|  | |||||||
| @ -87,13 +87,15 @@ const ( | |||||||
| 	mi2 = `012345678901234567890123456789012345678901234567890123456789` | 	mi2 = `012345678901234567890123456789012345678901234567890123456789` | ||||||
| 	s1  = `000000000011111111112222222222333333333344444444445555555555` | 	s1  = `000000000011111111112222222222333333333344444444445555555555` | ||||||
| 	s2  = `012345678901234567890123456789012345678901234567890123456789` | 	s2  = `012345678901234567890123456789012345678901234567890123456789` | ||||||
|  | 	ns1 = `0123456789` | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func formatTimeHeader(when time.Time) ([]byte, int) { | func formatTimeHeader(when time.Time) ([]byte, int) { | ||||||
| 	y, mo, d := when.Date() | 	y, mo, d := when.Date() | ||||||
| 	h, mi, s := when.Clock() | 	h, mi, s := when.Clock() | ||||||
| 	//len("2006/01/02 15:04:05 ")==20 | 	ns := when.Nanosecond()/1000000 | ||||||
| 	var buf [20]byte | 	//len("2006/01/02 15:04:05.123 ")==24 | ||||||
|  | 	var buf [24]byte | ||||||
| 
 | 
 | ||||||
| 	buf[0] = y1[y/1000%10] | 	buf[0] = y1[y/1000%10] | ||||||
| 	buf[1] = y2[y/100] | 	buf[1] = y2[y/100] | ||||||
| @ -114,7 +116,12 @@ func formatTimeHeader(when time.Time) ([]byte, int) { | |||||||
| 	buf[16] = ':' | 	buf[16] = ':' | ||||||
| 	buf[17] = s1[s] | 	buf[17] = s1[s] | ||||||
| 	buf[18] = s2[s] | 	buf[18] = s2[s] | ||||||
| 	buf[19] = ' ' | 	buf[19] = '.' | ||||||
|  | 	buf[20] = ns1[ns/100] | ||||||
|  | 	buf[21] = ns1[ns%100/10] | ||||||
|  | 	buf[22] = ns1[ns%10] | ||||||
|  | 
 | ||||||
|  | 	buf[23] = ' ' | ||||||
| 
 | 
 | ||||||
| 	return buf[0:], d | 	return buf[0:], d | ||||||
| } | } | ||||||
|  | |||||||
| @ -31,7 +31,7 @@ func TestFormatHeader_0(t *testing.T) { | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		h, _ := formatTimeHeader(tm) | 		h, _ := formatTimeHeader(tm) | ||||||
| 		if tm.Format("2006/01/02 15:04:05 ") != string(h) { | 		if tm.Format("2006/01/02 15:04:05.999 ") != string(h) { | ||||||
| 			t.Log(tm) | 			t.Log(tm) | ||||||
| 			t.FailNow() | 			t.FailNow() | ||||||
| 		} | 		} | ||||||
| @ -49,7 +49,7 @@ func TestFormatHeader_1(t *testing.T) { | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		h, _ := formatTimeHeader(tm) | 		h, _ := formatTimeHeader(tm) | ||||||
| 		if tm.Format("2006/01/02 15:04:05 ") != string(h) { | 		if tm.Format("2006/01/02 15:04:05.999 ") != string(h) { | ||||||
| 			t.Log(tm) | 			t.Log(tm) | ||||||
| 			t.FailNow() | 			t.FailNow() | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -172,7 +172,7 @@ func Register(name string, m Migrationer) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Upgrade upgrate the migration from lasttime | // Upgrade upgrade the migration from lasttime | ||||||
| func Upgrade(lasttime int64) error { | func Upgrade(lasttime int64) error { | ||||||
| 	sm := sortMap(migrationMap) | 	sm := sortMap(migrationMap) | ||||||
| 	i := 0 | 	i := 0 | ||||||
|  | |||||||
| @ -61,6 +61,9 @@ func init() { | |||||||
| 
 | 
 | ||||||
| 	// set default database | 	// set default database | ||||||
| 	orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30) | 	orm.RegisterDataBase("default", "mysql", "root:root@/my_db?charset=utf8", 30) | ||||||
|  | 	 | ||||||
|  | 	// create table | ||||||
|  | 	orm.RunSyncdb("default", false, true)	 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
|  | |||||||
| @ -51,12 +51,14 @@ checkColumn: | |||||||
| 	switch fieldType { | 	switch fieldType { | ||||||
| 	case TypeBooleanField: | 	case TypeBooleanField: | ||||||
| 		col = T["bool"] | 		col = T["bool"] | ||||||
| 	case TypeCharField: | 	case TypeVarCharField: | ||||||
| 		if al.Driver == DRPostgres && fi.toText { | 		if al.Driver == DRPostgres && fi.toText { | ||||||
| 			col = T["string-text"] | 			col = T["string-text"] | ||||||
| 		} else { | 		} else { | ||||||
| 			col = fmt.Sprintf(T["string"], fieldSize) | 			col = fmt.Sprintf(T["string"], fieldSize) | ||||||
| 		} | 		} | ||||||
|  | 	case TypeCharField: | ||||||
|  | 		col = fmt.Sprintf(T["string-char"], fieldSize) | ||||||
| 	case TypeTextField: | 	case TypeTextField: | ||||||
| 		col = T["string-text"] | 		col = T["string-text"] | ||||||
| 	case TypeTimeField: | 	case TypeTimeField: | ||||||
| @ -96,13 +98,13 @@ checkColumn: | |||||||
| 		} | 		} | ||||||
| 	case TypeJSONField: | 	case TypeJSONField: | ||||||
| 		if al.Driver != DRPostgres { | 		if al.Driver != DRPostgres { | ||||||
| 			fieldType = TypeCharField | 			fieldType = TypeVarCharField | ||||||
| 			goto checkColumn | 			goto checkColumn | ||||||
| 		} | 		} | ||||||
| 		col = T["json"] | 		col = T["json"] | ||||||
| 	case TypeJsonbField: | 	case TypeJsonbField: | ||||||
| 		if al.Driver != DRPostgres { | 		if al.Driver != DRPostgres { | ||||||
| 			fieldType = TypeCharField | 			fieldType = TypeVarCharField | ||||||
| 			goto checkColumn | 			goto checkColumn | ||||||
| 		} | 		} | ||||||
| 		col = T["jsonb"] | 		col = T["jsonb"] | ||||||
|  | |||||||
| @ -142,7 +142,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val | |||||||
| 				} else { | 				} else { | ||||||
| 					value = field.Bool() | 					value = field.Bool() | ||||||
| 				} | 				} | ||||||
| 			case TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField: | 			case TypeVarCharField, TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField: | ||||||
| 				if ns, ok := field.Interface().(sql.NullString); ok { | 				if ns, ok := field.Interface().(sql.NullString); ok { | ||||||
| 					value = nil | 					value = nil | ||||||
| 					if ns.Valid { | 					if ns.Valid { | ||||||
| @ -1240,7 +1240,7 @@ setValue: | |||||||
| 			} | 			} | ||||||
| 			value = b | 			value = b | ||||||
| 		} | 		} | ||||||
| 	case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: | 	case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: | ||||||
| 		if str == nil { | 		if str == nil { | ||||||
| 			value = ToStr(val) | 			value = ToStr(val) | ||||||
| 		} else { | 		} else { | ||||||
| @ -1386,7 +1386,7 @@ setValue: | |||||||
| 				field.SetBool(value.(bool)) | 				field.SetBool(value.(bool)) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	case fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: | 	case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField: | ||||||
| 		if isNative { | 		if isNative { | ||||||
| 			if ns, ok := field.Interface().(sql.NullString); ok { | 			if ns, ok := field.Interface().(sql.NullString); ok { | ||||||
| 				if value == nil { | 				if value == nil { | ||||||
|  | |||||||
| @ -119,7 +119,7 @@ type alias struct { | |||||||
| func detectTZ(al *alias) { | func detectTZ(al *alias) { | ||||||
| 	// orm timezone system match database | 	// orm timezone system match database | ||||||
| 	// default use Local | 	// default use Local | ||||||
| 	al.TZ = time.Local | 	al.TZ = DefaultTimeLoc | ||||||
| 
 | 
 | ||||||
| 	if al.DriverName == "sphinx" { | 	if al.DriverName == "sphinx" { | ||||||
| 		return | 		return | ||||||
| @ -136,7 +136,9 @@ func detectTZ(al *alias) { | |||||||
| 			} | 			} | ||||||
| 			t, err := time.Parse("-07:00:00", tz) | 			t, err := time.Parse("-07:00:00", tz) | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
|  | 				if t.Location().String() != "" { | ||||||
| 					al.TZ = t.Location() | 					al.TZ = t.Location() | ||||||
|  | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error()) | 				DebugLog.Printf("Detect DB timezone: %s %s\n", tz, err.Error()) | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -46,6 +46,7 @@ var mysqlTypes = map[string]string{ | |||||||
| 	"pk":              "NOT NULL PRIMARY KEY", | 	"pk":              "NOT NULL PRIMARY KEY", | ||||||
| 	"bool":            "bool", | 	"bool":            "bool", | ||||||
| 	"string":          "varchar(%d)", | 	"string":          "varchar(%d)", | ||||||
|  | 	"string-char":     "char(%d)", | ||||||
| 	"string-text":     "longtext", | 	"string-text":     "longtext", | ||||||
| 	"time.Time-date":  "date", | 	"time.Time-date":  "date", | ||||||
| 	"time.Time":       "datetime", | 	"time.Time":       "datetime", | ||||||
|  | |||||||
| @ -34,6 +34,7 @@ var oracleTypes = map[string]string{ | |||||||
| 	"pk":              "NOT NULL PRIMARY KEY", | 	"pk":              "NOT NULL PRIMARY KEY", | ||||||
| 	"bool":            "bool", | 	"bool":            "bool", | ||||||
| 	"string":          "VARCHAR2(%d)", | 	"string":          "VARCHAR2(%d)", | ||||||
|  | 	"string-char":     "CHAR(%d)", | ||||||
| 	"string-text":     "VARCHAR2(%d)", | 	"string-text":     "VARCHAR2(%d)", | ||||||
| 	"time.Time-date":  "DATE", | 	"time.Time-date":  "DATE", | ||||||
| 	"time.Time":       "TIMESTAMP", | 	"time.Time":       "TIMESTAMP", | ||||||
|  | |||||||
| @ -43,6 +43,7 @@ var postgresTypes = map[string]string{ | |||||||
| 	"pk":              "NOT NULL PRIMARY KEY", | 	"pk":              "NOT NULL PRIMARY KEY", | ||||||
| 	"bool":            "bool", | 	"bool":            "bool", | ||||||
| 	"string":          "varchar(%d)", | 	"string":          "varchar(%d)", | ||||||
|  | 	"string-char":     "char(%d)", | ||||||
| 	"string-text":     "text", | 	"string-text":     "text", | ||||||
| 	"time.Time-date":  "date", | 	"time.Time-date":  "date", | ||||||
| 	"time.Time":       "timestamp with time zone", | 	"time.Time":       "timestamp with time zone", | ||||||
|  | |||||||
| @ -43,6 +43,7 @@ var sqliteTypes = map[string]string{ | |||||||
| 	"pk":              "NOT NULL PRIMARY KEY", | 	"pk":              "NOT NULL PRIMARY KEY", | ||||||
| 	"bool":            "bool", | 	"bool":            "bool", | ||||||
| 	"string":          "varchar(%d)", | 	"string":          "varchar(%d)", | ||||||
|  | 	"string-char":     "character(%d)", | ||||||
| 	"string-text":     "text", | 	"string-text":     "text", | ||||||
| 	"time.Time-date":  "date", | 	"time.Time-date":  "date", | ||||||
| 	"time.Time":       "datetime", | 	"time.Time":       "datetime", | ||||||
|  | |||||||
| @ -52,7 +52,7 @@ func (mc *_modelCache) all() map[string]*modelInfo { | |||||||
| 	return m | 	return m | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // get orderd model info | // get ordered model info | ||||||
| func (mc *_modelCache) allOrdered() []*modelInfo { | func (mc *_modelCache) allOrdered() []*modelInfo { | ||||||
| 	m := make([]*modelInfo, 0, len(mc.orders)) | 	m := make([]*modelInfo, 0, len(mc.orders)) | ||||||
| 	for _, table := range mc.orders { | 	for _, table := range mc.orders { | ||||||
|  | |||||||
| @ -89,7 +89,7 @@ func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) { | |||||||
| 	modelCache.set(table, mi) | 	modelCache.set(table, mi) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // boostrap models | // bootstrap models | ||||||
| func bootStrap() { | func bootStrap() { | ||||||
| 	if modelCache.done { | 	if modelCache.done { | ||||||
| 		return | 		return | ||||||
| @ -332,7 +332,7 @@ func RegisterModelWithSuffix(suffix string, models ...interface{}) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // BootStrap bootrap models. | // BootStrap bootstrap models. | ||||||
| // make all model parsed and can not add more models | // make all model parsed and can not add more models | ||||||
| func BootStrap() { | func BootStrap() { | ||||||
| 	if modelCache.done { | 	if modelCache.done { | ||||||
|  | |||||||
| @ -23,6 +23,7 @@ import ( | |||||||
| // Define the Type enum | // Define the Type enum | ||||||
| const ( | const ( | ||||||
| 	TypeBooleanField = 1 << iota | 	TypeBooleanField = 1 << iota | ||||||
|  | 	TypeVarCharField | ||||||
| 	TypeCharField | 	TypeCharField | ||||||
| 	TypeTextField | 	TypeTextField | ||||||
| 	TypeTimeField | 	TypeTimeField | ||||||
| @ -49,9 +50,9 @@ const ( | |||||||
| 
 | 
 | ||||||
| // Define some logic enum | // Define some logic enum | ||||||
| const ( | const ( | ||||||
| 	IsIntegerField         = ^-TypePositiveBigIntegerField >> 5 << 6 | 	IsIntegerField         = ^-TypePositiveBigIntegerField >> 6 << 7 | ||||||
| 	IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 9 << 10 | 	IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 10 << 11 | ||||||
| 	IsRelField             = ^-RelReverseMany >> 17 << 18 | 	IsRelField             = ^-RelReverseMany >> 18 << 19 | ||||||
| 	IsFieldType            = ^-RelReverseMany<<1 + 1 | 	IsFieldType            = ^-RelReverseMany<<1 + 1 | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @ -126,7 +127,7 @@ func (e *CharField) String() string { | |||||||
| 
 | 
 | ||||||
| // FieldType return the enum type | // FieldType return the enum type | ||||||
| func (e *CharField) FieldType() int { | func (e *CharField) FieldType() int { | ||||||
| 	return TypeCharField | 	return TypeVarCharField | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetRaw set the interface to string | // SetRaw set the interface to string | ||||||
| @ -232,7 +233,7 @@ func (e *DateField) Set(d time.Time) { | |||||||
| 	*e = DateField(d) | 	*e = DateField(d) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // String convert datatime to string | // String convert datetime to string | ||||||
| func (e *DateField) String() string { | func (e *DateField) String() string { | ||||||
| 	return e.Value().String() | 	return e.Value().String() | ||||||
| } | } | ||||||
| @ -272,12 +273,12 @@ var _ Fielder = new(DateField) | |||||||
| // Takes the same extra arguments as DateField. | // Takes the same extra arguments as DateField. | ||||||
| type DateTimeField time.Time | type DateTimeField time.Time | ||||||
| 
 | 
 | ||||||
| // Value return the datatime value | // Value return the datetime value | ||||||
| func (e DateTimeField) Value() time.Time { | func (e DateTimeField) Value() time.Time { | ||||||
| 	return time.Time(e) | 	return time.Time(e) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Set set the time.Time to datatime | // Set set the time.Time to datetime | ||||||
| func (e *DateTimeField) Set(d time.Time) { | func (e *DateTimeField) Set(d time.Time) { | ||||||
| 	*e = DateTimeField(d) | 	*e = DateTimeField(d) | ||||||
| } | } | ||||||
| @ -309,12 +310,12 @@ func (e *DateTimeField) SetRaw(value interface{}) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // RawValue return the datatime value | // RawValue return the datetime value | ||||||
| func (e *DateTimeField) RawValue() interface{} { | func (e *DateTimeField) RawValue() interface{} { | ||||||
| 	return e.Value() | 	return e.Value() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // verify datatime implement fielder | // verify datetime implement fielder | ||||||
| var _ Fielder = new(DateTimeField) | var _ Fielder = new(DateTimeField) | ||||||
| 
 | 
 | ||||||
| // FloatField A floating-point number represented in go by a float32 value. | // FloatField A floating-point number represented in go by a float32 value. | ||||||
|  | |||||||
| @ -244,8 +244,10 @@ checkType: | |||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			goto end | 			goto end | ||||||
| 		} | 		} | ||||||
| 		if fieldType == TypeCharField { | 		if fieldType == TypeVarCharField { | ||||||
| 			switch tags["type"] { | 			switch tags["type"] { | ||||||
|  | 			case "char": | ||||||
|  | 				fieldType = TypeCharField | ||||||
| 			case "text": | 			case "text": | ||||||
| 				fieldType = TypeTextField | 				fieldType = TypeTextField | ||||||
| 			case "json": | 			case "json": | ||||||
| @ -357,7 +359,7 @@ checkType: | |||||||
| 
 | 
 | ||||||
| 	switch fieldType { | 	switch fieldType { | ||||||
| 	case TypeBooleanField: | 	case TypeBooleanField: | ||||||
| 	case TypeCharField, TypeJSONField, TypeJsonbField: | 	case TypeVarCharField, TypeCharField, TypeJSONField, TypeJsonbField: | ||||||
| 		if size != "" { | 		if size != "" { | ||||||
| 			v, e := StrTo(size).Int32() | 			v, e := StrTo(size).Int32() | ||||||
| 			if e != nil { | 			if e != nil { | ||||||
|  | |||||||
| @ -49,7 +49,7 @@ func (e *SliceStringField) String() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (e *SliceStringField) FieldType() int { | func (e *SliceStringField) FieldType() int { | ||||||
| 	return TypeCharField | 	return TypeVarCharField | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (e *SliceStringField) SetRaw(value interface{}) error { | func (e *SliceStringField) SetRaw(value interface{}) error { | ||||||
|  | |||||||
| @ -149,7 +149,7 @@ func getFieldType(val reflect.Value) (ft int, err error) { | |||||||
| 	case reflect.TypeOf(new(bool)): | 	case reflect.TypeOf(new(bool)): | ||||||
| 		ft = TypeBooleanField | 		ft = TypeBooleanField | ||||||
| 	case reflect.TypeOf(new(string)): | 	case reflect.TypeOf(new(string)): | ||||||
| 		ft = TypeCharField | 		ft = TypeVarCharField | ||||||
| 	case reflect.TypeOf(new(time.Time)): | 	case reflect.TypeOf(new(time.Time)): | ||||||
| 		ft = TypeDateTimeField | 		ft = TypeDateTimeField | ||||||
| 	default: | 	default: | ||||||
| @ -176,7 +176,7 @@ func getFieldType(val reflect.Value) (ft int, err error) { | |||||||
| 		case reflect.Bool: | 		case reflect.Bool: | ||||||
| 			ft = TypeBooleanField | 			ft = TypeBooleanField | ||||||
| 		case reflect.String: | 		case reflect.String: | ||||||
| 			ft = TypeCharField | 			ft = TypeVarCharField | ||||||
| 		default: | 		default: | ||||||
| 			if elm.Interface() == nil { | 			if elm.Interface() == nil { | ||||||
| 				panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val)) | 				panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val)) | ||||||
| @ -189,7 +189,7 @@ func getFieldType(val reflect.Value) (ft int, err error) { | |||||||
| 			case sql.NullBool: | 			case sql.NullBool: | ||||||
| 				ft = TypeBooleanField | 				ft = TypeBooleanField | ||||||
| 			case sql.NullString: | 			case sql.NullString: | ||||||
| 				ft = TypeCharField | 				ft = TypeVarCharField | ||||||
| 			case time.Time: | 			case time.Time: | ||||||
| 				ft = TypeDateTimeField | 				ft = TypeDateTimeField | ||||||
| 			} | 			} | ||||||
|  | |||||||
							
								
								
									
										123
									
								
								router.go
									
									
									
									
									
								
							
							
						
						
									
										123
									
								
								router.go
									
									
									
									
									
								
							| @ -50,23 +50,23 @@ const ( | |||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	// HTTPMETHOD list the supported http methods. | 	// HTTPMETHOD list the supported http methods. | ||||||
| 	HTTPMETHOD = map[string]string{ | 	HTTPMETHOD = map[string]bool{ | ||||||
| 		"GET":       "GET", | 		"GET":       true, | ||||||
| 		"POST":      "POST", | 		"POST":      true, | ||||||
| 		"PUT":       "PUT", | 		"PUT":       true, | ||||||
| 		"DELETE":    "DELETE", | 		"DELETE":    true, | ||||||
| 		"PATCH":     "PATCH", | 		"PATCH":     true, | ||||||
| 		"OPTIONS":   "OPTIONS", | 		"OPTIONS":   true, | ||||||
| 		"HEAD":      "HEAD", | 		"HEAD":      true, | ||||||
| 		"TRACE":     "TRACE", | 		"TRACE":     true, | ||||||
| 		"CONNECT":   "CONNECT", | 		"CONNECT":   true, | ||||||
| 		"MKCOL":     "MKCOL", | 		"MKCOL":     true, | ||||||
| 		"COPY":      "COPY", | 		"COPY":      true, | ||||||
| 		"MOVE":      "MOVE", | 		"MOVE":      true, | ||||||
| 		"PROPFIND":  "PROPFIND", | 		"PROPFIND":  true, | ||||||
| 		"PROPPATCH": "PROPPATCH", | 		"PROPPATCH": true, | ||||||
| 		"LOCK":      "LOCK", | 		"LOCK":      true, | ||||||
| 		"UNLOCK":    "UNLOCK", | 		"UNLOCK":    true, | ||||||
| 	} | 	} | ||||||
| 	// 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", | ||||||
| @ -117,6 +117,7 @@ type ControllerInfo struct { | |||||||
| 	handler        http.Handler | 	handler        http.Handler | ||||||
| 	runFunction    FilterFunc | 	runFunction    FilterFunc | ||||||
| 	routerType     int | 	routerType     int | ||||||
|  | 	initialize     func() ControllerInterface | ||||||
| 	methodParams   []*param.MethodParam | 	methodParams   []*param.MethodParam | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -169,7 +170,7 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt | |||||||
| 			} | 			} | ||||||
| 			comma := strings.Split(colon[0], ",") | 			comma := strings.Split(colon[0], ",") | ||||||
| 			for _, m := range comma { | 			for _, m := range comma { | ||||||
| 				if _, ok := HTTPMETHOD[strings.ToUpper(m)]; m == "*" || ok { | 				if m == "*" || HTTPMETHOD[strings.ToUpper(m)] { | ||||||
| 					if val := reflectVal.MethodByName(colon[1]); val.IsValid() { | 					if val := reflectVal.MethodByName(colon[1]); val.IsValid() { | ||||||
| 						methods[strings.ToUpper(m)] = colon[1] | 						methods[strings.ToUpper(m)] = colon[1] | ||||||
| 					} else { | 					} else { | ||||||
| @ -187,15 +188,36 @@ func (p *ControllerRegister) addWithMethodParams(pattern string, c ControllerInt | |||||||
| 	route.methods = methods | 	route.methods = methods | ||||||
| 	route.routerType = routerTypeBeego | 	route.routerType = routerTypeBeego | ||||||
| 	route.controllerType = t | 	route.controllerType = t | ||||||
|  | 	route.initialize = func() ControllerInterface { | ||||||
|  | 		vc := reflect.New(route.controllerType) | ||||||
|  | 		execController, ok := vc.Interface().(ControllerInterface) | ||||||
|  | 		if !ok { | ||||||
|  | 			panic("controller is not ControllerInterface") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		elemVal := reflect.ValueOf(c).Elem() | ||||||
|  | 		elemType := reflect.TypeOf(c).Elem() | ||||||
|  | 		execElem := reflect.ValueOf(execController).Elem() | ||||||
|  | 
 | ||||||
|  | 		numOfFields := elemVal.NumField() | ||||||
|  | 		for i := 0; i < numOfFields; i++ { | ||||||
|  | 			fieldVal := elemVal.Field(i) | ||||||
|  | 			fieldType := elemType.Field(i) | ||||||
|  | 			execElem.FieldByName(fieldType.Name).Set(fieldVal) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return execController | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	route.methodParams = methodParams | 	route.methodParams = methodParams | ||||||
| 	if len(methods) == 0 { | 	if len(methods) == 0 { | ||||||
| 		for _, m := range HTTPMETHOD { | 		for m := range HTTPMETHOD { | ||||||
| 			p.addToRouter(m, pattern, route) | 			p.addToRouter(m, pattern, route) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		for k := range methods { | 		for k := range methods { | ||||||
| 			if k == "*" { | 			if k == "*" { | ||||||
| 				for _, m := range HTTPMETHOD { | 				for m := range HTTPMETHOD { | ||||||
| 					p.addToRouter(m, pattern, route) | 					p.addToRouter(m, pattern, route) | ||||||
| 				} | 				} | ||||||
| 			} else { | 			} else { | ||||||
| @ -337,7 +359,7 @@ func (p *ControllerRegister) Any(pattern string, f FilterFunc) { | |||||||
| //    }) | //    }) | ||||||
| func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { | func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { | ||||||
| 	method = strings.ToUpper(method) | 	method = strings.ToUpper(method) | ||||||
| 	if _, ok := HTTPMETHOD[method]; method != "*" && !ok { | 	if method != "*" && !HTTPMETHOD[method] { | ||||||
| 		panic("not support http method: " + method) | 		panic("not support http method: " + method) | ||||||
| 	} | 	} | ||||||
| 	route := &ControllerInfo{} | 	route := &ControllerInfo{} | ||||||
| @ -346,7 +368,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { | |||||||
| 	route.runFunction = f | 	route.runFunction = f | ||||||
| 	methods := make(map[string]string) | 	methods := make(map[string]string) | ||||||
| 	if method == "*" { | 	if method == "*" { | ||||||
| 		for _, val := range HTTPMETHOD { | 		for val := range HTTPMETHOD { | ||||||
| 			methods[val] = val | 			methods[val] = val | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| @ -355,7 +377,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { | |||||||
| 	route.methods = methods | 	route.methods = methods | ||||||
| 	for k := range methods { | 	for k := range methods { | ||||||
| 		if k == "*" { | 		if k == "*" { | ||||||
| 			for _, m := range HTTPMETHOD { | 			for m := range HTTPMETHOD { | ||||||
| 				p.addToRouter(m, pattern, route) | 				p.addToRouter(m, pattern, route) | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @ -375,7 +397,7 @@ func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ... | |||||||
| 			pattern = path.Join(pattern, "?:all(.*)") | 			pattern = path.Join(pattern, "?:all(.*)") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	for _, m := range HTTPMETHOD { | 	for m := range HTTPMETHOD { | ||||||
| 		p.addToRouter(m, pattern, route) | 		p.addToRouter(m, pattern, route) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -410,7 +432,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) | |||||||
| 			patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name)) | 			patternFix := path.Join(prefix, strings.ToLower(controllerName), strings.ToLower(rt.Method(i).Name)) | ||||||
| 			patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name) | 			patternFixInit := path.Join(prefix, controllerName, rt.Method(i).Name) | ||||||
| 			route.pattern = pattern | 			route.pattern = pattern | ||||||
| 			for _, m := range HTTPMETHOD { | 			for m := range HTTPMETHOD { | ||||||
| 				p.addToRouter(m, pattern, route) | 				p.addToRouter(m, pattern, route) | ||||||
| 				p.addToRouter(m, patternInit, route) | 				p.addToRouter(m, patternInit, route) | ||||||
| 				p.addToRouter(m, patternFix, route) | 				p.addToRouter(m, patternFix, route) | ||||||
| @ -511,7 +533,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin | |||||||
| 			if c.routerType == routerTypeBeego && | 			if c.routerType == routerTypeBeego && | ||||||
| 				strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { | 				strings.HasSuffix(path.Join(c.controllerType.PkgPath(), c.controllerType.Name()), controllName) { | ||||||
| 				find := false | 				find := false | ||||||
| 				if _, ok := HTTPMETHOD[strings.ToUpper(methodName)]; ok { | 				if HTTPMETHOD[strings.ToUpper(methodName)] { | ||||||
| 					if len(c.methods) == 0 { | 					if len(c.methods) == 0 { | ||||||
| 						find = true | 						find = true | ||||||
| 					} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) { | 					} else if m, ok := c.methods[strings.ToUpper(methodName)]; ok && m == strings.ToUpper(methodName) { | ||||||
| @ -659,7 +681,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// filter wrong http method | 	// filter wrong http method | ||||||
| 	if _, ok := HTTPMETHOD[r.Method]; !ok { | 	if !HTTPMETHOD[r.Method] { | ||||||
| 		http.Error(rw, "Method Not Allowed", 405) | 		http.Error(rw, "Method Not Allowed", 405) | ||||||
| 		goto Admin | 		goto Admin | ||||||
| 	} | 	} | ||||||
| @ -768,14 +790,20 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) | |||||||
| 	// also defined runRouter & runMethod from filter | 	// also defined runRouter & runMethod from filter | ||||||
| 	if !isRunnable { | 	if !isRunnable { | ||||||
| 		//Invoke the request handler | 		//Invoke the request handler | ||||||
|  | 		var execController ControllerInterface | ||||||
|  | 		if routerInfo.initialize != nil { | ||||||
|  | 			execController = routerInfo.initialize() | ||||||
|  | 		} else { | ||||||
| 			vc := reflect.New(runRouter) | 			vc := reflect.New(runRouter) | ||||||
| 		execController, ok := vc.Interface().(ControllerInterface) | 			var ok bool | ||||||
|  | 			execController, ok = vc.Interface().(ControllerInterface) | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				panic("controller is not ControllerInterface") | 				panic("controller is not ControllerInterface") | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		//call the controller init function | 		//call the controller init function | ||||||
| 		execController.Init(context, runRouter.Name(), runMethod, vc.Interface()) | 		execController.Init(context, runRouter.Name(), runMethod, execController) | ||||||
| 
 | 
 | ||||||
| 		//call prepare function | 		//call prepare function | ||||||
| 		execController.Prepare() | 		execController.Prepare() | ||||||
| @ -810,6 +838,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) | |||||||
| 				execController.Options() | 				execController.Options() | ||||||
| 			default: | 			default: | ||||||
| 				if !execController.HandlerFunc(runMethod) { | 				if !execController.HandlerFunc(runMethod) { | ||||||
|  | 					vc := reflect.ValueOf(execController) | ||||||
| 					method := vc.MethodByName(runMethod) | 					method := vc.MethodByName(runMethod) | ||||||
| 					in := param.ConvertParams(methodParams, method.Type(), context) | 					in := param.ConvertParams(methodParams, method.Type(), context) | ||||||
| 					out := method.Call(in) | 					out := method.Call(in) | ||||||
| @ -846,16 +875,19 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) | |||||||
| 
 | 
 | ||||||
| Admin: | Admin: | ||||||
| 	//admin module record QPS | 	//admin module record QPS | ||||||
|  | 
 | ||||||
|  | 	statusCode := context.ResponseWriter.Status | ||||||
|  | 	if statusCode == 0 { | ||||||
|  | 		statusCode = 200 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if BConfig.Listen.EnableAdmin { | 	if BConfig.Listen.EnableAdmin { | ||||||
| 		timeDur := time.Since(startTime) | 		timeDur := time.Since(startTime) | ||||||
| 		pattern := "" | 		pattern := "" | ||||||
| 		if routerInfo != nil { | 		if routerInfo != nil { | ||||||
| 			pattern = routerInfo.pattern | 			pattern = routerInfo.pattern | ||||||
| 		} | 		} | ||||||
| 		statusCode := context.ResponseWriter.Status | 
 | ||||||
| 		if statusCode == 0 { |  | ||||||
| 			statusCode = 200 |  | ||||||
| 		} |  | ||||||
| 		if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { | 		if FilterMonitorFunc(r.Method, r.URL.Path, timeDur, pattern, statusCode) { | ||||||
| 			if runRouter != nil { | 			if runRouter != nil { | ||||||
| 				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur) | 				go toolbox.StatisticsMap.AddStatistics(r.Method, r.URL.Path, runRouter.Name(), timeDur) | ||||||
| @ -869,16 +901,27 @@ Admin: | |||||||
| 		timeDur := time.Since(startTime) | 		timeDur := time.Since(startTime) | ||||||
| 		var devInfo string | 		var devInfo string | ||||||
| 
 | 
 | ||||||
| 		statusCode := context.ResponseWriter.Status |  | ||||||
| 		if statusCode == 0 { |  | ||||||
| 			statusCode = 200 |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		iswin := (runtime.GOOS == "windows") | 		iswin := (runtime.GOOS == "windows") | ||||||
| 		statusColor := logs.ColorByStatus(iswin, statusCode) | 		statusColor := logs.ColorByStatus(iswin, statusCode) | ||||||
| 		methodColor := logs.ColorByMethod(iswin, r.Method) | 		methodColor := logs.ColorByMethod(iswin, r.Method) | ||||||
| 		resetColor := logs.ColorByMethod(iswin, "") | 		resetColor := logs.ColorByMethod(iswin, "") | ||||||
| 
 | 		if BConfig.Log.AccessLogsFormat != "" { | ||||||
|  | 			record := &logs.AccessLogRecord{ | ||||||
|  | 				RemoteAddr:     context.Input.IP(), | ||||||
|  | 				RequestTime:    startTime, | ||||||
|  | 				RequestMethod:  r.Method, | ||||||
|  | 				Request:        fmt.Sprintf("%s %s %s", r.Method, r.RequestURI, r.Proto), | ||||||
|  | 				ServerProtocol: r.Proto, | ||||||
|  | 				Host:           r.Host, | ||||||
|  | 				Status:         statusCode, | ||||||
|  | 				ElapsedTime:    timeDur, | ||||||
|  | 				HTTPReferrer:   r.Header.Get("Referer"), | ||||||
|  | 				HTTPUserAgent:  r.Header.Get("User-Agent"), | ||||||
|  | 				RemoteUser:     r.Header.Get("Remote-User"), | ||||||
|  | 				BodyBytesSent:  0, //@todo this one is missing! | ||||||
|  | 			} | ||||||
|  | 			logs.AccessLog(record, BConfig.Log.AccessLogsFormat) | ||||||
|  | 		} else { | ||||||
| 			if findRouter { | 			if findRouter { | ||||||
| 				if routerInfo != nil { | 				if routerInfo != nil { | ||||||
| 					devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s   r:%s", context.Input.IP(), statusColor, statusCode, | 					devInfo = fmt.Sprintf("|%15s|%s %3d %s|%13s|%8s|%s %-7s %s %-3s   r:%s", context.Input.IP(), statusColor, statusCode, | ||||||
| @ -898,7 +941,7 @@ Admin: | |||||||
| 				logs.Debug(devInfo) | 				logs.Debug(devInfo) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 	} | ||||||
| 	// Call WriteHeader if status code has been set changed | 	// Call WriteHeader if status code has been set changed | ||||||
| 	if context.Output.Status != 0 { | 	if context.Output.Status != 0 { | ||||||
| 		context.ResponseWriter.WriteHeader(context.Output.Status) | 		context.ResponseWriter.WriteHeader(context.Output.Status) | ||||||
|  | |||||||
| @ -160,11 +160,14 @@ func (rp *Provider) SessionInit(maxlifetime int64, savePath string) error { | |||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		//some redis proxy such as twemproxy is not support select command | ||||||
|  | 		if rp.dbNum > 0 { | ||||||
| 			_, err = c.Do("SELECT", rp.dbNum) | 			_, err = c.Do("SELECT", rp.dbNum) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				c.Close() | 				c.Close() | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
| 		return c, err | 		return c, err | ||||||
| 	}, rp.poolsize) | 	}, rp.poolsize) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -78,6 +78,8 @@ func (fs *FileSessionStore) SessionID() string { | |||||||
| 
 | 
 | ||||||
| // SessionRelease Write file session to local file with Gob string | // SessionRelease Write file session to local file with Gob string | ||||||
| func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { | func (fs *FileSessionStore) SessionRelease(w http.ResponseWriter) { | ||||||
|  | 	filepder.lock.Lock() | ||||||
|  | 	defer filepder.lock.Unlock() | ||||||
| 	b, err := EncodeGob(fs.values) | 	b, err := EncodeGob(fs.values) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		SLogger.Println(err) | 		SLogger.Println(err) | ||||||
| @ -164,7 +166,7 @@ func (fp *FileProvider) SessionRead(sid string) (Store, error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SessionExist Check file session exist. | // SessionExist Check file session exist. | ||||||
| // it checkes the file named from sid exist or not. | // it checks the file named from sid exist or not. | ||||||
| func (fp *FileProvider) SessionExist(sid string) bool { | func (fp *FileProvider) SessionExist(sid string) bool { | ||||||
| 	filepder.lock.Lock() | 	filepder.lock.Lock() | ||||||
| 	defer filepder.lock.Unlock() | 	defer filepder.lock.Unlock() | ||||||
|  | |||||||
| @ -149,7 +149,7 @@ func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime | |||||||
| 	// 2. Verify MAC. Value is "date|value|mac". | 	// 2. Verify MAC. Value is "date|value|mac". | ||||||
| 	parts := bytes.SplitN(b, []byte("|"), 3) | 	parts := bytes.SplitN(b, []byte("|"), 3) | ||||||
| 	if len(parts) != 3 { | 	if len(parts) != 3 { | ||||||
| 		return nil, errors.New("Decode: invalid value %v") | 		return nil, errors.New("Decode: invalid value format") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...) | 	b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...) | ||||||
|  | |||||||
| @ -121,6 +121,7 @@ type Schema struct { | |||||||
| 	Type        string               `json:"type,omitempty" yaml:"type,omitempty"` | 	Type        string               `json:"type,omitempty" yaml:"type,omitempty"` | ||||||
| 	Items       *Schema              `json:"items,omitempty" yaml:"items,omitempty"` | 	Items       *Schema              `json:"items,omitempty" yaml:"items,omitempty"` | ||||||
| 	Properties  map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"` | 	Properties  map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"` | ||||||
|  | 	Enum        []interface{}        `json:"enum,omitempty" yaml:"enum,omitempty"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification | // Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification | ||||||
| @ -141,7 +142,7 @@ type Propertie struct { | |||||||
| 
 | 
 | ||||||
| // Response as they are returned from executing this operation. | // Response as they are returned from executing this operation. | ||||||
| type Response struct { | type Response struct { | ||||||
| 	Description string  `json:"description,omitempty" yaml:"description,omitempty"` | 	Description string  `json:"description" yaml:"description"` | ||||||
| 	Schema      *Schema `json:"schema,omitempty" yaml:"schema,omitempty"` | 	Schema      *Schema `json:"schema,omitempty" yaml:"schema,omitempty"` | ||||||
| 	Ref         string  `json:"$ref,omitempty" yaml:"$ref,omitempty"` | 	Ref         string  `json:"$ref,omitempty" yaml:"$ref,omitempty"` | ||||||
| } | } | ||||||
|  | |||||||
| @ -218,9 +218,9 @@ func BuildTemplate(dir string, files ...string) error { | |||||||
| 				} | 				} | ||||||
| 				if err != nil { | 				if err != nil { | ||||||
| 					logs.Error("parse template err:", file, err) | 					logs.Error("parse template err:", file, err) | ||||||
| 				} else { | 					return err | ||||||
| 					beeTemplates[file] = t |  | ||||||
| 				} | 				} | ||||||
|  | 				beeTemplates[file] = t | ||||||
| 				templatesLock.Unlock() | 				templatesLock.Unlock() | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								tree.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								tree.go
									
									
									
									
									
								
							| @ -28,7 +28,7 @@ var ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Tree has three elements: FixRouter/wildcard/leaves | // Tree has three elements: FixRouter/wildcard/leaves | ||||||
| // fixRouter sotres Fixed Router | // fixRouter stores Fixed Router | ||||||
| // wildcard stores params | // wildcard stores params | ||||||
| // leaves store the endpoint information | // leaves store the endpoint information | ||||||
| type Tree struct { | type Tree struct { | ||||||
|  | |||||||
							
								
								
									
										226
									
								
								unregroute_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										226
									
								
								unregroute_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,226 @@ | |||||||
|  | // Copyright 2014 beego Author. All Rights Reserved. | ||||||
|  | // | ||||||
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
|  | // you may not use this file except in compliance with the License. | ||||||
|  | // You may obtain a copy of the License at | ||||||
|  | // | ||||||
|  | //      http://www.apache.org/licenses/LICENSE-2.0 | ||||||
|  | // | ||||||
|  | // Unless required by applicable law or agreed to in writing, software | ||||||
|  | // distributed under the License is distributed on an "AS IS" BASIS, | ||||||
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
|  | // See the License for the specific language governing permissions and | ||||||
|  | // limitations under the License. | ||||||
|  | 
 | ||||||
|  | package beego | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"net/http/httptest" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // | ||||||
|  | // The unregroute_test.go contains tests for the unregister route | ||||||
|  | // functionality, that allows overriding route paths in children project | ||||||
|  | // that embed parent routers. | ||||||
|  | // | ||||||
|  | 
 | ||||||
|  | const contentRootOriginal = "ok-original-root" | ||||||
|  | const contentLevel1Original = "ok-original-level1" | ||||||
|  | const contentLevel2Original = "ok-original-level2" | ||||||
|  | 
 | ||||||
|  | const contentRootReplacement = "ok-replacement-root" | ||||||
|  | const contentLevel1Replacement = "ok-replacement-level1" | ||||||
|  | const contentLevel2Replacement = "ok-replacement-level2" | ||||||
|  | 
 | ||||||
|  | // TestPreUnregController will supply content for the original routes, | ||||||
|  | // before unregistration | ||||||
|  | type TestPreUnregController struct { | ||||||
|  | 	Controller | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (tc *TestPreUnregController) GetFixedRoot() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentRootOriginal)) | ||||||
|  | } | ||||||
|  | func (tc *TestPreUnregController) GetFixedLevel1() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentLevel1Original)) | ||||||
|  | } | ||||||
|  | func (tc *TestPreUnregController) GetFixedLevel2() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentLevel2Original)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestPostUnregController will supply content for the overriding routes, | ||||||
|  | // after the original ones are unregistered. | ||||||
|  | type TestPostUnregController struct { | ||||||
|  | 	Controller | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (tc *TestPostUnregController) GetFixedRoot() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentRootReplacement)) | ||||||
|  | } | ||||||
|  | func (tc *TestPostUnregController) GetFixedLevel1() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentLevel1Replacement)) | ||||||
|  | } | ||||||
|  | func (tc *TestPostUnregController) GetFixedLevel2() { | ||||||
|  | 	tc.Ctx.Output.Body([]byte(contentLevel2Replacement)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestUnregisterFixedRouteRoot replaces just the root fixed route path. | ||||||
|  | // In this case, for a path like "/level1/level2" or "/level1", those actions | ||||||
|  | // should remain intact, and continue to serve the original content. | ||||||
|  | func TestUnregisterFixedRouteRoot(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	var method = "GET" | ||||||
|  | 
 | ||||||
|  | 	handler := NewControllerRegister() | ||||||
|  | 	handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") | ||||||
|  | 	handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") | ||||||
|  | 	handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") | ||||||
|  | 
 | ||||||
|  | 	// Test original root | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test original root", | ||||||
|  | 		method, "/", contentRootOriginal) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 1 | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test original level 1", | ||||||
|  | 		method, "/level1", contentLevel1Original) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 2 | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test original level 2", | ||||||
|  | 		method, "/level1/level2", contentLevel2Original) | ||||||
|  | 
 | ||||||
|  | 	// Remove only the root path | ||||||
|  | 	findAndRemoveSingleTree(handler.routers[method]) | ||||||
|  | 
 | ||||||
|  | 	// Replace the root path TestPreUnregController action with the action from | ||||||
|  | 	// TestPostUnregController | ||||||
|  | 	handler.Add("/", &TestPostUnregController{}, "get:GetFixedRoot") | ||||||
|  | 
 | ||||||
|  | 	// Test replacement root (expect change) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test replacement root (expect change)", method, "/", contentRootReplacement) | ||||||
|  | 
 | ||||||
|  | 	// Test level 1 (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 1 (expect no change from the original)", method, "/level1", contentLevel1Original) | ||||||
|  | 
 | ||||||
|  | 	// Test level 2 (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 2 (expect no change from the original)", method, "/level1/level2", contentLevel2Original) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestUnregisterFixedRouteLevel1 replaces just the "/level1" fixed route path. | ||||||
|  | // In this case, for a path like "/level1/level2" or "/", those actions | ||||||
|  | // should remain intact, and continue to serve the original content. | ||||||
|  | func TestUnregisterFixedRouteLevel1(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	var method = "GET" | ||||||
|  | 
 | ||||||
|  | 	handler := NewControllerRegister() | ||||||
|  | 	handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") | ||||||
|  | 	handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") | ||||||
|  | 	handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") | ||||||
|  | 
 | ||||||
|  | 	// Test original root | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original root", | ||||||
|  | 		method, "/", contentRootOriginal) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 1 | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original level 1", | ||||||
|  | 		method, "/level1", contentLevel1Original) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 2 | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original level 2", | ||||||
|  | 		method, "/level1/level2", contentLevel2Original) | ||||||
|  | 
 | ||||||
|  | 	// Remove only the level1 path | ||||||
|  | 	subPaths := splitPath("/level1") | ||||||
|  | 	if handler.routers[method].prefix == strings.Trim("/level1", "/ ") { | ||||||
|  | 		findAndRemoveSingleTree(handler.routers[method]) | ||||||
|  | 	} else { | ||||||
|  | 		findAndRemoveTree(subPaths, handler.routers[method], method) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace the "level1" path TestPreUnregController action with the action from | ||||||
|  | 	// TestPostUnregController | ||||||
|  | 	handler.Add("/level1", &TestPostUnregController{}, "get:GetFixedLevel1") | ||||||
|  | 
 | ||||||
|  | 	// Test replacement root (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal) | ||||||
|  | 
 | ||||||
|  | 	// Test level 1 (expect change) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 1 (expect change)", method, "/level1", contentLevel1Replacement) | ||||||
|  | 
 | ||||||
|  | 	// Test level 2 (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 2 (expect no change from the original)", method, "/level1/level2", contentLevel2Original) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // TestUnregisterFixedRouteLevel2 unregisters just the "/level1/level2" fixed | ||||||
|  | // route path. In this case, for a path like "/level1" or "/", those actions | ||||||
|  | // should remain intact, and continue to serve the original content. | ||||||
|  | func TestUnregisterFixedRouteLevel2(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | 	var method = "GET" | ||||||
|  | 
 | ||||||
|  | 	handler := NewControllerRegister() | ||||||
|  | 	handler.Add("/", &TestPreUnregController{}, "get:GetFixedRoot") | ||||||
|  | 	handler.Add("/level1", &TestPreUnregController{}, "get:GetFixedLevel1") | ||||||
|  | 	handler.Add("/level1/level2", &TestPreUnregController{}, "get:GetFixedLevel2") | ||||||
|  | 
 | ||||||
|  | 	// Test original root | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original root", | ||||||
|  | 		method, "/", contentRootOriginal) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 1 | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original level 1", | ||||||
|  | 		method, "/level1", contentLevel1Original) | ||||||
|  | 
 | ||||||
|  | 	// Test original level 2 | ||||||
|  | 	testHelperFnContentCheck(t, handler, | ||||||
|  | 		"TestUnregisterFixedRouteLevel1.Test original level 2", | ||||||
|  | 		method, "/level1/level2", contentLevel2Original) | ||||||
|  | 
 | ||||||
|  | 	// Remove only the level2 path | ||||||
|  | 	subPaths := splitPath("/level1/level2") | ||||||
|  | 	if handler.routers[method].prefix == strings.Trim("/level1/level2", "/ ") { | ||||||
|  | 		findAndRemoveSingleTree(handler.routers[method]) | ||||||
|  | 	} else { | ||||||
|  | 		findAndRemoveTree(subPaths, handler.routers[method], method) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Replace the "/level1/level2" path TestPreUnregController action with the action from | ||||||
|  | 	// TestPostUnregController | ||||||
|  | 	handler.Add("/level1/level2", &TestPostUnregController{}, "get:GetFixedLevel2") | ||||||
|  | 
 | ||||||
|  | 	// Test replacement root (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test replacement root (expect no change from the original)", method, "/", contentRootOriginal) | ||||||
|  | 
 | ||||||
|  | 	// Test level 1 (expect no change from the original) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 1 (expect no change from the original)", method, "/level1", contentLevel1Original) | ||||||
|  | 
 | ||||||
|  | 	// Test level 2 (expect change) | ||||||
|  | 	testHelperFnContentCheck(t, handler, "Test level 2 (expect change)", method, "/level1/level2", contentLevel2Replacement) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func testHelperFnContentCheck(t *testing.T, handler *ControllerRegister, | ||||||
|  | 	testName, method, path, expectedBodyContent string) { | ||||||
|  | 
 | ||||||
|  | 	r, err := http.NewRequest(method, path, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Errorf("httpRecorderBodyTest NewRequest error: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	w := httptest.NewRecorder() | ||||||
|  | 	handler.ServeHTTP(w, r) | ||||||
|  | 	body := w.Body.String() | ||||||
|  | 	if body != expectedBodyContent { | ||||||
|  | 		t.Errorf("%s: expected [%s], got [%s];", testName, expectedBodyContent, body) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -31,6 +31,22 @@ func TestSet(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestReSet(t *testing.T) { | ||||||
|  | 	safeMap := NewBeeMap() | ||||||
|  | 	if ok := safeMap.Set("astaxie", 1); !ok { | ||||||
|  | 		t.Error("expected", true, "got", false) | ||||||
|  | 	} | ||||||
|  | 	// set diff value | ||||||
|  | 	if ok := safeMap.Set("astaxie", -1); !ok { | ||||||
|  | 		t.Error("expected", true, "got", false) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// set same value | ||||||
|  | 	if ok := safeMap.Set("astaxie", -1); ok { | ||||||
|  | 		t.Error("expected", false, "got", true) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestCheck(t *testing.T) { | func TestCheck(t *testing.T) { | ||||||
| 	if exists := safeMap.Check("astaxie"); !exists { | 	if exists := safeMap.Check("astaxie"); !exists { | ||||||
| 		t.Error("expected", true, "got", false) | 		t.Error("expected", true, "got", false) | ||||||
| @ -50,6 +66,21 @@ func TestDelete(t *testing.T) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestItems(t *testing.T) { | ||||||
|  | 	safeMap := NewBeeMap() | ||||||
|  | 	safeMap.Set("astaxie", "hello") | ||||||
|  | 	for k, v := range safeMap.Items() { | ||||||
|  | 		key := k.(string) | ||||||
|  | 		value := v.(string) | ||||||
|  | 		if key != "astaxie" { | ||||||
|  | 			t.Error("expected the key should be astaxie") | ||||||
|  | 		} | ||||||
|  | 		if value != "hello" { | ||||||
|  | 			t.Error("expected the value should be hello") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestCount(t *testing.T) { | func TestCount(t *testing.T) { | ||||||
| 	if count := safeMap.Count(); count != 0 { | 	if count := safeMap.Count(); count != 0 { | ||||||
| 		t.Error("expected count to be", 0, "got", count) | 		t.Error("expected count to be", 0, "got", count) | ||||||
|  | |||||||
| @ -112,7 +112,7 @@ type Validation struct { | |||||||
| 	RequiredFirst bool | 	RequiredFirst bool | ||||||
| 
 | 
 | ||||||
| 	Errors    []*Error | 	Errors    []*Error | ||||||
| 	ErrorsMap map[string]*Error | 	ErrorsMap map[string][]*Error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Clear Clean all ValidationError. | // Clear Clean all ValidationError. | ||||||
| @ -129,7 +129,7 @@ func (v *Validation) HasErrors() bool { | |||||||
| // ErrorMap Return the errors mapped by key. | // ErrorMap Return the errors mapped by key. | ||||||
| // If there are multiple validation errors associated with a single key, the | // If there are multiple validation errors associated with a single key, the | ||||||
| // first one "wins".  (Typically the first validation will be the more basic). | // first one "wins".  (Typically the first validation will be the more basic). | ||||||
| func (v *Validation) ErrorMap() map[string]*Error { | func (v *Validation) ErrorMap() map[string][]*Error { | ||||||
| 	return v.ErrorsMap | 	return v.ErrorsMap | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -278,14 +278,35 @@ func (v *Validation) apply(chk Validator, obj interface{}) *Result { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // AddError adds independent error message for the provided key | ||||||
|  | func (v *Validation) AddError(key, message string) { | ||||||
|  | 	Name := key | ||||||
|  | 	Field := "" | ||||||
|  | 
 | ||||||
|  | 	parts := strings.Split(key, ".") | ||||||
|  | 	if len(parts) == 2 { | ||||||
|  | 		Field = parts[0] | ||||||
|  | 		Name = parts[1] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err := &Error{ | ||||||
|  | 		Message: message, | ||||||
|  | 		Key:     key, | ||||||
|  | 		Name:    Name, | ||||||
|  | 		Field:   Field, | ||||||
|  | 	} | ||||||
|  | 	v.setError(err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (v *Validation) setError(err *Error) { | func (v *Validation) setError(err *Error) { | ||||||
| 	v.Errors = append(v.Errors, err) | 	v.Errors = append(v.Errors, err) | ||||||
| 	if v.ErrorsMap == nil { | 	if v.ErrorsMap == nil { | ||||||
| 		v.ErrorsMap = make(map[string]*Error) | 		v.ErrorsMap = make(map[string][]*Error) | ||||||
| 	} | 	} | ||||||
| 	if _, ok := v.ErrorsMap[err.Field]; !ok { | 	if _, ok := v.ErrorsMap[err.Field]; !ok { | ||||||
| 		v.ErrorsMap[err.Field] = err | 		v.ErrorsMap[err.Field] = []*Error{} | ||||||
| 	} | 	} | ||||||
|  | 	v.ErrorsMap[err.Field] = append(v.ErrorsMap[err.Field], err) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // SetError Set error message for one field in ValidationError | // SetError Set error message for one field in ValidationError | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user