diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
new file mode 100644
index 00000000..db349198
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE
@@ -0,0 +1,17 @@
+Please answer these questions before submitting your issue. Thanks!
+
+1. What version of Go and beego are you using (`bee version`)?
+
+
+2. What operating system and processor architecture are you using (`go env`)?
+
+
+3. What did you do?
+If possible, provide a recipe for reproducing the error.
+A complete runnable program is good.
+
+
+4. What did you expect to see?
+
+
+5. What did you see instead?
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 3c821dcd..92d9ac8b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,7 @@
language: go
go:
- - tip
- - 1.6.0
+ - 1.6
- 1.5.3
- 1.4.3
services:
@@ -32,12 +31,12 @@ install:
- go get github.com/siddontang/ledisdb/config
- go get github.com/siddontang/ledisdb/ledis
- go get golang.org/x/tools/cmd/vet
- - go get github.com/golang/lint/golint
- go get github.com/ssdb/gossdb/ssdb
before_script:
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
+ - sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi"
- mkdir -p res/var
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
after_script:
@@ -45,7 +44,4 @@ after_script:
- rm -rf ./res/var/*
script:
- go vet -x ./...
- - $HOME/gopath/bin/golint ./...
- go test -v ./...
-notifications:
- webhooks: https://hooks.pubu.im/services/z7m9bvybl3rgtg9
diff --git a/admin.go b/admin.go
index 031e6421..cf5bc63a 100644
--- a/admin.go
+++ b/admin.go
@@ -196,7 +196,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
BeforeExec: "Before Exec",
AfterExec: "After Exec",
FinishRouter: "Finish Router"} {
- if bf, ok := BeeApp.Handlers.filters[k]; ok {
+ if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 {
filterType = fr
filterTypes = append(filterTypes, filterType)
resultList := new([][]string)
diff --git a/beego.go b/beego.go
index 8f82cdcf..16601ecc 100644
--- a/beego.go
+++ b/beego.go
@@ -51,6 +51,7 @@ func AddAPPStartHook(hf hookfunc) {
// beego.Run(":8089")
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {
+
initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" {
@@ -74,6 +75,7 @@ func initBeforeHTTPRun() {
AddAPPStartHook(registerDocs)
AddAPPStartHook(registerTemplate)
AddAPPStartHook(registerAdmin)
+ AddAPPStartHook(registerGzip)
for _, hk := range hooks {
if err := hk(); err != nil {
@@ -87,5 +89,6 @@ func TestBeegoInit(ap string) {
os.Setenv("BEEGO_RUNMODE", "test")
appConfigPath = filepath.Join(ap, "conf", "app.conf")
os.Chdir(ap)
+ LoadAppConfig(appConfigProvider, appConfigPath)
initBeforeHTTPRun()
}
diff --git a/config.go b/config.go
index 2761e7cb..f0316a62 100644
--- a/config.go
+++ b/config.go
@@ -316,10 +316,6 @@ func LoadAppConfig(adapterName, configPath string) error {
return fmt.Errorf("the target config file: %s don't exist", configPath)
}
- if absConfigPath == appConfigPath {
- return nil
- }
-
appConfigPath = absConfigPath
appConfigProvider = adapterName
@@ -353,7 +349,7 @@ func (b *beegoAppConfig) String(key string) string {
}
func (b *beegoAppConfig) Strings(key string) []string {
- if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); v[0] != "" {
+ if v := b.innerConfig.Strings(BConfig.RunMode + "::" + key); len(v) > 0 {
return v
}
return b.innerConfig.Strings(key)
diff --git a/context/acceptencoder.go b/context/acceptencoder.go
index 033d9ca8..cb735445 100644
--- a/context/acceptencoder.go
+++ b/context/acceptencoder.go
@@ -27,6 +27,33 @@ import (
"sync"
)
+var (
+ //Default size==20B same as nginx
+ defaultGzipMinLength = 20
+ //Content will only be compressed if content length is either unknown or greater than gzipMinLength.
+ gzipMinLength = defaultGzipMinLength
+ //The compression level used for deflate compression. (0-9).
+ gzipCompressLevel int
+ //List of HTTP methods to compress. If not set, only GET requests are compressed.
+ includedMethods map[string]bool
+ getMethodOnly bool
+)
+
+func InitGzip(minLength, compressLevel int, methods []string) {
+ if minLength >= 0 {
+ gzipMinLength = minLength
+ }
+ gzipCompressLevel = compressLevel
+ if gzipCompressLevel < flate.NoCompression || gzipCompressLevel > flate.BestCompression {
+ gzipCompressLevel = flate.BestSpeed
+ }
+ getMethodOnly = (len(methods) == 0) || (len(methods) == 1 && strings.ToUpper(methods[0]) == "GET")
+ includedMethods = make(map[string]bool, len(methods))
+ for _, v := range methods {
+ includedMethods[strings.ToUpper(v)] = true
+ }
+}
+
type resetWriter interface {
io.Writer
Reset(w io.Writer)
@@ -41,20 +68,20 @@ func (n nopResetWriter) Reset(w io.Writer) {
}
type acceptEncoder struct {
- name string
- levelEncode func(int) resetWriter
- bestSpeedPool *sync.Pool
- bestCompressionPool *sync.Pool
+ name string
+ levelEncode func(int) resetWriter
+ customCompressLevelPool *sync.Pool
+ bestCompressionPool *sync.Pool
}
func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
- if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil {
+ if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
return nopResetWriter{wr}
}
var rwr resetWriter
switch level {
case flate.BestSpeed:
- rwr = ac.bestSpeedPool.Get().(resetWriter)
+ rwr = ac.customCompressLevelPool.Get().(resetWriter)
case flate.BestCompression:
rwr = ac.bestCompressionPool.Get().(resetWriter)
default:
@@ -65,13 +92,18 @@ func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
}
func (ac acceptEncoder) put(wr resetWriter, level int) {
- if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil {
+ if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
return
}
wr.Reset(nil)
+
+ //notice
+ //compressionLevel==BestCompression DOES NOT MATTER
+ //sync.Pool will not memory leak
+
switch level {
- case flate.BestSpeed:
- ac.bestSpeedPool.Put(wr)
+ case gzipCompressLevel:
+ ac.customCompressLevelPool.Put(wr)
case flate.BestCompression:
ac.bestCompressionPool.Put(wr)
}
@@ -79,28 +111,22 @@ func (ac acceptEncoder) put(wr resetWriter, level int) {
var (
noneCompressEncoder = acceptEncoder{"", nil, nil, nil}
- gzipCompressEncoder = acceptEncoder{"gzip",
- func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr },
- &sync.Pool{
- New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestSpeed); return wr },
- },
- &sync.Pool{
- New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr },
- },
+ gzipCompressEncoder = acceptEncoder{
+ name: "gzip",
+ levelEncode: func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr },
+ customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, gzipCompressLevel); return wr }},
+ bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestCompression); return wr }},
}
//according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed
//deflate
//The "zlib" format defined in RFC 1950 [31] in combination with
//the "deflate" compression mechanism described in RFC 1951 [29].
- deflateCompressEncoder = acceptEncoder{"deflate",
- func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr },
- &sync.Pool{
- New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestSpeed); return wr },
- },
- &sync.Pool{
- New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr },
- },
+ deflateCompressEncoder = acceptEncoder{
+ name: "deflate",
+ levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr },
+ customCompressLevelPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, gzipCompressLevel); return wr }},
+ bestCompressionPool: &sync.Pool{New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestCompression); return wr }},
}
)
@@ -120,7 +146,11 @@ func WriteFile(encoding string, writer io.Writer, file *os.File) (bool, string,
// WriteBody reads writes content to writer by the specific encoding(gzip/deflate)
func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) {
- return writeLevel(encoding, writer, bytes.NewReader(content), flate.BestSpeed)
+ if encoding == "" || len(content) < gzipMinLength {
+ _, err := writer.Write(content)
+ return false, "", err
+ }
+ return writeLevel(encoding, writer, bytes.NewReader(content), gzipCompressLevel)
}
// writeLevel reads from reader,writes to writer by specific encoding and compress level
@@ -156,7 +186,10 @@ func ParseEncoding(r *http.Request) string {
if r == nil {
return ""
}
- return parseEncoding(r)
+ if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] {
+ return parseEncoding(r)
+ }
+ return ""
}
type q struct {
diff --git a/context/context.go b/context/context.go
index ab3a3d3f..fee5e1c5 100644
--- a/context/context.go
+++ b/context/context.go
@@ -77,6 +77,7 @@ func (ctx *Context) Redirect(status int, localurl string) {
// Abort stops this request.
// if beego.ErrorMaps exists, panic body.
func (ctx *Context) Abort(status int, body string) {
+ ctx.Output.SetStatus(status)
panic(body)
}
diff --git a/controller.go b/controller.go
index 85894275..9d265758 100644
--- a/controller.go
+++ b/controller.go
@@ -261,12 +261,13 @@ func (c *Controller) Abort(code string) {
// CustomAbort stops controller handler and show the error data, it's similar Aborts, but support status code and body.
func (c *Controller) CustomAbort(status int, body string) {
- c.Ctx.Output.Status = status
// first panic from ErrorMaps, is is user defined error functions.
if _, ok := ErrorMaps[body]; ok {
+ c.Ctx.Output.Status = status
panic(body)
}
// last panic user string
+ c.Ctx.ResponseWriter.WriteHeader(status)
c.Ctx.ResponseWriter.Write([]byte(body))
panic(ErrAbort)
}
diff --git a/error.go b/error.go
index 4f48fab2..8cfa0c67 100644
--- a/error.go
+++ b/error.go
@@ -210,159 +210,139 @@ var ErrorMaps = make(map[string]*errorInfo, 10)
// show 401 unauthorized error.
func unauthorized(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(401),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested can't be authorized." +
- "
Perhaps you are here because:" +
- "
" +
- "
The credentials you supplied are incorrect" +
- "
There are errors in the website address" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 401,
+ "
The page you have requested can't be authorized."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The credentials you supplied are incorrect"+
+ "
There are errors in the website address"+
+ "
",
+ )
}
// show 402 Payment Required
func paymentRequired(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(402),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested Payment Required." +
- "
Perhaps you are here because:" +
- "
" +
- "
The credentials you supplied are incorrect" +
- "
There are errors in the website address" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 402,
+ "
The page you have requested Payment Required."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The credentials you supplied are incorrect"+
+ "
There are errors in the website address"+
+ "
",
+ )
}
// show 403 forbidden error.
func forbidden(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(403),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested is forbidden." +
- "
Perhaps you are here because:" +
- "
" +
- "
Your address may be blocked" +
- "
The site may be disabled" +
- "
You need to log in" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 403,
+ "
The page you have requested is forbidden."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
Your address may be blocked"+
+ "
The site may be disabled"+
+ "
You need to log in"+
+ "
",
+ )
}
-// show 404 notfound error.
+// show 404 not found error.
func notFound(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(404),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested has flown the coop." +
- "
Perhaps you are here because:" +
- "
" +
- "
The page has moved" +
- "
The page no longer exists" +
- "
You were looking for your puppy and got lost" +
- "
You like 404 pages" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 404,
+ "
The page you have requested has flown the coop."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The page has moved"+
+ "
The page no longer exists"+
+ "
You were looking for your puppy and got lost"+
+ "
You like 404 pages"+
+ "
",
+ )
}
// show 405 Method Not Allowed
func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(405),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The method you have requested Not Allowed." +
- "
Perhaps you are here because:" +
- "
" +
- "
The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" +
- "
The response MUST include an Allow header containing a list of valid methods for the requested resource." +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 405,
+ "
The method you have requested Not Allowed."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The method specified in the Request-Line is not allowed for the resource identified by the Request-URI"+
+ "
The response MUST include an Allow header containing a list of valid methods for the requested resource."+
+ "
",
+ )
}
// show 500 internal server error.
func internalServerError(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(500),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested is down right now." +
- "
" +
- "
Please try again later and report the error to the website administrator" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 500,
+ "
The page you have requested is down right now."+
+ "
"+
+ "
Please try again later and report the error to the website administrator"+
+ "
",
+ )
}
// show 501 Not Implemented.
func notImplemented(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(504),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested is Not Implemented." +
- "
" +
- "
Please try again later and report the error to the website administrator" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 501,
+ "
The page you have requested is Not Implemented."+
+ "
"+
+ "
Please try again later and report the error to the website administrator"+
+ "
",
+ )
}
// show 502 Bad Gateway.
func badGateway(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(502),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested is down right now." +
- "
" +
- "
The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request." +
- "
Please try again later and report the error to the website administrator" +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 502,
+ "
The page you have requested is down right now."+
+ "
"+
+ "
The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request."+
+ "
Please try again later and report the error to the website administrator"+
+ "
",
+ )
}
// show 503 service unavailable error.
func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
- t, _ := template.New("beegoerrortemp").Parse(errtpl)
- data := map[string]interface{}{
- "Title": http.StatusText(503),
- "BeegoVersion": VERSION,
- }
- data["Content"] = template.HTML("
The page you have requested is unavailable." +
- "
Perhaps you are here because:" +
- "
" +
- "
The page is overloaded" +
- "
Please try again later." +
- "
")
- t.Execute(rw, data)
+ responseError(rw, r,
+ 503,
+ "
The page you have requested is unavailable."+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The page is overloaded"+
+ "
Please try again later."+
+ "
",
+ )
}
// show 504 Gateway Timeout.
func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
+ responseError(rw, r,
+ 504,
+ "
The page you have requested is unavailable"+
+ "
Perhaps you are here because:"+
+ "
"+
+ "
The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI."+
+ "
Please try again later."+
+ "
",
+ )
+}
+
+func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := map[string]interface{}{
- "Title": http.StatusText(504),
+ "Title": http.StatusText(errCode),
"BeegoVersion": VERSION,
+ "Content": errContent,
}
- data["Content"] = template.HTML("
The page you have requested is unavailable." +
- "
Perhaps you are here because:" +
- "
" +
- "
The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
- "
Please try again later." +
- "
")
t.Execute(rw, data)
}
@@ -408,7 +388,10 @@ func exception(errCode string, ctx *context.Context) {
if err == nil {
return v
}
- return 503
+ if ctx.Output.Status == 0 {
+ return 503
+ }
+ return ctx.Output.Status
}
for _, ec := range []string{errCode, "503", "500"} {
diff --git a/error_test.go b/error_test.go
new file mode 100644
index 00000000..f6e40c80
--- /dev/null
+++ b/error_test.go
@@ -0,0 +1,88 @@
+// Copyright 2016 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"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+type errorTestController struct {
+ Controller
+}
+
+const parseCodeError = "parse code error"
+
+func (ec *errorTestController) Get() {
+ errorCode, err := ec.GetInt("code")
+ if err != nil {
+ ec.Abort(parseCodeError)
+ }
+ if errorCode != 0 {
+ ec.CustomAbort(errorCode, ec.GetString("code"))
+ }
+ ec.Abort("404")
+}
+
+func TestErrorCode_01(t *testing.T) {
+ registerDefaultErrorHandler()
+ for k, _ := range ErrorMaps {
+ r, _ := http.NewRequest("GET", "/error?code="+k, nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/error", &errorTestController{})
+ handler.ServeHTTP(w, r)
+ code, _ := strconv.Atoi(k)
+ if w.Code != code {
+ t.Fail()
+ }
+ if !strings.Contains(string(w.Body.Bytes()), http.StatusText(code)) {
+ t.Fail()
+ }
+ }
+}
+
+func TestErrorCode_02(t *testing.T) {
+ registerDefaultErrorHandler()
+ r, _ := http.NewRequest("GET", "/error?code=0", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/error", &errorTestController{})
+ handler.ServeHTTP(w, r)
+ if w.Code != 404 {
+ t.Fail()
+ }
+}
+
+func TestErrorCode_03(t *testing.T) {
+ registerDefaultErrorHandler()
+ r, _ := http.NewRequest("GET", "/error?code=panic", nil)
+ w := httptest.NewRecorder()
+
+ handler := NewControllerRegister()
+ handler.Add("/error", &errorTestController{})
+ handler.ServeHTTP(w, r)
+ if w.Code != 200 {
+ t.Fail()
+ }
+ if string(w.Body.Bytes()) != parseCodeError {
+ t.Fail()
+ }
+}
diff --git a/hooks.go b/hooks.go
index 59b10b32..674f2858 100644
--- a/hooks.go
+++ b/hooks.go
@@ -6,6 +6,7 @@ import (
"net/http"
"path/filepath"
+ "github.com/astaxie/beego/context"
"github.com/astaxie/beego/session"
)
@@ -91,3 +92,14 @@ func registerAdmin() error {
}
return nil
}
+
+func registerGzip() error {
+ if BConfig.EnableGzip {
+ context.InitGzip(
+ AppConfig.DefaultInt("gzipMinLength", -1),
+ AppConfig.DefaultInt("gzipCompressLevel", -1),
+ AppConfig.DefaultStrings("includedMethods", []string{"GET"}),
+ )
+ }
+ return nil
+}
diff --git a/namespace.go b/namespace.go
index 4007d44c..cfde0111 100644
--- a/namespace.go
+++ b/namespace.go
@@ -44,7 +44,7 @@ func NewNamespace(prefix string, params ...LinkNamespace) *Namespace {
return ns
}
-// Cond set condtion function
+// Cond set condition function
// if cond return true can run this namespace, else can't
// usage:
// ns.Cond(func (ctx *context.Context) bool{
@@ -60,7 +60,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace {
exception("405", ctx)
}
}
- if v, ok := n.handlers.filters[BeforeRouter]; ok {
+ if v := n.handlers.filters[BeforeRouter]; len(v) > 0 {
mr := new(FilterRouter)
mr.tree = NewTree()
mr.pattern = "*"
diff --git a/orm/db.go b/orm/db.go
index 314c3535..9ff84411 100644
--- a/orm/db.go
+++ b/orm/db.go
@@ -181,7 +181,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
}
default:
switch {
- case fi.fieldType&IsPostiveIntegerField > 0:
+ case fi.fieldType&IsPositiveIntegerField > 0:
if field.Kind() == reflect.Ptr {
if field.IsNil() {
value = nil
@@ -516,7 +516,7 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
}
if num > 0 {
if mi.fields.pk.auto {
- if mi.fields.pk.fieldType&IsPostiveIntegerField > 0 {
+ if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0)
} else {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0)
@@ -1140,7 +1140,7 @@ setValue:
tErr = err
goto end
}
- if fieldType&IsPostiveIntegerField > 0 {
+ if fieldType&IsPositiveIntegerField > 0 {
v, _ := str.Uint64()
value = v
} else {
@@ -1292,7 +1292,7 @@ setValue:
field.Set(reflect.ValueOf(&v))
}
case fieldType&IsIntegerField > 0:
- if fieldType&IsPostiveIntegerField > 0 {
+ if fieldType&IsPositiveIntegerField > 0 {
if isNative {
if value == nil {
value = uint64(0)
diff --git a/orm/db_utils.go b/orm/db_utils.go
index c97caf36..ff36b286 100644
--- a/orm/db_utils.go
+++ b/orm/db_utils.go
@@ -33,13 +33,13 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac
fi := mi.fields.pk
v := ind.FieldByIndex(fi.fieldIndex)
- if fi.fieldType&IsPostiveIntegerField > 0 {
+ if fi.fieldType&IsPositiveIntegerField > 0 {
vu := v.Uint()
exist = vu > 0
value = vu
} else if fi.fieldType&IsIntegerField > 0 {
vu := v.Int()
- exist = vu > 0
+ exist = true
value = vu
} else {
vu := v.String()
diff --git a/orm/models_fields.go b/orm/models_fields.go
index a8cf8e4f..ad1fb6f9 100644
--- a/orm/models_fields.go
+++ b/orm/models_fields.go
@@ -46,10 +46,10 @@ const (
// Define some logic enum
const (
- IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5
- IsPostiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9
- IsRelField = ^-RelReverseMany >> 14 << 15
- IsFieldType = ^-RelReverseMany<<1 + 1
+ IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5
+ IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9
+ IsRelField = ^-RelReverseMany >> 14 << 15
+ IsFieldType = ^-RelReverseMany<<1 + 1
)
// BooleanField A true/false field.
diff --git a/orm/models_test.go b/orm/models_test.go
index 4c8d32f8..b6ae21cf 100644
--- a/orm/models_test.go
+++ b/orm/models_test.go
@@ -387,6 +387,11 @@ func NewInLineOneToOne() *InLineOneToOne {
return new(InLineOneToOne)
}
+type IntegerPk struct {
+ Id int64 `orm:"pk"`
+ Value string
+}
+
var DBARGS = struct {
Driver string
Source string
diff --git a/orm/orm.go b/orm/orm.go
index 0ffb6b86..38c89334 100644
--- a/orm/orm.go
+++ b/orm/orm.go
@@ -159,7 +159,7 @@ func (o *orm) Insert(md interface{}) (int64, error) {
// set auto pk field
func (o *orm) setPk(mi *modelInfo, ind reflect.Value, id int64) {
if mi.fields.pk.auto {
- if mi.fields.pk.fieldType&IsPostiveIntegerField > 0 {
+ if mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(uint64(id))
} else {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id)
diff --git a/orm/orm_object.go b/orm/orm_object.go
index 8a5d85e2..de3181ce 100644
--- a/orm/orm_object.go
+++ b/orm/orm_object.go
@@ -50,7 +50,7 @@ func (o *insertSet) Insert(md interface{}) (int64, error) {
}
if id > 0 {
if o.mi.fields.pk.auto {
- if o.mi.fields.pk.fieldType&IsPostiveIntegerField > 0 {
+ if o.mi.fields.pk.fieldType&IsPositiveIntegerField > 0 {
ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetUint(uint64(id))
} else {
ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetInt(id)
diff --git a/orm/orm_test.go b/orm/orm_test.go
index 181106bb..832c7301 100644
--- a/orm/orm_test.go
+++ b/orm/orm_test.go
@@ -19,6 +19,7 @@ import (
"database/sql"
"fmt"
"io/ioutil"
+ "math"
"os"
"path/filepath"
"reflect"
@@ -189,6 +190,7 @@ func TestSyncDb(t *testing.T) {
RegisterModel(new(GroupPermissions))
RegisterModel(new(InLine))
RegisterModel(new(InLineOneToOne))
+ RegisterModel(new(IntegerPk))
err := RunSyncdb("default", true, Debug)
throwFail(t, err)
@@ -210,6 +212,7 @@ func TestRegisterModels(t *testing.T) {
RegisterModel(new(GroupPermissions))
RegisterModel(new(InLine))
RegisterModel(new(InLineOneToOne))
+ RegisterModel(new(IntegerPk))
BootStrap()
@@ -1991,3 +1994,22 @@ func TestInLineOneToOne(t *testing.T) {
throwFail(t, AssertIs(rinline.Name, name))
throwFail(t, AssertIs(rinline.Email, email))
}
+
+func TestIntegerPk(t *testing.T) {
+ its := []IntegerPk{
+ {Id: math.MinInt64, Value: "-"},
+ {Id: 0, Value: "0"},
+ {Id: math.MaxInt64, Value: "+"},
+ }
+
+ num, err := dORM.InsertMulti(len(its), its)
+ throwFail(t, err)
+ throwFail(t, AssertIs(num, len(its)))
+
+ for _, intPk := range its {
+ out := IntegerPk{Id: intPk.Id}
+ err = dORM.Read(&out)
+ throwFail(t, err)
+ throwFail(t, AssertIs(out.Value, intPk.Value))
+ }
+}
diff --git a/router.go b/router.go
index 5b4b1ff9..5516ecda 100644
--- a/router.go
+++ b/router.go
@@ -114,7 +114,7 @@ type controllerInfo struct {
type ControllerRegister struct {
routers map[string]*Tree
enableFilter bool
- filters map[int][]*FilterRouter
+ filters [FinishRouter + 1][]*FilterRouter
pool sync.Pool
}
@@ -122,7 +122,6 @@ type ControllerRegister struct {
func NewControllerRegister() *ControllerRegister {
cr := &ControllerRegister{
routers: make(map[string]*Tree),
- filters: make(map[int][]*FilterRouter),
}
cr.pool.New = func() interface{} {
return beecontext.NewContext()
@@ -408,7 +407,6 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface)
// InsertFilter Add a FilterFunc with pattern rule and action constant.
// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute)
func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
-
mr := new(FilterRouter)
mr.tree = NewTree()
mr.pattern = pattern
@@ -426,9 +424,13 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter
}
// add Filter into
-func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error {
- p.filters[pos] = append(p.filters[pos], mr)
+func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {
+ if pos < BeforeStatic || pos > FinishRouter {
+ err = fmt.Errorf("can not find your filter postion")
+ return
+ }
p.enableFilter = true
+ p.filters[pos] = append(p.filters[pos], mr)
return nil
}
@@ -577,20 +579,16 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
return false, ""
}
-func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, urlPath string) (started bool) {
- if p.enableFilter {
- if l, ok := p.filters[pos]; ok {
- for _, filterR := range l {
- if filterR.returnOnOutput && context.ResponseWriter.Started {
- return true
- }
- if ok := filterR.ValidRouter(urlPath, context); ok {
- filterR.filterFunc(context)
- }
- if filterR.returnOnOutput && context.ResponseWriter.Started {
- return true
- }
- }
+func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) {
+ for _, filterR := range p.filters[pos] {
+ if filterR.returnOnOutput && context.ResponseWriter.Started {
+ return true
+ }
+ if ok := filterR.ValidRouter(urlPath, context); ok {
+ filterR.filterFunc(context)
+ }
+ if filterR.returnOnOutput && context.ResponseWriter.Started {
+ return true
}
}
return false
@@ -617,11 +615,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Output.Header("Server", BConfig.ServerName)
}
- var urlPath string
+ var urlPath = r.URL.Path
+
if !BConfig.RouterCaseSensitive {
- urlPath = strings.ToLower(r.URL.Path)
- } else {
- urlPath = r.URL.Path
+ urlPath = strings.ToLower(urlPath)
}
// filter wrong http method
@@ -631,11 +628,12 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
// filter for static file
- if p.execFilter(context, BeforeStatic, urlPath) {
+ if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) {
goto Admin
}
serverStaticRouter(context)
+
if context.ResponseWriter.Started {
findRouter = true
goto Admin
@@ -655,7 +653,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if err != nil {
Error(err)
exception("503", context)
- return
+ goto Admin
}
defer func() {
if context.Input.CruSession != nil {
@@ -663,8 +661,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
}()
}
-
- if p.execFilter(context, BeforeRouter, urlPath) {
+ if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) {
goto Admin
}
@@ -693,7 +690,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if findRouter {
//execute middleware filters
- if p.execFilter(context, BeforeExec, urlPath) {
+ if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
goto Admin
}
isRunnable := false
@@ -794,12 +791,13 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
}
//execute middleware filters
- if p.execFilter(context, AfterExec, urlPath) {
+ if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) {
goto Admin
}
}
-
- p.execFilter(context, FinishRouter, urlPath)
+ if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) {
+ goto Admin
+ }
Admin:
//admin module record QPS
@@ -844,27 +842,26 @@ func (p *ControllerRegister) recoverPanic(context *beecontext.Context) {
}
if !BConfig.RecoverPanic {
panic(err)
- } else {
- if BConfig.EnableErrorsShow {
- if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
- exception(fmt.Sprint(err), context)
- return
- }
+ }
+ if BConfig.EnableErrorsShow {
+ if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
+ exception(fmt.Sprint(err), context)
+ return
}
- var stack string
- Critical("the request url is ", context.Input.URL())
- Critical("Handler crashed with error", err)
- for i := 1; ; i++ {
- _, file, line, ok := runtime.Caller(i)
- if !ok {
- break
- }
- Critical(fmt.Sprintf("%s:%d", file, line))
- stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
- }
- if BConfig.RunMode == DEV {
- showErr(err, context, stack)
+ }
+ var stack string
+ Critical("the request url is ", context.Input.URL())
+ Critical("Handler crashed with error", err)
+ for i := 1; ; i++ {
+ _, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
}
+ Critical(fmt.Sprintf("%s:%d", file, line))
+ stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d", file, line))
+ }
+ if BConfig.RunMode == DEV {
+ showErr(err, context, stack)
}
}
}