1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-05 02:10:55 +00:00

Merge branch 'astaxie/develop' into issue

This commit is contained in:
ysqi 2016-03-23 21:26:55 +08:00
commit c4276d31c5
20 changed files with 373 additions and 219 deletions

17
.github/ISSUE_TEMPLATE vendored Normal file
View File

@ -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?

View File

@ -1,8 +1,7 @@
language: go language: go
go: go:
- tip - 1.6
- 1.6.0
- 1.5.3 - 1.5.3
- 1.4.3 - 1.4.3
services: services:
@ -32,12 +31,12 @@ install:
- go get github.com/siddontang/ledisdb/config - go get github.com/siddontang/ledisdb/config
- go get github.com/siddontang/ledisdb/ledis - go get github.com/siddontang/ledisdb/ledis
- go get golang.org/x/tools/cmd/vet - go get golang.org/x/tools/cmd/vet
- go get github.com/golang/lint/golint
- go get github.com/ssdb/gossdb/ssdb - go get github.com/ssdb/gossdb/ssdb
before_script: before_script:
- sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi" - sh -c "if [ '$ORM_DRIVER' = 'postgres' ]; then psql -c 'create database orm_test;' -U postgres; fi"
- sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi" - sh -c "if [ '$ORM_DRIVER' = 'mysql' ]; then mysql -u root -e 'create database orm_test;'; fi"
- sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi" - sh -c "if [ '$ORM_DRIVER' = 'sqlite' ]; then touch $TRAVIS_BUILD_DIR/orm_test.db; fi"
- sh -c "if [ $(go version) == *1.[5-9]* ]; then go get github.com/golang/lint/golint; golint ./...; fi"
- mkdir -p res/var - mkdir -p res/var
- ./ssdb/ssdb-server ./ssdb/ssdb.conf -d - ./ssdb/ssdb-server ./ssdb/ssdb.conf -d
after_script: after_script:
@ -45,7 +44,4 @@ after_script:
- rm -rf ./res/var/* - rm -rf ./res/var/*
script: script:
- go vet -x ./... - go vet -x ./...
- $HOME/gopath/bin/golint ./...
- go test -v ./... - go test -v ./...
notifications:
webhooks: https://hooks.pubu.im/services/z7m9bvybl3rgtg9

View File

@ -196,7 +196,7 @@ func listConf(rw http.ResponseWriter, r *http.Request) {
BeforeExec: "Before Exec", BeforeExec: "Before Exec",
AfterExec: "After Exec", AfterExec: "After Exec",
FinishRouter: "Finish Router"} { FinishRouter: "Finish Router"} {
if bf, ok := BeeApp.Handlers.filters[k]; ok { if bf := BeeApp.Handlers.filters[k]; len(bf) > 0 {
filterType = fr filterType = fr
filterTypes = append(filterTypes, filterType) filterTypes = append(filterTypes, filterType)
resultList := new([][]string) resultList := new([][]string)

View File

@ -51,6 +51,7 @@ func AddAPPStartHook(hf hookfunc) {
// beego.Run(":8089") // beego.Run(":8089")
// beego.Run("127.0.0.1:8089") // beego.Run("127.0.0.1:8089")
func Run(params ...string) { func Run(params ...string) {
initBeforeHTTPRun() initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" { if len(params) > 0 && params[0] != "" {
@ -74,6 +75,7 @@ func initBeforeHTTPRun() {
AddAPPStartHook(registerDocs) AddAPPStartHook(registerDocs)
AddAPPStartHook(registerTemplate) AddAPPStartHook(registerTemplate)
AddAPPStartHook(registerAdmin) AddAPPStartHook(registerAdmin)
AddAPPStartHook(registerGzip)
for _, hk := range hooks { for _, hk := range hooks {
if err := hk(); err != nil { if err := hk(); err != nil {
@ -87,5 +89,6 @@ func TestBeegoInit(ap string) {
os.Setenv("BEEGO_RUNMODE", "test") os.Setenv("BEEGO_RUNMODE", "test")
appConfigPath = filepath.Join(ap, "conf", "app.conf") appConfigPath = filepath.Join(ap, "conf", "app.conf")
os.Chdir(ap) os.Chdir(ap)
LoadAppConfig(appConfigProvider, appConfigPath)
initBeforeHTTPRun() initBeforeHTTPRun()
} }

View File

@ -316,10 +316,6 @@ func LoadAppConfig(adapterName, configPath string) error {
return fmt.Errorf("the target config file: %s don't exist", configPath) return fmt.Errorf("the target config file: %s don't exist", configPath)
} }
if absConfigPath == appConfigPath {
return nil
}
appConfigPath = absConfigPath appConfigPath = absConfigPath
appConfigProvider = adapterName appConfigProvider = adapterName
@ -353,7 +349,7 @@ func (b *beegoAppConfig) String(key string) string {
} }
func (b *beegoAppConfig) Strings(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 v
} }
return b.innerConfig.Strings(key) return b.innerConfig.Strings(key)

View File

@ -27,6 +27,33 @@ import (
"sync" "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 { type resetWriter interface {
io.Writer io.Writer
Reset(w io.Writer) Reset(w io.Writer)
@ -41,20 +68,20 @@ func (n nopResetWriter) Reset(w io.Writer) {
} }
type acceptEncoder struct { type acceptEncoder struct {
name string name string
levelEncode func(int) resetWriter levelEncode func(int) resetWriter
bestSpeedPool *sync.Pool customCompressLevelPool *sync.Pool
bestCompressionPool *sync.Pool bestCompressionPool *sync.Pool
} }
func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter { 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} return nopResetWriter{wr}
} }
var rwr resetWriter var rwr resetWriter
switch level { switch level {
case flate.BestSpeed: case flate.BestSpeed:
rwr = ac.bestSpeedPool.Get().(resetWriter) rwr = ac.customCompressLevelPool.Get().(resetWriter)
case flate.BestCompression: case flate.BestCompression:
rwr = ac.bestCompressionPool.Get().(resetWriter) rwr = ac.bestCompressionPool.Get().(resetWriter)
default: default:
@ -65,13 +92,18 @@ func (ac acceptEncoder) encode(wr io.Writer, level int) resetWriter {
} }
func (ac acceptEncoder) put(wr resetWriter, level int) { func (ac acceptEncoder) put(wr resetWriter, level int) {
if ac.bestSpeedPool == nil || ac.bestCompressionPool == nil { if ac.customCompressLevelPool == nil || ac.bestCompressionPool == nil {
return return
} }
wr.Reset(nil) wr.Reset(nil)
//notice
//compressionLevel==BestCompression DOES NOT MATTER
//sync.Pool will not memory leak
switch level { switch level {
case flate.BestSpeed: case gzipCompressLevel:
ac.bestSpeedPool.Put(wr) ac.customCompressLevelPool.Put(wr)
case flate.BestCompression: case flate.BestCompression:
ac.bestCompressionPool.Put(wr) ac.bestCompressionPool.Put(wr)
} }
@ -79,28 +111,22 @@ func (ac acceptEncoder) put(wr resetWriter, level int) {
var ( var (
noneCompressEncoder = acceptEncoder{"", nil, nil, nil} noneCompressEncoder = acceptEncoder{"", nil, nil, nil}
gzipCompressEncoder = acceptEncoder{"gzip", gzipCompressEncoder = acceptEncoder{
func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr }, name: "gzip",
&sync.Pool{ levelEncode: func(level int) resetWriter { wr, _ := gzip.NewWriterLevel(nil, level); return wr },
New: func() interface{} { wr, _ := gzip.NewWriterLevel(nil, flate.BestSpeed); 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 }},
&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 //according to the sec :http://tools.ietf.org/html/rfc2616#section-3.5 ,the deflate compress in http is zlib indeed
//deflate //deflate
//The "zlib" format defined in RFC 1950 [31] in combination with //The "zlib" format defined in RFC 1950 [31] in combination with
//the "deflate" compression mechanism described in RFC 1951 [29]. //the "deflate" compression mechanism described in RFC 1951 [29].
deflateCompressEncoder = acceptEncoder{"deflate", deflateCompressEncoder = acceptEncoder{
func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr }, name: "deflate",
&sync.Pool{ levelEncode: func(level int) resetWriter { wr, _ := zlib.NewWriterLevel(nil, level); return wr },
New: func() interface{} { wr, _ := zlib.NewWriterLevel(nil, flate.BestSpeed); 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 }},
&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) // WriteBody reads writes content to writer by the specific encoding(gzip/deflate)
func WriteBody(encoding string, writer io.Writer, content []byte) (bool, string, error) { 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 // 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 { if r == nil {
return "" return ""
} }
return parseEncoding(r) if (getMethodOnly && r.Method == "GET") || includedMethods[r.Method] {
return parseEncoding(r)
}
return ""
} }
type q struct { type q struct {

View File

@ -77,6 +77,7 @@ func (ctx *Context) Redirect(status int, localurl string) {
// Abort stops this request. // Abort stops this request.
// if beego.ErrorMaps exists, panic body. // if beego.ErrorMaps exists, panic body.
func (ctx *Context) Abort(status int, body string) { func (ctx *Context) Abort(status int, body string) {
ctx.Output.SetStatus(status)
panic(body) panic(body)
} }

View File

@ -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. // 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) { func (c *Controller) CustomAbort(status int, body string) {
c.Ctx.Output.Status = status
// first panic from ErrorMaps, is is user defined error functions. // first panic from ErrorMaps, is is user defined error functions.
if _, ok := ErrorMaps[body]; ok { if _, ok := ErrorMaps[body]; ok {
c.Ctx.Output.Status = status
panic(body) panic(body)
} }
// last panic user string // last panic user string
c.Ctx.ResponseWriter.WriteHeader(status)
c.Ctx.ResponseWriter.Write([]byte(body)) c.Ctx.ResponseWriter.Write([]byte(body))
panic(ErrAbort) panic(ErrAbort)
} }

213
error.go
View File

@ -210,159 +210,139 @@ var ErrorMaps = make(map[string]*errorInfo, 10)
// show 401 unauthorized error. // show 401 unauthorized error.
func unauthorized(rw http.ResponseWriter, r *http.Request) { func unauthorized(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 401,
"Title": http.StatusText(401), "<br>The page you have requested can't be authorized."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The page you have requested can't be authorized." + "<br>The credentials you supplied are incorrect"+
"<br>Perhaps you are here because:" + "<br>There are errors in the website address"+
"<br><br><ul>" + "</ul>",
"<br>The credentials you supplied are incorrect" + )
"<br>There are errors in the website address" +
"</ul>")
t.Execute(rw, data)
} }
// show 402 Payment Required // show 402 Payment Required
func paymentRequired(rw http.ResponseWriter, r *http.Request) { func paymentRequired(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 402,
"Title": http.StatusText(402), "<br>The page you have requested Payment Required."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The page you have requested Payment Required." + "<br>The credentials you supplied are incorrect"+
"<br>Perhaps you are here because:" + "<br>There are errors in the website address"+
"<br><br><ul>" + "</ul>",
"<br>The credentials you supplied are incorrect" + )
"<br>There are errors in the website address" +
"</ul>")
t.Execute(rw, data)
} }
// show 403 forbidden error. // show 403 forbidden error.
func forbidden(rw http.ResponseWriter, r *http.Request) { func forbidden(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 403,
"Title": http.StatusText(403), "<br>The page you have requested is forbidden."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The page you have requested is forbidden." + "<br>Your address may be blocked"+
"<br>Perhaps you are here because:" + "<br>The site may be disabled"+
"<br><br><ul>" + "<br>You need to log in"+
"<br>Your address may be blocked" + "</ul>",
"<br>The site may be disabled" + )
"<br>You need to log in" +
"</ul>")
t.Execute(rw, data)
} }
// show 404 notfound error. // show 404 not found error.
func notFound(rw http.ResponseWriter, r *http.Request) { func notFound(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 404,
"Title": http.StatusText(404), "<br>The page you have requested has flown the coop."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The page you have requested has flown the coop." + "<br>The page has moved"+
"<br>Perhaps you are here because:" + "<br>The page no longer exists"+
"<br><br><ul>" + "<br>You were looking for your puppy and got lost"+
"<br>The page has moved" + "<br>You like 404 pages"+
"<br>The page no longer exists" + "</ul>",
"<br>You were looking for your puppy and got lost" + )
"<br>You like 404 pages" +
"</ul>")
t.Execute(rw, data)
} }
// show 405 Method Not Allowed // show 405 Method Not Allowed
func methodNotAllowed(rw http.ResponseWriter, r *http.Request) { func methodNotAllowed(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 405,
"Title": http.StatusText(405), "<br>The method you have requested Not Allowed."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The method you have requested Not Allowed." + "<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI"+
"<br>Perhaps you are here because:" + "<br>The response MUST include an Allow header containing a list of valid methods for the requested resource."+
"<br><br><ul>" + "</ul>",
"<br>The method specified in the Request-Line is not allowed for the resource identified by the Request-URI" + )
"<br>The response MUST include an Allow header containing a list of valid methods for the requested resource." +
"</ul>")
t.Execute(rw, data)
} }
// show 500 internal server error. // show 500 internal server error.
func internalServerError(rw http.ResponseWriter, r *http.Request) { func internalServerError(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 500,
"Title": http.StatusText(500), "<br>The page you have requested is down right now."+
"BeegoVersion": VERSION, "<br><br><ul>"+
} "<br>Please try again later and report the error to the website administrator"+
data["Content"] = template.HTML("<br>The page you have requested is down right now." + "<br></ul>",
"<br><br><ul>" + )
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
t.Execute(rw, data)
} }
// show 501 Not Implemented. // show 501 Not Implemented.
func notImplemented(rw http.ResponseWriter, r *http.Request) { func notImplemented(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 501,
"Title": http.StatusText(504), "<br>The page you have requested is Not Implemented."+
"BeegoVersion": VERSION, "<br><br><ul>"+
} "<br>Please try again later and report the error to the website administrator"+
data["Content"] = template.HTML("<br>The page you have requested is Not Implemented." + "<br></ul>",
"<br><br><ul>" + )
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
t.Execute(rw, data)
} }
// show 502 Bad Gateway. // show 502 Bad Gateway.
func badGateway(rw http.ResponseWriter, r *http.Request) { func badGateway(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 502,
"Title": http.StatusText(502), "<br>The page you have requested is down right now."+
"BeegoVersion": VERSION, "<br><br><ul>"+
} "<br>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."+
data["Content"] = template.HTML("<br>The page you have requested is down right now." + "<br>Please try again later and report the error to the website administrator"+
"<br><br><ul>" + "<br></ul>",
"<br>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." + )
"<br>Please try again later and report the error to the website administrator" +
"<br></ul>")
t.Execute(rw, data)
} }
// show 503 service unavailable error. // show 503 service unavailable error.
func serviceUnavailable(rw http.ResponseWriter, r *http.Request) { func serviceUnavailable(rw http.ResponseWriter, r *http.Request) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) responseError(rw, r,
data := map[string]interface{}{ 503,
"Title": http.StatusText(503), "<br>The page you have requested is unavailable."+
"BeegoVersion": VERSION, "<br>Perhaps you are here because:"+
} "<br><br><ul>"+
data["Content"] = template.HTML("<br>The page you have requested is unavailable." + "<br><br>The page is overloaded"+
"<br>Perhaps you are here because:" + "<br>Please try again later."+
"<br><br><ul>" + "</ul>",
"<br><br>The page is overloaded" + )
"<br>Please try again later." +
"</ul>")
t.Execute(rw, data)
} }
// show 504 Gateway Timeout. // show 504 Gateway Timeout.
func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { func gatewayTimeout(rw http.ResponseWriter, r *http.Request) {
responseError(rw, r,
504,
"<br>The page you have requested is unavailable"+
"<br>Perhaps you are here because:"+
"<br><br><ul>"+
"<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI."+
"<br>Please try again later."+
"</ul>",
)
}
func responseError(rw http.ResponseWriter, r *http.Request, errCode int, errContent string) {
t, _ := template.New("beegoerrortemp").Parse(errtpl) t, _ := template.New("beegoerrortemp").Parse(errtpl)
data := map[string]interface{}{ data := map[string]interface{}{
"Title": http.StatusText(504), "Title": http.StatusText(errCode),
"BeegoVersion": VERSION, "BeegoVersion": VERSION,
"Content": errContent,
} }
data["Content"] = template.HTML("<br>The page you have requested is unavailable." +
"<br>Perhaps you are here because:" +
"<br><br><ul>" +
"<br><br>The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI." +
"<br>Please try again later." +
"</ul>")
t.Execute(rw, data) t.Execute(rw, data)
} }
@ -408,7 +388,10 @@ func exception(errCode string, ctx *context.Context) {
if err == nil { if err == nil {
return v return v
} }
return 503 if ctx.Output.Status == 0 {
return 503
}
return ctx.Output.Status
} }
for _, ec := range []string{errCode, "503", "500"} { for _, ec := range []string{errCode, "503", "500"} {

88
error_test.go Normal file
View File

@ -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()
}
}

View File

@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"github.com/astaxie/beego/context"
"github.com/astaxie/beego/session" "github.com/astaxie/beego/session"
) )
@ -91,3 +92,14 @@ func registerAdmin() error {
} }
return nil 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
}

View File

@ -44,7 +44,7 @@ func NewNamespace(prefix string, params ...LinkNamespace) *Namespace {
return ns return ns
} }
// Cond set condtion function // Cond set condition function
// if cond return true can run this namespace, else can't // if cond return true can run this namespace, else can't
// usage: // usage:
// ns.Cond(func (ctx *context.Context) bool{ // ns.Cond(func (ctx *context.Context) bool{
@ -60,7 +60,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace {
exception("405", ctx) exception("405", ctx)
} }
} }
if v, ok := n.handlers.filters[BeforeRouter]; ok { if v := n.handlers.filters[BeforeRouter]; len(v) > 0 {
mr := new(FilterRouter) mr := new(FilterRouter)
mr.tree = NewTree() mr.tree = NewTree()
mr.pattern = "*" mr.pattern = "*"

View File

@ -181,7 +181,7 @@ func (d *dbBase) collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Val
} }
default: default:
switch { switch {
case fi.fieldType&IsPostiveIntegerField > 0: case fi.fieldType&IsPositiveIntegerField > 0:
if field.Kind() == reflect.Ptr { if field.Kind() == reflect.Ptr {
if field.IsNil() { if field.IsNil() {
value = nil value = nil
@ -516,7 +516,7 @@ func (d *dbBase) Delete(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.
} }
if num > 0 { if num > 0 {
if mi.fields.pk.auto { 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) ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(0)
} else { } else {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0) ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(0)
@ -1140,7 +1140,7 @@ setValue:
tErr = err tErr = err
goto end goto end
} }
if fieldType&IsPostiveIntegerField > 0 { if fieldType&IsPositiveIntegerField > 0 {
v, _ := str.Uint64() v, _ := str.Uint64()
value = v value = v
} else { } else {
@ -1292,7 +1292,7 @@ setValue:
field.Set(reflect.ValueOf(&v)) field.Set(reflect.ValueOf(&v))
} }
case fieldType&IsIntegerField > 0: case fieldType&IsIntegerField > 0:
if fieldType&IsPostiveIntegerField > 0 { if fieldType&IsPositiveIntegerField > 0 {
if isNative { if isNative {
if value == nil { if value == nil {
value = uint64(0) value = uint64(0)

View File

@ -33,13 +33,13 @@ func getExistPk(mi *modelInfo, ind reflect.Value) (column string, value interfac
fi := mi.fields.pk fi := mi.fields.pk
v := ind.FieldByIndex(fi.fieldIndex) v := ind.FieldByIndex(fi.fieldIndex)
if fi.fieldType&IsPostiveIntegerField > 0 { if fi.fieldType&IsPositiveIntegerField > 0 {
vu := v.Uint() vu := v.Uint()
exist = vu > 0 exist = vu > 0
value = vu value = vu
} else if fi.fieldType&IsIntegerField > 0 { } else if fi.fieldType&IsIntegerField > 0 {
vu := v.Int() vu := v.Int()
exist = vu > 0 exist = true
value = vu value = vu
} else { } else {
vu := v.String() vu := v.String()

View File

@ -46,10 +46,10 @@ const (
// Define some logic enum // Define some logic enum
const ( const (
IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5 IsIntegerField = ^-TypePositiveBigIntegerField >> 4 << 5
IsPostiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9 IsPositiveIntegerField = ^-TypePositiveBigIntegerField >> 8 << 9
IsRelField = ^-RelReverseMany >> 14 << 15 IsRelField = ^-RelReverseMany >> 14 << 15
IsFieldType = ^-RelReverseMany<<1 + 1 IsFieldType = ^-RelReverseMany<<1 + 1
) )
// BooleanField A true/false field. // BooleanField A true/false field.

View File

@ -387,6 +387,11 @@ func NewInLineOneToOne() *InLineOneToOne {
return new(InLineOneToOne) return new(InLineOneToOne)
} }
type IntegerPk struct {
Id int64 `orm:"pk"`
Value string
}
var DBARGS = struct { var DBARGS = struct {
Driver string Driver string
Source string Source string

View File

@ -159,7 +159,7 @@ func (o *orm) Insert(md interface{}) (int64, error) {
// set auto pk field // set auto pk field
func (o *orm) setPk(mi *modelInfo, ind reflect.Value, id int64) { func (o *orm) setPk(mi *modelInfo, ind reflect.Value, id int64) {
if mi.fields.pk.auto { 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)) ind.FieldByIndex(mi.fields.pk.fieldIndex).SetUint(uint64(id))
} else { } else {
ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id) ind.FieldByIndex(mi.fields.pk.fieldIndex).SetInt(id)

View File

@ -50,7 +50,7 @@ func (o *insertSet) Insert(md interface{}) (int64, error) {
} }
if id > 0 { if id > 0 {
if o.mi.fields.pk.auto { 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)) ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetUint(uint64(id))
} else { } else {
ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetInt(id) ind.FieldByIndex(o.mi.fields.pk.fieldIndex).SetInt(id)

View File

@ -19,6 +19,7 @@ import (
"database/sql" "database/sql"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
@ -189,6 +190,7 @@ func TestSyncDb(t *testing.T) {
RegisterModel(new(GroupPermissions)) RegisterModel(new(GroupPermissions))
RegisterModel(new(InLine)) RegisterModel(new(InLine))
RegisterModel(new(InLineOneToOne)) RegisterModel(new(InLineOneToOne))
RegisterModel(new(IntegerPk))
err := RunSyncdb("default", true, Debug) err := RunSyncdb("default", true, Debug)
throwFail(t, err) throwFail(t, err)
@ -210,6 +212,7 @@ func TestRegisterModels(t *testing.T) {
RegisterModel(new(GroupPermissions)) RegisterModel(new(GroupPermissions))
RegisterModel(new(InLine)) RegisterModel(new(InLine))
RegisterModel(new(InLineOneToOne)) RegisterModel(new(InLineOneToOne))
RegisterModel(new(IntegerPk))
BootStrap() BootStrap()
@ -1991,3 +1994,22 @@ func TestInLineOneToOne(t *testing.T) {
throwFail(t, AssertIs(rinline.Name, name)) throwFail(t, AssertIs(rinline.Name, name))
throwFail(t, AssertIs(rinline.Email, email)) 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))
}
}

View File

@ -114,7 +114,7 @@ type controllerInfo struct {
type ControllerRegister struct { type ControllerRegister struct {
routers map[string]*Tree routers map[string]*Tree
enableFilter bool enableFilter bool
filters map[int][]*FilterRouter filters [FinishRouter + 1][]*FilterRouter
pool sync.Pool pool sync.Pool
} }
@ -122,7 +122,6 @@ type ControllerRegister struct {
func NewControllerRegister() *ControllerRegister { func NewControllerRegister() *ControllerRegister {
cr := &ControllerRegister{ cr := &ControllerRegister{
routers: make(map[string]*Tree), routers: make(map[string]*Tree),
filters: make(map[int][]*FilterRouter),
} }
cr.pool.New = func() interface{} { cr.pool.New = func() interface{} {
return beecontext.NewContext() 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. // 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) // 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 { func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error {
mr := new(FilterRouter) mr := new(FilterRouter)
mr.tree = NewTree() mr.tree = NewTree()
mr.pattern = pattern mr.pattern = pattern
@ -426,9 +424,13 @@ func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter Filter
} }
// add Filter into // add Filter into
func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error { func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) (err error) {
p.filters[pos] = append(p.filters[pos], mr) if pos < BeforeStatic || pos > FinishRouter {
err = fmt.Errorf("can not find your filter postion")
return
}
p.enableFilter = true p.enableFilter = true
p.filters[pos] = append(p.filters[pos], mr)
return nil return nil
} }
@ -577,20 +579,16 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin
return false, "" return false, ""
} }
func (p *ControllerRegister) execFilter(context *beecontext.Context, pos int, urlPath string) (started bool) { func (p *ControllerRegister) execFilter(context *beecontext.Context, urlPath string, pos int) (started bool) {
if p.enableFilter { for _, filterR := range p.filters[pos] {
if l, ok := p.filters[pos]; ok { if filterR.returnOnOutput && context.ResponseWriter.Started {
for _, filterR := range l { return true
if filterR.returnOnOutput && context.ResponseWriter.Started { }
return true if ok := filterR.ValidRouter(urlPath, context); ok {
} filterR.filterFunc(context)
if ok := filterR.ValidRouter(urlPath, context); ok { }
filterR.filterFunc(context) if filterR.returnOnOutput && context.ResponseWriter.Started {
} return true
if filterR.returnOnOutput && context.ResponseWriter.Started {
return true
}
}
} }
} }
return false return false
@ -617,11 +615,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
context.Output.Header("Server", BConfig.ServerName) context.Output.Header("Server", BConfig.ServerName)
} }
var urlPath string var urlPath = r.URL.Path
if !BConfig.RouterCaseSensitive { if !BConfig.RouterCaseSensitive {
urlPath = strings.ToLower(r.URL.Path) urlPath = strings.ToLower(urlPath)
} else {
urlPath = r.URL.Path
} }
// filter wrong http method // filter wrong http method
@ -631,11 +628,12 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
// filter for static file // filter for static file
if p.execFilter(context, BeforeStatic, urlPath) { if len(p.filters[BeforeStatic]) > 0 && p.execFilter(context, urlPath, BeforeStatic) {
goto Admin goto Admin
} }
serverStaticRouter(context) serverStaticRouter(context)
if context.ResponseWriter.Started { if context.ResponseWriter.Started {
findRouter = true findRouter = true
goto Admin goto Admin
@ -655,7 +653,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if err != nil { if err != nil {
Error(err) Error(err)
exception("503", context) exception("503", context)
return goto Admin
} }
defer func() { defer func() {
if context.Input.CruSession != nil { if context.Input.CruSession != nil {
@ -663,8 +661,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
}() }()
} }
if len(p.filters[BeforeRouter]) > 0 && p.execFilter(context, urlPath, BeforeRouter) {
if p.execFilter(context, BeforeRouter, urlPath) {
goto Admin goto Admin
} }
@ -693,7 +690,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
if findRouter { if findRouter {
//execute middleware filters //execute middleware filters
if p.execFilter(context, BeforeExec, urlPath) { if len(p.filters[BeforeExec]) > 0 && p.execFilter(context, urlPath, BeforeExec) {
goto Admin goto Admin
} }
isRunnable := false isRunnable := false
@ -794,12 +791,13 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request)
} }
//execute middleware filters //execute middleware filters
if p.execFilter(context, AfterExec, urlPath) { if len(p.filters[AfterExec]) > 0 && p.execFilter(context, urlPath, AfterExec) {
goto Admin goto Admin
} }
} }
if len(p.filters[FinishRouter]) > 0 && p.execFilter(context, urlPath, FinishRouter) {
p.execFilter(context, FinishRouter, urlPath) goto Admin
}
Admin: Admin:
//admin module record QPS //admin module record QPS
@ -844,27 +842,26 @@ func (p *ControllerRegister) recoverPanic(context *beecontext.Context) {
} }
if !BConfig.RecoverPanic { if !BConfig.RecoverPanic {
panic(err) panic(err)
} else { }
if BConfig.EnableErrorsShow { if BConfig.EnableErrorsShow {
if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok {
exception(fmt.Sprint(err), context) exception(fmt.Sprint(err), context)
return return
}
} }
var stack string }
Critical("the request url is ", context.Input.URL()) var stack string
Critical("Handler crashed with error", err) Critical("the request url is ", context.Input.URL())
for i := 1; ; i++ { Critical("Handler crashed with error", err)
_, file, line, ok := runtime.Caller(i) for i := 1; ; i++ {
if !ok { _, file, line, ok := runtime.Caller(i)
break 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)
} }
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)
} }
} }
} }