From 647e6ae1c42fade1283a095ac9298919785cb6c6 Mon Sep 17 00:00:00 2001 From: Lei Cao Date: Mon, 24 Nov 2014 23:21:03 +0800 Subject: [PATCH 001/436] Added JWT plugin --- plugins/jwt/jwt.go | 135 +++++++++++++++++++++++++++++++++++ plugins/jwt/jwt_test.go | 88 +++++++++++++++++++++++ plugins/jwt/test/jwt.rsa | 15 ++++ plugins/jwt/test/jwt.rsa.pub | 6 ++ 4 files changed, 244 insertions(+) create mode 100644 plugins/jwt/jwt.go create mode 100644 plugins/jwt/jwt_test.go create mode 100644 plugins/jwt/test/jwt.rsa create mode 100644 plugins/jwt/test/jwt.rsa.pub diff --git a/plugins/jwt/jwt.go b/plugins/jwt/jwt.go new file mode 100644 index 00000000..fd064686 --- /dev/null +++ b/plugins/jwt/jwt.go @@ -0,0 +1,135 @@ +// 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 jwt provides JWT (Json Web Token) authentication +// +// Usage +// In file main.go +// +// import ( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/jwt" +// ) +// +// func main() { +// // JWT for Url matching /v1/* +// // PrivateKeyPath: The path for the private RSA key used by JWT +// // PublicKeyPath: The path for the public RSA key used by JWT +// // The list of Urls should be excluded from the JWT Auth +// beego.InsertFilter("/v1/*", beego.BeforeRouter, jwt.AuthRequest(&jwt.Options{ +// PrivateKeyPath: "conf/beeblog.rsa", +// PublicKeyPath: "conf/beeblog.rsa.pub", +// WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, +// })) +// beego.Run() +// } +// +// In file routers/router.go +// +// import ( +// "github.com/astaxie/beego" +// "github.com/astaxie/beego/plugins/jwt" +// ) +// func init() { +// ns := beego.NSNamespace("/jwt", +// beego.NSInclude( +// &jwt.JwtController{}, +// ), +// ) +// beego.AddNamespace(ns) +// } +// + +package jwt + +import ( + "github.com/astaxie/beego" + "github.com/astaxie/beego/context" + "github.com/astaxie/beego/logs" + goJwt "github.com/dgrijalva/jwt-go" + "io/ioutil" + "net/http" + "time" +) + +// Options for the JWT Auth +type Options struct { + PrivateKeyPath string + PublicKeyPath string + WhiteList []string +} + +var RSAKeys struct { + PrivateKey []byte + PublicKey []byte +} + +func AuthRequest(o *Options) beego.FilterFunc { + RSAKeys.PrivateKey, _ = ioutil.ReadFile(o.PrivateKeyPath) + RSAKeys.PublicKey, _ = ioutil.ReadFile(o.PublicKeyPath) + + return func(ctx *context.Context) { + // :TODO the url patterns should be considered here. + // Shouldn't only use the string equal + for _, method := range o.WhiteList { + if method == ctx.Request.URL.Path { + return + } + } + + parsedToken, err := goJwt.ParseFromRequest(ctx.Request, func(t *goJwt.Token) (interface{}, error) { + return RSAKeys.PublicKey, nil + }) + + if err == nil && parsedToken.Valid { + ctx.Output.SetStatus(http.StatusOK) + } else { + ctx.Output.SetStatus(http.StatusUnauthorized) + } + + } +} + +// oprations for Jwt +type JwtController struct { + beego.Controller +} + +func (this *JwtController) URLMapping() { + this.Mapping("IssueToken", this.IssueToken) +} + +// @Title IssueToken +// @Description Issue a Json Web Token +// @Success 200 string +// @Failure 403 no privilege to access +// @Failure 500 server inner error +// @router /issue-token [get] +func (this *JwtController) IssueToken() { + this.Data["json"] = CreateToken() + this.ServeJson() +} + +func CreateToken() map[string]string { + log := logs.NewLogger(10000) + log.SetLogger("console", "") + + token := goJwt.New(goJwt.GetSigningMethod("RS256")) // Create a Token that will be signed with RSA 256. + token.Claims["ID"] = "This is my super fake ID" + token.Claims["exp"] = time.Now().Unix() + 36000 + // The claims object allows you to store information in the actual token. + tokenString, _ := token.SignedString(RSAKeys.PrivateKey) + // tokenString Contains the actual token you should share with your client. + return map[string]string{"token": tokenString} +} diff --git a/plugins/jwt/jwt_test.go b/plugins/jwt/jwt_test.go new file mode 100644 index 00000000..dd8cafe7 --- /dev/null +++ b/plugins/jwt/jwt_test.go @@ -0,0 +1,88 @@ +package jwt + +import ( + "github.com/astaxie/beego" + "net/http" + "net/http/httptest" + "testing" +) + +func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) { + request, _ := http.NewRequest(method, path, nil) + recorder := httptest.NewRecorder() + + return recorder, request +} + +func Test_IssueTokenAction(t *testing.T) { + url := "/v1/jwt/issue-token" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/v1/jwt/issue-token", &JwtController{}, "get:IssueToken") + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusOK { + t.Errorf("Shoud return 200") + } +} + +func (tc *JwtController) Foo() { + tc.Ctx.Output.Body([]byte("ok")) +} + +func Test_AuthRequestWithAuthorizationHeader(t *testing.T) { + + url := "/foo" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/foo", &JwtController{}, "get:Foo") + newToken := CreateToken() + + rw, r := testRequest("GET", url) + r.Header.Add("Authorization", "Bearer "+newToken["token"]) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusOK { + t.Errorf("Shoud return 200") + } + if rw.Body.String() != "ok" { + t.Errorf("Should output ok") + } +} + +func Test_AuthRequestWithoutAuthorizationHeader(t *testing.T) { + url := "/foo" + + mux := beego.NewControllerRegister() + + mux.InsertFilter("*", beego.BeforeRouter, AuthRequest(&Options{ + PrivateKeyPath: "test/jwt.rsa", + PublicKeyPath: "test/jwt.rsa.pub", + WhiteList: []string{"/v1/jwt/issue-token", "/docs"}, + })) + + mux.Add("/foo", &JwtController{}, "get:Foo") + + rw, r := testRequest("GET", url) + mux.ServeHTTP(rw, r) + + if rw.Code != http.StatusUnauthorized { + t.Errorf("Shoud return 401") + } +} diff --git a/plugins/jwt/test/jwt.rsa b/plugins/jwt/test/jwt.rsa new file mode 100644 index 00000000..8d584fc6 --- /dev/null +++ b/plugins/jwt/test/jwt.rsa @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICWwIBAAKBgQCdu+23Y/0J/FTQKnIPnxupoOo9/OYCv90DPXN/KLLRAMjYzgcC +DsBST2xVR5jlimI/gyfCpVB62dwpSzzr0cA3MoDhbaGWuTdQUX9zmiLoQ4I7X6h0 +dwyiihOz+CzOMlAg5+qBhiTGcKvIFlfEc1FUcn/tB3PVRG9j6B1Ibz5CnQIDAQAB +AoGAFGg+/i4ai9MwqeoD7c95Bb5C8BgrLgnir0uhCL+cOvwuABbPw01jRoLuEi58 +Mp5vzaXLXByFSA+ts03/qMbvZkDGac5g5kLli5TjHIONMxVBrdfGQ1+OApnaPayN +N+HYjZKs6xao6J5iFqfA0FqzDR9kQhUoeosdQoo1GlxDckECQQDO/0LJrFiLzYWe +qS/DxfAnFu2BlClKZjxRJ3vIkRRaON6HPl8BeJW901bFKG5+WSfO+OwQ9egWaf3X +fFm/oEHRAkEAwxMor4fOkBZbL4KPW7sen169vwnXuYusqj0t3dIeiIVrCigkOMT4 +OvX/63u4CTdXh1D5u/4Z/1HTYH92VCP7DQJAJPxbNKnE0IYSf/z++d4eQP3JxkNw +9Ug7Msz5QycZGd3bdRLh6uNe7iIa+PN2esD3afX0SDuIEqkxoBUp/CFoYQJAUmi3 +mV+/7bLkFrALK+9iwmTdt+TKk4HkEY8C32CysW3biFDo7GqZix79XFfJqWsNuQaG +WdrA1NGWgH+YV3dTyQJAIWEZGAuUXRkQB20LfjGzpsKgQFbqjTisMS0qe3JjnDwu +0JR8sYXapgeEEEisH+OtkZKIfyeFOwoUyNC83bcvgw== +-----END RSA PRIVATE KEY----- diff --git a/plugins/jwt/test/jwt.rsa.pub b/plugins/jwt/test/jwt.rsa.pub new file mode 100644 index 00000000..3fdd8ce7 --- /dev/null +++ b/plugins/jwt/test/jwt.rsa.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdu+23Y/0J/FTQKnIPnxupoOo9 +/OYCv90DPXN/KLLRAMjYzgcCDsBST2xVR5jlimI/gyfCpVB62dwpSzzr0cA3MoDh +baGWuTdQUX9zmiLoQ4I7X6h0dwyiihOz+CzOMlAg5+qBhiTGcKvIFlfEc1FUcn/t +B3PVRG9j6B1Ibz5CnQIDAQAB +-----END PUBLIC KEY----- From 223f57bb4cfe27d844c565d248ba99881766dec5 Mon Sep 17 00:00:00 2001 From: Hubery Date: Fri, 6 Mar 2015 14:12:24 +0800 Subject: [PATCH 002/436] add JsonBody --- httplib/httplib.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index 7ff2f1d2..bd8bc776 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -253,6 +253,22 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { return b } + +// JsonBody adds request raw body encoding by JSON. +func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) { + if b.req.Body == nil && obj != nil { + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(obj); err != nil { + return b, err + } + b.req.Body = ioutil.NopCloser(buf) + b.req.ContentLength = int64(buf.Len()) + b.req.Header.Set("Content-Type", "application/json") + } + return b, nil +} + func (b *BeegoHttpRequest) buildUrl(paramBody string) { // build GET url with query string if b.req.Method == "GET" && len(paramBody) > 0 { From 2d26f7df2f44e8df1de5dbf20468c0caa6c1dea7 Mon Sep 17 00:00:00 2001 From: supiyun Date: Mon, 16 Mar 2015 17:40:55 +0800 Subject: [PATCH 003/436] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81reload?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当页面放置一段时间,验证码将从缓存中失效。当用户再来刷新验证码将出现验证码404。对于reload操作应该直接生成验证码。 --- utils/captcha/captcha.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/utils/captcha/captcha.go b/utils/captcha/captcha.go index 62adc81d..14c6c3b3 100644 --- a/utils/captcha/captcha.go +++ b/utils/captcha/captcha.go @@ -132,15 +132,6 @@ func (c *Captcha) Handler(ctx *context.Context) { key := c.key(id) - if v, ok := c.store.Get(key).([]byte); ok { - chars = v - } else { - ctx.Output.SetStatus(404) - ctx.WriteString("captcha not found") - return - } - - // reload captcha if len(ctx.Input.Query("reload")) > 0 { chars = c.genRandChars() if err := c.store.Put(key, chars, c.Expiration); err != nil { @@ -149,6 +140,14 @@ func (c *Captcha) Handler(ctx *context.Context) { beego.Error("Reload Create Captcha Error:", err) return } + } else { + if v, ok := c.store.Get(key).([]byte); ok { + chars = v + } else { + ctx.Output.SetStatus(404) + ctx.WriteString("captcha not found") + return + } } img := NewImage(chars, c.StdWidth, c.StdHeight) From 8aa9455900261b9123d65c88de8afb80915c1499 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 19 Mar 2015 22:29:01 -0700 Subject: [PATCH 004/436] gofmt -s & go_vet --- cache/memory.go | 2 +- orm/db.go | 12 +- orm/models_test.go | 8 +- orm/orm_raw.go | 4 +- orm/orm_test.go | 32 +-- orm/utils.go | 4 +- router.go | 6 +- session/memcache/sess_memcache.go | 1 - session/sess_mem.go | 398 +++++++++++++++--------------- utils/mail.go | 2 +- utils/pagination/paginator.go | 6 +- utils/slice.go | 2 +- validation/util.go | 2 +- 13 files changed, 239 insertions(+), 240 deletions(-) diff --git a/cache/memory.go b/cache/memory.go index b90d227c..0233be7d 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -202,7 +202,7 @@ func (bc *MemoryCache) vaccuum() { if bc.items == nil { return } - for name, _ := range bc.items { + for name := range bc.items { bc.item_expired(name) } } diff --git a/orm/db.go b/orm/db.go index 10f65fee..060d83bc 100644 --- a/orm/db.go +++ b/orm/db.go @@ -324,7 +324,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q) refs := make([]interface{}, colsNum) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } @@ -423,7 +423,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s Q := d.ins.TableQuote() marks := make([]string, len(names)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } @@ -693,7 +693,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con } marks := make([]string, len(args)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) @@ -824,7 +824,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } refs := make([]interface{}, colsNum) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } @@ -964,7 +964,7 @@ func (d *dbBase) GenerateOperatorSql(mi *modelInfo, fi *fieldInfo, operator stri switch operator { case "in": marks := make([]string, len(params)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) @@ -1460,7 +1460,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond } refs := make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } diff --git a/orm/models_test.go b/orm/models_test.go index 1a92ef5d..6ca9590c 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -242,14 +242,14 @@ type User struct { func (u *User) TableIndex() [][]string { return [][]string{ - []string{"Id", "UserName"}, - []string{"Id", "Created"}, + {"Id", "UserName"}, + {"Id", "Created"}, } } func (u *User) TableUnique() [][]string { return [][]string{ - []string{"UserName", "Email"}, + {"UserName", "Email"}, } } @@ -287,7 +287,7 @@ type Post struct { func (u *Post) TableIndex() [][]string { return [][]string{ - []string{"Id", "Created"}, + {"Id", "Created"}, } } diff --git a/orm/orm_raw.go b/orm/orm_raw.go index 1393d414..1452d6fc 100644 --- a/orm/orm_raw.go +++ b/orm/orm_raw.go @@ -585,7 +585,7 @@ func (o *rawSet) readValues(container interface{}, needCols []string) (int64, er cols = columns refs = make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { var ref sql.NullString refs[i] = &ref @@ -711,7 +711,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in } else { cols = columns refs = make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { if keyCol == cols[i] { keyIndex = i } diff --git a/orm/orm_test.go b/orm/orm_test.go index e1c8e0f0..14eadabd 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -586,29 +586,29 @@ func TestInsertTestData(t *testing.T) { throwFail(t, AssertIs(id, 4)) tags := []*Tag{ - &Tag{Name: "golang", BestPost: &Post{Id: 2}}, - &Tag{Name: "example"}, - &Tag{Name: "format"}, - &Tag{Name: "c++"}, + {Name: "golang", BestPost: &Post{Id: 2}}, + {Name: "example"}, + {Name: "format"}, + {Name: "c++"}, } posts := []*Post{ - &Post{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand. + {User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand. This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`}, - &Post{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`}, - &Post{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide. + {User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`}, + {User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide. With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`}, - &Post{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code. + {User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code. The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`}, } comments := []*Comment{ - &Comment{Post: posts[0], Content: "a comment"}, - &Comment{Post: posts[1], Content: "yes"}, - &Comment{Post: posts[1]}, - &Comment{Post: posts[1]}, - &Comment{Post: posts[2]}, - &Comment{Post: posts[2]}, + {Post: posts[0], Content: "a comment"}, + {Post: posts[1], Content: "yes"}, + {Post: posts[1]}, + {Post: posts[1]}, + {Post: posts[2]}, + {Post: posts[2]}, } for _, tag := range tags { @@ -1248,7 +1248,7 @@ func TestQueryM2M(t *testing.T) { post := Post{Id: 4} m2m := dORM.QueryM2M(&post, "Tags") - tag1 := []*Tag{&Tag{Name: "TestTag1"}, &Tag{Name: "TestTag2"}} + tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}} tag2 := &Tag{Name: "TestTag3"} tag3 := []interface{}{&Tag{Name: "TestTag4"}} @@ -1311,7 +1311,7 @@ func TestQueryM2M(t *testing.T) { m2m = dORM.QueryM2M(&tag, "Posts") - post1 := []*Post{&Post{Title: "TestPost1"}, &Post{Title: "TestPost2"}} + post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}} post2 := &Post{Title: "TestPost3"} post3 := []interface{}{&Post{Title: "TestPost4"}} diff --git a/orm/utils.go b/orm/utils.go index df6147c1..88168763 100644 --- a/orm/utils.go +++ b/orm/utils.go @@ -195,7 +195,7 @@ func snakeString(s string) string { } data = append(data, d) } - return strings.ToLower(string(data[:len(data)])) + return strings.ToLower(string(data[:])) } // camel string, xx_yy to XxYy @@ -220,7 +220,7 @@ func camelString(s string) string { } data = append(data, d) } - return string(data[:len(data)]) + return string(data[:]) } type argString []string diff --git a/router.go b/router.go index b9d649a2..c1bda637 100644 --- a/router.go +++ b/router.go @@ -88,7 +88,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool { if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { return true } - for prefix, _ := range StaticDir { + for prefix := range StaticDir { if strings.HasPrefix(requestPath, prefix) { return true } @@ -171,7 +171,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM p.addToRouter(m, pattern, route) } } else { - for k, _ := range methods { + for k := range methods { if k == "*" { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) @@ -332,7 +332,7 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { methods[strings.ToUpper(method)] = strings.ToUpper(method) } route.methods = methods - for k, _ := range methods { + for k := range methods { if k == "*" { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index 5827a0a9..24aecb82 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -179,7 +179,6 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.SessionSto } else { client.Delete(oldsid) item.Key = sid - item.Value = item.Value item.Expiration = int32(rp.maxlifetime) client.Set(item) contain = item.Value diff --git a/session/sess_mem.go b/session/sess_mem.go index 627a3246..dd066703 100644 --- a/session/sess_mem.go +++ b/session/sess_mem.go @@ -1,199 +1,199 @@ -// 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 session - -import ( - "container/list" - "net/http" - "sync" - "time" -) - -var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} - -// memory session store. -// it saved sessions in a map in memory. -type MemSessionStore struct { - sid string //session id - timeAccessed time.Time //last access time - value map[interface{}]interface{} //session store - lock sync.RWMutex -} - -// set value to memory session -func (st *MemSessionStore) Set(key, value interface{}) error { - st.lock.Lock() - defer st.lock.Unlock() - st.value[key] = value - return nil -} - -// get value from memory session by key -func (st *MemSessionStore) Get(key interface{}) interface{} { - st.lock.RLock() - defer st.lock.RUnlock() - if v, ok := st.value[key]; ok { - return v - } else { - return nil - } -} - -// delete in memory session by key -func (st *MemSessionStore) Delete(key interface{}) error { - st.lock.Lock() - defer st.lock.Unlock() - delete(st.value, key) - return nil -} - -// clear all values in memory session -func (st *MemSessionStore) Flush() error { - st.lock.Lock() - defer st.lock.Unlock() - st.value = make(map[interface{}]interface{}) - return nil -} - -// get this id of memory session store -func (st *MemSessionStore) SessionID() string { - return st.sid -} - -// Implement method, no used. -func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { -} - -type MemProvider struct { - lock sync.RWMutex // locker - sessions map[string]*list.Element // map in memory - list *list.List // for gc - maxlifetime int64 - savePath string -} - -// init memory session -func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { - pder.maxlifetime = maxlifetime - pder.savePath = savePath - return nil -} - -// get memory session store by sid -func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { - pder.lock.RLock() - if element, ok := pder.sessions[sid]; ok { - go pder.SessionUpdate(sid) - pder.lock.RUnlock() - return element.Value.(*MemSessionStore), nil - } else { - pder.lock.RUnlock() - pder.lock.Lock() - newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} - element := pder.list.PushBack(newsess) - pder.sessions[sid] = element - pder.lock.Unlock() - return newsess, nil - } -} - -// check session store exist in memory session by sid -func (pder *MemProvider) SessionExist(sid string) bool { - pder.lock.RLock() - defer pder.lock.RUnlock() - if _, ok := pder.sessions[sid]; ok { - return true - } else { - return false - } -} - -// generate new sid for session store in memory session -func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { - pder.lock.RLock() - if element, ok := pder.sessions[oldsid]; ok { - go pder.SessionUpdate(oldsid) - pder.lock.RUnlock() - pder.lock.Lock() - element.Value.(*MemSessionStore).sid = sid - pder.sessions[sid] = element - delete(pder.sessions, oldsid) - pder.lock.Unlock() - return element.Value.(*MemSessionStore), nil - } else { - pder.lock.RUnlock() - pder.lock.Lock() - newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} - element := pder.list.PushBack(newsess) - pder.sessions[sid] = element - pder.lock.Unlock() - return newsess, nil - } -} - -// delete session store in memory session by id -func (pder *MemProvider) SessionDestroy(sid string) error { - pder.lock.Lock() - defer pder.lock.Unlock() - if element, ok := pder.sessions[sid]; ok { - delete(pder.sessions, sid) - pder.list.Remove(element) - return nil - } - return nil -} - -// clean expired session stores in memory session -func (pder *MemProvider) SessionGC() { - pder.lock.RLock() - for { - element := pder.list.Back() - if element == nil { - break - } - if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() { - pder.lock.RUnlock() - pder.lock.Lock() - pder.list.Remove(element) - delete(pder.sessions, element.Value.(*MemSessionStore).sid) - pder.lock.Unlock() - pder.lock.RLock() - } else { - break - } - } - pder.lock.RUnlock() -} - -// get count number of memory session -func (pder *MemProvider) SessionAll() int { - return pder.list.Len() -} - -// expand time of session store by id in memory session -func (pder *MemProvider) SessionUpdate(sid string) error { - pder.lock.Lock() - defer pder.lock.Unlock() - if element, ok := pder.sessions[sid]; ok { - element.Value.(*MemSessionStore).timeAccessed = time.Now() - pder.list.MoveToFront(element) - return nil - } - return nil -} - -func init() { - Register("memory", mempder) -} +// 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 session + +import ( + "container/list" + "net/http" + "sync" + "time" +) + +var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} + +// memory session store. +// it saved sessions in a map in memory. +type MemSessionStore struct { + sid string //session id + timeAccessed time.Time //last access time + value map[interface{}]interface{} //session store + lock sync.RWMutex +} + +// set value to memory session +func (st *MemSessionStore) Set(key, value interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + st.value[key] = value + return nil +} + +// get value from memory session by key +func (st *MemSessionStore) Get(key interface{}) interface{} { + st.lock.RLock() + defer st.lock.RUnlock() + if v, ok := st.value[key]; ok { + return v + } else { + return nil + } +} + +// delete in memory session by key +func (st *MemSessionStore) Delete(key interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + delete(st.value, key) + return nil +} + +// clear all values in memory session +func (st *MemSessionStore) Flush() error { + st.lock.Lock() + defer st.lock.Unlock() + st.value = make(map[interface{}]interface{}) + return nil +} + +// get this id of memory session store +func (st *MemSessionStore) SessionID() string { + return st.sid +} + +// Implement method, no used. +func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { +} + +type MemProvider struct { + lock sync.RWMutex // locker + sessions map[string]*list.Element // map in memory + list *list.List // for gc + maxlifetime int64 + savePath string +} + +// init memory session +func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { + pder.maxlifetime = maxlifetime + pder.savePath = savePath + return nil +} + +// get memory session store by sid +func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { + pder.lock.RLock() + if element, ok := pder.sessions[sid]; ok { + go pder.SessionUpdate(sid) + pder.lock.RUnlock() + return element.Value.(*MemSessionStore), nil + } else { + pder.lock.RUnlock() + pder.lock.Lock() + newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} + element := pder.list.PushBack(newsess) + pder.sessions[sid] = element + pder.lock.Unlock() + return newsess, nil + } +} + +// check session store exist in memory session by sid +func (pder *MemProvider) SessionExist(sid string) bool { + pder.lock.RLock() + defer pder.lock.RUnlock() + if _, ok := pder.sessions[sid]; ok { + return true + } else { + return false + } +} + +// generate new sid for session store in memory session +func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { + pder.lock.RLock() + if element, ok := pder.sessions[oldsid]; ok { + go pder.SessionUpdate(oldsid) + pder.lock.RUnlock() + pder.lock.Lock() + element.Value.(*MemSessionStore).sid = sid + pder.sessions[sid] = element + delete(pder.sessions, oldsid) + pder.lock.Unlock() + return element.Value.(*MemSessionStore), nil + } else { + pder.lock.RUnlock() + pder.lock.Lock() + newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} + element := pder.list.PushBack(newsess) + pder.sessions[sid] = element + pder.lock.Unlock() + return newsess, nil + } +} + +// delete session store in memory session by id +func (pder *MemProvider) SessionDestroy(sid string) error { + pder.lock.Lock() + defer pder.lock.Unlock() + if element, ok := pder.sessions[sid]; ok { + delete(pder.sessions, sid) + pder.list.Remove(element) + return nil + } + return nil +} + +// clean expired session stores in memory session +func (pder *MemProvider) SessionGC() { + pder.lock.RLock() + for { + element := pder.list.Back() + if element == nil { + break + } + if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() { + pder.lock.RUnlock() + pder.lock.Lock() + pder.list.Remove(element) + delete(pder.sessions, element.Value.(*MemSessionStore).sid) + pder.lock.Unlock() + pder.lock.RLock() + } else { + break + } + } + pder.lock.RUnlock() +} + +// get count number of memory session +func (pder *MemProvider) SessionAll() int { + return pder.list.Len() +} + +// expand time of session store by id in memory session +func (pder *MemProvider) SessionUpdate(sid string) error { + pder.lock.Lock() + defer pder.lock.Unlock() + if element, ok := pder.sessions[sid]; ok { + element.Value.(*MemSessionStore).timeAccessed = time.Now() + pder.list.MoveToFront(element) + return nil + } + return nil +} + +func init() { + Register("memory", mempder) +} diff --git a/utils/mail.go b/utils/mail.go index aa219626..4959268c 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -292,7 +292,7 @@ func qpEscape(dest []byte, c byte) { const nums = "0123456789ABCDEF" dest[0] = '=' dest[1] = nums[(c&0xf0)>>4] - dest[2] = nums[(c&0xf)] + dest[2] = nums[(c & 0xf)] } // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer diff --git a/utils/pagination/paginator.go b/utils/pagination/paginator.go index e537f1ad..e813f798 100644 --- a/utils/pagination/paginator.go +++ b/utils/pagination/paginator.go @@ -92,18 +92,18 @@ func (p *Paginator) Pages() []int { case page >= pageNums-4 && pageNums > 9: start := pageNums - 9 + 1 pages = make([]int, 9) - for i, _ := range pages { + for i := range pages { pages[i] = start + i } case page >= 5 && pageNums > 9: start := page - 5 + 1 pages = make([]int, int(math.Min(9, float64(page+4+1)))) - for i, _ := range pages { + for i := range pages { pages[i] = start + i } default: pages = make([]int, int(math.Min(9, float64(pageNums)))) - for i, _ := range pages { + for i := range pages { pages[i] = i + 1 } } diff --git a/utils/slice.go b/utils/slice.go index 25032b7f..e413004d 100644 --- a/utils/slice.go +++ b/utils/slice.go @@ -51,7 +51,7 @@ func SliceRandList(min, max int) []int { t0 := time.Now() rand.Seed(int64(t0.Nanosecond())) list := rand.Perm(length) - for index, _ := range list { + for index := range list { list[index] += min } return list diff --git a/validation/util.go b/validation/util.go index 249462d4..367da5dc 100644 --- a/validation/util.go +++ b/validation/util.go @@ -137,7 +137,7 @@ func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) { if err != nil { return } - vfs = []ValidFunc{ValidFunc{"Match", []interface{}{reg, key + ".Match"}}} + vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}} str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):]) return } From 74e0af4a9a72d299e94dbbe02d676c297dca5f69 Mon Sep 17 00:00:00 2001 From: toalexjin Date: Wed, 25 Mar 2015 15:14:57 +0800 Subject: [PATCH 005/436] For enhancing performance, check log level before fmt.Sprintf() --- logs/log.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/logs/log.go b/logs/log.go index 32e0187c..e9d2673c 100644 --- a/logs/log.go +++ b/logs/log.go @@ -207,48 +207,72 @@ func (bl *BeeLogger) startLogger() { // Log EMERGENCY level message. func (bl *BeeLogger) Emergency(format string, v ...interface{}) { + if LevelEmergency > bl.level { + return + } msg := fmt.Sprintf("[M] "+format, v...) bl.writerMsg(LevelEmergency, msg) } // Log ALERT level message. func (bl *BeeLogger) Alert(format string, v ...interface{}) { + if LevelAlert > bl.level { + return + } msg := fmt.Sprintf("[A] "+format, v...) bl.writerMsg(LevelAlert, msg) } // Log CRITICAL level message. func (bl *BeeLogger) Critical(format string, v ...interface{}) { + if LevelCritical > bl.level { + return + } msg := fmt.Sprintf("[C] "+format, v...) bl.writerMsg(LevelCritical, msg) } // Log ERROR level message. func (bl *BeeLogger) Error(format string, v ...interface{}) { + if LevelError > bl.level { + return + } msg := fmt.Sprintf("[E] "+format, v...) bl.writerMsg(LevelError, msg) } // Log WARNING level message. func (bl *BeeLogger) Warning(format string, v ...interface{}) { + if LevelWarning > bl.level { + return + } msg := fmt.Sprintf("[W] "+format, v...) bl.writerMsg(LevelWarning, msg) } // Log NOTICE level message. func (bl *BeeLogger) Notice(format string, v ...interface{}) { + if LevelNotice > bl.level { + return + } msg := fmt.Sprintf("[N] "+format, v...) bl.writerMsg(LevelNotice, msg) } // Log INFORMATIONAL level message. func (bl *BeeLogger) Informational(format string, v ...interface{}) { + if LevelInformational > bl.level { + return + } msg := fmt.Sprintf("[I] "+format, v...) bl.writerMsg(LevelInformational, msg) } // Log DEBUG level message. func (bl *BeeLogger) Debug(format string, v ...interface{}) { + if LevelDebug > bl.level { + return + } msg := fmt.Sprintf("[D] "+format, v...) bl.writerMsg(LevelDebug, msg) } From 185ee872c45756b01901b1a63c23e92da0b81d86 Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Wed, 25 Mar 2015 14:47:20 +0100 Subject: [PATCH 006/436] Added to input.go: AcceptHtml, AcceptsXml and AcceptsJson functions which check the header agains a regex for simpler mult-content-type handling. --- context/input.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/context/input.go b/context/input.go index f535e6a2..95f56c1f 100644 --- a/context/input.go +++ b/context/input.go @@ -21,12 +21,21 @@ import ( "net/http" "net/url" "reflect" + "regexp" "strconv" "strings" "github.com/astaxie/beego/session" ) +// Regexes for checking the accept headers +// TODO make sure these are correct +var ( + acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) + acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) + acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)?`) +) + // BeegoInput operates the http request header, data, cookie and body. // it also contains router params and current session. type BeegoInput struct { @@ -163,6 +172,21 @@ func (input *BeegoInput) IsUpload() bool { return strings.Contains(input.Header("Content-Type"), "multipart/form-data") } +// Checks if request accepts html response +func (input *BeegoInput) AcceptsHtml() bool { + return acceptsHtmlRegex.MatchString(input.Header("Accept")) +} + +// Checks if request accepts xml response +func (input *BeegoInput) AcceptsXml() bool { + return acceptsXmlRegex.MatchString(input.Header("Accept")) +} + +// Checks if request accepts json response +func (input *BeegoInput) AcceptsJson() bool { + return acceptsJsonRegex.MatchString(input.Header("Accept")) +} + // IP returns request client ip. // if in proxy, return first proxy id. // if error, return 127.0.0.1. From 06b5c7f644eb67724ebb9fa2654063397c5466ee Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Wed, 25 Mar 2015 14:54:39 +0100 Subject: [PATCH 007/436] Remove unnecessary optional group flag '?' since has to match one of comma or end of string --- context/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index 95f56c1f..24d43bbb 100644 --- a/context/input.go +++ b/context/input.go @@ -33,7 +33,7 @@ import ( var ( acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) - acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)?`) + acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)`) ) // BeegoInput operates the http request header, data, cookie and body. From d3cdebbee280717ace26b3a32b30e3baaa8f8c26 Mon Sep 17 00:00:00 2001 From: toalexjin Date: Thu, 26 Mar 2015 14:40:12 +0800 Subject: [PATCH 008/436] Do not check log level in writerMsg() because the check is already done outside. --- logs/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/logs/log.go b/logs/log.go index e9d2673c..3103ac15 100644 --- a/logs/log.go +++ b/logs/log.go @@ -148,9 +148,6 @@ func (bl *BeeLogger) DelLogger(adaptername string) error { } func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { - if loglevel > bl.level { - return nil - } lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { From 260b5b19518a3c02137ad1d77c1e47dc8dcb105a Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Thu, 26 Mar 2015 20:23:00 +0100 Subject: [PATCH 009/436] Set ErrorsMap to nil in Validation.Clear function --- validation/validation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validation/validation.go b/validation/validation.go index addf6b7e..90a757a9 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -107,6 +107,7 @@ type Validation struct { // Clean all ValidationError. func (v *Validation) Clear() { v.Errors = []*ValidationError{} + v.ErrorsMap = nil } // Has ValidationError nor not. From 5fa55ca2d3ec0e73c02ac83e208bd43aa77e9f67 Mon Sep 17 00:00:00 2001 From: pylemon Date: Fri, 27 Mar 2015 13:30:59 +0800 Subject: [PATCH 010/436] bugfix: if a form field type is bool, valid Required should always return true instead of return its value. --- validation/validators.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation/validators.go b/validation/validators.go index 25415bdd..725b1f7a 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -64,8 +64,8 @@ func (r Required) IsSatisfied(obj interface{}) bool { if str, ok := obj.(string); ok { return len(str) > 0 } - if b, ok := obj.(bool); ok { - return b + if _, ok := obj.(bool); ok { + return true } if i, ok := obj.(int); ok { return i != 0 From caa260f053de2b5ea5c0e33e8dd8cb70e008329d Mon Sep 17 00:00:00 2001 From: pylemon Date: Fri, 27 Mar 2015 13:43:20 +0800 Subject: [PATCH 011/436] add tests to ensure bool value require test always return true. --- validation/validation_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation/validation_test.go b/validation/validation_test.go index be63ac93..0727fda4 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -26,6 +26,12 @@ func TestRequired(t *testing.T) { if valid.Required(nil, "nil").Ok { t.Error("nil object should be false") } + if !valid.Required(true, "bool").Ok { + t.Error("Bool value should always return true") + } + if !valid.Required(false, "bool").Ok { + t.Error("Bool value should always return true") + } if valid.Required("", "string").Ok { t.Error("\"'\" string should be false") } From eedaea2feae952dd1d192a0807094b6099da3ad3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 30 Mar 2015 20:35:57 +0800 Subject: [PATCH 012/436] fix #1073 --- error.go | 18 +++++++++--------- router.go | 14 +++----------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/error.go b/error.go index 71be6916..6db4a5a8 100644 --- a/error.go +++ b/error.go @@ -439,24 +439,26 @@ func exception(errcode string, ctx *context.Context) { if err != nil { code = 503 } - ctx.ResponseWriter.WriteHeader(code) if h, ok := ErrorMaps[errcode]; ok { - executeError(h, ctx) + executeError(h, ctx, code) return } else if h, ok := ErrorMaps["503"]; ok { - executeError(h, ctx) + executeError(h, ctx, code) return } else { + ctx.ResponseWriter.WriteHeader(code) ctx.WriteString(errcode) } } -func executeError(err *errorInfo, ctx *context.Context) { +func executeError(err *errorInfo, ctx *context.Context, code int) { if err.errorType == errorTypeHandler { + ctx.ResponseWriter.WriteHeader(code) err.handler(ctx.ResponseWriter, ctx.Request) return } if err.errorType == errorTypeController { + ctx.Output.SetStatus(code) //Invoke the request handler vc := reflect.New(err.controllerType) execController, ok := vc.Interface().(ControllerInterface) @@ -476,11 +478,9 @@ func executeError(err *errorInfo, ctx *context.Context) { method.Call(in) //render template - if ctx.Output.Status == 0 { - if AutoRender { - if err := execController.Render(); err != nil { - panic(err) - } + if AutoRender { + if err := execController.Render(); err != nil { + panic(err) } } diff --git a/router.go b/router.go index c1bda637..e9f45615 100644 --- a/router.go +++ b/router.go @@ -862,8 +862,8 @@ func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { panic(err) } else { if ErrorsShow { - if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { - executeError(handler, context) + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { + exception(fmt.Sprint(err), context) return } } @@ -886,15 +886,7 @@ func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { } else { // in production model show all infomation if ErrorsShow { - if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { - executeError(handler, context) - return - } else if handler, ok := ErrorMaps["503"]; ok { - executeError(handler, context) - return - } else { - context.WriteString(fmt.Sprint(err)) - } + exception(fmt.Sprint(err), context) } else { Critical("the request url is ", context.Input.Url()) Critical("Handler crashed with error", err) From 217e24815bc014b23f84171b74260b1c96f85716 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Tue, 31 Mar 2015 12:30:47 +0800 Subject: [PATCH 013/436] Update output.go fix cookie not work in IE --- context/output.go | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/context/output.go b/context/output.go index 2141513d..f981df50 100644 --- a/context/output.go +++ b/context/output.go @@ -98,28 +98,30 @@ func (output *BeegoOutput) Body(content []byte) { func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { var b bytes.Buffer fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) - if len(others) > 0 { - switch v := others[0].(type) { - case int: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int64: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int32: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - } - } + + //fix cookie not work in IE + if len(others) > 0 { + switch v := others[0].(type) { + case int: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + case int64: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + case int32: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + } + } // the settings below // Path, Domain, Secure, HttpOnly From 9261c80509cbb08f65d28e64fd396be5722eb2d2 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Tue, 31 Mar 2015 12:36:39 +0800 Subject: [PATCH 014/436] Update output.go --- context/output.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context/output.go b/context/output.go index f981df50..7edde552 100644 --- a/context/output.go +++ b/context/output.go @@ -29,6 +29,7 @@ import ( "path/filepath" "strconv" "strings" + "time" ) // BeegoOutput does work for sending response header. From 49c0f8906f7eafaad8f4794483080cc03d6f2d9b Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Apr 2015 23:31:40 +0800 Subject: [PATCH 015/436] fix #1081 --- controller.go | 72 ++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/controller.go b/controller.go index e056f52d..81ee1230 100644 --- a/controller.go +++ b/controller.go @@ -406,106 +406,90 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { // GetInt returns input as an int or the default value while it's present and input is blank func (c *Controller) GetInt(key string, def ...int) (int, error) { - var defv int - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.Atoi(strv) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.Atoi(strv) } } // GetInt8 return input as an int8 or the default value while it's present and input is blank func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { - var defv int8 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(strv, 10, 8) i8 := int8(i64) return i8, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(strv, 10, 8) + i8 := int8(i64) + return i8, err } } // GetInt16 returns input as an int16 or the default value while it's present and input is blank func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { - var defv int16 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(strv, 10, 16) i16 := int16(i64) - return i16, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(strv, 10, 16) + i16 := int16(i64) + return i16, err } } // GetInt32 returns input as an int32 or the default value while it's present and input is blank func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { - var defv int32 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) i32 := int32(i64) return i32, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) + i32 := int32(i64) + return i32, err } } // GetInt64 returns input value as int64 or the default value while it's present and input is blank. func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { - var defv int64 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.ParseInt(strv, 10, 64) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseInt(strv, 10, 64) } } // GetBool returns input value as bool or the default value while it's present and input is blank. func (c *Controller) GetBool(key string, def ...bool) (bool, error) { - var defv bool - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.ParseBool(strv) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseBool(strv) } } // GetFloat returns input value as float64 or the default value while it's present and input is blank. func (c *Controller) GetFloat(key string, def ...float64) (float64, error) { - var defv float64 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { - return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) + return strconv.ParseFloat(strv, 64) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseFloat(strv, 64) } } From 8a82e25e85f3f1a091b711a1cd3b1b793a1dcf12 Mon Sep 17 00:00:00 2001 From: peeped Date: Thu, 2 Apr 2015 12:07:12 +0800 Subject: [PATCH 016/436] Update validators.go edit mobile regular expression ; add 184 Section No. --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 725b1f7a..645f4ec3 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][012356789]))\\d{8}$") +var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][0-9]))\\d{8}$") type Mobile struct { Match From e675594e465189ead90bdc57308fea9cd1c960f5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 2 Apr 2015 14:02:39 +0800 Subject: [PATCH 017/436] session cookie support IE --- session/session.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/session/session.go b/session/session.go index 3cbd2b05..01aae1a5 100644 --- a/session/session.go +++ b/session/session.go @@ -151,6 +151,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -177,6 +178,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) From a5124a1d4501567eaa9d5c9450ec4e7402d7792f Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Fri, 3 Apr 2015 17:41:09 +0800 Subject: [PATCH 018/436] Update session.go move expire in line 154 to 247, because it will cause session_cookie not writen to explorer --- session/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/session.go b/session/session.go index 01aae1a5..35561242 100644 --- a/session/session.go +++ b/session/session.go @@ -151,7 +151,6 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime - cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -245,6 +244,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque } if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } http.SetCookie(w, cookie) r.AddCookie(cookie) From 5e1e618d0fc83406942ef09c6a070829f9ad2d52 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sat, 4 Apr 2015 00:44:22 +0800 Subject: [PATCH 019/436] Update session.go remove = in if statement --- session/session.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/session/session.go b/session/session.go index 35561242..4214320c 100644 --- a/session/session.go +++ b/session/session.go @@ -149,8 +149,9 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se HttpOnly: true, Secure: manager.config.Secure, Domain: manager.config.Domain} - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -175,7 +176,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se HttpOnly: true, Secure: manager.config.Secure, Domain: manager.config.Domain} - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } @@ -242,7 +243,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque cookie.HttpOnly = true cookie.Path = "/" } - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } From e0e8b986222d333f52a50fffb885ab0d5aac9b52 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:11:50 +0800 Subject: [PATCH 020/436] fix #1112 --- tree.go | 42 +++++++++++++++++++++++------------------- tree_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/tree.go b/tree.go index 93e87470..e0d9c1c2 100644 --- a/tree.go +++ b/tree.go @@ -70,18 +70,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else { regexpStr = "/" + regexpStr } - } else { - for _, w := range wildcards { - if w == "." || w == ":" { - continue + } else if reg != "" { + if seg == "*.*" { + regexpStr = "([^.]+).(.+)" + } else { + for _, w := range params { + if w == "." || w == ":" { + continue + } + regexpStr = "([^/]+)/" + regexpStr } - regexpStr = "([^/]+)/" + regexpStr } } - reg = strings.Trim(reg+regexpStr, "/") + reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.wildcard = tree } else { + reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.fixrouters[seg] = tree } @@ -104,23 +109,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st rr = rr + "([^/]+)/" } } - regexpStr = rr + regexpStr + "/" + regexpStr = rr + regexpStr } else { - regexpStr = "/" + regexpStr + "/" + regexpStr = "/" + regexpStr } - } else { - for _, w := range wildcards { - if w == "." || w == ":" { - continue - } - if w == ":splat" { - regexpStr = "(.+)/" + regexpStr - } else { + } else if reg != "" { + if seg == "*.*" { + regexpStr = "([^.]+).(.+)" + } else { + for _, w := range params { + if w == "." || w == ":" { + continue + } regexpStr = "([^/]+)/" + regexpStr } } } - reg = reg + regexpStr + reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) } else { subTree := NewTree() @@ -140,7 +145,7 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { if reg != "" { if l.regexps != nil { l.wildcards = append(wildcards, l.wildcards...) - l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$") + l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") } else { for _, v := range l.wildcards { if v == ":" || v == "." { @@ -248,7 +253,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, regexpStr = "/([^/]+)" + regexpStr } } - } t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) } else { diff --git a/tree_test.go b/tree_test.go index fa289716..1d2890e1 100644 --- a/tree_test.go +++ b/tree_test.go @@ -148,6 +148,50 @@ func TestAddTree2(t *testing.T) { } } +func TestAddTree3(t *testing.T) { + tr := NewTree() + tr.AddRouter("/create", "astaxie") + tr.AddRouter("/shop/:sd/account", "astaxie") + t3 := NewTree() + t3.AddTree("/table/:num", tr) + obj, param := t3.Match("/table/123/shop/123/account") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/table/:num/shop/:sd/account can't get obj ") + } + if param == nil { + t.Fatal("get param error") + } + if param[":num"] != "123" || param[":sd"] != "123" { + t.Fatal("get :num :sd param error") + } + obj, param = t3.Match("/table/123/create") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/table/:num/create can't get obj ") + } +} + +func TestAddTree4(t *testing.T) { + tr := NewTree() + tr.AddRouter("/create", "astaxie") + tr.AddRouter("/shop/:sd/:account", "astaxie") + t4 := NewTree() + t4.AddTree("/:info:int/:num/:id", tr) + obj, param := t4.Match("/12/123/456/shop/123/account") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") + } + if param == nil { + t.Fatal("get param error") + } + if param[":info"] != "12" || param[":num"] != "123" || param[":id"] != "456" || param[":sd"] != "123" || param[":account"] != "account" { + t.Fatal("get :info :num :id :sd :account param error") + } + obj, param = t4.Match("/12/123/456/create") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/:info:int/:num/:id/create can't get obj ") + } +} + func TestSplitPath(t *testing.T) { a := splitPath("") if len(a) != 0 { From 47848fa77bed27a4971b986a456223afb5089a99 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:21:13 +0800 Subject: [PATCH 021/436] config read and set support Runmode --- config.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/config.go b/config.go index f326ad22..a649eaa8 100644 --- a/config.go +++ b/config.go @@ -98,6 +98,10 @@ func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, err } func (b *beegoAppConfig) Set(key, val string) error { + err := b.innerConfig.Set(RunMode+"::"+key, val) + if err == nil { + return err + } return b.innerConfig.Set(key, val) } @@ -150,26 +154,50 @@ func (b *beegoAppConfig) Float(key string) (float64, error) { } func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { + v := b.String(key) + if v != "" { + return v + } return b.innerConfig.DefaultString(key, defaultval) } func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { + v := b.Strings(key) + if len(v) != 0 { + return v + } return b.innerConfig.DefaultStrings(key, defaultval) } func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { + v, err := b.Int(key) + if err == nil { + return v + } return b.innerConfig.DefaultInt(key, defaultval) } func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { + v, err := b.Int64(key) + if err == nil { + return v + } return b.innerConfig.DefaultInt64(key, defaultval) } func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { + v, err := b.Bool(key) + if err == nil { + return v + } return b.innerConfig.DefaultBool(key, defaultval) } func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { + v, err := b.Float(key) + if err == nil { + return v + } return b.innerConfig.DefaultFloat(key, defaultval) } From 7caeb91f9b0124f6c13e293d5da2e36283b4ecdc Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:23:35 +0800 Subject: [PATCH 022/436] improve the defaultval --- config.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config.go b/config.go index a649eaa8..b22db163 100644 --- a/config.go +++ b/config.go @@ -158,7 +158,7 @@ func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { if v != "" { return v } - return b.innerConfig.DefaultString(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { @@ -166,7 +166,7 @@ func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []strin if len(v) != 0 { return v } - return b.innerConfig.DefaultStrings(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { @@ -174,7 +174,7 @@ func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { if err == nil { return v } - return b.innerConfig.DefaultInt(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { @@ -182,7 +182,7 @@ func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { if err == nil { return v } - return b.innerConfig.DefaultInt64(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { @@ -190,7 +190,7 @@ func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { if err == nil { return v } - return b.innerConfig.DefaultBool(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { @@ -198,7 +198,7 @@ func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { if err == nil { return v } - return b.innerConfig.DefaultFloat(key, defaultval) + return defaultval } func (b *beegoAppConfig) DIY(key string) (interface{}, error) { From 56dfe418dd6e5a53334ac64aee148877775961bb Mon Sep 17 00:00:00 2001 From: Lionel Lee Date: Tue, 7 Apr 2015 10:35:18 +0800 Subject: [PATCH 023/436] fix a comment error. --- context/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index d9aa2634..00e65ee7 100644 --- a/context/input.go +++ b/context/input.go @@ -306,7 +306,7 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { // Bind data from request.Form[key] to dest // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie // var id int beegoInput.Bind(&id, "id") id ==123 -// var isok bool beegoInput.Bind(&isok, "isok") id ==true +// var isok bool beegoInput.Bind(&isok, "isok") isok ==true // var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2 // ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2] // ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array] From d1c9cb228118ce6a84ff82054a6119aec337e51e Mon Sep 17 00:00:00 2001 From: peeped Date: Tue, 7 Apr 2015 17:48:51 +0800 Subject: [PATCH 024/436] Update validators.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit //176 中国联通 //177 中国电信 //145 中国联通 //147 中国移动 //149 中国电信 --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 645f4ec3..61b0414a 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][0-9]))\\d{8}$") +var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][67]|[4][579]))\\d{8}$") type Mobile struct { Match From 642a69de0253ad909e4e079332be278313245364 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 20:12:10 +0800 Subject: [PATCH 025/436] add sethost --- httplib/httplib.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bd8bc776..bdd2d65a 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -184,6 +184,12 @@ func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest { return b } +// Set HOST +func (b *BeegoHttpRequest) SetHost(host string) *BeegoHttpRequest { + b.req.Host = host + return b +} + // Set the protocol version for incoming requests. // Client requests always use HTTP/1.1. func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest { @@ -253,7 +259,6 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { return b } - // JsonBody adds request raw body encoding by JSON. func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) { if b.req.Body == nil && obj != nil { From 6f6b412709ab117c384b88568d209fac1ea9fc9e Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 21:45:00 +0800 Subject: [PATCH 026/436] httplib support gzip --- httplib/httplib.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bdd2d65a..284aef7b 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -32,6 +32,7 @@ package httplib import ( "bytes" + "compress/gzip" "crypto/tls" "encoding/json" "encoding/xml" @@ -50,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false} +var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false, true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex @@ -122,6 +123,7 @@ type BeegoHttpSettings struct { Proxy func(*http.Request) (*url.URL, error) Transport http.RoundTripper EnableCookie bool + Gzip bool } // BeegoHttpRequest provides more useful methods for requesting one url than http.Request. @@ -434,7 +436,15 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return nil, nil } defer resp.Body.Close() - b.body, err = ioutil.ReadAll(resp.Body) + if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { + reader, err := gzip.NewReader(resp.Body) + if err != nil { + return nil, err + } + b.body, err = ioutil.ReadAll(reader) + } else { + b.body, err = ioutil.ReadAll(resp.Body) + } if err != nil { return nil, err } From cc5ca458ab7cbb0d8040404e13695d47a2b38e2a Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 22:58:37 +0800 Subject: [PATCH 027/436] add DumpRequest --- httplib/httplib.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index 284aef7b..aba7f534 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -167,6 +167,11 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { return b } +// return the DumpRequest +func (b *BeegoHttpRequest) DumpRequest() { + return httputil.DumpRequest(b.req, true) +} + // SetTimeout sets connect time out and read-write time out for BeegoRequest. func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { b.setting.ConnectTimeout = connectTimeout From 44a39a6b3ebde75843341af3efcd3d418a871ab0 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 23:00:08 +0800 Subject: [PATCH 028/436] fix the params --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index aba7f534..57d3de61 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -168,7 +168,7 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { } // return the DumpRequest -func (b *BeegoHttpRequest) DumpRequest() { +func (b *BeegoHttpRequest) DumpRequest() (dump []byte, err error) { return httputil.DumpRequest(b.req, true) } From 44c8534477441a5fb09f448ce634c1e4c45a2211 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:11:25 +0800 Subject: [PATCH 029/436] fix the dump has no body --- httplib/httplib.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 57d3de61..f09c8509 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -135,6 +135,7 @@ type BeegoHttpRequest struct { setting BeegoHttpSettings resp *http.Response body []byte + dump []byte } // Change request settings @@ -168,8 +169,8 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { } // return the DumpRequest -func (b *BeegoHttpRequest) DumpRequest() (dump []byte, err error) { - return httputil.DumpRequest(b.req, true) +func (b *BeegoHttpRequest) DumpRequest() []byte { + return b.dump } // SetTimeout sets connect time out and read-write time out for BeegoRequest. @@ -405,6 +406,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { if err != nil { println(err.Error()) } + b.dump = dump println(string(dump)) } From d55997e520bd363cea70f725c0e28c1b3601fc1c Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:12:41 +0800 Subject: [PATCH 030/436] fix the init struct --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index f09c8509..8ee56bcd 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -85,7 +85,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { ProtoMajor: 1, ProtoMinor: 1, } - return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil} + return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil, nil} } // Get returns *BeegoHttpRequest with GET method. From ecb27f34e6ad361092786657158c17b8047f8c31 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:18:02 +0800 Subject: [PATCH 031/436] no output the dump --- httplib/httplib.go | 1 - 1 file changed, 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 8ee56bcd..935b7a71 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -407,7 +407,6 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { println(err.Error()) } b.dump = dump - println(string(dump)) } resp, err := client.Do(b.req) From e229a4762f36ad0a6805b989ae822d1ec8ed0311 Mon Sep 17 00:00:00 2001 From: wuranbo Date: Mon, 13 Apr 2015 10:33:57 +0800 Subject: [PATCH 032/436] =?UTF-8?q?*feature)=20=E5=A2=9E=E5=8A=A0logcallde?= =?UTF-8?q?pth=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=BD=BF=E5=BE=97=E8=83=BD?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E5=86=8D=E6=AC=A1=E5=B0=81=E8=A3=85log?= =?UTF-8?q?=E7=B3=BB=E5=88=97=E5=87=BD=E6=95=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/log.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/logs/log.go b/logs/log.go index 3103ac15..5f5f1cea 100644 --- a/logs/log.go +++ b/logs/log.go @@ -181,6 +181,11 @@ func (bl *BeeLogger) SetLogFuncCallDepth(d int) { bl.loggerFuncCallDepth = d } +// get log funcCallDepth for wrapper +func (bl *BeeLogger) GetLogFuncCallDepth() int { + return bl.loggerFuncCallDepth +} + // enable log funcCallDepth func (bl *BeeLogger) EnableFuncCallDepth(b bool) { bl.enableFuncCallDepth = b From 6d72fc63ab2c8a90fdce7f47a16130a53deddbe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BA=A7n=20V=C4=83n=20Thanh?= Date: Wed, 15 Apr 2015 17:41:41 +0700 Subject: [PATCH 033/436] fixed: when RelatedSel have multi string/relation, it only get last string --- orm/orm_queryset.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 4f5d5485..5cc47617 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -115,23 +115,21 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter { // set relation model to query together. // it will query relation models and assign to parent model. func (o querySet) RelatedSel(params ...interface{}) QuerySeter { - var related []string - if len(params) == 0 { - o.relDepth = DefaultRelsDepth - } else { - for _, p := range params { - switch val := p.(type) { - case string: - related = append(o.related, val) - case int: - o.relDepth = val - default: - panic(fmt.Errorf(" wrong param kind: %v", val)) - } - } - } - o.related = related - return &o + if len(params) == 0 { + o.relDepth = DefaultRelsDepth + } else { + for _, p := range params { + switch val := p.(type) { + case string: + o.related = append(o.related, val) + case int: + o.relDepth = val + default: + panic(fmt.Errorf(" wrong param kind: %v", val)) + } + } + } + return &o } // set condition to QuerySeter. From dc58ec83161d42a7162b639ab4f5fb55125db381 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 19 Apr 2015 15:40:23 +0800 Subject: [PATCH 034/436] remove unreached code --- admin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/admin.go b/admin.go index 90142c55..7eb07cc0 100644 --- a/admin.go +++ b/admin.go @@ -333,7 +333,6 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) } tmpl.Execute(rw, data) - } else { } } From d0e7dd686bd151c0b3fce7c0004819576360a00d Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 10:58:46 +0800 Subject: [PATCH 035/436] add Recursively validation --- validation/validation.go | 39 +++++++++++++++++++++++++++++++++++ validation/validation_test.go | 30 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/validation/validation.go b/validation/validation.go index addf6b7e..d91679f7 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -333,3 +333,42 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { return !v.HasErrors(), nil } + +// Recursively validate a struct. +// Step1: Validate by v.Valid +// Step2: If pass on step1, then reflect obj's fields +// Step3: Do the Recursively validation to all struct or struct pointer fields +// Anonymous fields will be ignored +func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { + //Step 1: validate obj itself firstly + pass, err := v.Valid(objc) + if err != nil || false == pass { + return pass, err // Stop recursive validation + } else { //pass + // Step 2: Validate struct's struct fields + objT := reflect.TypeOf(objc) + objV := reflect.ValueOf(objc) + if isStruct(objT) || isStructPtr(objT) { + + if isStructPtr(objT) { + objT = objT.Elem() + objV = objV.Elem() + } + + for i := 0; i < objT.NumField(); i++ { + + t := objT.Field(i).Type + + if isStruct(t) || isStructPtr(t) { + // Step 3: do the recursive validation + // Only valid the Public field recursively + if objV.Field(i).CanInterface() { + pass, err = v.RecursiveValid(objV.Field(i).Interface()) + } + } + } + } + + return pass, err + } +} diff --git a/validation/validation_test.go b/validation/validation_test.go index be63ac93..78201198 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -343,3 +343,33 @@ func TestValid(t *testing.T) { t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key) } } + +func TestRecursiveValid(t *testing.T) { + type User struct { + Id int + Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"` + Age int `valid:"Required;Range(1, 140)"` + } + + type AnonymouseUser struct { + Id2 int + Name2 string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"` + Age2 int `valid:"Required;Range(1, 140)"` + } + + type Account struct { + Password string `valid:"Required"` + U User + AnonymouseUser + } + valid := Validation{} + + u := Account{Password: "abc123_", U: User{}} + b, err := valid.RecursiveValid(u) + if err != nil { + t.Fatal(err) + } + if b { + t.Error("validation should not be passed") + } +} From 73650e1f2be427a355e313179560831d242500dd Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 11:14:49 +0800 Subject: [PATCH 036/436] remove the double isStruct/isStructPtr check --- validation/validation.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/validation/validation.go b/validation/validation.go index d91679f7..21226e02 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -341,6 +341,7 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { // Anonymous fields will be ignored func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly + // fails if objc is not struct pass, err := v.Valid(objc) if err != nil || false == pass { return pass, err // Stop recursive validation @@ -348,23 +349,22 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { // Step 2: Validate struct's struct fields objT := reflect.TypeOf(objc) objV := reflect.ValueOf(objc) - if isStruct(objT) || isStructPtr(objT) { - if isStructPtr(objT) { - objT = objT.Elem() - objV = objV.Elem() - } + if isStructPtr(objT) { + objT = objT.Elem() + objV = objV.Elem() + } - for i := 0; i < objT.NumField(); i++ { + for i := 0; i < objT.NumField(); i++ { - t := objT.Field(i).Type + t := objT.Field(i).Type - if isStruct(t) || isStructPtr(t) { - // Step 3: do the recursive validation - // Only valid the Public field recursively - if objV.Field(i).CanInterface() { - pass, err = v.RecursiveValid(objV.Field(i).Interface()) - } + // Recursive applies to struct or pointer to structs fields + if isStruct(t) || isStructPtr(t) { + // Step 3: do the recursive validation + // Only valid the Public field recursively + if objV.Field(i).CanInterface() { + pass, err = v.RecursiveValid(objV.Field(i).Interface()) } } } From 5534258e222b905994e59b5c553653f109fbc7ae Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 11:17:12 +0800 Subject: [PATCH 037/436] remove useless comments --- validation/validation.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validation/validation.go b/validation/validation.go index 21226e02..362e14ad 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -338,7 +338,6 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { // Step1: Validate by v.Valid // Step2: If pass on step1, then reflect obj's fields // Step3: Do the Recursively validation to all struct or struct pointer fields -// Anonymous fields will be ignored func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly // fails if objc is not struct From 4498a02c1555490b477767bf7a0eef4de28ab926 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 01:23:18 +0800 Subject: [PATCH 038/436] better go style --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 935b7a71..ab734b3c 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false, true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectionTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Giz: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex From e1d20aea5d0d270304bbdac7c72277b25d4c2e9d Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:04:34 +0800 Subject: [PATCH 039/436] better code and fixed --- httplib/httplib.go | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index ab734b3c..ca893163 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -85,7 +85,14 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { ProtoMajor: 1, ProtoMinor: 1, } - return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil, nil} + return &BeegoHttpRequest{ + url: url, + req: &req, + paras: map[string]string{}, + files: map[string]string{}, + setting: defaultSetting, + resp: &resp, + } } // Get returns *BeegoHttpRequest with GET method. @@ -157,14 +164,14 @@ func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest { } // SetUserAgent sets User-Agent header field -func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest { - b.setting.UserAgent = useragent +func (b *BeegoHttpRequest) SetUserAgent(userAgent string) *BeegoHttpRequest { + b.setting.UserAgent = userAgent return b } // Debug sets show debug or not when executing request. -func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { - b.setting.ShowDebug = isdebug +func (b *BeegoHttpRequest) Debug(isDebug bool) *BeegoHttpRequest { + b.setting.ShowDebug = isDebug return b } @@ -409,12 +416,8 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { b.dump = dump } - resp, err := client.Do(b.req) - if err != nil { - return nil, err - } - b.resp = resp - return resp, nil + b.resp, err = client.Do(b.req) + return b.resp, err } // String returns the body string in response. @@ -435,12 +438,9 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return b.body, nil } resp, err := b.getResponse() - if err != nil { + if resp == nil || resp.Body == nil { return nil, err } - if resp.Body == nil { - return nil, nil - } defer resp.Body.Close() if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(resp.Body) @@ -451,29 +451,24 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { } else { b.body, err = ioutil.ReadAll(resp.Body) } - if err != nil { - return nil, err - } - return b.body, nil + return b.body, err } // ToFile saves the body data in response to one file. // it calls Response inner. func (b *BeegoHttpRequest) ToFile(filename string) error { + resp, err := b.getResponse() + if resp == nil || resp.Body == nil { + return err + } + defer resp.Body.Close() + f, err := os.Create(filename) if err != nil { return err } defer f.Close() - resp, err := b.getResponse() - if err != nil { - return err - } - if resp.Body == nil { - return nil - } - defer resp.Body.Close() _, err = io.Copy(f, resp.Body) return err } @@ -510,7 +505,7 @@ func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, ad if err != nil { return nil, err } - conn.SetDeadline(time.Now().Add(rwTimeout)) - return conn, nil + err = conn.SetDeadline(time.Now().Add(rwTimeout)) + return conn, err } } From cddb4fdb609715664ae0d10987fa2ae3461f9277 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:05:50 +0800 Subject: [PATCH 040/436] typo fixed --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index ca893163..502ef276 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectionTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Giz: true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeoutt: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex From 0c1bb6409a86dbb77ac5f90ecad7e8737da93742 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:06:19 +0800 Subject: [PATCH 041/436] typo fixed --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 502ef276..6e0acdab 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeoutt: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex @@ -88,7 +88,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: url, req: &req, - paras: map[string]string{}, + params: map[string]string{}, files: map[string]string{}, setting: defaultSetting, resp: &resp, From da8c3c39103bee08796f89a9ef47071839b06373 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:17:46 +0800 Subject: [PATCH 042/436] zero timeout means wait until resp --- httplib/httplib.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 6e0acdab..140d541b 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -65,14 +65,8 @@ func createDefaultCookie() { // Overwrite default settings func SetDefaultSetting(setting BeegoHttpSettings) { settingMutex.Lock() - defer settingMutex.Unlock() defaultSetting = setting - if defaultSetting.ConnectTimeout == 0 { - defaultSetting.ConnectTimeout = 60 * time.Second - } - if defaultSetting.ReadWriteTimeout == 0 { - defaultSetting.ReadWriteTimeout = 60 * time.Second - } + settingMutex.Unlock() } // return *BeegoHttpRequest with specific method @@ -262,11 +256,11 @@ func (b *BeegoHttpRequest) PostFile(formname, filename string) *BeegoHttpRequest // it supports string and []byte. func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { switch t := data.(type) { - case string: + case string: bf := bytes.NewBufferString(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) - case []byte: + case []byte: bf := bytes.NewBuffer(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) From 973306b28ddba2b27a9fb63036d073c51347ea4a Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:19:38 +0800 Subject: [PATCH 043/436] no need defer here --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 140d541b..f84586a5 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -58,8 +58,8 @@ var settingMutex sync.Mutex // createDefaultCookie creates a global cookiejar to store cookies. func createDefaultCookie() { settingMutex.Lock() - defer settingMutex.Unlock() defaultCookieJar, _ = cookiejar.New(nil) + settingMutex.Unlock() } // Overwrite default settings From 0afd04ec6f8e4420f880be81a7a2b69d773d0e52 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 15:24:04 +0800 Subject: [PATCH 044/436] no need lock here --- httplib/httplib.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index f84586a5..54c70283 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -47,26 +47,20 @@ import ( "net/url" "os" "strings" - "sync" "time" ) var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar -var settingMutex sync.Mutex // createDefaultCookie creates a global cookiejar to store cookies. func createDefaultCookie() { - settingMutex.Lock() defaultCookieJar, _ = cookiejar.New(nil) - settingMutex.Unlock() } // Overwrite default settings func SetDefaultSetting(setting BeegoHttpSettings) { - settingMutex.Lock() defaultSetting = setting - settingMutex.Unlock() } // return *BeegoHttpRequest with specific method @@ -82,7 +76,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: url, req: &req, - params: map[string]string{}, + params: map[string]string{}, files: map[string]string{}, setting: defaultSetting, resp: &resp, @@ -256,11 +250,11 @@ func (b *BeegoHttpRequest) PostFile(formname, filename string) *BeegoHttpRequest // it supports string and []byte. func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { switch t := data.(type) { - case string: + case string: bf := bytes.NewBufferString(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) - case []byte: + case []byte: bf := bytes.NewBuffer(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) From d8fa118727d40238e03a2ec700bc05e53285a6f2 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 15:42:10 +0800 Subject: [PATCH 045/436] more fixed --- httplib/httplib.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 54c70283..ae50791c 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -36,6 +36,7 @@ import ( "crypto/tls" "encoding/json" "encoding/xml" + "fmt" "io" "io/ioutil" "log" @@ -278,12 +279,15 @@ func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) } func (b *BeegoHttpRequest) buildUrl(paramBody string) { + if paramBody == "" { + return + } // build GET url with query string - if b.req.Method == "GET" && len(paramBody) > 0 { - if strings.Index(b.url, "?") != -1 { - b.url += "&" + paramBody - } else { + if b.req.Method == "GET" { + if strings.Index(b.url, "?") == -1 { b.url = b.url + "?" + paramBody + } else { + b.url += "&" + paramBody } return } @@ -336,18 +340,14 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } var paramBody string if len(b.params) > 0 { - var buf bytes.Buffer for k, v := range b.params { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v)) - buf.WriteByte('&') + paramBody += fmt.Sprintf("&%s=%v", url.QueryEscape(k), url.QueryEscape(v)) } - paramBody = buf.String() - paramBody = paramBody[0 : len(paramBody)-1] + paramBody = paramBody[1:] } b.buildUrl(paramBody) + url, err := url.Parse(b.url) if err != nil { return nil, err @@ -399,7 +399,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { if b.setting.ShowDebug { dump, err := httputil.DumpRequest(b.req, true) if err != nil { - println(err.Error()) + log.Println(err.Error()) } b.dump = dump } From f56bdb628448d7dac14eabede88e3351a415dc92 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 16:08:25 +0800 Subject: [PATCH 046/436] set default timeout --- httplib/httplib.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index ae50791c..7464c16f 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -357,6 +357,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { trans := b.setting.Transport + if b.setting.ConnectTimeout == 0 { + b.setting.ConnectTimeout = 30 * time.Second + } + if b.setting.ReadWriteTimeout == 0 { + b.setting.ReadWriteTimeout = 30 * time.Second + } + if trans == nil { // create default transport trans = &http.Transport{ From 6c3e274b6ec143a3325c185247a8da8459d0925c Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 16:10:18 +0800 Subject: [PATCH 047/436] set default timeout --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 7464c16f..2389a7cb 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -358,10 +358,10 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { trans := b.setting.Transport if b.setting.ConnectTimeout == 0 { - b.setting.ConnectTimeout = 30 * time.Second + b.setting.ConnectTimeout = 60 * time.Second } if b.setting.ReadWriteTimeout == 0 { - b.setting.ReadWriteTimeout = 30 * time.Second + b.setting.ReadWriteTimeout = 60 * time.Second } if trans == nil { From c0bb5b9237dfd3292dd4e80c4c5c3f99dd1a9b86 Mon Sep 17 00:00:00 2001 From: cr7pt0gr4ph7 Date: Sun, 3 May 2015 23:21:32 +0200 Subject: [PATCH 048/436] Improve documentation of filter.go. --- filter.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/filter.go b/filter.go index ddd61094..f673ab66 100644 --- a/filter.go +++ b/filter.go @@ -16,11 +16,12 @@ package beego import "github.com/astaxie/beego/context" -// FilterFunc defines filter function type. +// FilterFunc defines a filter function which is invoked before the controller handler is executed. type FilterFunc func(*context.Context) -// FilterRouter defines filter operation before controller handler execution. -// it can match patterned url and do filter function when action arrives. +// FilterRouter defines a filter operation which is invoked before the controller handler is executed. +// It can match the URL against a pattern, and execute a filter function +// when a request with a matching URL arrives. type FilterRouter struct { filterFunc FilterFunc tree *Tree @@ -28,10 +29,11 @@ type FilterRouter struct { returnOnOutput bool } -// ValidRouter check current request is valid for this filter. -// if matched, returns parsed params in this request by defined filter router pattern. -func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) { - isok, params := f.tree.Match(router) +// ValidRouter checks if the current request is matched by this filter. +// If the request is matched, the values of the URL parameters defined +// by the filter pattern are also returned. +func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { + isok, params := f.tree.Match(url) if isok == nil { return false, nil } From 1d72629334fe4712253aa58ca8b5725fab70221f Mon Sep 17 00:00:00 2001 From: mlgd Date: Mon, 4 May 2015 15:54:03 +0200 Subject: [PATCH 049/436] Fix save config ini file --- config/ini.go | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/config/ini.go b/config/ini.go index 837c9ffe..31fe9b5f 100644 --- a/config/ini.go +++ b/config/ini.go @@ -300,21 +300,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { defer f.Close() buf := bytes.NewBuffer(nil) - for section, dt := range c.data { - // Write section comments. - if v, ok := c.sectionComment[section]; ok { - if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { - return err - } - } - - if section != DEFAULT_SECTION { - // Write section name. - if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil { - return err - } - } - + // Save default section at first place + if dt, ok := c.data[DEFAULT_SECTION]; ok { for key, val := range dt { if key != " " { // Write key comments. @@ -336,6 +323,43 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { return err } } + // Save named sections + for section, dt := range c.data { + if section != DEFAULT_SECTION { + // Write section comments. + if v, ok := c.sectionComment[section]; ok { + if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + return err + } + } + + // Write section name. + if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil { + return err + } + + for key, val := range dt { + if key != " " { + // Write key comments. + if v, ok := c.keyComment[key]; ok { + if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + return err + } + } + + // Write key and value. + if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil { + return err + } + } + } + + // Put a line between sections. + if _, err = buf.WriteString(lineBreak); err != nil { + return err + } + } + } if _, err = buf.WriteTo(f); err != nil { return err From 47be2fadb5a0d356ccdb46811f43cd66f3f55590 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 5 May 2015 21:36:31 +0800 Subject: [PATCH 050/436] fix #1143 --- error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/error.go b/error.go index 6db4a5a8..99a1fcf3 100644 --- a/error.go +++ b/error.go @@ -92,7 +92,7 @@ func showErr(err interface{}, ctx *context.Context, Stack string) { data["Stack"] = Stack data["BeegoVersion"] = VERSION data["GoVersion"] = runtime.Version() - ctx.Output.SetStatus(500) + ctx.ResponseWriter.WriteHeader(500) t.Execute(ctx.ResponseWriter, data) } From 56e2143630fd1cb61a08d9bc39905df800bbb693 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 9 May 2015 14:54:30 +0800 Subject: [PATCH 051/436] add apk mime --- mime.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mime.go b/mime.go index 155e5e12..20246c21 100644 --- a/mime.go +++ b/mime.go @@ -40,6 +40,7 @@ var mimemaps map[string]string = map[string]string{ ".ani": "application/x-navi-animation", ".aos": "application/x-nokia-9000-communicator-add-on-software", ".aps": "application/mime", + ".apk": "application/vnd.android.package-archive", ".arc": "application/x-arc-compressed", ".arj": "application/arj", ".art": "image/x-jg", From 98289cd8de5a8e6eea70844366ba8a66bdbad897 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 13 May 2015 21:17:47 +0800 Subject: [PATCH 052/436] beego suppot graceful restart application --- admin.go | 12 +- app.go | 133 ++++++++----- config.go | 4 + grace/grace.go | 522 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 621 insertions(+), 50 deletions(-) create mode 100644 grace/grace.go diff --git a/admin.go b/admin.go index 7eb07cc0..52388c76 100644 --- a/admin.go +++ b/admin.go @@ -19,9 +19,11 @@ import ( "encoding/json" "fmt" "net/http" + "os" "text/template" "time" + "github.com/astaxie/beego/grace" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -458,8 +460,14 @@ func (admin *adminApp) Run() { http.Handle(p, f) } BeeLogger.Info("Admin server Running on %s", addr) - err := http.ListenAndServe(addr, nil) + + var err error + if Graceful { + err = grace.ListenAndServe(addr, nil) + } else { + err = http.ListenAndServe(addr, nil) + } if err != nil { - BeeLogger.Critical("Admin ListenAndServe: ", err) + BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprint(os.Getpid())) } } diff --git a/app.go b/app.go index 35040f33..4740e7e5 100644 --- a/app.go +++ b/app.go @@ -22,6 +22,7 @@ import ( "os" "time" + "github.com/astaxie/beego/grace" "github.com/astaxie/beego/utils" ) @@ -76,57 +77,93 @@ func (app *App) Run() { err = fcgi.Serve(l, app.Handlers) } } else { - app.Server.Addr = addr - app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + if Graceful { + if EnableHttpTLS { + go func() { + time.Sleep(20 * time.Microsecond) + if HttpsPort != 0 { + addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + } + server := grace.NewServer(addr, app.Handlers) + server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + if err != nil { + BeeLogger.Critical("ListenAndServeTLS: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + if EnableHttpListen { + go func() { + server := grace.NewServer(addr, app.Handlers) + server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + if ListenTCP4 && HttpAddr == "" { + server.Network = "tcp4" + } + err := server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + } else { + app.Server.Addr = addr + app.Server.Handler = app.Handlers + app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second - if EnableHttpTLS { - go func() { - time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) - } - BeeLogger.Info("https server Running on %s", app.Server.Addr) - err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) - if err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - }() + if EnableHttpTLS { + go func() { + time.Sleep(20 * time.Microsecond) + if HttpsPort != 0 { + app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + } + BeeLogger.Info("https server Running on %s", app.Server.Addr) + err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + if err != nil { + BeeLogger.Critical("ListenAndServeTLS: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + + if EnableHttpListen { + go func() { + app.Server.Addr = addr + BeeLogger.Info("http server Running on %s", app.Server.Addr) + if ListenTCP4 && HttpAddr == "" { + ln, err := net.Listen("tcp4", app.Server.Addr) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + err = app.Server.Serve(ln) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + } else { + err := app.Server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + } + }() + } } - if EnableHttpListen { - go func() { - app.Server.Addr = addr - BeeLogger.Info("http server Running on %s", app.Server.Addr) - if ListenTCP4 && HttpAddr == "" { - ln, err := net.Listen("tcp4", app.Server.Addr) - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - err = app.Server.Serve(ln) - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - } else { - err := app.Server.ListenAndServe() - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - } - }() - } } - <-endRunning } diff --git a/config.go b/config.go index b22db163..09d0df24 100644 --- a/config.go +++ b/config.go @@ -82,6 +82,7 @@ var ( EnableDocs bool // enable generate docs & server docs API Swagger RouterCaseSensitive bool // router case sensitive default is true AccessLogs bool // print access logs, default is false + Graceful bool // use graceful start the server ) type beegoAppConfig struct { @@ -509,5 +510,8 @@ func ParseConfig() (err error) { if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil { RouterCaseSensitive = casesensitive } + if graceful, err := AppConfig.Bool("Graceful"); err == nil { + Graceful = graceful + } return nil } diff --git a/grace/grace.go b/grace/grace.go new file mode 100644 index 00000000..e77112d0 --- /dev/null +++ b/grace/grace.go @@ -0,0 +1,522 @@ +// 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. + +// Description: http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/ +// +// Usage: +// +// import( +// "log" +// "net/http" +// "os" +// +// "github.com/astaxie/beego/grace" +// ) +// +// func handler(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("WORLD!")) +// } +// +// func main() { +// mux := http.NewServeMux() +// mux.HandleFunc("/hello", handler) +// +// err := grace.ListenAndServe("localhost:8080", mux1) +// if err != nil { +// log.Println(err) +// } +// log.Println("Server on 8080 stopped") +// os.Exit(0) +// } +package grace + +import ( + "crypto/tls" + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "os/exec" + "os/signal" + "strings" + "sync" + "syscall" + "time" +) + +const ( + PRE_SIGNAL = iota + POST_SIGNAL + + STATE_INIT + STATE_RUNNING + STATE_SHUTTING_DOWN + STATE_TERMINATE +) + +var ( + regLock *sync.Mutex + runningServers map[string]*graceServer + runningServersOrder []string + socketPtrOffsetMap map[string]uint + runningServersForked bool + + DefaultReadTimeOut time.Duration + DefaultWriteTimeOut time.Duration + DefaultMaxHeaderBytes int + DefaultTimeout time.Duration + + isChild bool + socketOrder string +) + +func init() { + regLock = &sync.Mutex{} + flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)") + flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started") + runningServers = make(map[string]*graceServer) + runningServersOrder = []string{} + socketPtrOffsetMap = make(map[string]uint) + + DefaultMaxHeaderBytes = 0 + + // after a restart the parent will finish ongoing requests before + // shutting down. set to a negative value to disable + DefaultTimeout = 60 * time.Second +} + +type graceServer struct { + http.Server + GraceListener net.Listener + SignalHooks map[int]map[os.Signal][]func() + tlsInnerListener *graceListener + wg sync.WaitGroup + sigChan chan os.Signal + isChild bool + state uint8 + Network string +} + +// NewServer returns an intialized graceServer. Calling Serve on it will +// actually "start" the server. +func NewServer(addr string, handler http.Handler) (srv *graceServer) { + regLock.Lock() + defer regLock.Unlock() + if !flag.Parsed() { + flag.Parse() + } + if len(socketOrder) > 0 { + for i, addr := range strings.Split(socketOrder, ",") { + socketPtrOffsetMap[addr] = uint(i) + } + } else { + socketPtrOffsetMap[addr] = uint(len(runningServersOrder)) + } + + srv = &graceServer{ + wg: sync.WaitGroup{}, + sigChan: make(chan os.Signal), + isChild: isChild, + SignalHooks: map[int]map[os.Signal][]func(){ + PRE_SIGNAL: map[os.Signal][]func(){ + syscall.SIGHUP: []func(){}, + syscall.SIGUSR1: []func(){}, + syscall.SIGUSR2: []func(){}, + syscall.SIGINT: []func(){}, + syscall.SIGTERM: []func(){}, + syscall.SIGTSTP: []func(){}, + }, + POST_SIGNAL: map[os.Signal][]func(){ + syscall.SIGHUP: []func(){}, + syscall.SIGUSR1: []func(){}, + syscall.SIGUSR2: []func(){}, + syscall.SIGINT: []func(){}, + syscall.SIGTERM: []func(){}, + syscall.SIGTSTP: []func(){}, + }, + }, + state: STATE_INIT, + Network: "tcp", + } + + srv.Server.Addr = addr + srv.Server.ReadTimeout = DefaultReadTimeOut + srv.Server.WriteTimeout = DefaultWriteTimeOut + srv.Server.MaxHeaderBytes = DefaultMaxHeaderBytes + srv.Server.Handler = handler + + runningServersOrder = append(runningServersOrder, addr) + runningServers[addr] = srv + + return +} + +// ListenAndServe listens on the TCP network address addr +// and then calls Serve to handle requests on incoming connections. +func ListenAndServe(addr string, handler http.Handler) error { + server := NewServer(addr, handler) + return server.ListenAndServe() +} + +// ListenAndServeTLS listens on the TCP network address addr and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must be provided. +// If the certificate is signed by a certificate authority, +// the certFile should be the concatenation of the server's certificate followed by the CA's certificate. +func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error { + server := NewServer(addr, handler) + return server.ListenAndServeTLS(certFile, keyFile) +} + +// Serve accepts incoming connections on the Listener l, +// creating a new service goroutine for each. +// The service goroutines read requests and then call srv.Handler to reply to them. +func (srv *graceServer) Serve() (err error) { + srv.state = STATE_RUNNING + err = srv.Server.Serve(srv.GraceListener) + log.Println(syscall.Getpid(), "Waiting for connections to finish...") + srv.wg.Wait() + srv.state = STATE_TERMINATE + return +} + +// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve +// to handle requests on incoming connections. If srv.Addr is blank, ":http" is +// used. +func (srv *graceServer) ListenAndServe() (err error) { + addr := srv.Addr + if addr == "" { + addr = ":http" + } + + go srv.handleSignals() + + l, err := srv.getListener(addr) + if err != nil { + log.Println(err) + return + } + + srv.GraceListener = newGraceListener(l, srv) + + if srv.isChild { + syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + } + + log.Println(syscall.Getpid(), srv.Addr) + return srv.Serve() +} + +// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must +// be provided. If the certificate is signed by a certificate authority, the +// certFile should be the concatenation of the server's certificate followed by the +// CA's certificate. +// +// If srv.Addr is blank, ":https" is used. +func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) { + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + config := &tls.Config{} + if srv.TLSConfig != nil { + *config = *srv.TLSConfig + } + if config.NextProtos == nil { + config.NextProtos = []string{"http/1.1"} + } + + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return + } + + go srv.handleSignals() + + l, err := srv.getListener(addr) + if err != nil { + log.Println(err) + return + } + + srv.tlsInnerListener = newGraceListener(l, srv) + srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config) + + if srv.isChild { + syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + } + + log.Println(syscall.Getpid(), srv.Addr) + return srv.Serve() +} + +// getListener either opens a new socket to listen on, or takes the acceptor socket +// it got passed when restarted. +func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) { + if srv.isChild { + var ptrOffset uint = 0 + if len(socketPtrOffsetMap) > 0 { + ptrOffset = socketPtrOffsetMap[laddr] + log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr]) + } + + f := os.NewFile(uintptr(3+ptrOffset), "") + l, err = net.FileListener(f) + if err != nil { + err = fmt.Errorf("net.FileListener error: %v", err) + return + } + } else { + l, err = net.Listen(srv.Network, laddr) + if err != nil { + err = fmt.Errorf("net.Listen error: %v", err) + return + } + } + return +} + +// handleSignals listens for os Signals and calls any hooked in function that the +// user had registered with the signal. +func (srv *graceServer) handleSignals() { + var sig os.Signal + + signal.Notify( + srv.sigChan, + syscall.SIGHUP, + syscall.SIGUSR1, + syscall.SIGUSR2, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGTSTP, + ) + + pid := syscall.Getpid() + for { + sig = <-srv.sigChan + srv.signalHooks(PRE_SIGNAL, sig) + switch sig { + case syscall.SIGHUP: + log.Println(pid, "Received SIGHUP. forking.") + err := srv.fork() + if err != nil { + log.Println("Fork err:", err) + } + case syscall.SIGUSR1: + log.Println(pid, "Received SIGUSR1.") + case syscall.SIGUSR2: + log.Println(pid, "Received SIGUSR2.") + srv.serverTimeout(0 * time.Second) + case syscall.SIGINT: + log.Println(pid, "Received SIGINT.") + srv.shutdown() + case syscall.SIGTERM: + log.Println(pid, "Received SIGTERM.") + srv.shutdown() + case syscall.SIGTSTP: + log.Println(pid, "Received SIGTSTP.") + default: + log.Printf("Received %v: nothing i care about...\n", sig) + } + srv.signalHooks(POST_SIGNAL, sig) + } +} + +func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) { + if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet { + return + } + for _, f := range srv.SignalHooks[ppFlag][sig] { + f() + } + return +} + +// shutdown closes the listener so that no new connections are accepted. it also +// starts a goroutine that will hammer (stop all running requests) the server +// after DefaultTimeout. +func (srv *graceServer) shutdown() { + if srv.state != STATE_RUNNING { + return + } + + srv.state = STATE_SHUTTING_DOWN + if DefaultTimeout >= 0 { + go srv.serverTimeout(DefaultTimeout) + } + err := srv.GraceListener.Close() + if err != nil { + log.Println(syscall.Getpid(), "Listener.Close() error:", err) + } else { + log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.") + } +} + +// hammerTime forces the server to shutdown in a given timeout - whether it +// finished outstanding requests or not. if Read/WriteTimeout are not set or the +// max header size is very big a connection could hang... +// +// srv.Serve() will not return until all connections are served. this will +// unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe(TLS) to +// return. +func (srv *graceServer) serverTimeout(d time.Duration) { + defer func() { + // we are calling srv.wg.Done() until it panics which means we called + // Done() when the counter was already at 0 and we're done. + // (and thus Serve() will return and the parent will exit) + if r := recover(); r != nil { + log.Println("WaitGroup at 0", r) + } + }() + if srv.state != STATE_SHUTTING_DOWN { + return + } + time.Sleep(d) + log.Println("[STOP - Hammer Time] Forcefully shutting down parent") + for { + if srv.state == STATE_TERMINATE { + break + } + srv.wg.Done() + } +} + +func (srv *graceServer) fork() (err error) { + // only one server isntance should fork! + regLock.Lock() + defer regLock.Unlock() + if runningServersForked { + return + } + runningServersForked = true + + var files = make([]*os.File, len(runningServers)) + var orderArgs = make([]string, len(runningServers)) + // get the accessor socket fds for _all_ server instances + for _, srvPtr := range runningServers { + // introspect.PrintTypeDump(srvPtr.EndlessListener) + switch srvPtr.GraceListener.(type) { + case *graceListener: + // normal listener + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File() + default: + // tls listener + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File() + } + orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr + } + + log.Println(files) + path := os.Args[0] + var args []string + if len(os.Args) > 1 { + for _, arg := range os.Args[1:] { + if arg == "-graceful" { + break + } + args = append(args, arg) + } + } + args = append(args, "-graceful") + if len(runningServers) > 1 { + args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ","))) + log.Println(args) + } + cmd := exec.Command(path, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.ExtraFiles = files + err = cmd.Start() + if err != nil { + log.Fatalf("Restart: Failed to launch, error: %v", err) + } + + return +} + +type graceListener struct { + net.Listener + stop chan error + stopped bool + server *graceServer +} + +func (gl *graceListener) Accept() (c net.Conn, err error) { + tc, err := gl.Listener.(*net.TCPListener).AcceptTCP() + if err != nil { + return + } + + tc.SetKeepAlive(true) // see http.tcpKeepAliveListener + tc.SetKeepAlivePeriod(3 * time.Minute) // see http.tcpKeepAliveListener + + c = graceConn{ + Conn: tc, + server: gl.server, + } + + gl.server.wg.Add(1) + return +} + +func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) { + el = &graceListener{ + Listener: l, + stop: make(chan error), + server: srv, + } + + // Starting the listener for the stop signal here because Accept blocks on + // el.Listener.(*net.TCPListener).AcceptTCP() + // The goroutine will unblock it by closing the listeners fd + go func() { + _ = <-el.stop + el.stopped = true + el.stop <- el.Listener.Close() + }() + return +} + +func (el *graceListener) Close() error { + if el.stopped { + return syscall.EINVAL + } + el.stop <- nil + return <-el.stop +} + +func (el *graceListener) File() *os.File { + // returns a dup(2) - FD_CLOEXEC flag *not* set + tl := el.Listener.(*net.TCPListener) + fl, _ := tl.File() + return fl +} + +type graceConn struct { + net.Conn + server *graceServer +} + +func (c graceConn) Close() error { + c.server.wg.Done() + return c.Conn.Close() +} From 740a5261057b3300e21ffe404996450a325966a3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 13 May 2015 21:20:50 +0800 Subject: [PATCH 053/436] fix sesseion redis db error --- session/redis/sess_redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 887fb520..d31feb2c 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -145,7 +145,7 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { rp.password = configs[2] } if len(configs) > 3 { - dbnum, err := strconv.Atoi(configs[1]) + dbnum, err := strconv.Atoi(configs[3]) if err != nil || dbnum < 0 { rp.dbNum = 0 } else { From 519602a553a2897cf813dd52a8d8308cb4bbe7e9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 15 May 2015 15:04:08 +0800 Subject: [PATCH 054/436] fix #1152 --- session/session.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/session/session.go b/session/session.go index 4214320c..ffc08edc 100644 --- a/session/session.go +++ b/session/session.go @@ -147,7 +147,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain} if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime @@ -174,7 +174,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain} if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime @@ -233,7 +233,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain, } } else { @@ -270,3 +270,17 @@ func (manager *Manager) sessionId(r *http.Request) (string, error) { } return hex.EncodeToString(b), nil } + +// Set cookie with https. +func (manager *Manager) isSecure(req *http.Request) bool { + if !manager.config.Secure { + return false + } + if req.URL.Scheme != "" { + return req.URL.Scheme == "https" + } + if req.TLS == nil { + return false + } + return true +} From 9bb985515370a3cd577f4097b60fa35dfd46dca3 Mon Sep 17 00:00:00 2001 From: ziyel Date: Mon, 18 May 2015 14:42:18 +0800 Subject: [PATCH 055/436] Let filter function get more params info from ctx.Input.Params --- router.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/router.go b/router.go index b9d649a2..232f67d2 100644 --- a/router.go +++ b/router.go @@ -612,6 +612,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if l, ok := p.filters[pos]; ok { for _, filterR := range l { if ok, p := filterR.ValidRouter(urlPath); ok { + for k, v := range context.Input.Params { + p[k] = v + } context.Input.Params = p filterR.filterFunc(context) if filterR.returnOnOutput && w.started { From 185089299cfac2cf66b90d8acfb62b0cbce1d9b1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 20 May 2015 11:07:23 +0800 Subject: [PATCH 056/436] sync beeApp.Server to graceful --- app.go | 11 +++++++---- grace/grace.go | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index 4740e7e5..6c1eeab6 100644 --- a/app.go +++ b/app.go @@ -78,15 +78,19 @@ func (app *App) Run() { } } else { if Graceful { + app.Server.Addr = addr + app.Server.Handler = app.Handlers + app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second if EnableHttpTLS { go func() { time.Sleep(20 * time.Microsecond) if HttpsPort != 0 { addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + app.Server.Addr = addr } server := grace.NewServer(addr, app.Handlers) - server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server = app.Server err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) @@ -98,8 +102,7 @@ func (app *App) Run() { if EnableHttpListen { go func() { server := grace.NewServer(addr, app.Handlers) - server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server = app.Server if ListenTCP4 && HttpAddr == "" { server.Network = "tcp4" } diff --git a/grace/grace.go b/grace/grace.go index e77112d0..33053c4b 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -99,7 +99,7 @@ func init() { } type graceServer struct { - http.Server + *http.Server GraceListener net.Listener SignalHooks map[int]map[os.Signal][]func() tlsInnerListener *graceListener @@ -151,7 +151,7 @@ func NewServer(addr string, handler http.Handler) (srv *graceServer) { state: STATE_INIT, Network: "tcp", } - + srv.Server = &http.Server{} srv.Server.Addr = addr srv.Server.ReadTimeout = DefaultReadTimeOut srv.Server.WriteTimeout = DefaultWriteTimeOut From 92f3de40275ed9ce9a35da44c146db31fa74faa2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 20 May 2015 11:09:30 +0800 Subject: [PATCH 057/436] update the string --- admin.go | 2 +- app.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/admin.go b/admin.go index 52388c76..64d7fe34 100644 --- a/admin.go +++ b/admin.go @@ -468,6 +468,6 @@ func (admin *adminApp) Run() { err = http.ListenAndServe(addr, nil) } if err != nil { - BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) } } diff --git a/app.go b/app.go index 6c1eeab6..8fc320ad 100644 --- a/app.go +++ b/app.go @@ -93,7 +93,7 @@ func (app *App) Run() { server.Server = app.Server err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err) + BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -108,7 +108,7 @@ func (app *App) Run() { } err := server.ListenAndServe() if err != nil { - BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } From b1efae6ff8682d1ed4c98cbab4241729375e1a92 Mon Sep 17 00:00:00 2001 From: Liu Peng Date: Sun, 24 May 2015 23:59:39 +0800 Subject: [PATCH 058/436] add missed LICENSE of captcha --- utils/captcha/LICENSE | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 utils/captcha/LICENSE diff --git a/utils/captcha/LICENSE b/utils/captcha/LICENSE new file mode 100644 index 00000000..0ad73ae0 --- /dev/null +++ b/utils/captcha/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2011-2014 Dmitry Chestnykh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. From bf6b0d3e1ff4275d7ec325ffb27ecd855315886f Mon Sep 17 00:00:00 2001 From: Hubery Date: Fri, 6 Mar 2015 14:12:24 +0800 Subject: [PATCH 059/436] add JsonBody --- httplib/httplib.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index 7ff2f1d2..bd8bc776 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -253,6 +253,22 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { return b } + +// JsonBody adds request raw body encoding by JSON. +func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) { + if b.req.Body == nil && obj != nil { + buf := bytes.NewBuffer(nil) + enc := json.NewEncoder(buf) + if err := enc.Encode(obj); err != nil { + return b, err + } + b.req.Body = ioutil.NopCloser(buf) + b.req.ContentLength = int64(buf.Len()) + b.req.Header.Set("Content-Type", "application/json") + } + return b, nil +} + func (b *BeegoHttpRequest) buildUrl(paramBody string) { // build GET url with query string if b.req.Method == "GET" && len(paramBody) > 0 { From 1592e9c04dd73e26fb35cab9e9370f89b414c8c8 Mon Sep 17 00:00:00 2001 From: supiyun Date: Mon, 16 Mar 2015 17:40:55 +0800 Subject: [PATCH 060/436] =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81reload?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当页面放置一段时间,验证码将从缓存中失效。当用户再来刷新验证码将出现验证码404。对于reload操作应该直接生成验证码。 --- utils/captcha/captcha.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/utils/captcha/captcha.go b/utils/captcha/captcha.go index 62adc81d..14c6c3b3 100644 --- a/utils/captcha/captcha.go +++ b/utils/captcha/captcha.go @@ -132,15 +132,6 @@ func (c *Captcha) Handler(ctx *context.Context) { key := c.key(id) - if v, ok := c.store.Get(key).([]byte); ok { - chars = v - } else { - ctx.Output.SetStatus(404) - ctx.WriteString("captcha not found") - return - } - - // reload captcha if len(ctx.Input.Query("reload")) > 0 { chars = c.genRandChars() if err := c.store.Put(key, chars, c.Expiration); err != nil { @@ -149,6 +140,14 @@ func (c *Captcha) Handler(ctx *context.Context) { beego.Error("Reload Create Captcha Error:", err) return } + } else { + if v, ok := c.store.Get(key).([]byte); ok { + chars = v + } else { + ctx.Output.SetStatus(404) + ctx.WriteString("captcha not found") + return + } } img := NewImage(chars, c.StdWidth, c.StdHeight) From 1d8afdc9c9677615f241dda3ebe1d2604e44f5de Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 19 Mar 2015 22:29:01 -0700 Subject: [PATCH 061/436] gofmt -s & go_vet --- cache/memory.go | 2 +- orm/db.go | 12 +- orm/models_test.go | 8 +- orm/orm_raw.go | 4 +- orm/orm_test.go | 32 +-- orm/utils.go | 4 +- router.go | 6 +- session/memcache/sess_memcache.go | 1 - session/sess_mem.go | 398 +++++++++++++++--------------- utils/mail.go | 2 +- utils/pagination/paginator.go | 6 +- utils/slice.go | 2 +- validation/util.go | 2 +- 13 files changed, 239 insertions(+), 240 deletions(-) diff --git a/cache/memory.go b/cache/memory.go index b90d227c..0233be7d 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -202,7 +202,7 @@ func (bc *MemoryCache) vaccuum() { if bc.items == nil { return } - for name, _ := range bc.items { + for name := range bc.items { bc.item_expired(name) } } diff --git a/orm/db.go b/orm/db.go index 10f65fee..060d83bc 100644 --- a/orm/db.go +++ b/orm/db.go @@ -324,7 +324,7 @@ func (d *dbBase) Read(q dbQuerier, mi *modelInfo, ind reflect.Value, tz *time.Lo query := fmt.Sprintf("SELECT %s%s%s FROM %s%s%s WHERE %s%s%s = ?", Q, sels, Q, Q, mi.table, Q, Q, wheres, Q) refs := make([]interface{}, colsNum) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } @@ -423,7 +423,7 @@ func (d *dbBase) InsertValue(q dbQuerier, mi *modelInfo, isMulti bool, names []s Q := d.ins.TableQuote() marks := make([]string, len(names)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } @@ -693,7 +693,7 @@ func (d *dbBase) DeleteBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Con } marks := make([]string, len(args)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } sql := fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) @@ -824,7 +824,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } refs := make([]interface{}, colsNum) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } @@ -964,7 +964,7 @@ func (d *dbBase) GenerateOperatorSql(mi *modelInfo, fi *fieldInfo, operator stri switch operator { case "in": marks := make([]string, len(params)) - for i, _ := range marks { + for i := range marks { marks[i] = "?" } sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", ")) @@ -1460,7 +1460,7 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond } refs := make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { var ref interface{} refs[i] = &ref } diff --git a/orm/models_test.go b/orm/models_test.go index 1a92ef5d..6ca9590c 100644 --- a/orm/models_test.go +++ b/orm/models_test.go @@ -242,14 +242,14 @@ type User struct { func (u *User) TableIndex() [][]string { return [][]string{ - []string{"Id", "UserName"}, - []string{"Id", "Created"}, + {"Id", "UserName"}, + {"Id", "Created"}, } } func (u *User) TableUnique() [][]string { return [][]string{ - []string{"UserName", "Email"}, + {"UserName", "Email"}, } } @@ -287,7 +287,7 @@ type Post struct { func (u *Post) TableIndex() [][]string { return [][]string{ - []string{"Id", "Created"}, + {"Id", "Created"}, } } diff --git a/orm/orm_raw.go b/orm/orm_raw.go index 1393d414..1452d6fc 100644 --- a/orm/orm_raw.go +++ b/orm/orm_raw.go @@ -585,7 +585,7 @@ func (o *rawSet) readValues(container interface{}, needCols []string) (int64, er cols = columns refs = make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { var ref sql.NullString refs[i] = &ref @@ -711,7 +711,7 @@ func (o *rawSet) queryRowsTo(container interface{}, keyCol, valueCol string) (in } else { cols = columns refs = make([]interface{}, len(cols)) - for i, _ := range refs { + for i := range refs { if keyCol == cols[i] { keyIndex = i } diff --git a/orm/orm_test.go b/orm/orm_test.go index e1c8e0f0..14eadabd 100644 --- a/orm/orm_test.go +++ b/orm/orm_test.go @@ -586,29 +586,29 @@ func TestInsertTestData(t *testing.T) { throwFail(t, AssertIs(id, 4)) tags := []*Tag{ - &Tag{Name: "golang", BestPost: &Post{Id: 2}}, - &Tag{Name: "example"}, - &Tag{Name: "format"}, - &Tag{Name: "c++"}, + {Name: "golang", BestPost: &Post{Id: 2}}, + {Name: "example"}, + {Name: "format"}, + {Name: "c++"}, } posts := []*Post{ - &Post{User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand. + {User: users[0], Tags: []*Tag{tags[0]}, Title: "Introduction", Content: `Go is a new language. Although it borrows ideas from existing languages, it has unusual properties that make effective Go programs different in character from programs written in its relatives. A straightforward translation of a C++ or Java program into Go is unlikely to produce a satisfactory result—Java programs are written in Java, not Go. On the other hand, thinking about the problem from a Go perspective could produce a successful but quite different program. In other words, to write Go well, it's important to understand its properties and idioms. It's also important to know the established conventions for programming in Go, such as naming, formatting, program construction, and so on, so that programs you write will be easy for other Go programmers to understand. This document gives tips for writing clear, idiomatic Go code. It augments the language specification, the Tour of Go, and How to Write Go Code, all of which you should read first.`}, - &Post{User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`}, - &Post{User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide. + {User: users[1], Tags: []*Tag{tags[0], tags[1]}, Title: "Examples", Content: `The Go package sources are intended to serve not only as the core library but also as examples of how to use the language. Moreover, many of the packages contain working, self-contained executable examples you can run directly from the golang.org web site, such as this one (click on the word "Example" to open it up). If you have a question about how to approach a problem or how something might be implemented, the documentation, code and examples in the library can provide answers, ideas and background.`}, + {User: users[1], Tags: []*Tag{tags[0], tags[2]}, Title: "Formatting", Content: `Formatting issues are the most contentious but the least consequential. People can adapt to different formatting styles but it's better if they don't have to, and less time is devoted to the topic if everyone adheres to the same style. The problem is how to approach this Utopia without a long prescriptive style guide. With Go we take an unusual approach and let the machine take care of most formatting issues. The gofmt program (also available as go fmt, which operates at the package level rather than source file level) reads a Go program and emits the source in a standard style of indentation and vertical alignment, retaining and if necessary reformatting comments. If you want to know how to handle some new layout situation, run gofmt; if the answer doesn't seem right, rearrange your program (or file a bug about gofmt), don't work around it.`}, - &Post{User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code. + {User: users[2], Tags: []*Tag{tags[3]}, Title: "Commentary", Content: `Go provides C-style /* */ block comments and C++-style // line comments. Line comments are the norm; block comments appear mostly as package comments, but are useful within an expression or to disable large swaths of code. The program—and web server—godoc processes Go source files to extract documentation about the contents of the package. Comments that appear before top-level declarations, with no intervening newlines, are extracted along with the declaration to serve as explanatory text for the item. The nature and style of these comments determines the quality of the documentation godoc produces.`}, } comments := []*Comment{ - &Comment{Post: posts[0], Content: "a comment"}, - &Comment{Post: posts[1], Content: "yes"}, - &Comment{Post: posts[1]}, - &Comment{Post: posts[1]}, - &Comment{Post: posts[2]}, - &Comment{Post: posts[2]}, + {Post: posts[0], Content: "a comment"}, + {Post: posts[1], Content: "yes"}, + {Post: posts[1]}, + {Post: posts[1]}, + {Post: posts[2]}, + {Post: posts[2]}, } for _, tag := range tags { @@ -1248,7 +1248,7 @@ func TestQueryM2M(t *testing.T) { post := Post{Id: 4} m2m := dORM.QueryM2M(&post, "Tags") - tag1 := []*Tag{&Tag{Name: "TestTag1"}, &Tag{Name: "TestTag2"}} + tag1 := []*Tag{{Name: "TestTag1"}, {Name: "TestTag2"}} tag2 := &Tag{Name: "TestTag3"} tag3 := []interface{}{&Tag{Name: "TestTag4"}} @@ -1311,7 +1311,7 @@ func TestQueryM2M(t *testing.T) { m2m = dORM.QueryM2M(&tag, "Posts") - post1 := []*Post{&Post{Title: "TestPost1"}, &Post{Title: "TestPost2"}} + post1 := []*Post{{Title: "TestPost1"}, {Title: "TestPost2"}} post2 := &Post{Title: "TestPost3"} post3 := []interface{}{&Post{Title: "TestPost4"}} diff --git a/orm/utils.go b/orm/utils.go index df6147c1..88168763 100644 --- a/orm/utils.go +++ b/orm/utils.go @@ -195,7 +195,7 @@ func snakeString(s string) string { } data = append(data, d) } - return strings.ToLower(string(data[:len(data)])) + return strings.ToLower(string(data[:])) } // camel string, xx_yy to XxYy @@ -220,7 +220,7 @@ func camelString(s string) string { } data = append(data, d) } - return string(data[:len(data)]) + return string(data[:]) } type argString []string diff --git a/router.go b/router.go index b9d649a2..c1bda637 100644 --- a/router.go +++ b/router.go @@ -88,7 +88,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool { if requestPath == "/favicon.ico" || requestPath == "/robots.txt" { return true } - for prefix, _ := range StaticDir { + for prefix := range StaticDir { if strings.HasPrefix(requestPath, prefix) { return true } @@ -171,7 +171,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM p.addToRouter(m, pattern, route) } } else { - for k, _ := range methods { + for k := range methods { if k == "*" { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) @@ -332,7 +332,7 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { methods[strings.ToUpper(method)] = strings.ToUpper(method) } route.methods = methods - for k, _ := range methods { + for k := range methods { if k == "*" { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index 5827a0a9..24aecb82 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -179,7 +179,6 @@ func (rp *MemProvider) SessionRegenerate(oldsid, sid string) (session.SessionSto } else { client.Delete(oldsid) item.Key = sid - item.Value = item.Value item.Expiration = int32(rp.maxlifetime) client.Set(item) contain = item.Value diff --git a/session/sess_mem.go b/session/sess_mem.go index 627a3246..dd066703 100644 --- a/session/sess_mem.go +++ b/session/sess_mem.go @@ -1,199 +1,199 @@ -// 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 session - -import ( - "container/list" - "net/http" - "sync" - "time" -) - -var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} - -// memory session store. -// it saved sessions in a map in memory. -type MemSessionStore struct { - sid string //session id - timeAccessed time.Time //last access time - value map[interface{}]interface{} //session store - lock sync.RWMutex -} - -// set value to memory session -func (st *MemSessionStore) Set(key, value interface{}) error { - st.lock.Lock() - defer st.lock.Unlock() - st.value[key] = value - return nil -} - -// get value from memory session by key -func (st *MemSessionStore) Get(key interface{}) interface{} { - st.lock.RLock() - defer st.lock.RUnlock() - if v, ok := st.value[key]; ok { - return v - } else { - return nil - } -} - -// delete in memory session by key -func (st *MemSessionStore) Delete(key interface{}) error { - st.lock.Lock() - defer st.lock.Unlock() - delete(st.value, key) - return nil -} - -// clear all values in memory session -func (st *MemSessionStore) Flush() error { - st.lock.Lock() - defer st.lock.Unlock() - st.value = make(map[interface{}]interface{}) - return nil -} - -// get this id of memory session store -func (st *MemSessionStore) SessionID() string { - return st.sid -} - -// Implement method, no used. -func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { -} - -type MemProvider struct { - lock sync.RWMutex // locker - sessions map[string]*list.Element // map in memory - list *list.List // for gc - maxlifetime int64 - savePath string -} - -// init memory session -func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { - pder.maxlifetime = maxlifetime - pder.savePath = savePath - return nil -} - -// get memory session store by sid -func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { - pder.lock.RLock() - if element, ok := pder.sessions[sid]; ok { - go pder.SessionUpdate(sid) - pder.lock.RUnlock() - return element.Value.(*MemSessionStore), nil - } else { - pder.lock.RUnlock() - pder.lock.Lock() - newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} - element := pder.list.PushBack(newsess) - pder.sessions[sid] = element - pder.lock.Unlock() - return newsess, nil - } -} - -// check session store exist in memory session by sid -func (pder *MemProvider) SessionExist(sid string) bool { - pder.lock.RLock() - defer pder.lock.RUnlock() - if _, ok := pder.sessions[sid]; ok { - return true - } else { - return false - } -} - -// generate new sid for session store in memory session -func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { - pder.lock.RLock() - if element, ok := pder.sessions[oldsid]; ok { - go pder.SessionUpdate(oldsid) - pder.lock.RUnlock() - pder.lock.Lock() - element.Value.(*MemSessionStore).sid = sid - pder.sessions[sid] = element - delete(pder.sessions, oldsid) - pder.lock.Unlock() - return element.Value.(*MemSessionStore), nil - } else { - pder.lock.RUnlock() - pder.lock.Lock() - newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} - element := pder.list.PushBack(newsess) - pder.sessions[sid] = element - pder.lock.Unlock() - return newsess, nil - } -} - -// delete session store in memory session by id -func (pder *MemProvider) SessionDestroy(sid string) error { - pder.lock.Lock() - defer pder.lock.Unlock() - if element, ok := pder.sessions[sid]; ok { - delete(pder.sessions, sid) - pder.list.Remove(element) - return nil - } - return nil -} - -// clean expired session stores in memory session -func (pder *MemProvider) SessionGC() { - pder.lock.RLock() - for { - element := pder.list.Back() - if element == nil { - break - } - if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() { - pder.lock.RUnlock() - pder.lock.Lock() - pder.list.Remove(element) - delete(pder.sessions, element.Value.(*MemSessionStore).sid) - pder.lock.Unlock() - pder.lock.RLock() - } else { - break - } - } - pder.lock.RUnlock() -} - -// get count number of memory session -func (pder *MemProvider) SessionAll() int { - return pder.list.Len() -} - -// expand time of session store by id in memory session -func (pder *MemProvider) SessionUpdate(sid string) error { - pder.lock.Lock() - defer pder.lock.Unlock() - if element, ok := pder.sessions[sid]; ok { - element.Value.(*MemSessionStore).timeAccessed = time.Now() - pder.list.MoveToFront(element) - return nil - } - return nil -} - -func init() { - Register("memory", mempder) -} +// 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 session + +import ( + "container/list" + "net/http" + "sync" + "time" +) + +var mempder = &MemProvider{list: list.New(), sessions: make(map[string]*list.Element)} + +// memory session store. +// it saved sessions in a map in memory. +type MemSessionStore struct { + sid string //session id + timeAccessed time.Time //last access time + value map[interface{}]interface{} //session store + lock sync.RWMutex +} + +// set value to memory session +func (st *MemSessionStore) Set(key, value interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + st.value[key] = value + return nil +} + +// get value from memory session by key +func (st *MemSessionStore) Get(key interface{}) interface{} { + st.lock.RLock() + defer st.lock.RUnlock() + if v, ok := st.value[key]; ok { + return v + } else { + return nil + } +} + +// delete in memory session by key +func (st *MemSessionStore) Delete(key interface{}) error { + st.lock.Lock() + defer st.lock.Unlock() + delete(st.value, key) + return nil +} + +// clear all values in memory session +func (st *MemSessionStore) Flush() error { + st.lock.Lock() + defer st.lock.Unlock() + st.value = make(map[interface{}]interface{}) + return nil +} + +// get this id of memory session store +func (st *MemSessionStore) SessionID() string { + return st.sid +} + +// Implement method, no used. +func (st *MemSessionStore) SessionRelease(w http.ResponseWriter) { +} + +type MemProvider struct { + lock sync.RWMutex // locker + sessions map[string]*list.Element // map in memory + list *list.List // for gc + maxlifetime int64 + savePath string +} + +// init memory session +func (pder *MemProvider) SessionInit(maxlifetime int64, savePath string) error { + pder.maxlifetime = maxlifetime + pder.savePath = savePath + return nil +} + +// get memory session store by sid +func (pder *MemProvider) SessionRead(sid string) (SessionStore, error) { + pder.lock.RLock() + if element, ok := pder.sessions[sid]; ok { + go pder.SessionUpdate(sid) + pder.lock.RUnlock() + return element.Value.(*MemSessionStore), nil + } else { + pder.lock.RUnlock() + pder.lock.Lock() + newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} + element := pder.list.PushBack(newsess) + pder.sessions[sid] = element + pder.lock.Unlock() + return newsess, nil + } +} + +// check session store exist in memory session by sid +func (pder *MemProvider) SessionExist(sid string) bool { + pder.lock.RLock() + defer pder.lock.RUnlock() + if _, ok := pder.sessions[sid]; ok { + return true + } else { + return false + } +} + +// generate new sid for session store in memory session +func (pder *MemProvider) SessionRegenerate(oldsid, sid string) (SessionStore, error) { + pder.lock.RLock() + if element, ok := pder.sessions[oldsid]; ok { + go pder.SessionUpdate(oldsid) + pder.lock.RUnlock() + pder.lock.Lock() + element.Value.(*MemSessionStore).sid = sid + pder.sessions[sid] = element + delete(pder.sessions, oldsid) + pder.lock.Unlock() + return element.Value.(*MemSessionStore), nil + } else { + pder.lock.RUnlock() + pder.lock.Lock() + newsess := &MemSessionStore{sid: sid, timeAccessed: time.Now(), value: make(map[interface{}]interface{})} + element := pder.list.PushBack(newsess) + pder.sessions[sid] = element + pder.lock.Unlock() + return newsess, nil + } +} + +// delete session store in memory session by id +func (pder *MemProvider) SessionDestroy(sid string) error { + pder.lock.Lock() + defer pder.lock.Unlock() + if element, ok := pder.sessions[sid]; ok { + delete(pder.sessions, sid) + pder.list.Remove(element) + return nil + } + return nil +} + +// clean expired session stores in memory session +func (pder *MemProvider) SessionGC() { + pder.lock.RLock() + for { + element := pder.list.Back() + if element == nil { + break + } + if (element.Value.(*MemSessionStore).timeAccessed.Unix() + pder.maxlifetime) < time.Now().Unix() { + pder.lock.RUnlock() + pder.lock.Lock() + pder.list.Remove(element) + delete(pder.sessions, element.Value.(*MemSessionStore).sid) + pder.lock.Unlock() + pder.lock.RLock() + } else { + break + } + } + pder.lock.RUnlock() +} + +// get count number of memory session +func (pder *MemProvider) SessionAll() int { + return pder.list.Len() +} + +// expand time of session store by id in memory session +func (pder *MemProvider) SessionUpdate(sid string) error { + pder.lock.Lock() + defer pder.lock.Unlock() + if element, ok := pder.sessions[sid]; ok { + element.Value.(*MemSessionStore).timeAccessed = time.Now() + pder.list.MoveToFront(element) + return nil + } + return nil +} + +func init() { + Register("memory", mempder) +} diff --git a/utils/mail.go b/utils/mail.go index aa219626..4959268c 100644 --- a/utils/mail.go +++ b/utils/mail.go @@ -292,7 +292,7 @@ func qpEscape(dest []byte, c byte) { const nums = "0123456789ABCDEF" dest[0] = '=' dest[1] = nums[(c&0xf0)>>4] - dest[2] = nums[(c&0xf)] + dest[2] = nums[(c & 0xf)] } // headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer diff --git a/utils/pagination/paginator.go b/utils/pagination/paginator.go index e537f1ad..e813f798 100644 --- a/utils/pagination/paginator.go +++ b/utils/pagination/paginator.go @@ -92,18 +92,18 @@ func (p *Paginator) Pages() []int { case page >= pageNums-4 && pageNums > 9: start := pageNums - 9 + 1 pages = make([]int, 9) - for i, _ := range pages { + for i := range pages { pages[i] = start + i } case page >= 5 && pageNums > 9: start := page - 5 + 1 pages = make([]int, int(math.Min(9, float64(page+4+1)))) - for i, _ := range pages { + for i := range pages { pages[i] = start + i } default: pages = make([]int, int(math.Min(9, float64(pageNums)))) - for i, _ := range pages { + for i := range pages { pages[i] = i + 1 } } diff --git a/utils/slice.go b/utils/slice.go index 25032b7f..e413004d 100644 --- a/utils/slice.go +++ b/utils/slice.go @@ -51,7 +51,7 @@ func SliceRandList(min, max int) []int { t0 := time.Now() rand.Seed(int64(t0.Nanosecond())) list := rand.Perm(length) - for index, _ := range list { + for index := range list { list[index] += min } return list diff --git a/validation/util.go b/validation/util.go index 249462d4..367da5dc 100644 --- a/validation/util.go +++ b/validation/util.go @@ -137,7 +137,7 @@ func getRegFuncs(tag, key string) (vfs []ValidFunc, str string, err error) { if err != nil { return } - vfs = []ValidFunc{ValidFunc{"Match", []interface{}{reg, key + ".Match"}}} + vfs = []ValidFunc{{"Match", []interface{}{reg, key + ".Match"}}} str = strings.TrimSpace(tag[:index]) + strings.TrimSpace(tag[end+len("/)"):]) return } From 2e51c124f153350fca514c323c58fda022fe48b6 Mon Sep 17 00:00:00 2001 From: toalexjin Date: Wed, 25 Mar 2015 15:14:57 +0800 Subject: [PATCH 062/436] For enhancing performance, check log level before fmt.Sprintf() --- logs/log.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/logs/log.go b/logs/log.go index 32e0187c..e9d2673c 100644 --- a/logs/log.go +++ b/logs/log.go @@ -207,48 +207,72 @@ func (bl *BeeLogger) startLogger() { // Log EMERGENCY level message. func (bl *BeeLogger) Emergency(format string, v ...interface{}) { + if LevelEmergency > bl.level { + return + } msg := fmt.Sprintf("[M] "+format, v...) bl.writerMsg(LevelEmergency, msg) } // Log ALERT level message. func (bl *BeeLogger) Alert(format string, v ...interface{}) { + if LevelAlert > bl.level { + return + } msg := fmt.Sprintf("[A] "+format, v...) bl.writerMsg(LevelAlert, msg) } // Log CRITICAL level message. func (bl *BeeLogger) Critical(format string, v ...interface{}) { + if LevelCritical > bl.level { + return + } msg := fmt.Sprintf("[C] "+format, v...) bl.writerMsg(LevelCritical, msg) } // Log ERROR level message. func (bl *BeeLogger) Error(format string, v ...interface{}) { + if LevelError > bl.level { + return + } msg := fmt.Sprintf("[E] "+format, v...) bl.writerMsg(LevelError, msg) } // Log WARNING level message. func (bl *BeeLogger) Warning(format string, v ...interface{}) { + if LevelWarning > bl.level { + return + } msg := fmt.Sprintf("[W] "+format, v...) bl.writerMsg(LevelWarning, msg) } // Log NOTICE level message. func (bl *BeeLogger) Notice(format string, v ...interface{}) { + if LevelNotice > bl.level { + return + } msg := fmt.Sprintf("[N] "+format, v...) bl.writerMsg(LevelNotice, msg) } // Log INFORMATIONAL level message. func (bl *BeeLogger) Informational(format string, v ...interface{}) { + if LevelInformational > bl.level { + return + } msg := fmt.Sprintf("[I] "+format, v...) bl.writerMsg(LevelInformational, msg) } // Log DEBUG level message. func (bl *BeeLogger) Debug(format string, v ...interface{}) { + if LevelDebug > bl.level { + return + } msg := fmt.Sprintf("[D] "+format, v...) bl.writerMsg(LevelDebug, msg) } From 1a6ea693b5273111bb896bc3131e03aaaa84573e Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Wed, 25 Mar 2015 14:47:20 +0100 Subject: [PATCH 063/436] Added to input.go: AcceptHtml, AcceptsXml and AcceptsJson functions which check the header agains a regex for simpler mult-content-type handling. --- context/input.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/context/input.go b/context/input.go index f535e6a2..95f56c1f 100644 --- a/context/input.go +++ b/context/input.go @@ -21,12 +21,21 @@ import ( "net/http" "net/url" "reflect" + "regexp" "strconv" "strings" "github.com/astaxie/beego/session" ) +// Regexes for checking the accept headers +// TODO make sure these are correct +var ( + acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) + acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) + acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)?`) +) + // BeegoInput operates the http request header, data, cookie and body. // it also contains router params and current session. type BeegoInput struct { @@ -163,6 +172,21 @@ func (input *BeegoInput) IsUpload() bool { return strings.Contains(input.Header("Content-Type"), "multipart/form-data") } +// Checks if request accepts html response +func (input *BeegoInput) AcceptsHtml() bool { + return acceptsHtmlRegex.MatchString(input.Header("Accept")) +} + +// Checks if request accepts xml response +func (input *BeegoInput) AcceptsXml() bool { + return acceptsXmlRegex.MatchString(input.Header("Accept")) +} + +// Checks if request accepts json response +func (input *BeegoInput) AcceptsJson() bool { + return acceptsJsonRegex.MatchString(input.Header("Accept")) +} + // IP returns request client ip. // if in proxy, return first proxy id. // if error, return 127.0.0.1. From 34940d00c0e30453ffce2af0173b694ec649de5a Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Wed, 25 Mar 2015 14:54:39 +0100 Subject: [PATCH 064/436] Remove unnecessary optional group flag '?' since has to match one of comma or end of string --- context/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index 95f56c1f..24d43bbb 100644 --- a/context/input.go +++ b/context/input.go @@ -33,7 +33,7 @@ import ( var ( acceptsHtmlRegex = regexp.MustCompile(`(text/html|application/xhtml\+xml)(?:,|$)`) acceptsXmlRegex = regexp.MustCompile(`(application/xml|text/xml)(?:,|$)`) - acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)?`) + acceptsJsonRegex = regexp.MustCompile(`(application/json)(?:,|$)`) ) // BeegoInput operates the http request header, data, cookie and body. From 5b1705b2d6aed081393b3fe2d51dd9dcdb28633c Mon Sep 17 00:00:00 2001 From: toalexjin Date: Thu, 26 Mar 2015 14:40:12 +0800 Subject: [PATCH 065/436] Do not check log level in writerMsg() because the check is already done outside. --- logs/log.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/logs/log.go b/logs/log.go index e9d2673c..3103ac15 100644 --- a/logs/log.go +++ b/logs/log.go @@ -148,9 +148,6 @@ func (bl *BeeLogger) DelLogger(adaptername string) error { } func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { - if loglevel > bl.level { - return nil - } lm := new(logMsg) lm.level = loglevel if bl.enableFuncCallDepth { From a7e60c93dc8d785ca11cb9e12f07ef3c152b38f4 Mon Sep 17 00:00:00 2001 From: Donal Byrne Date: Thu, 26 Mar 2015 20:23:00 +0100 Subject: [PATCH 066/436] Set ErrorsMap to nil in Validation.Clear function --- validation/validation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/validation/validation.go b/validation/validation.go index addf6b7e..90a757a9 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -107,6 +107,7 @@ type Validation struct { // Clean all ValidationError. func (v *Validation) Clear() { v.Errors = []*ValidationError{} + v.ErrorsMap = nil } // Has ValidationError nor not. From 91886a45477f2297750309448d527257721e9b69 Mon Sep 17 00:00:00 2001 From: pylemon Date: Fri, 27 Mar 2015 13:30:59 +0800 Subject: [PATCH 067/436] bugfix: if a form field type is bool, valid Required should always return true instead of return its value. --- validation/validators.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/validation/validators.go b/validation/validators.go index 25415bdd..725b1f7a 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -64,8 +64,8 @@ func (r Required) IsSatisfied(obj interface{}) bool { if str, ok := obj.(string); ok { return len(str) > 0 } - if b, ok := obj.(bool); ok { - return b + if _, ok := obj.(bool); ok { + return true } if i, ok := obj.(int); ok { return i != 0 From 2c9363d29b0e7d075719ad5fedeaa7d0f986bae5 Mon Sep 17 00:00:00 2001 From: pylemon Date: Fri, 27 Mar 2015 13:43:20 +0800 Subject: [PATCH 068/436] add tests to ensure bool value require test always return true. --- validation/validation_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validation/validation_test.go b/validation/validation_test.go index be63ac93..0727fda4 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -26,6 +26,12 @@ func TestRequired(t *testing.T) { if valid.Required(nil, "nil").Ok { t.Error("nil object should be false") } + if !valid.Required(true, "bool").Ok { + t.Error("Bool value should always return true") + } + if !valid.Required(false, "bool").Ok { + t.Error("Bool value should always return true") + } if valid.Required("", "string").Ok { t.Error("\"'\" string should be false") } From 26130a5df6756673bcbb52c8b8783d77070405e8 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 30 Mar 2015 20:35:57 +0800 Subject: [PATCH 069/436] fix #1073 --- error.go | 18 +++++++++--------- router.go | 14 +++----------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/error.go b/error.go index 71be6916..6db4a5a8 100644 --- a/error.go +++ b/error.go @@ -439,24 +439,26 @@ func exception(errcode string, ctx *context.Context) { if err != nil { code = 503 } - ctx.ResponseWriter.WriteHeader(code) if h, ok := ErrorMaps[errcode]; ok { - executeError(h, ctx) + executeError(h, ctx, code) return } else if h, ok := ErrorMaps["503"]; ok { - executeError(h, ctx) + executeError(h, ctx, code) return } else { + ctx.ResponseWriter.WriteHeader(code) ctx.WriteString(errcode) } } -func executeError(err *errorInfo, ctx *context.Context) { +func executeError(err *errorInfo, ctx *context.Context, code int) { if err.errorType == errorTypeHandler { + ctx.ResponseWriter.WriteHeader(code) err.handler(ctx.ResponseWriter, ctx.Request) return } if err.errorType == errorTypeController { + ctx.Output.SetStatus(code) //Invoke the request handler vc := reflect.New(err.controllerType) execController, ok := vc.Interface().(ControllerInterface) @@ -476,11 +478,9 @@ func executeError(err *errorInfo, ctx *context.Context) { method.Call(in) //render template - if ctx.Output.Status == 0 { - if AutoRender { - if err := execController.Render(); err != nil { - panic(err) - } + if AutoRender { + if err := execController.Render(); err != nil { + panic(err) } } diff --git a/router.go b/router.go index c1bda637..e9f45615 100644 --- a/router.go +++ b/router.go @@ -862,8 +862,8 @@ func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { panic(err) } else { if ErrorsShow { - if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { - executeError(handler, context) + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { + exception(fmt.Sprint(err), context) return } } @@ -886,15 +886,7 @@ func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { } else { // in production model show all infomation if ErrorsShow { - if handler, ok := ErrorMaps[fmt.Sprint(err)]; ok { - executeError(handler, context) - return - } else if handler, ok := ErrorMaps["503"]; ok { - executeError(handler, context) - return - } else { - context.WriteString(fmt.Sprint(err)) - } + exception(fmt.Sprint(err), context) } else { Critical("the request url is ", context.Input.Url()) Critical("Handler crashed with error", err) From bb5351bb9f1bad898fd1ba1e6bbd378690a7dbbe Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Tue, 31 Mar 2015 12:30:47 +0800 Subject: [PATCH 070/436] Update output.go fix cookie not work in IE --- context/output.go | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/context/output.go b/context/output.go index 2141513d..f981df50 100644 --- a/context/output.go +++ b/context/output.go @@ -98,28 +98,30 @@ func (output *BeegoOutput) Body(content []byte) { func (output *BeegoOutput) Cookie(name string, value string, others ...interface{}) { var b bytes.Buffer fmt.Fprintf(&b, "%s=%s", sanitizeName(name), sanitizeValue(value)) - if len(others) > 0 { - switch v := others[0].(type) { - case int: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int64: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - case int32: - if v > 0 { - fmt.Fprintf(&b, "; Max-Age=%d", v) - } else if v < 0 { - fmt.Fprintf(&b, "; Max-Age=0") - } - } - } + + //fix cookie not work in IE + if len(others) > 0 { + switch v := others[0].(type) { + case int: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + case int64: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + case int32: + if v > 0 { + fmt.Fprintf(&b, "; Expires=%s; Max-Age=%d", time.Now().Add(time.Duration(v) * time.Second).UTC().Format(time.RFC1123), v) + } else if v < 0 { + fmt.Fprintf(&b, "; Max-Age=0") + } + } + } // the settings below // Path, Domain, Secure, HttpOnly From a311d712a51b1568bcba5c741dbaff295cbba6da Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Tue, 31 Mar 2015 12:36:39 +0800 Subject: [PATCH 071/436] Update output.go --- context/output.go | 1 + 1 file changed, 1 insertion(+) diff --git a/context/output.go b/context/output.go index f981df50..7edde552 100644 --- a/context/output.go +++ b/context/output.go @@ -29,6 +29,7 @@ import ( "path/filepath" "strconv" "strings" + "time" ) // BeegoOutput does work for sending response header. From c4c9a50c42432e688b789626ec3e07de07d4afe1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 1 Apr 2015 23:31:40 +0800 Subject: [PATCH 072/436] fix #1081 --- controller.go | 72 ++++++++++++++++++++------------------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/controller.go b/controller.go index e056f52d..81ee1230 100644 --- a/controller.go +++ b/controller.go @@ -406,106 +406,90 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { // GetInt returns input as an int or the default value while it's present and input is blank func (c *Controller) GetInt(key string, def ...int) (int, error) { - var defv int - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.Atoi(strv) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.Atoi(strv) } } // GetInt8 return input as an int8 or the default value while it's present and input is blank func (c *Controller) GetInt8(key string, def ...int8) (int8, error) { - var defv int8 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(strv, 10, 8) i8 := int8(i64) return i8, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(strv, 10, 8) + i8 := int8(i64) + return i8, err } } // GetInt16 returns input as an int16 or the default value while it's present and input is blank func (c *Controller) GetInt16(key string, def ...int16) (int16, error) { - var defv int16 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(strv, 10, 16) i16 := int16(i64) - return i16, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(strv, 10, 16) + i16 := int16(i64) + return i16, err } } // GetInt32 returns input as an int32 or the default value while it's present and input is blank func (c *Controller) GetInt32(key string, def ...int32) (int32, error) { - var defv int32 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) i32 := int32(i64) return i32, err + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + i64, err := strconv.ParseInt(c.Ctx.Input.Query(key), 10, 32) + i32 := int32(i64) + return i32, err } } // GetInt64 returns input value as int64 or the default value while it's present and input is blank. func (c *Controller) GetInt64(key string, def ...int64) (int64, error) { - var defv int64 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.ParseInt(strv, 10, 64) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseInt(strv, 10, 64) } } // GetBool returns input value as bool or the default value while it's present and input is blank. func (c *Controller) GetBool(key string, def ...bool) (bool, error) { - var defv bool - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { return strconv.ParseBool(strv) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseBool(strv) } } // GetFloat returns input value as float64 or the default value while it's present and input is blank. func (c *Controller) GetFloat(key string, def ...float64) (float64, error) { - var defv float64 - if len(def) > 0 { - defv = def[0] - } - if strv := c.Ctx.Input.Query(key); strv != "" { - return strconv.ParseFloat(c.Ctx.Input.Query(key), 64) + return strconv.ParseFloat(strv, 64) + } else if len(def) > 0 { + return def[0], nil } else { - return defv, nil + return strconv.ParseFloat(strv, 64) } } From 55b1d6a897a5da2299af7c75340bb0a4887bab39 Mon Sep 17 00:00:00 2001 From: peeped Date: Thu, 2 Apr 2015 12:07:12 +0800 Subject: [PATCH 073/436] Update validators.go edit mobile regular expression ; add 184 Section No. --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 725b1f7a..645f4ec3 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][012356789]))\\d{8}$") +var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][0-9]))\\d{8}$") type Mobile struct { Match From 11483595704e452a74eddc4a400ce475983cee2f Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 2 Apr 2015 14:02:39 +0800 Subject: [PATCH 074/436] session cookie support IE --- session/session.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/session/session.go b/session/session.go index 3cbd2b05..01aae1a5 100644 --- a/session/session.go +++ b/session/session.go @@ -151,6 +151,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -177,6 +178,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) From fd610d67778141210bfdcc299f6b4cfaf6751901 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Fri, 3 Apr 2015 17:41:09 +0800 Subject: [PATCH 075/436] Update session.go move expire in line 154 to 247, because it will cause session_cookie not writen to explorer --- session/session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/session.go b/session/session.go index 01aae1a5..35561242 100644 --- a/session/session.go +++ b/session/session.go @@ -151,7 +151,6 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Domain: manager.config.Domain} if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime - cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -245,6 +244,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque } if manager.config.CookieLifeTime >= 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } http.SetCookie(w, cookie) r.AddCookie(cookie) From 322b208566b1387df454a4fbe2ab5ba367bfa007 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sat, 4 Apr 2015 00:44:22 +0800 Subject: [PATCH 076/436] Update session.go remove = in if statement --- session/session.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/session/session.go b/session/session.go index 35561242..4214320c 100644 --- a/session/session.go +++ b/session/session.go @@ -149,8 +149,9 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se HttpOnly: true, Secure: manager.config.Secure, Domain: manager.config.Domain} - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime + cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } if manager.config.EnableSetCookie { http.SetCookie(w, cookie) @@ -175,7 +176,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se HttpOnly: true, Secure: manager.config.Secure, Domain: manager.config.Domain} - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } @@ -242,7 +243,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque cookie.HttpOnly = true cookie.Path = "/" } - if manager.config.CookieLifeTime >= 0 { + if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) } From 8cc57e2fc8928c52b154b063bf70fb452c5b1eb2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:11:50 +0800 Subject: [PATCH 077/436] fix #1112 --- tree.go | 42 +++++++++++++++++++++++------------------- tree_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 19 deletions(-) diff --git a/tree.go b/tree.go index 93e87470..e0d9c1c2 100644 --- a/tree.go +++ b/tree.go @@ -70,18 +70,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st } else { regexpStr = "/" + regexpStr } - } else { - for _, w := range wildcards { - if w == "." || w == ":" { - continue + } else if reg != "" { + if seg == "*.*" { + regexpStr = "([^.]+).(.+)" + } else { + for _, w := range params { + if w == "." || w == ":" { + continue + } + regexpStr = "([^/]+)/" + regexpStr } - regexpStr = "([^/]+)/" + regexpStr } } - reg = strings.Trim(reg+regexpStr, "/") + reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.wildcard = tree } else { + reg = strings.Trim(reg+"/"+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.fixrouters[seg] = tree } @@ -104,23 +109,23 @@ func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg st rr = rr + "([^/]+)/" } } - regexpStr = rr + regexpStr + "/" + regexpStr = rr + regexpStr } else { - regexpStr = "/" + regexpStr + "/" + regexpStr = "/" + regexpStr } - } else { - for _, w := range wildcards { - if w == "." || w == ":" { - continue - } - if w == ":splat" { - regexpStr = "(.+)/" + regexpStr - } else { + } else if reg != "" { + if seg == "*.*" { + regexpStr = "([^.]+).(.+)" + } else { + for _, w := range params { + if w == "." || w == ":" { + continue + } regexpStr = "([^/]+)/" + regexpStr } } } - reg = reg + regexpStr + reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) } else { subTree := NewTree() @@ -140,7 +145,7 @@ func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { if reg != "" { if l.regexps != nil { l.wildcards = append(wildcards, l.wildcards...) - l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$") + l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") } else { for _, v := range l.wildcards { if v == ":" || v == "." { @@ -248,7 +253,6 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, regexpStr = "/([^/]+)" + regexpStr } } - } t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) } else { diff --git a/tree_test.go b/tree_test.go index fa289716..1d2890e1 100644 --- a/tree_test.go +++ b/tree_test.go @@ -148,6 +148,50 @@ func TestAddTree2(t *testing.T) { } } +func TestAddTree3(t *testing.T) { + tr := NewTree() + tr.AddRouter("/create", "astaxie") + tr.AddRouter("/shop/:sd/account", "astaxie") + t3 := NewTree() + t3.AddTree("/table/:num", tr) + obj, param := t3.Match("/table/123/shop/123/account") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/table/:num/shop/:sd/account can't get obj ") + } + if param == nil { + t.Fatal("get param error") + } + if param[":num"] != "123" || param[":sd"] != "123" { + t.Fatal("get :num :sd param error") + } + obj, param = t3.Match("/table/123/create") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/table/:num/create can't get obj ") + } +} + +func TestAddTree4(t *testing.T) { + tr := NewTree() + tr.AddRouter("/create", "astaxie") + tr.AddRouter("/shop/:sd/:account", "astaxie") + t4 := NewTree() + t4.AddTree("/:info:int/:num/:id", tr) + obj, param := t4.Match("/12/123/456/shop/123/account") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/:info:int/:num/:id/shop/:sd/:account can't get obj ") + } + if param == nil { + t.Fatal("get param error") + } + if param[":info"] != "12" || param[":num"] != "123" || param[":id"] != "456" || param[":sd"] != "123" || param[":account"] != "account" { + t.Fatal("get :info :num :id :sd :account param error") + } + obj, param = t4.Match("/12/123/456/create") + if obj == nil || obj.(string) != "astaxie" { + t.Fatal("/:info:int/:num/:id/create can't get obj ") + } +} + func TestSplitPath(t *testing.T) { a := splitPath("") if len(a) != 0 { From 2ba12ad1e155b108853bcc10fbf8be3fa052a02a Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:21:13 +0800 Subject: [PATCH 078/436] config read and set support Runmode --- config.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/config.go b/config.go index f326ad22..a649eaa8 100644 --- a/config.go +++ b/config.go @@ -98,6 +98,10 @@ func newAppConfig(AppConfigProvider, AppConfigPath string) (*beegoAppConfig, err } func (b *beegoAppConfig) Set(key, val string) error { + err := b.innerConfig.Set(RunMode+"::"+key, val) + if err == nil { + return err + } return b.innerConfig.Set(key, val) } @@ -150,26 +154,50 @@ func (b *beegoAppConfig) Float(key string) (float64, error) { } func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { + v := b.String(key) + if v != "" { + return v + } return b.innerConfig.DefaultString(key, defaultval) } func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { + v := b.Strings(key) + if len(v) != 0 { + return v + } return b.innerConfig.DefaultStrings(key, defaultval) } func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { + v, err := b.Int(key) + if err == nil { + return v + } return b.innerConfig.DefaultInt(key, defaultval) } func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { + v, err := b.Int64(key) + if err == nil { + return v + } return b.innerConfig.DefaultInt64(key, defaultval) } func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { + v, err := b.Bool(key) + if err == nil { + return v + } return b.innerConfig.DefaultBool(key, defaultval) } func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { + v, err := b.Float(key) + if err == nil { + return v + } return b.innerConfig.DefaultFloat(key, defaultval) } From d446b5b011f4f53e8a3793f2f39c593108187c12 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 5 Apr 2015 23:23:35 +0800 Subject: [PATCH 079/436] improve the defaultval --- config.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/config.go b/config.go index a649eaa8..b22db163 100644 --- a/config.go +++ b/config.go @@ -158,7 +158,7 @@ func (b *beegoAppConfig) DefaultString(key string, defaultval string) string { if v != "" { return v } - return b.innerConfig.DefaultString(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []string { @@ -166,7 +166,7 @@ func (b *beegoAppConfig) DefaultStrings(key string, defaultval []string) []strin if len(v) != 0 { return v } - return b.innerConfig.DefaultStrings(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { @@ -174,7 +174,7 @@ func (b *beegoAppConfig) DefaultInt(key string, defaultval int) int { if err == nil { return v } - return b.innerConfig.DefaultInt(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { @@ -182,7 +182,7 @@ func (b *beegoAppConfig) DefaultInt64(key string, defaultval int64) int64 { if err == nil { return v } - return b.innerConfig.DefaultInt64(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { @@ -190,7 +190,7 @@ func (b *beegoAppConfig) DefaultBool(key string, defaultval bool) bool { if err == nil { return v } - return b.innerConfig.DefaultBool(key, defaultval) + return defaultval } func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { @@ -198,7 +198,7 @@ func (b *beegoAppConfig) DefaultFloat(key string, defaultval float64) float64 { if err == nil { return v } - return b.innerConfig.DefaultFloat(key, defaultval) + return defaultval } func (b *beegoAppConfig) DIY(key string) (interface{}, error) { From 676595213ffc94d6bac1cc295a4818d40ea2a565 Mon Sep 17 00:00:00 2001 From: Lionel Lee Date: Tue, 7 Apr 2015 10:35:18 +0800 Subject: [PATCH 080/436] fix a comment error. --- context/input.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/context/input.go b/context/input.go index 24d43bbb..1985df21 100644 --- a/context/input.go +++ b/context/input.go @@ -330,7 +330,7 @@ func (input *BeegoInput) ParseFormOrMulitForm(maxMemory int64) error { // Bind data from request.Form[key] to dest // like /?id=123&isok=true&ft=1.2&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=astaxie // var id int beegoInput.Bind(&id, "id") id ==123 -// var isok bool beegoInput.Bind(&isok, "isok") id ==true +// var isok bool beegoInput.Bind(&isok, "isok") isok ==true // var ft float64 beegoInput.Bind(&ft, "ft") ft ==1.2 // ol := make([]int, 0, 2) beegoInput.Bind(&ol, "ol") ol ==[1 2] // ul := make([]string, 0, 2) beegoInput.Bind(&ul, "ul") ul ==[str array] From d7791ba837971e9527f5be99b88d84bebdef579f Mon Sep 17 00:00:00 2001 From: peeped Date: Tue, 7 Apr 2015 17:48:51 +0800 Subject: [PATCH 081/436] Update validators.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit //176 中国联通 //177 中国电信 //145 中国联通 //147 中国移动 //149 中国电信 --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 645f4ec3..61b0414a 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|(47)|[8][0-9]))\\d{8}$") +var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][67]|[4][579]))\\d{8}$") type Mobile struct { Match From 23457ed2a03a27b4be642dfc828fa6972ff5a98c Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 20:12:10 +0800 Subject: [PATCH 082/436] add sethost --- httplib/httplib.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bd8bc776..bdd2d65a 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -184,6 +184,12 @@ func (b *BeegoHttpRequest) Header(key, value string) *BeegoHttpRequest { return b } +// Set HOST +func (b *BeegoHttpRequest) SetHost(host string) *BeegoHttpRequest { + b.req.Host = host + return b +} + // Set the protocol version for incoming requests. // Client requests always use HTTP/1.1. func (b *BeegoHttpRequest) SetProtocolVersion(vers string) *BeegoHttpRequest { @@ -253,7 +259,6 @@ func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { return b } - // JsonBody adds request raw body encoding by JSON. func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) { if b.req.Body == nil && obj != nil { From d90ce1570717f574f2714e0a56d5291fc42a91c3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 21:45:00 +0800 Subject: [PATCH 083/436] httplib support gzip --- httplib/httplib.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index bdd2d65a..284aef7b 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -32,6 +32,7 @@ package httplib import ( "bytes" + "compress/gzip" "crypto/tls" "encoding/json" "encoding/xml" @@ -50,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false} +var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false, true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex @@ -122,6 +123,7 @@ type BeegoHttpSettings struct { Proxy func(*http.Request) (*url.URL, error) Transport http.RoundTripper EnableCookie bool + Gzip bool } // BeegoHttpRequest provides more useful methods for requesting one url than http.Request. @@ -434,7 +436,15 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return nil, nil } defer resp.Body.Close() - b.body, err = ioutil.ReadAll(resp.Body) + if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { + reader, err := gzip.NewReader(resp.Body) + if err != nil { + return nil, err + } + b.body, err = ioutil.ReadAll(reader) + } else { + b.body, err = ioutil.ReadAll(resp.Body) + } if err != nil { return nil, err } From 743628a946f0ddf410e668c1d166260a32d30151 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 22:58:37 +0800 Subject: [PATCH 084/436] add DumpRequest --- httplib/httplib.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index 284aef7b..aba7f534 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -167,6 +167,11 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { return b } +// return the DumpRequest +func (b *BeegoHttpRequest) DumpRequest() { + return httputil.DumpRequest(b.req, true) +} + // SetTimeout sets connect time out and read-write time out for BeegoRequest. func (b *BeegoHttpRequest) SetTimeout(connectTimeout, readWriteTimeout time.Duration) *BeegoHttpRequest { b.setting.ConnectTimeout = connectTimeout From 6f802b0a05bef883e5d8398b44ec8aea3968a473 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 8 Apr 2015 23:00:08 +0800 Subject: [PATCH 085/436] fix the params --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index aba7f534..57d3de61 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -168,7 +168,7 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { } // return the DumpRequest -func (b *BeegoHttpRequest) DumpRequest() { +func (b *BeegoHttpRequest) DumpRequest() (dump []byte, err error) { return httputil.DumpRequest(b.req, true) } From 68ccd8e5a4ea96cb3852c75d69120a61b613eda6 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:11:25 +0800 Subject: [PATCH 086/436] fix the dump has no body --- httplib/httplib.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 57d3de61..f09c8509 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -135,6 +135,7 @@ type BeegoHttpRequest struct { setting BeegoHttpSettings resp *http.Response body []byte + dump []byte } // Change request settings @@ -168,8 +169,8 @@ func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { } // return the DumpRequest -func (b *BeegoHttpRequest) DumpRequest() (dump []byte, err error) { - return httputil.DumpRequest(b.req, true) +func (b *BeegoHttpRequest) DumpRequest() []byte { + return b.dump } // SetTimeout sets connect time out and read-write time out for BeegoRequest. @@ -405,6 +406,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { if err != nil { println(err.Error()) } + b.dump = dump println(string(dump)) } From 4414659df4d6cca191154030683a3558efa4c0ed Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:12:41 +0800 Subject: [PATCH 087/436] fix the init struct --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index f09c8509..8ee56bcd 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -85,7 +85,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { ProtoMajor: 1, ProtoMinor: 1, } - return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil} + return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil, nil} } // Get returns *BeegoHttpRequest with GET method. From ee4fd60e4d6601db1946b22adac7f31b910df2f5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 9 Apr 2015 00:18:02 +0800 Subject: [PATCH 088/436] no output the dump --- httplib/httplib.go | 1 - 1 file changed, 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 8ee56bcd..935b7a71 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -407,7 +407,6 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { println(err.Error()) } b.dump = dump - println(string(dump)) } resp, err := client.Do(b.req) From 23268b788aee1171832bbc60ea29b600111424f7 Mon Sep 17 00:00:00 2001 From: wuranbo Date: Mon, 13 Apr 2015 10:33:57 +0800 Subject: [PATCH 089/436] =?UTF-8?q?*feature)=20=E5=A2=9E=E5=8A=A0logcallde?= =?UTF-8?q?pth=E6=8E=A5=E5=8F=A3=EF=BC=8C=E4=BD=BF=E5=BE=97=E8=83=BD?= =?UTF-8?q?=E7=AE=80=E5=8D=95=E5=86=8D=E6=AC=A1=E5=B0=81=E8=A3=85log?= =?UTF-8?q?=E7=B3=BB=E5=88=97=E5=87=BD=E6=95=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- logs/log.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/logs/log.go b/logs/log.go index 3103ac15..5f5f1cea 100644 --- a/logs/log.go +++ b/logs/log.go @@ -181,6 +181,11 @@ func (bl *BeeLogger) SetLogFuncCallDepth(d int) { bl.loggerFuncCallDepth = d } +// get log funcCallDepth for wrapper +func (bl *BeeLogger) GetLogFuncCallDepth() int { + return bl.loggerFuncCallDepth +} + // enable log funcCallDepth func (bl *BeeLogger) EnableFuncCallDepth(b bool) { bl.enableFuncCallDepth = b From 0222b8d69336cf9903f0cdb946eb36ba3cbb80cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BA=A7n=20V=C4=83n=20Thanh?= Date: Wed, 15 Apr 2015 17:41:41 +0700 Subject: [PATCH 090/436] fixed: when RelatedSel have multi string/relation, it only get last string --- orm/orm_queryset.go | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 4f5d5485..5cc47617 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -115,23 +115,21 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter { // set relation model to query together. // it will query relation models and assign to parent model. func (o querySet) RelatedSel(params ...interface{}) QuerySeter { - var related []string - if len(params) == 0 { - o.relDepth = DefaultRelsDepth - } else { - for _, p := range params { - switch val := p.(type) { - case string: - related = append(o.related, val) - case int: - o.relDepth = val - default: - panic(fmt.Errorf(" wrong param kind: %v", val)) - } - } - } - o.related = related - return &o + if len(params) == 0 { + o.relDepth = DefaultRelsDepth + } else { + for _, p := range params { + switch val := p.(type) { + case string: + o.related = append(o.related, val) + case int: + o.relDepth = val + default: + panic(fmt.Errorf(" wrong param kind: %v", val)) + } + } + } + return &o } // set condition to QuerySeter. From 7e3b5e5307262978c38bf0c815beda6ad87971ac Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 19 Apr 2015 15:40:23 +0800 Subject: [PATCH 091/436] remove unreached code --- admin.go | 1 - 1 file changed, 1 deletion(-) diff --git a/admin.go b/admin.go index 90142c55..7eb07cc0 100644 --- a/admin.go +++ b/admin.go @@ -333,7 +333,6 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { tmpl = template.Must(tmpl.Parse(defaultScriptsTpl)) } tmpl.Execute(rw, data) - } else { } } From 4255630564ec9041d03511c717ab09ba3142482e Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 10:58:46 +0800 Subject: [PATCH 092/436] add Recursively validation --- validation/validation.go | 39 +++++++++++++++++++++++++++++++++++ validation/validation_test.go | 30 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/validation/validation.go b/validation/validation.go index 90a757a9..864a3ab5 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -334,3 +334,42 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { return !v.HasErrors(), nil } + +// Recursively validate a struct. +// Step1: Validate by v.Valid +// Step2: If pass on step1, then reflect obj's fields +// Step3: Do the Recursively validation to all struct or struct pointer fields +// Anonymous fields will be ignored +func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { + //Step 1: validate obj itself firstly + pass, err := v.Valid(objc) + if err != nil || false == pass { + return pass, err // Stop recursive validation + } else { //pass + // Step 2: Validate struct's struct fields + objT := reflect.TypeOf(objc) + objV := reflect.ValueOf(objc) + if isStruct(objT) || isStructPtr(objT) { + + if isStructPtr(objT) { + objT = objT.Elem() + objV = objV.Elem() + } + + for i := 0; i < objT.NumField(); i++ { + + t := objT.Field(i).Type + + if isStruct(t) || isStructPtr(t) { + // Step 3: do the recursive validation + // Only valid the Public field recursively + if objV.Field(i).CanInterface() { + pass, err = v.RecursiveValid(objV.Field(i).Interface()) + } + } + } + } + + return pass, err + } +} diff --git a/validation/validation_test.go b/validation/validation_test.go index 0727fda4..d07b1435 100644 --- a/validation/validation_test.go +++ b/validation/validation_test.go @@ -349,3 +349,33 @@ func TestValid(t *testing.T) { t.Errorf("Message key should be `Name.Match` but got %s", valid.Errors[0].Key) } } + +func TestRecursiveValid(t *testing.T) { + type User struct { + Id int + Name string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"` + Age int `valid:"Required;Range(1, 140)"` + } + + type AnonymouseUser struct { + Id2 int + Name2 string `valid:"Required;Match(/^(test)?\\w*@(/test/);com$/)"` + Age2 int `valid:"Required;Range(1, 140)"` + } + + type Account struct { + Password string `valid:"Required"` + U User + AnonymouseUser + } + valid := Validation{} + + u := Account{Password: "abc123_", U: User{}} + b, err := valid.RecursiveValid(u) + if err != nil { + t.Fatal(err) + } + if b { + t.Error("validation should not be passed") + } +} From 74b22649c8a6c71015b56b0130cd2714678922d0 Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 11:14:49 +0800 Subject: [PATCH 093/436] remove the double isStruct/isStructPtr check --- validation/validation.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/validation/validation.go b/validation/validation.go index 864a3ab5..b9f001e8 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -342,6 +342,7 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { // Anonymous fields will be ignored func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly + // fails if objc is not struct pass, err := v.Valid(objc) if err != nil || false == pass { return pass, err // Stop recursive validation @@ -349,23 +350,22 @@ func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { // Step 2: Validate struct's struct fields objT := reflect.TypeOf(objc) objV := reflect.ValueOf(objc) - if isStruct(objT) || isStructPtr(objT) { - if isStructPtr(objT) { - objT = objT.Elem() - objV = objV.Elem() - } + if isStructPtr(objT) { + objT = objT.Elem() + objV = objV.Elem() + } - for i := 0; i < objT.NumField(); i++ { + for i := 0; i < objT.NumField(); i++ { - t := objT.Field(i).Type + t := objT.Field(i).Type - if isStruct(t) || isStructPtr(t) { - // Step 3: do the recursive validation - // Only valid the Public field recursively - if objV.Field(i).CanInterface() { - pass, err = v.RecursiveValid(objV.Field(i).Interface()) - } + // Recursive applies to struct or pointer to structs fields + if isStruct(t) || isStructPtr(t) { + // Step 3: do the recursive validation + // Only valid the Public field recursively + if objV.Field(i).CanInterface() { + pass, err = v.RecursiveValid(objV.Field(i).Interface()) } } } From dfe055c47c41ff3ac9fcbefec398aa89d7bad976 Mon Sep 17 00:00:00 2001 From: Wyatt Fang Date: Fri, 24 Apr 2015 11:17:12 +0800 Subject: [PATCH 094/436] remove useless comments --- validation/validation.go | 1 - 1 file changed, 1 deletion(-) diff --git a/validation/validation.go b/validation/validation.go index b9f001e8..d98ff190 100644 --- a/validation/validation.go +++ b/validation/validation.go @@ -339,7 +339,6 @@ func (v *Validation) Valid(obj interface{}) (b bool, err error) { // Step1: Validate by v.Valid // Step2: If pass on step1, then reflect obj's fields // Step3: Do the Recursively validation to all struct or struct pointer fields -// Anonymous fields will be ignored func (v *Validation) RecursiveValid(objc interface{}) (bool, error) { //Step 1: validate obj itself firstly // fails if objc is not struct From 3415a5b0919dfa59c8352915e38cd7566cd4c06a Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 01:23:18 +0800 Subject: [PATCH 095/436] better go style --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 935b7a71..ab734b3c 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{false, "beegoServer", 60 * time.Second, 60 * time.Second, nil, nil, nil, false, true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectionTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Giz: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex From 8e618192c226f575df7e67db99f5c4256d382498 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:04:34 +0800 Subject: [PATCH 096/436] better code and fixed --- httplib/httplib.go | 53 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index ab734b3c..ca893163 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -85,7 +85,14 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { ProtoMajor: 1, ProtoMinor: 1, } - return &BeegoHttpRequest{url, &req, map[string]string{}, map[string]string{}, defaultSetting, &resp, nil, nil} + return &BeegoHttpRequest{ + url: url, + req: &req, + paras: map[string]string{}, + files: map[string]string{}, + setting: defaultSetting, + resp: &resp, + } } // Get returns *BeegoHttpRequest with GET method. @@ -157,14 +164,14 @@ func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest { } // SetUserAgent sets User-Agent header field -func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest { - b.setting.UserAgent = useragent +func (b *BeegoHttpRequest) SetUserAgent(userAgent string) *BeegoHttpRequest { + b.setting.UserAgent = userAgent return b } // Debug sets show debug or not when executing request. -func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { - b.setting.ShowDebug = isdebug +func (b *BeegoHttpRequest) Debug(isDebug bool) *BeegoHttpRequest { + b.setting.ShowDebug = isDebug return b } @@ -409,12 +416,8 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { b.dump = dump } - resp, err := client.Do(b.req) - if err != nil { - return nil, err - } - b.resp = resp - return resp, nil + b.resp, err = client.Do(b.req) + return b.resp, err } // String returns the body string in response. @@ -435,12 +438,9 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return b.body, nil } resp, err := b.getResponse() - if err != nil { + if resp == nil || resp.Body == nil { return nil, err } - if resp.Body == nil { - return nil, nil - } defer resp.Body.Close() if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(resp.Body) @@ -451,29 +451,24 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { } else { b.body, err = ioutil.ReadAll(resp.Body) } - if err != nil { - return nil, err - } - return b.body, nil + return b.body, err } // ToFile saves the body data in response to one file. // it calls Response inner. func (b *BeegoHttpRequest) ToFile(filename string) error { + resp, err := b.getResponse() + if resp == nil || resp.Body == nil { + return err + } + defer resp.Body.Close() + f, err := os.Create(filename) if err != nil { return err } defer f.Close() - resp, err := b.getResponse() - if err != nil { - return err - } - if resp.Body == nil { - return nil - } - defer resp.Body.Close() _, err = io.Copy(f, resp.Body) return err } @@ -510,7 +505,7 @@ func TimeoutDialer(cTimeout time.Duration, rwTimeout time.Duration) func(net, ad if err != nil { return nil, err } - conn.SetDeadline(time.Now().Add(rwTimeout)) - return conn, nil + err = conn.SetDeadline(time.Now().Add(rwTimeout)) + return conn, err } } From 4cd7177ece32d0b13c1c2089bbf259b35ff947b7 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:05:50 +0800 Subject: [PATCH 097/436] typo fixed --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index ca893163..502ef276 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectionTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Giz: true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeoutt: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex From 69fc22f0df1f4936a10c1f129efe6d1a9a65a094 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:06:19 +0800 Subject: [PATCH 098/436] typo fixed --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 502ef276..6e0acdab 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -51,7 +51,7 @@ import ( "time" ) -var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeoutt: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} +var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar var settingMutex sync.Mutex @@ -88,7 +88,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: url, req: &req, - paras: map[string]string{}, + params: map[string]string{}, files: map[string]string{}, setting: defaultSetting, resp: &resp, From 738e22e3899730c4bd19f684c9fd59725e93eb7a Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:17:46 +0800 Subject: [PATCH 099/436] zero timeout means wait until resp --- httplib/httplib.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 6e0acdab..140d541b 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -65,14 +65,8 @@ func createDefaultCookie() { // Overwrite default settings func SetDefaultSetting(setting BeegoHttpSettings) { settingMutex.Lock() - defer settingMutex.Unlock() defaultSetting = setting - if defaultSetting.ConnectTimeout == 0 { - defaultSetting.ConnectTimeout = 60 * time.Second - } - if defaultSetting.ReadWriteTimeout == 0 { - defaultSetting.ReadWriteTimeout = 60 * time.Second - } + settingMutex.Unlock() } // return *BeegoHttpRequest with specific method @@ -262,11 +256,11 @@ func (b *BeegoHttpRequest) PostFile(formname, filename string) *BeegoHttpRequest // it supports string and []byte. func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { switch t := data.(type) { - case string: + case string: bf := bytes.NewBufferString(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) - case []byte: + case []byte: bf := bytes.NewBuffer(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) From 9f070c622b49ea2c6cb6f12d5751d5745e104f3e Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 02:19:38 +0800 Subject: [PATCH 100/436] no need defer here --- httplib/httplib.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 140d541b..f84586a5 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -58,8 +58,8 @@ var settingMutex sync.Mutex // createDefaultCookie creates a global cookiejar to store cookies. func createDefaultCookie() { settingMutex.Lock() - defer settingMutex.Unlock() defaultCookieJar, _ = cookiejar.New(nil) + settingMutex.Unlock() } // Overwrite default settings From 6da0cdb9e20aaf21f119dabb02c96d42201c82b0 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 15:24:04 +0800 Subject: [PATCH 101/436] no need lock here --- httplib/httplib.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index f84586a5..54c70283 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -47,26 +47,20 @@ import ( "net/url" "os" "strings" - "sync" "time" ) var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} var defaultCookieJar http.CookieJar -var settingMutex sync.Mutex // createDefaultCookie creates a global cookiejar to store cookies. func createDefaultCookie() { - settingMutex.Lock() defaultCookieJar, _ = cookiejar.New(nil) - settingMutex.Unlock() } // Overwrite default settings func SetDefaultSetting(setting BeegoHttpSettings) { - settingMutex.Lock() defaultSetting = setting - settingMutex.Unlock() } // return *BeegoHttpRequest with specific method @@ -82,7 +76,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: url, req: &req, - params: map[string]string{}, + params: map[string]string{}, files: map[string]string{}, setting: defaultSetting, resp: &resp, @@ -256,11 +250,11 @@ func (b *BeegoHttpRequest) PostFile(formname, filename string) *BeegoHttpRequest // it supports string and []byte. func (b *BeegoHttpRequest) Body(data interface{}) *BeegoHttpRequest { switch t := data.(type) { - case string: + case string: bf := bytes.NewBufferString(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) - case []byte: + case []byte: bf := bytes.NewBuffer(t) b.req.Body = ioutil.NopCloser(bf) b.req.ContentLength = int64(len(t)) From 51161361dbe9283797f9faeccb83981e16356432 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 15:42:10 +0800 Subject: [PATCH 102/436] more fixed --- httplib/httplib.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 54c70283..ae50791c 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -36,6 +36,7 @@ import ( "crypto/tls" "encoding/json" "encoding/xml" + "fmt" "io" "io/ioutil" "log" @@ -278,12 +279,15 @@ func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) } func (b *BeegoHttpRequest) buildUrl(paramBody string) { + if paramBody == "" { + return + } // build GET url with query string - if b.req.Method == "GET" && len(paramBody) > 0 { - if strings.Index(b.url, "?") != -1 { - b.url += "&" + paramBody - } else { + if b.req.Method == "GET" { + if strings.Index(b.url, "?") == -1 { b.url = b.url + "?" + paramBody + } else { + b.url += "&" + paramBody } return } @@ -336,18 +340,14 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } var paramBody string if len(b.params) > 0 { - var buf bytes.Buffer for k, v := range b.params { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v)) - buf.WriteByte('&') + paramBody += fmt.Sprintf("&%s=%v", url.QueryEscape(k), url.QueryEscape(v)) } - paramBody = buf.String() - paramBody = paramBody[0 : len(paramBody)-1] + paramBody = paramBody[1:] } b.buildUrl(paramBody) + url, err := url.Parse(b.url) if err != nil { return nil, err @@ -399,7 +399,7 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { if b.setting.ShowDebug { dump, err := httputil.DumpRequest(b.req, true) if err != nil { - println(err.Error()) + log.Println(err.Error()) } b.dump = dump } From d8734cf58d8d7b201f4d87c6b1797701cdd15b51 Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 16:08:25 +0800 Subject: [PATCH 103/436] set default timeout --- httplib/httplib.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/httplib/httplib.go b/httplib/httplib.go index ae50791c..7464c16f 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -357,6 +357,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { trans := b.setting.Transport + if b.setting.ConnectTimeout == 0 { + b.setting.ConnectTimeout = 30 * time.Second + } + if b.setting.ReadWriteTimeout == 0 { + b.setting.ReadWriteTimeout = 30 * time.Second + } + if trans == nil { // create default transport trans = &http.Transport{ From 3bd6caae0aa962b6a2dbf655a61f6695913f39ac Mon Sep 17 00:00:00 2001 From: JessonChan Date: Sun, 26 Apr 2015 16:10:18 +0800 Subject: [PATCH 104/436] set default timeout --- httplib/httplib.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 7464c16f..2389a7cb 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -358,10 +358,10 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { trans := b.setting.Transport if b.setting.ConnectTimeout == 0 { - b.setting.ConnectTimeout = 30 * time.Second + b.setting.ConnectTimeout = 60 * time.Second } if b.setting.ReadWriteTimeout == 0 { - b.setting.ReadWriteTimeout = 30 * time.Second + b.setting.ReadWriteTimeout = 60 * time.Second } if trans == nil { From 1053b63bbce1a97f494360d61020d23703132f2e Mon Sep 17 00:00:00 2001 From: cr7pt0gr4ph7 Date: Sun, 3 May 2015 23:21:32 +0200 Subject: [PATCH 105/436] Improve documentation of filter.go. --- filter.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/filter.go b/filter.go index ddd61094..f673ab66 100644 --- a/filter.go +++ b/filter.go @@ -16,11 +16,12 @@ package beego import "github.com/astaxie/beego/context" -// FilterFunc defines filter function type. +// FilterFunc defines a filter function which is invoked before the controller handler is executed. type FilterFunc func(*context.Context) -// FilterRouter defines filter operation before controller handler execution. -// it can match patterned url and do filter function when action arrives. +// FilterRouter defines a filter operation which is invoked before the controller handler is executed. +// It can match the URL against a pattern, and execute a filter function +// when a request with a matching URL arrives. type FilterRouter struct { filterFunc FilterFunc tree *Tree @@ -28,10 +29,11 @@ type FilterRouter struct { returnOnOutput bool } -// ValidRouter check current request is valid for this filter. -// if matched, returns parsed params in this request by defined filter router pattern. -func (f *FilterRouter) ValidRouter(router string) (bool, map[string]string) { - isok, params := f.tree.Match(router) +// ValidRouter checks if the current request is matched by this filter. +// If the request is matched, the values of the URL parameters defined +// by the filter pattern are also returned. +func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { + isok, params := f.tree.Match(url) if isok == nil { return false, nil } From 9252301fa007ff126ea76da6403c9faae74e02f1 Mon Sep 17 00:00:00 2001 From: mlgd Date: Mon, 4 May 2015 15:54:03 +0200 Subject: [PATCH 106/436] Fix save config ini file --- config/ini.go | 54 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/config/ini.go b/config/ini.go index 837c9ffe..31fe9b5f 100644 --- a/config/ini.go +++ b/config/ini.go @@ -300,21 +300,8 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { defer f.Close() buf := bytes.NewBuffer(nil) - for section, dt := range c.data { - // Write section comments. - if v, ok := c.sectionComment[section]; ok { - if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { - return err - } - } - - if section != DEFAULT_SECTION { - // Write section name. - if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil { - return err - } - } - + // Save default section at first place + if dt, ok := c.data[DEFAULT_SECTION]; ok { for key, val := range dt { if key != " " { // Write key comments. @@ -336,6 +323,43 @@ func (c *IniConfigContainer) SaveConfigFile(filename string) (err error) { return err } } + // Save named sections + for section, dt := range c.data { + if section != DEFAULT_SECTION { + // Write section comments. + if v, ok := c.sectionComment[section]; ok { + if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + return err + } + } + + // Write section name. + if _, err = buf.WriteString(string(sectionStart) + section + string(sectionEnd) + lineBreak); err != nil { + return err + } + + for key, val := range dt { + if key != " " { + // Write key comments. + if v, ok := c.keyComment[key]; ok { + if _, err = buf.WriteString(string(bNumComment) + v + lineBreak); err != nil { + return err + } + } + + // Write key and value. + if _, err = buf.WriteString(key + string(bEqual) + val + lineBreak); err != nil { + return err + } + } + } + + // Put a line between sections. + if _, err = buf.WriteString(lineBreak); err != nil { + return err + } + } + } if _, err = buf.WriteTo(f); err != nil { return err From 18bdec951d3b735c0aefee4b8e1eafb3fecf2b4e Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 5 May 2015 21:36:31 +0800 Subject: [PATCH 107/436] fix #1143 --- error.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/error.go b/error.go index 6db4a5a8..99a1fcf3 100644 --- a/error.go +++ b/error.go @@ -92,7 +92,7 @@ func showErr(err interface{}, ctx *context.Context, Stack string) { data["Stack"] = Stack data["BeegoVersion"] = VERSION data["GoVersion"] = runtime.Version() - ctx.Output.SetStatus(500) + ctx.ResponseWriter.WriteHeader(500) t.Execute(ctx.ResponseWriter, data) } From 245762f7d968e2969dc920f63db756d74f022e76 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 9 May 2015 14:54:30 +0800 Subject: [PATCH 108/436] add apk mime --- mime.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mime.go b/mime.go index 155e5e12..20246c21 100644 --- a/mime.go +++ b/mime.go @@ -40,6 +40,7 @@ var mimemaps map[string]string = map[string]string{ ".ani": "application/x-navi-animation", ".aos": "application/x-nokia-9000-communicator-add-on-software", ".aps": "application/mime", + ".apk": "application/vnd.android.package-archive", ".arc": "application/x-arc-compressed", ".arj": "application/arj", ".art": "image/x-jg", From 4138fe021736d8be0d29f224acc54ab6dd1d93a4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 13 May 2015 21:17:47 +0800 Subject: [PATCH 109/436] beego suppot graceful restart application --- admin.go | 12 +- app.go | 133 ++++++++----- config.go | 4 + grace/grace.go | 522 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 621 insertions(+), 50 deletions(-) create mode 100644 grace/grace.go diff --git a/admin.go b/admin.go index 7eb07cc0..52388c76 100644 --- a/admin.go +++ b/admin.go @@ -19,9 +19,11 @@ import ( "encoding/json" "fmt" "net/http" + "os" "text/template" "time" + "github.com/astaxie/beego/grace" "github.com/astaxie/beego/toolbox" "github.com/astaxie/beego/utils" ) @@ -458,8 +460,14 @@ func (admin *adminApp) Run() { http.Handle(p, f) } BeeLogger.Info("Admin server Running on %s", addr) - err := http.ListenAndServe(addr, nil) + + var err error + if Graceful { + err = grace.ListenAndServe(addr, nil) + } else { + err = http.ListenAndServe(addr, nil) + } if err != nil { - BeeLogger.Critical("Admin ListenAndServe: ", err) + BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprint(os.Getpid())) } } diff --git a/app.go b/app.go index 35040f33..4740e7e5 100644 --- a/app.go +++ b/app.go @@ -22,6 +22,7 @@ import ( "os" "time" + "github.com/astaxie/beego/grace" "github.com/astaxie/beego/utils" ) @@ -76,57 +77,93 @@ func (app *App) Run() { err = fcgi.Serve(l, app.Handlers) } } else { - app.Server.Addr = addr - app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + if Graceful { + if EnableHttpTLS { + go func() { + time.Sleep(20 * time.Microsecond) + if HttpsPort != 0 { + addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + } + server := grace.NewServer(addr, app.Handlers) + server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + if err != nil { + BeeLogger.Critical("ListenAndServeTLS: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + if EnableHttpListen { + go func() { + server := grace.NewServer(addr, app.Handlers) + server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + if ListenTCP4 && HttpAddr == "" { + server.Network = "tcp4" + } + err := server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + } else { + app.Server.Addr = addr + app.Server.Handler = app.Handlers + app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second - if EnableHttpTLS { - go func() { - time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) - } - BeeLogger.Info("https server Running on %s", app.Server.Addr) - err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) - if err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - }() + if EnableHttpTLS { + go func() { + time.Sleep(20 * time.Microsecond) + if HttpsPort != 0 { + app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + } + BeeLogger.Info("https server Running on %s", app.Server.Addr) + err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + if err != nil { + BeeLogger.Critical("ListenAndServeTLS: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + }() + } + + if EnableHttpListen { + go func() { + app.Server.Addr = addr + BeeLogger.Info("http server Running on %s", app.Server.Addr) + if ListenTCP4 && HttpAddr == "" { + ln, err := net.Listen("tcp4", app.Server.Addr) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + err = app.Server.Serve(ln) + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + return + } + } else { + err := app.Server.ListenAndServe() + if err != nil { + BeeLogger.Critical("ListenAndServe: ", err) + time.Sleep(100 * time.Microsecond) + endRunning <- true + } + } + }() + } } - if EnableHttpListen { - go func() { - app.Server.Addr = addr - BeeLogger.Info("http server Running on %s", app.Server.Addr) - if ListenTCP4 && HttpAddr == "" { - ln, err := net.Listen("tcp4", app.Server.Addr) - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - err = app.Server.Serve(ln) - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - return - } - } else { - err := app.Server.ListenAndServe() - if err != nil { - BeeLogger.Critical("ListenAndServe: ", err) - time.Sleep(100 * time.Microsecond) - endRunning <- true - } - } - }() - } } - <-endRunning } diff --git a/config.go b/config.go index b22db163..09d0df24 100644 --- a/config.go +++ b/config.go @@ -82,6 +82,7 @@ var ( EnableDocs bool // enable generate docs & server docs API Swagger RouterCaseSensitive bool // router case sensitive default is true AccessLogs bool // print access logs, default is false + Graceful bool // use graceful start the server ) type beegoAppConfig struct { @@ -509,5 +510,8 @@ func ParseConfig() (err error) { if casesensitive, err := AppConfig.Bool("RouterCaseSensitive"); err == nil { RouterCaseSensitive = casesensitive } + if graceful, err := AppConfig.Bool("Graceful"); err == nil { + Graceful = graceful + } return nil } diff --git a/grace/grace.go b/grace/grace.go new file mode 100644 index 00000000..e77112d0 --- /dev/null +++ b/grace/grace.go @@ -0,0 +1,522 @@ +// 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. + +// Description: http://grisha.org/blog/2014/06/03/graceful-restart-in-golang/ +// +// Usage: +// +// import( +// "log" +// "net/http" +// "os" +// +// "github.com/astaxie/beego/grace" +// ) +// +// func handler(w http.ResponseWriter, r *http.Request) { +// w.Write([]byte("WORLD!")) +// } +// +// func main() { +// mux := http.NewServeMux() +// mux.HandleFunc("/hello", handler) +// +// err := grace.ListenAndServe("localhost:8080", mux1) +// if err != nil { +// log.Println(err) +// } +// log.Println("Server on 8080 stopped") +// os.Exit(0) +// } +package grace + +import ( + "crypto/tls" + "flag" + "fmt" + "log" + "net" + "net/http" + "os" + "os/exec" + "os/signal" + "strings" + "sync" + "syscall" + "time" +) + +const ( + PRE_SIGNAL = iota + POST_SIGNAL + + STATE_INIT + STATE_RUNNING + STATE_SHUTTING_DOWN + STATE_TERMINATE +) + +var ( + regLock *sync.Mutex + runningServers map[string]*graceServer + runningServersOrder []string + socketPtrOffsetMap map[string]uint + runningServersForked bool + + DefaultReadTimeOut time.Duration + DefaultWriteTimeOut time.Duration + DefaultMaxHeaderBytes int + DefaultTimeout time.Duration + + isChild bool + socketOrder string +) + +func init() { + regLock = &sync.Mutex{} + flag.BoolVar(&isChild, "graceful", false, "listen on open fd (after forking)") + flag.StringVar(&socketOrder, "socketorder", "", "previous initialization order - used when more than one listener was started") + runningServers = make(map[string]*graceServer) + runningServersOrder = []string{} + socketPtrOffsetMap = make(map[string]uint) + + DefaultMaxHeaderBytes = 0 + + // after a restart the parent will finish ongoing requests before + // shutting down. set to a negative value to disable + DefaultTimeout = 60 * time.Second +} + +type graceServer struct { + http.Server + GraceListener net.Listener + SignalHooks map[int]map[os.Signal][]func() + tlsInnerListener *graceListener + wg sync.WaitGroup + sigChan chan os.Signal + isChild bool + state uint8 + Network string +} + +// NewServer returns an intialized graceServer. Calling Serve on it will +// actually "start" the server. +func NewServer(addr string, handler http.Handler) (srv *graceServer) { + regLock.Lock() + defer regLock.Unlock() + if !flag.Parsed() { + flag.Parse() + } + if len(socketOrder) > 0 { + for i, addr := range strings.Split(socketOrder, ",") { + socketPtrOffsetMap[addr] = uint(i) + } + } else { + socketPtrOffsetMap[addr] = uint(len(runningServersOrder)) + } + + srv = &graceServer{ + wg: sync.WaitGroup{}, + sigChan: make(chan os.Signal), + isChild: isChild, + SignalHooks: map[int]map[os.Signal][]func(){ + PRE_SIGNAL: map[os.Signal][]func(){ + syscall.SIGHUP: []func(){}, + syscall.SIGUSR1: []func(){}, + syscall.SIGUSR2: []func(){}, + syscall.SIGINT: []func(){}, + syscall.SIGTERM: []func(){}, + syscall.SIGTSTP: []func(){}, + }, + POST_SIGNAL: map[os.Signal][]func(){ + syscall.SIGHUP: []func(){}, + syscall.SIGUSR1: []func(){}, + syscall.SIGUSR2: []func(){}, + syscall.SIGINT: []func(){}, + syscall.SIGTERM: []func(){}, + syscall.SIGTSTP: []func(){}, + }, + }, + state: STATE_INIT, + Network: "tcp", + } + + srv.Server.Addr = addr + srv.Server.ReadTimeout = DefaultReadTimeOut + srv.Server.WriteTimeout = DefaultWriteTimeOut + srv.Server.MaxHeaderBytes = DefaultMaxHeaderBytes + srv.Server.Handler = handler + + runningServersOrder = append(runningServersOrder, addr) + runningServers[addr] = srv + + return +} + +// ListenAndServe listens on the TCP network address addr +// and then calls Serve to handle requests on incoming connections. +func ListenAndServe(addr string, handler http.Handler) error { + server := NewServer(addr, handler) + return server.ListenAndServe() +} + +// ListenAndServeTLS listens on the TCP network address addr and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must be provided. +// If the certificate is signed by a certificate authority, +// the certFile should be the concatenation of the server's certificate followed by the CA's certificate. +func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error { + server := NewServer(addr, handler) + return server.ListenAndServeTLS(certFile, keyFile) +} + +// Serve accepts incoming connections on the Listener l, +// creating a new service goroutine for each. +// The service goroutines read requests and then call srv.Handler to reply to them. +func (srv *graceServer) Serve() (err error) { + srv.state = STATE_RUNNING + err = srv.Server.Serve(srv.GraceListener) + log.Println(syscall.Getpid(), "Waiting for connections to finish...") + srv.wg.Wait() + srv.state = STATE_TERMINATE + return +} + +// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve +// to handle requests on incoming connections. If srv.Addr is blank, ":http" is +// used. +func (srv *graceServer) ListenAndServe() (err error) { + addr := srv.Addr + if addr == "" { + addr = ":http" + } + + go srv.handleSignals() + + l, err := srv.getListener(addr) + if err != nil { + log.Println(err) + return + } + + srv.GraceListener = newGraceListener(l, srv) + + if srv.isChild { + syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + } + + log.Println(syscall.Getpid(), srv.Addr) + return srv.Serve() +} + +// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must +// be provided. If the certificate is signed by a certificate authority, the +// certFile should be the concatenation of the server's certificate followed by the +// CA's certificate. +// +// If srv.Addr is blank, ":https" is used. +func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) { + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + config := &tls.Config{} + if srv.TLSConfig != nil { + *config = *srv.TLSConfig + } + if config.NextProtos == nil { + config.NextProtos = []string{"http/1.1"} + } + + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return + } + + go srv.handleSignals() + + l, err := srv.getListener(addr) + if err != nil { + log.Println(err) + return + } + + srv.tlsInnerListener = newGraceListener(l, srv) + srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config) + + if srv.isChild { + syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + } + + log.Println(syscall.Getpid(), srv.Addr) + return srv.Serve() +} + +// getListener either opens a new socket to listen on, or takes the acceptor socket +// it got passed when restarted. +func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) { + if srv.isChild { + var ptrOffset uint = 0 + if len(socketPtrOffsetMap) > 0 { + ptrOffset = socketPtrOffsetMap[laddr] + log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr]) + } + + f := os.NewFile(uintptr(3+ptrOffset), "") + l, err = net.FileListener(f) + if err != nil { + err = fmt.Errorf("net.FileListener error: %v", err) + return + } + } else { + l, err = net.Listen(srv.Network, laddr) + if err != nil { + err = fmt.Errorf("net.Listen error: %v", err) + return + } + } + return +} + +// handleSignals listens for os Signals and calls any hooked in function that the +// user had registered with the signal. +func (srv *graceServer) handleSignals() { + var sig os.Signal + + signal.Notify( + srv.sigChan, + syscall.SIGHUP, + syscall.SIGUSR1, + syscall.SIGUSR2, + syscall.SIGINT, + syscall.SIGTERM, + syscall.SIGTSTP, + ) + + pid := syscall.Getpid() + for { + sig = <-srv.sigChan + srv.signalHooks(PRE_SIGNAL, sig) + switch sig { + case syscall.SIGHUP: + log.Println(pid, "Received SIGHUP. forking.") + err := srv.fork() + if err != nil { + log.Println("Fork err:", err) + } + case syscall.SIGUSR1: + log.Println(pid, "Received SIGUSR1.") + case syscall.SIGUSR2: + log.Println(pid, "Received SIGUSR2.") + srv.serverTimeout(0 * time.Second) + case syscall.SIGINT: + log.Println(pid, "Received SIGINT.") + srv.shutdown() + case syscall.SIGTERM: + log.Println(pid, "Received SIGTERM.") + srv.shutdown() + case syscall.SIGTSTP: + log.Println(pid, "Received SIGTSTP.") + default: + log.Printf("Received %v: nothing i care about...\n", sig) + } + srv.signalHooks(POST_SIGNAL, sig) + } +} + +func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) { + if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet { + return + } + for _, f := range srv.SignalHooks[ppFlag][sig] { + f() + } + return +} + +// shutdown closes the listener so that no new connections are accepted. it also +// starts a goroutine that will hammer (stop all running requests) the server +// after DefaultTimeout. +func (srv *graceServer) shutdown() { + if srv.state != STATE_RUNNING { + return + } + + srv.state = STATE_SHUTTING_DOWN + if DefaultTimeout >= 0 { + go srv.serverTimeout(DefaultTimeout) + } + err := srv.GraceListener.Close() + if err != nil { + log.Println(syscall.Getpid(), "Listener.Close() error:", err) + } else { + log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.") + } +} + +// hammerTime forces the server to shutdown in a given timeout - whether it +// finished outstanding requests or not. if Read/WriteTimeout are not set or the +// max header size is very big a connection could hang... +// +// srv.Serve() will not return until all connections are served. this will +// unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe(TLS) to +// return. +func (srv *graceServer) serverTimeout(d time.Duration) { + defer func() { + // we are calling srv.wg.Done() until it panics which means we called + // Done() when the counter was already at 0 and we're done. + // (and thus Serve() will return and the parent will exit) + if r := recover(); r != nil { + log.Println("WaitGroup at 0", r) + } + }() + if srv.state != STATE_SHUTTING_DOWN { + return + } + time.Sleep(d) + log.Println("[STOP - Hammer Time] Forcefully shutting down parent") + for { + if srv.state == STATE_TERMINATE { + break + } + srv.wg.Done() + } +} + +func (srv *graceServer) fork() (err error) { + // only one server isntance should fork! + regLock.Lock() + defer regLock.Unlock() + if runningServersForked { + return + } + runningServersForked = true + + var files = make([]*os.File, len(runningServers)) + var orderArgs = make([]string, len(runningServers)) + // get the accessor socket fds for _all_ server instances + for _, srvPtr := range runningServers { + // introspect.PrintTypeDump(srvPtr.EndlessListener) + switch srvPtr.GraceListener.(type) { + case *graceListener: + // normal listener + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File() + default: + // tls listener + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File() + } + orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr + } + + log.Println(files) + path := os.Args[0] + var args []string + if len(os.Args) > 1 { + for _, arg := range os.Args[1:] { + if arg == "-graceful" { + break + } + args = append(args, arg) + } + } + args = append(args, "-graceful") + if len(runningServers) > 1 { + args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ","))) + log.Println(args) + } + cmd := exec.Command(path, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.ExtraFiles = files + err = cmd.Start() + if err != nil { + log.Fatalf("Restart: Failed to launch, error: %v", err) + } + + return +} + +type graceListener struct { + net.Listener + stop chan error + stopped bool + server *graceServer +} + +func (gl *graceListener) Accept() (c net.Conn, err error) { + tc, err := gl.Listener.(*net.TCPListener).AcceptTCP() + if err != nil { + return + } + + tc.SetKeepAlive(true) // see http.tcpKeepAliveListener + tc.SetKeepAlivePeriod(3 * time.Minute) // see http.tcpKeepAliveListener + + c = graceConn{ + Conn: tc, + server: gl.server, + } + + gl.server.wg.Add(1) + return +} + +func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) { + el = &graceListener{ + Listener: l, + stop: make(chan error), + server: srv, + } + + // Starting the listener for the stop signal here because Accept blocks on + // el.Listener.(*net.TCPListener).AcceptTCP() + // The goroutine will unblock it by closing the listeners fd + go func() { + _ = <-el.stop + el.stopped = true + el.stop <- el.Listener.Close() + }() + return +} + +func (el *graceListener) Close() error { + if el.stopped { + return syscall.EINVAL + } + el.stop <- nil + return <-el.stop +} + +func (el *graceListener) File() *os.File { + // returns a dup(2) - FD_CLOEXEC flag *not* set + tl := el.Listener.(*net.TCPListener) + fl, _ := tl.File() + return fl +} + +type graceConn struct { + net.Conn + server *graceServer +} + +func (c graceConn) Close() error { + c.server.wg.Done() + return c.Conn.Close() +} From 2dca48f26edb74ba46dce569d0fd21d1023300d2 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 13 May 2015 21:20:50 +0800 Subject: [PATCH 110/436] fix sesseion redis db error --- session/redis/sess_redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/redis/sess_redis.go b/session/redis/sess_redis.go index 887fb520..d31feb2c 100644 --- a/session/redis/sess_redis.go +++ b/session/redis/sess_redis.go @@ -145,7 +145,7 @@ func (rp *RedisProvider) SessionInit(maxlifetime int64, savePath string) error { rp.password = configs[2] } if len(configs) > 3 { - dbnum, err := strconv.Atoi(configs[1]) + dbnum, err := strconv.Atoi(configs[3]) if err != nil || dbnum < 0 { rp.dbNum = 0 } else { From 9fd7acf663d2f038b98717079f54a0281ae438e1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 15 May 2015 15:04:08 +0800 Subject: [PATCH 111/436] fix #1152 --- session/session.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/session/session.go b/session/session.go index 4214320c..ffc08edc 100644 --- a/session/session.go +++ b/session/session.go @@ -147,7 +147,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain} if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime @@ -174,7 +174,7 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain} if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime @@ -233,7 +233,7 @@ func (manager *Manager) SessionRegenerateId(w http.ResponseWriter, r *http.Reque Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, - Secure: manager.config.Secure, + Secure: manager.isSecure(r), Domain: manager.config.Domain, } } else { @@ -270,3 +270,17 @@ func (manager *Manager) sessionId(r *http.Request) (string, error) { } return hex.EncodeToString(b), nil } + +// Set cookie with https. +func (manager *Manager) isSecure(req *http.Request) bool { + if !manager.config.Secure { + return false + } + if req.URL.Scheme != "" { + return req.URL.Scheme == "https" + } + if req.TLS == nil { + return false + } + return true +} From d56491ab3ac7369c2770d1b2c08cd30a0c2021ec Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 20 May 2015 11:07:23 +0800 Subject: [PATCH 112/436] sync beeApp.Server to graceful --- app.go | 11 +++++++---- grace/grace.go | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index 4740e7e5..6c1eeab6 100644 --- a/app.go +++ b/app.go @@ -78,15 +78,19 @@ func (app *App) Run() { } } else { if Graceful { + app.Server.Addr = addr + app.Server.Handler = app.Handlers + app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second if EnableHttpTLS { go func() { time.Sleep(20 * time.Microsecond) if HttpsPort != 0 { addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + app.Server.Addr = addr } server := grace.NewServer(addr, app.Handlers) - server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server = app.Server err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) @@ -98,8 +102,7 @@ func (app *App) Run() { if EnableHttpListen { go func() { server := grace.NewServer(addr, app.Handlers) - server.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - server.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + server.Server = app.Server if ListenTCP4 && HttpAddr == "" { server.Network = "tcp4" } diff --git a/grace/grace.go b/grace/grace.go index e77112d0..33053c4b 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -99,7 +99,7 @@ func init() { } type graceServer struct { - http.Server + *http.Server GraceListener net.Listener SignalHooks map[int]map[os.Signal][]func() tlsInnerListener *graceListener @@ -151,7 +151,7 @@ func NewServer(addr string, handler http.Handler) (srv *graceServer) { state: STATE_INIT, Network: "tcp", } - + srv.Server = &http.Server{} srv.Server.Addr = addr srv.Server.ReadTimeout = DefaultReadTimeOut srv.Server.WriteTimeout = DefaultWriteTimeOut From edbaa080f1d9dbd72541b05b7a8d26f1681b8b9b Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 20 May 2015 11:09:30 +0800 Subject: [PATCH 113/436] update the string --- admin.go | 2 +- app.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/admin.go b/admin.go index 52388c76..64d7fe34 100644 --- a/admin.go +++ b/admin.go @@ -468,6 +468,6 @@ func (admin *adminApp) Run() { err = http.ListenAndServe(addr, nil) } if err != nil { - BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + BeeLogger.Critical("Admin ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) } } diff --git a/app.go b/app.go index 6c1eeab6..8fc320ad 100644 --- a/app.go +++ b/app.go @@ -93,7 +93,7 @@ func (app *App) Run() { server.Server = app.Server err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) if err != nil { - BeeLogger.Critical("ListenAndServeTLS: ", err) + BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } @@ -108,7 +108,7 @@ func (app *App) Run() { } err := server.ListenAndServe() if err != nil { - BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprint(os.Getpid())) + BeeLogger.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) endRunning <- true } From ae3768931480d48b768bd30d74d495f6ffc033a4 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 27 May 2015 23:22:05 +0800 Subject: [PATCH 114/436] fix #1176 grace support windows --- grace/grace.go | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/grace/grace.go b/grace/grace.go index 33053c4b..eb57d38e 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -133,19 +133,13 @@ func NewServer(addr string, handler http.Handler) (srv *graceServer) { SignalHooks: map[int]map[os.Signal][]func(){ PRE_SIGNAL: map[os.Signal][]func(){ syscall.SIGHUP: []func(){}, - syscall.SIGUSR1: []func(){}, - syscall.SIGUSR2: []func(){}, syscall.SIGINT: []func(){}, syscall.SIGTERM: []func(){}, - syscall.SIGTSTP: []func(){}, }, POST_SIGNAL: map[os.Signal][]func(){ syscall.SIGHUP: []func(){}, - syscall.SIGUSR1: []func(){}, - syscall.SIGUSR2: []func(){}, syscall.SIGINT: []func(){}, syscall.SIGTERM: []func(){}, - syscall.SIGTSTP: []func(){}, }, }, state: STATE_INIT, @@ -208,16 +202,24 @@ func (srv *graceServer) ListenAndServe() (err error) { l, err := srv.getListener(addr) if err != nil { log.Println(err) - return + return err } srv.GraceListener = newGraceListener(l, srv) if srv.isChild { - syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + process, err := os.FindProcess(os.Getppid()) + if err != nil { + log.Println(err) + return err + } + err = process.Kill() + if err != nil { + return err + } } - log.Println(syscall.Getpid(), srv.Addr) + log.Println(os.Getpid(), srv.Addr) return srv.Serve() } @@ -255,17 +257,24 @@ func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) l, err := srv.getListener(addr) if err != nil { log.Println(err) - return + return err } srv.tlsInnerListener = newGraceListener(l, srv) srv.GraceListener = tls.NewListener(srv.tlsInnerListener, config) if srv.isChild { - syscall.Kill(syscall.Getppid(), syscall.SIGTERM) + process, err := os.FindProcess(os.Getppid()) + if err != nil { + log.Println(err) + return err + } + err = process.Kill() + if err != nil { + return err + } } - - log.Println(syscall.Getpid(), srv.Addr) + log.Println(os.Getpid(), srv.Addr) return srv.Serve() } @@ -303,11 +312,8 @@ func (srv *graceServer) handleSignals() { signal.Notify( srv.sigChan, syscall.SIGHUP, - syscall.SIGUSR1, - syscall.SIGUSR2, syscall.SIGINT, syscall.SIGTERM, - syscall.SIGTSTP, ) pid := syscall.Getpid() @@ -321,19 +327,12 @@ func (srv *graceServer) handleSignals() { if err != nil { log.Println("Fork err:", err) } - case syscall.SIGUSR1: - log.Println(pid, "Received SIGUSR1.") - case syscall.SIGUSR2: - log.Println(pid, "Received SIGUSR2.") - srv.serverTimeout(0 * time.Second) case syscall.SIGINT: log.Println(pid, "Received SIGINT.") srv.shutdown() case syscall.SIGTERM: log.Println(pid, "Received SIGTERM.") srv.shutdown() - case syscall.SIGTSTP: - log.Println(pid, "Received SIGTSTP.") default: log.Printf("Received %v: nothing i care about...\n", sig) } From 3abd01799d11d653f615c43d7a8e005a070cddac Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 27 May 2015 23:46:45 +0800 Subject: [PATCH 115/436] split into small files --- grace/conn.go | 13 ++ grace/grace.go | 377 +--------------------------------------------- grace/listener.go | 62 ++++++++ grace/server.go | 292 +++++++++++++++++++++++++++++++++++ 4 files changed, 370 insertions(+), 374 deletions(-) create mode 100644 grace/conn.go create mode 100644 grace/listener.go create mode 100644 grace/server.go diff --git a/grace/conn.go b/grace/conn.go new file mode 100644 index 00000000..2cf3a93d --- /dev/null +++ b/grace/conn.go @@ -0,0 +1,13 @@ +package grace + +import "net" + +type graceConn struct { + net.Conn + server *graceServer +} + +func (c graceConn) Close() error { + c.server.wg.Done() + return c.Conn.Close() +} diff --git a/grace/grace.go b/grace/grace.go index eb57d38e..e5577267 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -42,15 +42,9 @@ package grace import ( - "crypto/tls" "flag" - "fmt" - "log" - "net" "net/http" "os" - "os/exec" - "os/signal" "strings" "sync" "syscall" @@ -93,25 +87,10 @@ func init() { DefaultMaxHeaderBytes = 0 - // after a restart the parent will finish ongoing requests before - // shutting down. set to a negative value to disable DefaultTimeout = 60 * time.Second } -type graceServer struct { - *http.Server - GraceListener net.Listener - SignalHooks map[int]map[os.Signal][]func() - tlsInnerListener *graceListener - wg sync.WaitGroup - sigChan chan os.Signal - isChild bool - state uint8 - Network string -} - -// NewServer returns an intialized graceServer. Calling Serve on it will -// actually "start" the server. +// NewServer returns a new graceServer. func NewServer(addr string, handler http.Handler) (srv *graceServer) { regLock.Lock() defer regLock.Unlock() @@ -158,364 +137,14 @@ func NewServer(addr string, handler http.Handler) (srv *graceServer) { return } -// ListenAndServe listens on the TCP network address addr -// and then calls Serve to handle requests on incoming connections. +// refer http.ListenAndServe func ListenAndServe(addr string, handler http.Handler) error { server := NewServer(addr, handler) return server.ListenAndServe() } -// ListenAndServeTLS listens on the TCP network address addr and then calls -// Serve to handle requests on incoming TLS connections. -// -// Filenames containing a certificate and matching private key for the server must be provided. -// If the certificate is signed by a certificate authority, -// the certFile should be the concatenation of the server's certificate followed by the CA's certificate. +// refer http.ListenAndServeTLS func ListenAndServeTLS(addr string, certFile string, keyFile string, handler http.Handler) error { server := NewServer(addr, handler) return server.ListenAndServeTLS(certFile, keyFile) } - -// Serve accepts incoming connections on the Listener l, -// creating a new service goroutine for each. -// The service goroutines read requests and then call srv.Handler to reply to them. -func (srv *graceServer) Serve() (err error) { - srv.state = STATE_RUNNING - err = srv.Server.Serve(srv.GraceListener) - log.Println(syscall.Getpid(), "Waiting for connections to finish...") - srv.wg.Wait() - srv.state = STATE_TERMINATE - return -} - -// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve -// to handle requests on incoming connections. If srv.Addr is blank, ":http" is -// used. -func (srv *graceServer) ListenAndServe() (err error) { - addr := srv.Addr - if addr == "" { - addr = ":http" - } - - go srv.handleSignals() - - l, err := srv.getListener(addr) - if err != nil { - log.Println(err) - return err - } - - srv.GraceListener = newGraceListener(l, srv) - - if srv.isChild { - process, err := os.FindProcess(os.Getppid()) - if err != nil { - log.Println(err) - return err - } - err = process.Kill() - if err != nil { - return err - } - } - - log.Println(os.Getpid(), srv.Addr) - return srv.Serve() -} - -// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls -// Serve to handle requests on incoming TLS connections. -// -// Filenames containing a certificate and matching private key for the server must -// be provided. If the certificate is signed by a certificate authority, the -// certFile should be the concatenation of the server's certificate followed by the -// CA's certificate. -// -// If srv.Addr is blank, ":https" is used. -func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) { - addr := srv.Addr - if addr == "" { - addr = ":https" - } - - config := &tls.Config{} - if srv.TLSConfig != nil { - *config = *srv.TLSConfig - } - if config.NextProtos == nil { - config.NextProtos = []string{"http/1.1"} - } - - config.Certificates = make([]tls.Certificate, 1) - config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return - } - - 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, config) - - if srv.isChild { - process, err := os.FindProcess(os.Getppid()) - if err != nil { - log.Println(err) - return err - } - err = process.Kill() - if err != nil { - return err - } - } - log.Println(os.Getpid(), srv.Addr) - return srv.Serve() -} - -// getListener either opens a new socket to listen on, or takes the acceptor socket -// it got passed when restarted. -func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) { - if srv.isChild { - var ptrOffset uint = 0 - if len(socketPtrOffsetMap) > 0 { - ptrOffset = socketPtrOffsetMap[laddr] - log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr]) - } - - f := os.NewFile(uintptr(3+ptrOffset), "") - l, err = net.FileListener(f) - if err != nil { - err = fmt.Errorf("net.FileListener error: %v", err) - return - } - } else { - l, err = net.Listen(srv.Network, laddr) - if err != nil { - err = fmt.Errorf("net.Listen error: %v", err) - return - } - } - return -} - -// handleSignals listens for os Signals and calls any hooked in function that the -// user had registered with the signal. -func (srv *graceServer) handleSignals() { - var sig os.Signal - - signal.Notify( - srv.sigChan, - syscall.SIGHUP, - syscall.SIGINT, - syscall.SIGTERM, - ) - - pid := syscall.Getpid() - for { - sig = <-srv.sigChan - srv.signalHooks(PRE_SIGNAL, sig) - switch sig { - case syscall.SIGHUP: - log.Println(pid, "Received SIGHUP. forking.") - err := srv.fork() - if err != nil { - log.Println("Fork err:", err) - } - case syscall.SIGINT: - log.Println(pid, "Received SIGINT.") - srv.shutdown() - case syscall.SIGTERM: - log.Println(pid, "Received SIGTERM.") - srv.shutdown() - default: - log.Printf("Received %v: nothing i care about...\n", sig) - } - srv.signalHooks(POST_SIGNAL, sig) - } -} - -func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) { - if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet { - return - } - for _, f := range srv.SignalHooks[ppFlag][sig] { - f() - } - return -} - -// shutdown closes the listener so that no new connections are accepted. it also -// starts a goroutine that will hammer (stop all running requests) the server -// after DefaultTimeout. -func (srv *graceServer) shutdown() { - if srv.state != STATE_RUNNING { - return - } - - srv.state = STATE_SHUTTING_DOWN - if DefaultTimeout >= 0 { - go srv.serverTimeout(DefaultTimeout) - } - err := srv.GraceListener.Close() - if err != nil { - log.Println(syscall.Getpid(), "Listener.Close() error:", err) - } else { - log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.") - } -} - -// hammerTime forces the server to shutdown in a given timeout - whether it -// finished outstanding requests or not. if Read/WriteTimeout are not set or the -// max header size is very big a connection could hang... -// -// srv.Serve() will not return until all connections are served. this will -// unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe(TLS) to -// return. -func (srv *graceServer) serverTimeout(d time.Duration) { - defer func() { - // we are calling srv.wg.Done() until it panics which means we called - // Done() when the counter was already at 0 and we're done. - // (and thus Serve() will return and the parent will exit) - if r := recover(); r != nil { - log.Println("WaitGroup at 0", r) - } - }() - if srv.state != STATE_SHUTTING_DOWN { - return - } - time.Sleep(d) - log.Println("[STOP - Hammer Time] Forcefully shutting down parent") - for { - if srv.state == STATE_TERMINATE { - break - } - srv.wg.Done() - } -} - -func (srv *graceServer) fork() (err error) { - // only one server isntance should fork! - regLock.Lock() - defer regLock.Unlock() - if runningServersForked { - return - } - runningServersForked = true - - var files = make([]*os.File, len(runningServers)) - var orderArgs = make([]string, len(runningServers)) - // get the accessor socket fds for _all_ server instances - for _, srvPtr := range runningServers { - // introspect.PrintTypeDump(srvPtr.EndlessListener) - switch srvPtr.GraceListener.(type) { - case *graceListener: - // normal listener - files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File() - default: - // tls listener - files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File() - } - orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr - } - - log.Println(files) - path := os.Args[0] - var args []string - if len(os.Args) > 1 { - for _, arg := range os.Args[1:] { - if arg == "-graceful" { - break - } - args = append(args, arg) - } - } - args = append(args, "-graceful") - if len(runningServers) > 1 { - args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ","))) - log.Println(args) - } - cmd := exec.Command(path, args...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.ExtraFiles = files - err = cmd.Start() - if err != nil { - log.Fatalf("Restart: Failed to launch, error: %v", err) - } - - return -} - -type graceListener struct { - net.Listener - stop chan error - stopped bool - server *graceServer -} - -func (gl *graceListener) Accept() (c net.Conn, err error) { - tc, err := gl.Listener.(*net.TCPListener).AcceptTCP() - if err != nil { - return - } - - tc.SetKeepAlive(true) // see http.tcpKeepAliveListener - tc.SetKeepAlivePeriod(3 * time.Minute) // see http.tcpKeepAliveListener - - c = graceConn{ - Conn: tc, - server: gl.server, - } - - gl.server.wg.Add(1) - return -} - -func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) { - el = &graceListener{ - Listener: l, - stop: make(chan error), - server: srv, - } - - // Starting the listener for the stop signal here because Accept blocks on - // el.Listener.(*net.TCPListener).AcceptTCP() - // The goroutine will unblock it by closing the listeners fd - go func() { - _ = <-el.stop - el.stopped = true - el.stop <- el.Listener.Close() - }() - return -} - -func (el *graceListener) Close() error { - if el.stopped { - return syscall.EINVAL - } - el.stop <- nil - return <-el.stop -} - -func (el *graceListener) File() *os.File { - // returns a dup(2) - FD_CLOEXEC flag *not* set - tl := el.Listener.(*net.TCPListener) - fl, _ := tl.File() - return fl -} - -type graceConn struct { - net.Conn - server *graceServer -} - -func (c graceConn) Close() error { - c.server.wg.Done() - return c.Conn.Close() -} diff --git a/grace/listener.go b/grace/listener.go new file mode 100644 index 00000000..8c5d4f9b --- /dev/null +++ b/grace/listener.go @@ -0,0 +1,62 @@ +package grace + +import ( + "net" + "os" + "syscall" + "time" +) + +type graceListener struct { + net.Listener + stop chan error + stopped bool + server *graceServer +} + +func newGraceListener(l net.Listener, srv *graceServer) (el *graceListener) { + el = &graceListener{ + Listener: l, + stop: make(chan error), + server: srv, + } + go func() { + _ = <-el.stop + el.stopped = true + el.stop <- el.Listener.Close() + }() + return +} + +func (gl *graceListener) Accept() (c net.Conn, err error) { + tc, err := gl.Listener.(*net.TCPListener).AcceptTCP() + if err != nil { + return + } + + tc.SetKeepAlive(true) + tc.SetKeepAlivePeriod(3 * time.Minute) + + c = graceConn{ + Conn: tc, + server: gl.server, + } + + gl.server.wg.Add(1) + return +} + +func (el *graceListener) Close() error { + if el.stopped { + return syscall.EINVAL + } + el.stop <- nil + return <-el.stop +} + +func (el *graceListener) File() *os.File { + // returns a dup(2) - FD_CLOEXEC flag *not* set + tl := el.Listener.(*net.TCPListener) + fl, _ := tl.File() + return fl +} diff --git a/grace/server.go b/grace/server.go new file mode 100644 index 00000000..aea8d7d3 --- /dev/null +++ b/grace/server.go @@ -0,0 +1,292 @@ +package grace + +import ( + "crypto/tls" + "fmt" + "log" + "net" + "net/http" + "os" + "os/exec" + "os/signal" + "strings" + "sync" + "syscall" + "time" +) + +type graceServer struct { + *http.Server + GraceListener net.Listener + SignalHooks map[int]map[os.Signal][]func() + tlsInnerListener *graceListener + wg sync.WaitGroup + sigChan chan os.Signal + isChild bool + state uint8 + Network string +} + +// Serve accepts incoming connections on the Listener l, +// creating a new service goroutine for each. +// The service goroutines read requests and then call srv.Handler to reply to them. +func (srv *graceServer) Serve() (err error) { + srv.state = STATE_RUNNING + err = srv.Server.Serve(srv.GraceListener) + log.Println(syscall.Getpid(), "Waiting for connections to finish...") + srv.wg.Wait() + srv.state = STATE_TERMINATE + return +} + +// ListenAndServe listens on the TCP network address srv.Addr and then calls Serve +// to handle requests on incoming connections. If srv.Addr is blank, ":http" is +// used. +func (srv *graceServer) ListenAndServe() (err error) { + addr := srv.Addr + if addr == "" { + addr = ":http" + } + + go srv.handleSignals() + + l, err := srv.getListener(addr) + if err != nil { + log.Println(err) + return err + } + + srv.GraceListener = newGraceListener(l, srv) + + if srv.isChild { + process, err := os.FindProcess(os.Getppid()) + if err != nil { + log.Println(err) + return err + } + err = process.Kill() + if err != nil { + return err + } + } + + log.Println(os.Getpid(), srv.Addr) + return srv.Serve() +} + +// ListenAndServeTLS listens on the TCP network address srv.Addr and then calls +// Serve to handle requests on incoming TLS connections. +// +// Filenames containing a certificate and matching private key for the server must +// be provided. If the certificate is signed by a certificate authority, the +// certFile should be the concatenation of the server's certificate followed by the +// CA's certificate. +// +// If srv.Addr is blank, ":https" is used. +func (srv *graceServer) ListenAndServeTLS(certFile, keyFile string) (err error) { + addr := srv.Addr + if addr == "" { + addr = ":https" + } + + config := &tls.Config{} + if srv.TLSConfig != nil { + *config = *srv.TLSConfig + } + if config.NextProtos == nil { + config.NextProtos = []string{"http/1.1"} + } + + config.Certificates = make([]tls.Certificate, 1) + config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return + } + + 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, config) + + if srv.isChild { + process, err := os.FindProcess(os.Getppid()) + if err != nil { + log.Println(err) + return err + } + err = process.Kill() + if err != nil { + return err + } + } + log.Println(os.Getpid(), srv.Addr) + return srv.Serve() +} + +// getListener either opens a new socket to listen on, or takes the acceptor socket +// it got passed when restarted. +func (srv *graceServer) getListener(laddr string) (l net.Listener, err error) { + if srv.isChild { + var ptrOffset uint = 0 + if len(socketPtrOffsetMap) > 0 { + ptrOffset = socketPtrOffsetMap[laddr] + log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr]) + } + + f := os.NewFile(uintptr(3+ptrOffset), "") + l, err = net.FileListener(f) + if err != nil { + err = fmt.Errorf("net.FileListener error: %v", err) + return + } + } else { + l, err = net.Listen(srv.Network, laddr) + if err != nil { + err = fmt.Errorf("net.Listen error: %v", err) + return + } + } + return +} + +// handleSignals listens for os Signals and calls any hooked in function that the +// user had registered with the signal. +func (srv *graceServer) handleSignals() { + var sig os.Signal + + signal.Notify( + srv.sigChan, + syscall.SIGHUP, + syscall.SIGINT, + syscall.SIGTERM, + ) + + pid := syscall.Getpid() + for { + sig = <-srv.sigChan + srv.signalHooks(PRE_SIGNAL, sig) + switch sig { + case syscall.SIGHUP: + log.Println(pid, "Received SIGHUP. forking.") + err := srv.fork() + if err != nil { + log.Println("Fork err:", err) + } + case syscall.SIGINT: + log.Println(pid, "Received SIGINT.") + srv.shutdown() + case syscall.SIGTERM: + log.Println(pid, "Received SIGTERM.") + srv.shutdown() + default: + log.Printf("Received %v: nothing i care about...\n", sig) + } + srv.signalHooks(POST_SIGNAL, sig) + } +} + +func (srv *graceServer) signalHooks(ppFlag int, sig os.Signal) { + if _, notSet := srv.SignalHooks[ppFlag][sig]; !notSet { + return + } + for _, f := range srv.SignalHooks[ppFlag][sig] { + f() + } + return +} + +// shutdown closes the listener so that no new connections are accepted. it also +// starts a goroutine that will serverTimeout (stop all running requests) the server +// after DefaultTimeout. +func (srv *graceServer) shutdown() { + if srv.state != STATE_RUNNING { + return + } + + srv.state = STATE_SHUTTING_DOWN + if DefaultTimeout >= 0 { + go srv.serverTimeout(DefaultTimeout) + } + err := srv.GraceListener.Close() + if err != nil { + log.Println(syscall.Getpid(), "Listener.Close() error:", err) + } else { + log.Println(syscall.Getpid(), srv.GraceListener.Addr(), "Listener closed.") + } +} + +// serverTimeout forces the server to shutdown in a given timeout - whether it +// finished outstanding requests or not. if Read/WriteTimeout are not set or the +// max header size is very big a connection could hang +func (srv *graceServer) serverTimeout(d time.Duration) { + defer func() { + if r := recover(); r != nil { + log.Println("WaitGroup at 0", r) + } + }() + if srv.state != STATE_SHUTTING_DOWN { + return + } + time.Sleep(d) + log.Println("[STOP - Hammer Time] Forcefully shutting down parent") + for { + if srv.state == STATE_TERMINATE { + break + } + srv.wg.Done() + } +} + +func (srv *graceServer) fork() (err error) { + regLock.Lock() + defer regLock.Unlock() + if runningServersForked { + return + } + runningServersForked = true + + var files = make([]*os.File, len(runningServers)) + var orderArgs = make([]string, len(runningServers)) + for _, srvPtr := range runningServers { + switch srvPtr.GraceListener.(type) { + case *graceListener: + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.GraceListener.(*graceListener).File() + default: + files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File() + } + orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr + } + + log.Println(files) + path := os.Args[0] + var args []string + if len(os.Args) > 1 { + for _, arg := range os.Args[1:] { + if arg == "-graceful" { + break + } + args = append(args, arg) + } + } + args = append(args, "-graceful") + if len(runningServers) > 1 { + args = append(args, fmt.Sprintf(`-socketorder=%s`, strings.Join(orderArgs, ","))) + log.Println(args) + } + cmd := exec.Command(path, args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.ExtraFiles = files + err = cmd.Start() + if err != nil { + log.Fatalf("Restart: Failed to launch, error: %v", err) + } + + return +} From db06e954b5d1cb9bdcb05b3c809fbcb1e4a3cecc Mon Sep 17 00:00:00 2001 From: astaxie Date: Thu, 28 May 2015 12:04:19 +0800 Subject: [PATCH 116/436] fix the session memcache bug --- session/memcache/sess_memcache.go | 6 +++--- session/session.go | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/session/memcache/sess_memcache.go b/session/memcache/sess_memcache.go index 24aecb82..bb33075a 100644 --- a/session/memcache/sess_memcache.go +++ b/session/memcache/sess_memcache.go @@ -129,8 +129,9 @@ func (rp *MemProvider) SessionRead(sid string) (session.SessionStore, error) { } } item, err := client.Get(sid) - if err != nil { - return nil, err + if err != nil && err == memcache.ErrCacheMiss { + rs := &MemcacheSessionStore{sid: sid, values: make(map[interface{}]interface{}), maxlifetime: rp.maxlifetime} + return rs, nil } var kv map[interface{}]interface{} if len(item.Value) == 0 { @@ -141,7 +142,6 @@ func (rp *MemProvider) SessionRead(sid string) (session.SessionStore, error) { return nil, err } } - rs := &MemcacheSessionStore{sid: sid, values: kv, maxlifetime: rp.maxlifetime} return rs, nil } diff --git a/session/session.go b/session/session.go index ffc08edc..f0895de1 100644 --- a/session/session.go +++ b/session/session.go @@ -143,12 +143,14 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se return nil, errs } session, err = manager.provider.SessionRead(sid) - cookie = &http.Cookie{Name: manager.config.CookieName, + cookie = &http.Cookie{ + Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, Secure: manager.isSecure(r), - Domain: manager.config.Domain} + Domain: manager.config.Domain, + } if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) @@ -170,12 +172,14 @@ func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (se return nil, err } session, err = manager.provider.SessionRead(sid) - cookie = &http.Cookie{Name: manager.config.CookieName, + cookie = &http.Cookie{ + Name: manager.config.CookieName, Value: url.QueryEscape(sid), Path: "/", HttpOnly: true, Secure: manager.isSecure(r), - Domain: manager.config.Domain} + Domain: manager.config.Domain, + } if manager.config.CookieLifeTime > 0 { cookie.MaxAge = manager.config.CookieLifeTime cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second) From 8e71d31dbee03d1b65a36bd5f3c1b1972829cd63 Mon Sep 17 00:00:00 2001 From: Nikolay Kirsh Date: Thu, 4 Jun 2015 18:40:10 +0500 Subject: [PATCH 117/436] fix session table --- session/mysql/sess_mysql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 0f6d3e4f..76a13932 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -21,7 +21,7 @@ // mysql session support need create table as sql: // CREATE TABLE `session` ( // `session_key` char(64) NOT NULL, -// session_data` blob, +// `session_data` blob, // `session_expiry` int(11) unsigned NOT NULL, // PRIMARY KEY (`session_key`) // ) ENGINE=MyISAM DEFAULT CHARSET=utf8; From a58c8180e87591e67e45717df65f842e7c99e0d0 Mon Sep 17 00:00:00 2001 From: "weizili.laptop" Date: Sun, 7 Jun 2015 16:26:23 +0800 Subject: [PATCH 118/436] Execute AUTH command when the "password" is configured --- cache/redis/redis.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 0e07eaed..ba543f61 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -51,6 +51,7 @@ type RedisCache struct { conninfo string dbNum int key string + password string } // create new redis cache with default collection name. @@ -149,16 +150,20 @@ func (rc *RedisCache) StartAndGC(config string) error { if _, ok := cf["key"]; !ok { cf["key"] = DefaultKey } - if _, ok := cf["conn"]; !ok { return errors.New("config has no conn key") } if _, ok := cf["dbNum"]; !ok { cf["dbNum"] = "0" } + if _, ok := cf["password"]; !ok { + cf["password"] = "" + } rc.key = cf["key"] rc.conninfo = cf["conn"] rc.dbNum, _ = strconv.Atoi(cf["dbNum"]) + rc.password = cf["password"] + rc.connectInit() c := rc.p.Get() @@ -171,6 +176,17 @@ func (rc *RedisCache) StartAndGC(config string) error { func (rc *RedisCache) connectInit() { dialFunc := func() (c redis.Conn, err error) { c, err = redis.Dial("tcp", rc.conninfo) + if err != nil { + return nil, err + } + + if rc.password != "" { + if _, err := c.Do("AUTH", rc.password); err != nil { + c.Close() + return nil, err + } + } + _, selecterr := c.Do("SELECT", rc.dbNum) if selecterr != nil { c.Close() From 970f0b460c63b483def912ca605e3269ae650e59 Mon Sep 17 00:00:00 2001 From: "weizili.build17" Date: Sun, 7 Jun 2015 21:33:01 +0800 Subject: [PATCH 119/436] Add GetMulti method for Cache interface --- cache/cache.go | 2 + cache/cache_test.go | 51 ++++++++++++++- cache/file.go | 10 +++ cache/memcache/memcache.go | 26 ++++++++ cache/memcache/memcache_test.go | 106 ++++++++++++++++++++++++++++++++ cache/memory.go | 10 +++ cache/redis/redis.go | 33 ++++++++++ cache/redis/redis_test.go | 21 +++++++ 8 files changed, 258 insertions(+), 1 deletion(-) create mode 100644 cache/memcache/memcache_test.go diff --git a/cache/cache.go b/cache/cache.go index ddb2f857..7ca87802 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -47,6 +47,8 @@ import ( type Cache interface { // get cached value by key. Get(key string) interface{} + // GetMulti is a batch version of Get. + GetMulti(keys []string) []interface{} // set cached value with key and expire time. Put(key string, val interface{}, timeout int64) error // delete cached value by key. diff --git a/cache/cache_test.go b/cache/cache_test.go index 7c43e539..481309fd 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -65,6 +65,35 @@ func TestCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + + //test GetMulti + if err = bm.Put("astaxie", "author", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + if v := bm.Get("astaxie"); v.(string) != "author" { + t.Error("get err") + } + + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } } func TestFileCache(t *testing.T) { @@ -102,6 +131,7 @@ func TestFileCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + //test string if err = bm.Put("astaxie", "author", 10); err != nil { t.Error("set Error", err) @@ -109,9 +139,28 @@ func TestFileCache(t *testing.T) { if !bm.IsExist("astaxie") { t.Error("check err") } - if v := bm.Get("astaxie"); v.(string) != "author" { t.Error("get err") } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } + os.RemoveAll("cache") } diff --git a/cache/file.go b/cache/file.go index bbbbbad2..65f114f3 100644 --- a/cache/file.go +++ b/cache/file.go @@ -132,6 +132,16 @@ func (fc *FileCache) Get(key string) interface{} { return to.Data } +// GetMulti gets values from file cache. +// if non-exist or expired, return empty string. +func (fc *FileCache) GetMulti(keys []string) []interface{} { + var rc []interface{} + for _, key := range keys { + rc = append(rc, fc.Get(key)) + } + return rc +} + // Put value into file cache. // timeout means how long to keep this file, unit of ms. // if timeout equals FileCacheEmbedExpiry(default is 0), cache this item forever. diff --git a/cache/memcache/memcache.go b/cache/memcache/memcache.go index f5a5c6ef..c6829054 100644 --- a/cache/memcache/memcache.go +++ b/cache/memcache/memcache.go @@ -63,6 +63,32 @@ func (rc *MemcacheCache) Get(key string) interface{} { return nil } +// get value from memcache. +func (rc *MemcacheCache) GetMulti(keys []string) []interface{} { + size := len(keys) + var rv []interface{} + if rc.conn == nil { + if err := rc.connectInit(); err != nil { + for i := 0; i < size; i++ { + rv = append(rv, err) + } + return rv + } + } + mv, err := rc.conn.GetMulti(keys) + if err == nil { + for _, v := range mv { + rv = append(rv, string(v.Value)) + } + return rv + } else { + for i := 0; i < size; i++ { + rv = append(rv, err) + } + return rv + } +} + // put value to memcache. only support string. func (rc *MemcacheCache) Put(key string, val interface{}, timeout int64) error { if rc.conn == nil { diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go new file mode 100644 index 00000000..0523ae85 --- /dev/null +++ b/cache/memcache/memcache_test.go @@ -0,0 +1,106 @@ +// 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 memcache + +import ( + _ "github.com/bradfitz/gomemcache/memcache" + + "github.com/astaxie/beego/cache" + "strconv" + "testing" + "time" +) + +func TestRedisCache(t *testing.T) { + bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`) + if err != nil { + t.Error("init err") + } + if err = bm.Put("astaxie", "1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + + time.Sleep(10 * time.Second) + + if bm.IsExist("astaxie") { + t.Error("check err") + } + if err = bm.Put("astaxie", "1", 10); err != nil { + t.Error("set Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + t.Error("get err") + } + + if err = bm.Incr("astaxie"); err != nil { + t.Error("Incr Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 2 { + t.Error("get err") + } + + if err = bm.Decr("astaxie"); err != nil { + t.Error("Decr Error", err) + } + + if v, err := strconv.Atoi(bm.Get("astaxie").(string)); err != nil || v != 1 { + t.Error("get err") + } + bm.Delete("astaxie") + if bm.IsExist("astaxie") { + t.Error("delete err") + } + + //test string + if err = bm.Put("astaxie", "author", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie") { + t.Error("check err") + } + + if v := bm.Get("astaxie").(string); v != "author" { + t.Error("get err") + } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if vv[0].(string) != "author" { + t.Error("GetMulti ERROR") + } + if vv[1].(string) != "author1" { + t.Error("GetMulti ERROR") + } + + // test clear all + if err = bm.ClearAll(); err != nil { + t.Error("clear all err") + } +} diff --git a/cache/memory.go b/cache/memory.go index 0233be7d..b6657048 100644 --- a/cache/memory.go +++ b/cache/memory.go @@ -64,6 +64,16 @@ func (bc *MemoryCache) Get(name string) interface{} { return nil } +// GetMulti gets caches from memory. +// if non-existed or expired, return nil. +func (bc *MemoryCache) GetMulti(names []string) []interface{} { + var rc []interface{} + for _, name := range names { + rc = append(rc, bc.Get(name)) + } + return rc +} + // Put cache to memory. // if expired is 0, it will be cleaned by next gc operation ( default gc clock is 1 minute). func (bc *MemoryCache) Put(name string, value interface{}, expired int64) error { diff --git a/cache/redis/redis.go b/cache/redis/redis.go index 0e07eaed..9f28786c 100644 --- a/cache/redis/redis.go +++ b/cache/redis/redis.go @@ -74,6 +74,39 @@ func (rc *RedisCache) Get(key string) interface{} { return nil } +// GetMulti get cache from redis. +func (rc *RedisCache) GetMulti(keys []string) []interface{} { + size := len(keys) + var rv []interface{} + c := rc.p.Get() + defer c.Close() + var err error + for _, key := range keys { + err = c.Send("GET", key) + if err != nil { + goto ERROR + } + } + 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 cache to redis. func (rc *RedisCache) Put(key string, val interface{}, timeout int64) error { var err error diff --git a/cache/redis/redis_test.go b/cache/redis/redis_test.go index fbe82ac5..1f74fd27 100644 --- a/cache/redis/redis_test.go +++ b/cache/redis/redis_test.go @@ -67,6 +67,7 @@ func TestRedisCache(t *testing.T) { if bm.IsExist("astaxie") { t.Error("delete err") } + //test string if err = bm.Put("astaxie", "author", 10); err != nil { t.Error("set Error", err) @@ -78,6 +79,26 @@ func TestRedisCache(t *testing.T) { if v, _ := redis.String(bm.Get("astaxie"), err); v != "author" { t.Error("get err") } + + //test GetMulti + if err = bm.Put("astaxie1", "author1", 10); err != nil { + t.Error("set Error", err) + } + if !bm.IsExist("astaxie1") { + t.Error("check err") + } + + vv := bm.GetMulti([]string{"astaxie", "astaxie1"}) + if len(vv) != 2 { + t.Error("GetMulti ERROR") + } + if v, _ := redis.String(vv[0], nil); v != "author" { + t.Error("GetMulti ERROR") + } + if v, _ := redis.String(vv[1], nil); v != "author1" { + t.Error("GetMulti ERROR") + } + // test clear all if err = bm.ClearAll(); err != nil { t.Error("clear all err") From e1d7bc88265bfae3b177fb28ccd77f14ad9255d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A9=BA=E8=A7=81?= Date: Mon, 8 Jun 2015 17:25:46 +0800 Subject: [PATCH 120/436] remove space after int()& add sort for commentsRouter file --- parser.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/parser.go b/parser.go index be91b4cb..cf8dee22 100644 --- a/parser.go +++ b/parser.go @@ -24,6 +24,7 @@ import ( "io/ioutil" "os" "path" + "sort" "strings" "github.com/astaxie/beego/utils" @@ -36,7 +37,7 @@ import ( ) func init() { - {{.globalinfo}} +{{.globalinfo}} } ` @@ -129,7 +130,13 @@ func genRouterCode() { os.Mkdir(path.Join(workPath, "routers"), 0755) Info("generate router from comments") var globalinfo string - for k, cList := range genInfoList { + sortKey := make([]string, 0) + for k, _ := range genInfoList { + sortKey = append(sortKey, k) + } + sort.Strings(sortKey) + for _, k := range sortKey { + cList := genInfoList[k] for _, c := range cList { allmethod := "nil" if len(c.AllowHTTPMethods) > 0 { From 364cacf6593887728e2fca7c69eae57509ba8a33 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 8 Jun 2015 22:00:28 +0800 Subject: [PATCH 121/436] record the critical logs in Prod --- router.go | 57 +++++++++++++++++++------------------------------------ 1 file changed, 19 insertions(+), 38 deletions(-) diff --git a/router.go b/router.go index e9f45615..2d5ed78d 100644 --- a/router.go +++ b/router.go @@ -857,48 +857,29 @@ func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { if err == USERSTOPRUN { return } - if RunMode == "dev" { - if !RecoverPanic { - panic(err) - } else { - if ErrorsShow { - 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)) - } - showErr(err, context, stack) - } + if !RecoverPanic { + panic(err) } else { - if !RecoverPanic { - panic(err) - } else { - // in production model show all infomation - if ErrorsShow { + if ErrorsShow { + if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { exception(fmt.Sprint(err), context) - } else { - 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)) - } + 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 RunMode == "dev" { + showErr(err, context, stack) + } } } } From bbb6f31f1604abf411abbcef6ce452da6188103f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A9=BA=E8=A7=81?= Date: Tue, 9 Jun 2015 10:18:21 +0800 Subject: [PATCH 122/436] support eq&ne for orm --- orm/db.go | 2 ++ orm/db_mysql.go | 2 ++ orm/db_postgres.go | 2 ++ orm/db_sqlite.go | 2 ++ 4 files changed, 8 insertions(+) diff --git a/orm/db.go b/orm/db.go index 060d83bc..20dc80f2 100644 --- a/orm/db.go +++ b/orm/db.go @@ -44,6 +44,8 @@ var ( "gte": true, "lt": true, "lte": true, + "eq": true, + "nq": true, "startswith": true, "endswith": true, "istartswith": true, diff --git a/orm/db_mysql.go b/orm/db_mysql.go index 3c2ad3a4..182914a2 100644 --- a/orm/db_mysql.go +++ b/orm/db_mysql.go @@ -30,6 +30,8 @@ var mysqlOperators = map[string]string{ "gte": ">= ?", "lt": "< ?", "lte": "<= ?", + "eq": "= ?", + "ne": "!= ?", "startswith": "LIKE BINARY ?", "endswith": "LIKE BINARY ?", "istartswith": "LIKE ?", diff --git a/orm/db_postgres.go b/orm/db_postgres.go index 296ee6a0..6500ef52 100644 --- a/orm/db_postgres.go +++ b/orm/db_postgres.go @@ -29,6 +29,8 @@ var postgresOperators = map[string]string{ "gte": ">= ?", "lt": "< ?", "lte": "<= ?", + "eq": "= ?", + "ne": "!= ?", "startswith": "LIKE ?", "endswith": "LIKE ?", "istartswith": "LIKE UPPER(?)", diff --git a/orm/db_sqlite.go b/orm/db_sqlite.go index 0a2f32c8..c2dcbb46 100644 --- a/orm/db_sqlite.go +++ b/orm/db_sqlite.go @@ -29,6 +29,8 @@ var sqliteOperators = map[string]string{ "gte": ">= ?", "lt": "< ?", "lte": "<= ?", + "eq": "= ?", + "ne": "!= ?", "startswith": "LIKE ? ESCAPE '\\'", "endswith": "LIKE ? ESCAPE '\\'", "istartswith": "LIKE ? ESCAPE '\\'", From d943d16d522cefb97ae2735f1e3dd8b41e6d8641 Mon Sep 17 00:00:00 2001 From: ziyel Date: Wed, 10 Jun 2015 21:26:04 +0800 Subject: [PATCH 123/436] gofmt --- router.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/router.go b/router.go index 232f67d2..35b442ab 100644 --- a/router.go +++ b/router.go @@ -612,9 +612,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if l, ok := p.filters[pos]; ok { for _, filterR := range l { if ok, p := filterR.ValidRouter(urlPath); ok { - for k, v := range context.Input.Params { - p[k] = v - } + for k, v := range context.Input.Params { + p[k] = v + } context.Input.Params = p filterR.filterFunc(context) if filterR.returnOnOutput && w.started { From 9c6775c22ca7c7343d3236a383ab6189ecf7b009 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 13 Jun 2015 00:25:21 +0800 Subject: [PATCH 124/436] log default use synchro, and support async --- logs/log.go | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/logs/log.go b/logs/log.go index 5f5f1cea..5c94a9d8 100644 --- a/logs/log.go +++ b/logs/log.go @@ -92,6 +92,7 @@ type BeeLogger struct { level int enableFuncCallDepth bool loggerFuncCallDepth int + asynchronous bool msg chan *logMsg outputs map[string]LoggerInterface } @@ -110,7 +111,11 @@ func NewLogger(channellen int64) *BeeLogger { bl.loggerFuncCallDepth = 2 bl.msg = make(chan *logMsg, channellen) bl.outputs = make(map[string]LoggerInterface) - //bl.SetLogger("console", "") // default output to console + return bl +} + +func (bl *BeeLogger) Async() *BeeLogger { + bl.asynchronous = true go bl.startLogger() return bl } @@ -164,7 +169,17 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { } else { lm.msg = msg } - bl.msg <- lm + if bl.asynchronous { + bl.msg <- lm + } else { + for name, l := range bl.outputs { + err := l.WriteMsg(lm.msg, lm.level) + if err != nil { + fmt.Println("unable to WriteMsg to adapter:", name, err) + return err + } + } + } return nil } From d9bb1a359278d87e095d0d5f8745c08d6a5595d3 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 13 Jun 2015 00:25:48 +0800 Subject: [PATCH 125/436] logs support elasticsearch adapter --- logs/es/es.go | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 logs/es/es.go diff --git a/logs/es/es.go b/logs/es/es.go new file mode 100644 index 00000000..3a73d4dd --- /dev/null +++ b/logs/es/es.go @@ -0,0 +1,76 @@ +package es + +import ( + "encoding/json" + "errors" + "fmt" + "net" + "net/url" + "time" + + "github.com/astaxie/beego/logs" + "github.com/belogik/goes" +) + +func NewES() logs.LoggerInterface { + cw := &esLogger{ + Level: logs.LevelDebug, + } + return cw +} + +type esLogger struct { + *goes.Connection + DSN string `json:"dsn"` + Level int `json:"level"` +} + +// {"dsn":"http://localhost:9200/","level":1} +func (el *esLogger) Init(jsonconfig string) error { + err := json.Unmarshal([]byte(jsonconfig), el) + if err != nil { + return err + } + if el.DSN == "" { + return errors.New("empty dsn") + } else if u, err := url.Parse(el.DSN); err != nil { + return err + } else if u.Path == "" { + return errors.New("missing prefix") + } else if host, port, err := net.SplitHostPort(u.Host); err != nil { + return err + } else { + conn := goes.NewConnection(host, port) + el.Connection = conn + } + return nil +} + +func (el *esLogger) WriteMsg(msg string, level int) error { + if level > el.Level { + return nil + } + t := time.Now() + vals := make(map[string]interface{}) + vals["@timestamp"] = t.Format(time.RFC3339) + vals["@msg"] = msg + d := goes.Document{ + Index: fmt.Sprintf("%04d.%02d.%02d", t.Year(), t.Month(), t.Day()), + Type: "logs", + Fields: vals, + } + _, err := el.Index(d, nil) + return err +} + +func (el *esLogger) Destroy() { + +} + +func (el *esLogger) Flush() { + +} + +func init() { + logs.Register("es", NewES) +} From 491238ce7d698f7a0ab65b8d820edb11339bcd3f Mon Sep 17 00:00:00 2001 From: "MrLee.Kun" Date: Sat, 13 Jun 2015 01:04:46 +0800 Subject: [PATCH 126/436] Don't overwrite the params from function ValidRouter just add new params to context.Input.Params --- router.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/router.go b/router.go index 8afb9ee2..4d195bc8 100644 --- a/router.go +++ b/router.go @@ -612,10 +612,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if l, ok := p.filters[pos]; ok { for _, filterR := range l { if ok, p := filterR.ValidRouter(urlPath); ok { - for k, v := range context.Input.Params { - p[k] = v + for k, v := range p { + context.Input.Params[k] = v } - context.Input.Params = p filterR.filterFunc(context) if filterR.returnOnOutput && w.started { return true From b776e43962c4a4761bb232f763b20a8d6372ff33 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 13 Jun 2015 11:15:13 +0800 Subject: [PATCH 127/436] merge bat/httplib to httplib --- httplib/httplib.go | 127 ++++++++++++++++++++++++++++++--------------- 1 file changed, 84 insertions(+), 43 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 2389a7cb..68c22d70 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -36,7 +36,6 @@ import ( "crypto/tls" "encoding/json" "encoding/xml" - "fmt" "io" "io/ioutil" "log" @@ -48,26 +47,50 @@ import ( "net/url" "os" "strings" + "sync" "time" ) -var defaultSetting = BeegoHttpSettings{UserAgent: "beegoServer", ConnectTimeout: 60 * time.Second, ReadWriteTimeout: 60 * time.Second, Gzip: true} +var defaultSetting = BeegoHttpSettings{ + UserAgent: "beegoServer", + ConnectTimeout: 60 * time.Second, + ReadWriteTimeout: 60 * time.Second, + Gzip: true, + DumpBody: true, +} + var defaultCookieJar http.CookieJar +var settingMutex sync.Mutex // createDefaultCookie creates a global cookiejar to store cookies. func createDefaultCookie() { + settingMutex.Lock() + defer settingMutex.Unlock() defaultCookieJar, _ = cookiejar.New(nil) } // Overwrite default settings func SetDefaultSetting(setting BeegoHttpSettings) { + settingMutex.Lock() + defer settingMutex.Unlock() defaultSetting = setting + if defaultSetting.ConnectTimeout == 0 { + defaultSetting.ConnectTimeout = 60 * time.Second + } + if defaultSetting.ReadWriteTimeout == 0 { + defaultSetting.ReadWriteTimeout = 60 * time.Second + } } // return *BeegoHttpRequest with specific method -func newBeegoRequest(url, method string) *BeegoHttpRequest { +func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest { var resp http.Response + u, err := url.Parse(rawurl) + if err != nil { + log.Fatal(err) + } req := http.Request{ + URL: u, Method: method, Header: make(http.Header), Proto: "HTTP/1.1", @@ -75,7 +98,7 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { ProtoMinor: 1, } return &BeegoHttpRequest{ - url: url, + url: rawurl, req: &req, params: map[string]string{}, files: map[string]string{}, @@ -86,27 +109,27 @@ func newBeegoRequest(url, method string) *BeegoHttpRequest { // Get returns *BeegoHttpRequest with GET method. func Get(url string) *BeegoHttpRequest { - return newBeegoRequest(url, "GET") + return NewBeegoRequest(url, "GET") } // Post returns *BeegoHttpRequest with POST method. func Post(url string) *BeegoHttpRequest { - return newBeegoRequest(url, "POST") + return NewBeegoRequest(url, "POST") } // Put returns *BeegoHttpRequest with PUT method. func Put(url string) *BeegoHttpRequest { - return newBeegoRequest(url, "PUT") + return NewBeegoRequest(url, "PUT") } // Delete returns *BeegoHttpRequest DELETE method. func Delete(url string) *BeegoHttpRequest { - return newBeegoRequest(url, "DELETE") + return NewBeegoRequest(url, "DELETE") } // Head returns *BeegoHttpRequest with HEAD method. func Head(url string) *BeegoHttpRequest { - return newBeegoRequest(url, "HEAD") + return NewBeegoRequest(url, "HEAD") } // BeegoHttpSettings @@ -120,6 +143,7 @@ type BeegoHttpSettings struct { Transport http.RoundTripper EnableCookie bool Gzip bool + DumpBody bool } // BeegoHttpRequest provides more useful methods for requesting one url than http.Request. @@ -134,6 +158,11 @@ type BeegoHttpRequest struct { dump []byte } +// get request +func (b *BeegoHttpRequest) GetRequest() *http.Request { + return b.req +} + // Change request settings func (b *BeegoHttpRequest) Setting(setting BeegoHttpSettings) *BeegoHttpRequest { b.setting = setting @@ -153,14 +182,20 @@ func (b *BeegoHttpRequest) SetEnableCookie(enable bool) *BeegoHttpRequest { } // SetUserAgent sets User-Agent header field -func (b *BeegoHttpRequest) SetUserAgent(userAgent string) *BeegoHttpRequest { - b.setting.UserAgent = userAgent +func (b *BeegoHttpRequest) SetUserAgent(useragent string) *BeegoHttpRequest { + b.setting.UserAgent = useragent return b } // Debug sets show debug or not when executing request. -func (b *BeegoHttpRequest) Debug(isDebug bool) *BeegoHttpRequest { - b.setting.ShowDebug = isDebug +func (b *BeegoHttpRequest) Debug(isdebug bool) *BeegoHttpRequest { + b.setting.ShowDebug = isdebug + return b +} + +// Dump Body. +func (b *BeegoHttpRequest) DumpBody(isdump bool) *BeegoHttpRequest { + b.setting.DumpBody = isdump return b } @@ -279,21 +314,18 @@ func (b *BeegoHttpRequest) JsonBody(obj interface{}) (*BeegoHttpRequest, error) } func (b *BeegoHttpRequest) buildUrl(paramBody string) { - if paramBody == "" { - return - } // build GET url with query string - if b.req.Method == "GET" { - if strings.Index(b.url, "?") == -1 { - b.url = b.url + "?" + paramBody - } else { + if b.req.Method == "GET" && len(paramBody) > 0 { + if strings.Index(b.url, "?") != -1 { b.url += "&" + paramBody + } else { + b.url = b.url + "?" + paramBody } return } - // build POST url and body - if b.req.Method == "POST" && b.req.Body == nil { + // build POST/PUT/PATCH url and body + if (b.req.Method == "POST" || b.req.Method == "PUT" || b.req.Method == "PATCH") && b.req.Body == nil { // with files if len(b.files) > 0 { pr, pw := io.Pipe() @@ -338,16 +370,29 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { if b.resp.StatusCode != 0 { return b.resp, nil } + resp, err := b.SendOut() + if err != nil { + return nil, err + } + b.resp = resp + return resp, nil +} + +func (b *BeegoHttpRequest) SendOut() (*http.Response, error) { var paramBody string if len(b.params) > 0 { + var buf bytes.Buffer for k, v := range b.params { - paramBody += fmt.Sprintf("&%s=%v", url.QueryEscape(k), url.QueryEscape(v)) + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + buf.WriteByte('&') } - paramBody = paramBody[1:] + paramBody = buf.String() + paramBody = paramBody[0 : len(paramBody)-1] } b.buildUrl(paramBody) - url, err := url.Parse(b.url) if err != nil { return nil, err @@ -357,13 +402,6 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { trans := b.setting.Transport - if b.setting.ConnectTimeout == 0 { - b.setting.ConnectTimeout = 60 * time.Second - } - if b.setting.ReadWriteTimeout == 0 { - b.setting.ReadWriteTimeout = 60 * time.Second - } - if trans == nil { // create default transport trans = &http.Transport{ @@ -404,15 +442,13 @@ func (b *BeegoHttpRequest) getResponse() (*http.Response, error) { } if b.setting.ShowDebug { - dump, err := httputil.DumpRequest(b.req, true) + dump, err := httputil.DumpRequest(b.req, b.setting.DumpBody) if err != nil { log.Println(err.Error()) } b.dump = dump } - - b.resp, err = client.Do(b.req) - return b.resp, err + return client.Do(b.req) } // String returns the body string in response. @@ -433,9 +469,12 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { return b.body, nil } resp, err := b.getResponse() - if resp == nil || resp.Body == nil { + if err != nil { return nil, err } + if resp.Body == nil { + return nil, nil + } defer resp.Body.Close() if b.setting.Gzip && resp.Header.Get("Content-Encoding") == "gzip" { reader, err := gzip.NewReader(resp.Body) @@ -452,18 +491,20 @@ func (b *BeegoHttpRequest) Bytes() ([]byte, error) { // ToFile saves the body data in response to one file. // it calls Response inner. func (b *BeegoHttpRequest) ToFile(filename string) error { - resp, err := b.getResponse() - if resp == nil || resp.Body == nil { - return err - } - defer resp.Body.Close() - f, err := os.Create(filename) if err != nil { return err } defer f.Close() + resp, err := b.getResponse() + if err != nil { + return err + } + if resp.Body == nil { + return nil + } + defer resp.Body.Close() _, err = io.Copy(f, resp.Body) return err } From e619d839901efc0120af07e25c47eff8e824fc46 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 13 Jun 2015 12:47:01 +0800 Subject: [PATCH 128/436] fix the filter router issues --- router.go | 14 ++++++++------ router_test.go | 6 +++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/router.go b/router.go index 4d195bc8..ca6d917a 100644 --- a/router.go +++ b/router.go @@ -611,19 +611,21 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { - if ok, p := filterR.ValidRouter(urlPath); ok { - for k, v := range p { + if filterR.returnOnOutput && w.started { + return true + } + if ok, params := filterR.ValidRouter(urlPath); ok { + for k, v := range params { context.Input.Params[k] = v } filterR.filterFunc(context) - if filterR.returnOnOutput && w.started { - return true - } + } + if filterR.returnOnOutput && w.started { + return true } } } } - return false } diff --git a/router_test.go b/router_test.go index ee712167..005f32d6 100644 --- a/router_test.go +++ b/router_test.go @@ -444,7 +444,7 @@ func TestFilterAfterExec(t *testing.T) { mux := NewControllerRegister() mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput) mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput) - mux.InsertFilter(url, AfterExec, beegoAfterExec1) + mux.InsertFilter(url, AfterExec, beegoAfterExec1, false) mux.Get(url, beegoFilterFunc) @@ -506,7 +506,7 @@ func TestFilterFinishRouterMultiFirstOnly(t *testing.T) { url := "/finishRouterMultiFirstOnly" mux := NewControllerRegister() - mux.InsertFilter(url, FinishRouter, beegoFinishRouter1) + mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false) mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) mux.Get(url, beegoFilterFunc) @@ -534,7 +534,7 @@ func TestFilterFinishRouterMulti(t *testing.T) { mux := NewControllerRegister() mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, false) - mux.InsertFilter(url, FinishRouter, beegoFinishRouter2) + mux.InsertFilter(url, FinishRouter, beegoFinishRouter2, false) mux.Get(url, beegoFilterFunc) From c143a6ec19accb1be464666e197049dddc276275 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sat, 13 Jun 2015 16:20:26 +0800 Subject: [PATCH 129/436] fix #1090 add Getfiles to support mulit file upload --- controller.go | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/controller.go b/controller.go index 81ee1230..9011593a 100644 --- a/controller.go +++ b/controller.go @@ -499,6 +499,41 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, return c.Ctx.Request.FormFile(key) } +// GetFiles return multi-upload files +// files, err:=c.Getfiles("myfiles") +// if err != nil { +// http.Error(w, err.Error(), http.StatusNoContent) +// return +// } +// for i, _ := range files { +// //for each fileheader, get a handle to the actual file +// file, err := files[i].Open() +// defer file.Close() +// if err != nil { +// http.Error(w, err.Error(), http.StatusInternalServerError) +// return +// } +// //create destination file making sure the path is writeable. +// dst, err := os.Create("upload/" + files[i].Filename) +// defer dst.Close() +// if err != nil { +// http.Error(w, err.Error(), http.StatusInternalServerError) +// return +// } +// //copy the uploaded file to the destination file +// if _, err := io.Copy(dst, file); err != nil { +// http.Error(w, err.Error(), http.StatusInternalServerError) +// return +// } +// } +func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) { + files, ok := c.Ctx.Request.MultipartForm.File["key"] + if ok { + return files, nil + } + return nil, http.ErrMissingFile +} + // SaveToFile saves uploaded file to new path. // it only operates the first one of mutil-upload form file field. func (c *Controller) SaveToFile(fromfile, tofile string) error { From 2af0c569a578aed91df53c1c3bebd53fd8e5062c Mon Sep 17 00:00:00 2001 From: "MrLee.Kun" Date: Sun, 14 Jun 2015 01:02:41 +0800 Subject: [PATCH 130/436] The last filterFunc with returnOnOutput=ture won't be executed ex: beego.InsertFilter("/*", beego.BeforeExec, FilterLoginCheck1,false) beego.InsertFilter("/*", beego.BeforeExec, FilterLoginCheck2) In function FilterLoginCheck1 , I'll write data via ResponseWriter, and w.started = true FilterLoginCheck2 won't be executed, it should be. --- router.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/router.go b/router.go index ca6d917a..688b227d 100644 --- a/router.go +++ b/router.go @@ -611,9 +611,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { - if filterR.returnOnOutput && w.started { - return true - } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { context.Input.Params[k] = v From fc11169ee38c7698e84e4e1af2c3e9f1e847215f Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 14 Jun 2015 01:13:34 +0800 Subject: [PATCH 131/436] Revert "fix multiple filters execute issue" --- router.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/router.go b/router.go index 688b227d..ca6d917a 100644 --- a/router.go +++ b/router.go @@ -611,6 +611,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { + if filterR.returnOnOutput && w.started { + return true + } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { context.Input.Params[k] = v From b275d7c6f5800be132a3f37734bf91e693726759 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 14 Jun 2015 01:14:33 +0800 Subject: [PATCH 132/436] Revert "Revert "fix multiple filters execute issue"" --- router.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/router.go b/router.go index ca6d917a..688b227d 100644 --- a/router.go +++ b/router.go @@ -611,9 +611,6 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { - if filterR.returnOnOutput && w.started { - return true - } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { context.Input.Params[k] = v From 64d4f6518b4558cd1c10a591705ba9ea6471dbd6 Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 14 Jun 2015 18:10:10 +0800 Subject: [PATCH 133/436] fix #1213 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 9c34c9f9..1ee4c65f 100644 --- a/beego.go +++ b/beego.go @@ -336,7 +336,7 @@ func initBeforeHttpRun() { // this function is for test package init func TestBeegoInit(apppath string) { AppPath = apppath - RunMode = "test" + os.Setenv("BEEGO_RUNMODE", "test") AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") err := ParseConfig() if err != nil && !os.IsNotExist(err) { From ba1232dfafeffd52cd86cb002b61bd44d8e0fdff Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 14 Jun 2015 18:35:46 +0800 Subject: [PATCH 134/436] filter should be always the same --- router.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/router.go b/router.go index 688b227d..ca6d917a 100644 --- a/router.go +++ b/router.go @@ -611,6 +611,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { + if filterR.returnOnOutput && w.started { + return true + } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { context.Input.Params[k] = v From 817650aa3341cf9ce21fa9cc416c6c9cf99118e9 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Jun 2015 20:20:37 +0800 Subject: [PATCH 135/436] keep the short name for logs --- logs/log.go | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/logs/log.go b/logs/log.go index 5c94a9d8..a7f45956 100644 --- a/logs/log.go +++ b/logs/log.go @@ -298,21 +298,33 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { // // Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. func (bl *BeeLogger) Warn(format string, v ...interface{}) { - bl.Warning(format, v...) + if LevelWarning > bl.level { + return + } + msg := fmt.Sprintf("[W] "+format, v...) + bl.writerMsg(LevelWarning, msg) } // Log INFO level message. // // Deprecated: compatibility alias for Informational(), Will be removed in 1.5.0. func (bl *BeeLogger) Info(format string, v ...interface{}) { - bl.Informational(format, v...) + if LevelInformational > bl.level { + return + } + msg := fmt.Sprintf("[I] "+format, v...) + bl.writerMsg(LevelInformational, msg) } // Log TRACE level message. // // Deprecated: compatibility alias for Debug(), Will be removed in 1.5.0. func (bl *BeeLogger) Trace(format string, v ...interface{}) { - bl.Debug(format, v...) + if LevelDebug > bl.level { + return + } + msg := fmt.Sprintf("[D] "+format, v...) + bl.writerMsg(LevelDebug, msg) } // flush all chan data. From d629c1d3d0142661c687137fd0ef5aaa3848ecb5 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Jun 2015 20:22:05 +0800 Subject: [PATCH 136/436] change the comments --- logs/log.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/logs/log.go b/logs/log.go index a7f45956..49899a48 100644 --- a/logs/log.go +++ b/logs/log.go @@ -295,8 +295,7 @@ func (bl *BeeLogger) Debug(format string, v ...interface{}) { } // Log WARN level message. -// -// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. +// compatibility alias for Warning() func (bl *BeeLogger) Warn(format string, v ...interface{}) { if LevelWarning > bl.level { return @@ -306,8 +305,7 @@ func (bl *BeeLogger) Warn(format string, v ...interface{}) { } // Log INFO level message. -// -// Deprecated: compatibility alias for Informational(), Will be removed in 1.5.0. +// compatibility alias for Informational() func (bl *BeeLogger) Info(format string, v ...interface{}) { if LevelInformational > bl.level { return @@ -317,8 +315,7 @@ func (bl *BeeLogger) Info(format string, v ...interface{}) { } // Log TRACE level message. -// -// Deprecated: compatibility alias for Debug(), Will be removed in 1.5.0. +// compatibility alias for Debug() func (bl *BeeLogger) Trace(format string, v ...interface{}) { if LevelDebug > bl.level { return From 0207caab6f3af311b8630ab4d0c74096e1ec3053 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Jun 2015 20:44:14 +0800 Subject: [PATCH 137/436] keep the shortname for logs info/warn/debug --- log.go | 10 +++++----- staticfile.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/log.go b/log.go index 5afba8ed..7949ed96 100644 --- a/log.go +++ b/log.go @@ -78,9 +78,9 @@ func Warning(v ...interface{}) { BeeLogger.Warning(generateFmtStr(len(v)), v...) } -// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. +// compatibility alias for Warning() func Warn(v ...interface{}) { - Warning(v...) + BeeLogger.Warn(generateFmtStr(len(v)), v...) } func Notice(v ...interface{}) { @@ -92,9 +92,9 @@ func Informational(v ...interface{}) { BeeLogger.Informational(generateFmtStr(len(v)), v...) } -// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. +// compatibility alias for Warning() func Info(v ...interface{}) { - Informational(v...) + BeeLogger.Info(generateFmtStr(len(v)), v...) } // Debug logs a message at debug level. @@ -103,7 +103,7 @@ func Debug(v ...interface{}) { } // Trace logs a message at trace level. -// Deprecated: compatibility alias for Warning(), Will be removed in 1.5.0. +// compatibility alias for Warning() func Trace(v ...interface{}) { BeeLogger.Trace(generateFmtStr(len(v)), v...) } diff --git a/staticfile.go b/staticfile.go index 5ab853a3..7c1ed98c 100644 --- a/staticfile.go +++ b/staticfile.go @@ -58,7 +58,7 @@ func serverStaticRouter(ctx *context.Context) { finfo, err := os.Stat(file) if err != nil { if RunMode == "dev" { - Warn(err) + Warn("Can't find the file:", file, err) } http.NotFound(ctx.ResponseWriter, ctx.Request) return From c8f6e0f1560b1ee52d45a5b6b53ad07d2448bf62 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Jun 2015 20:53:49 +0800 Subject: [PATCH 138/436] remove the hardcode in runtime.Caller --- logs/log.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/logs/log.go b/logs/log.go index 49899a48..cebbc737 100644 --- a/logs/log.go +++ b/logs/log.go @@ -157,15 +157,12 @@ func (bl *BeeLogger) writerMsg(loglevel int, msg string) error { lm.level = loglevel if bl.enableFuncCallDepth { _, file, line, ok := runtime.Caller(bl.loggerFuncCallDepth) - if _, filename := path.Split(file); filename == "log.go" && (line == 97 || line == 83) { - _, file, line, ok = runtime.Caller(bl.loggerFuncCallDepth + 1) - } - if ok { - _, filename := path.Split(file) - lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) - } else { - lm.msg = msg + if !ok { + file = "???" + line = 0 } + _, filename := path.Split(file) + lm.msg = fmt.Sprintf("[%s:%d] %s", filename, line, msg) } else { lm.msg = msg } From 8f7246e17b504c858592a28a87e96fa7537a5aaf Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 15 Jun 2015 23:49:13 +0800 Subject: [PATCH 139/436] change to version 1.5.0 --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index 1ee4c65f..cfebfbea 100644 --- a/beego.go +++ b/beego.go @@ -37,7 +37,7 @@ import ( ) // beego web framework version. -const VERSION = "1.4.3" +const VERSION = "1.5.0" type hookfunc func() error //hook function to run var hooks []hookfunc //hook function slice to store the hookfunc From f7f390dfecc5d2c37e5457e9789b8962c78a4403 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 16 Jun 2015 14:53:38 +0800 Subject: [PATCH 140/436] fix #1221 --- controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controller.go b/controller.go index 9011593a..a8b9e8d3 100644 --- a/controller.go +++ b/controller.go @@ -527,7 +527,7 @@ func (c *Controller) GetFile(key string) (multipart.File, *multipart.FileHeader, // } // } func (c *Controller) GetFiles(key string) ([]*multipart.FileHeader, error) { - files, ok := c.Ctx.Request.MultipartForm.File["key"] + files, ok := c.Ctx.Request.MultipartForm.File[key] if ok { return files, nil } From 87e8bcc9bea41794117b9b34201b3fe261465af3 Mon Sep 17 00:00:00 2001 From: "MrLee.Kun" Date: Fri, 19 Jun 2015 11:19:35 +0800 Subject: [PATCH 141/436] fix FilterHandler crash issue Filter Handler will crash with error assignment to entry in nil map , params from function Tree.Match() maybe nil. --- router.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/router.go b/router.go index ca6d917a..3e1ebab3 100644 --- a/router.go +++ b/router.go @@ -616,6 +616,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { + if context.Input.Params == nil { + context.Input.Params = make(map[string]string) + } context.Input.Params[k] = v } filterR.filterFunc(context) @@ -699,7 +702,9 @@ func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) p[strconv.Itoa(k)] = v } } - context.Input.Params = p + if p != nil { + context.Input.Params = p + } } } From 002302818ddf31ff7e562f07758cf82310d4a9e9 Mon Sep 17 00:00:00 2001 From: Mawueli Kofi Adzoe Date: Wed, 1 Jul 2015 04:17:53 +0000 Subject: [PATCH 142/436] Fix #1237 Package description uses same text as the README. --- beego.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beego.go b/beego.go index cfebfbea..c89ceaf8 100644 --- a/beego.go +++ b/beego.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// beego is an open-source, high-performance, modularity, full-stack web framework +// beego is an open-source, high-performance, modular, full-stack web framework // // package main // From 06b25deab2aa76912289c212d63a68ea86b8fc58 Mon Sep 17 00:00:00 2001 From: SimMan Date: Sat, 4 Jul 2015 17:55:01 +0800 Subject: [PATCH 143/436] Update validators.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support virtual operators paragraph 170! --- validation/validators.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/validators.go b/validation/validators.go index 61b0414a..2dcec356 100644 --- a/validation/validators.go +++ b/validation/validators.go @@ -457,7 +457,7 @@ func (b Base64) GetLimitValue() interface{} { } // just for chinese mobile phone number -var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][67]|[4][579]))\\d{8}$") +var mobilePattern = regexp.MustCompile("^((\\+86)|(86))?(1(([35][0-9])|[8][0-9]|[7][067]|[4][579]))\\d{8}$") type Mobile struct { Match From 079993b9f722f17d0521fe6e2be4850d7b158d22 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 6 Jul 2015 13:54:14 +0800 Subject: [PATCH 144/436] fix #1245 --- context/input.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/context/input.go b/context/input.go index 1985df21..c8a8aaa9 100644 --- a/context/input.go +++ b/context/input.go @@ -353,7 +353,7 @@ func (input *BeegoInput) Bind(dest interface{}, key string) error { } func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { - rv := reflect.Zero(reflect.TypeOf(0)) + rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: val := input.Query(key) @@ -398,7 +398,7 @@ func (input *BeegoInput) bind(key string, typ reflect.Type) reflect.Value { } func (input *BeegoInput) bindValue(val string, typ reflect.Type) reflect.Value { - rv := reflect.Zero(reflect.TypeOf(0)) + rv := reflect.Zero(typ) switch typ.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: rv = input.bindInt(val, typ) From 8c0ad5ef8834c69920a8254403877826d1959c3f Mon Sep 17 00:00:00 2001 From: skycrab Date: Mon, 6 Jul 2015 21:12:03 +0800 Subject: [PATCH 145/436] fix example/chat i/o timeout --- example/chat/controllers/ws.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/example/chat/controllers/ws.go b/example/chat/controllers/ws.go index fc3917b3..9e405513 100644 --- a/example/chat/controllers/ws.go +++ b/example/chat/controllers/ws.go @@ -54,14 +54,13 @@ func (c *connection) readPump() { }() c.ws.SetReadLimit(maxMessageSize) c.ws.SetReadDeadline(time.Now().Add(readWait)) + c.ws.SetPongHandler(func(string) error { c.ws.SetReadDeadline(time.Now().Add(readWait)); return nil }) for { op, r, err := c.ws.NextReader() if err != nil { break } switch op { - case websocket.PongMessage: - c.ws.SetReadDeadline(time.Now().Add(readWait)) case websocket.TextMessage: message, err := ioutil.ReadAll(r) if err != nil { From 5612f61a9384e61279cf289faee2fceb667ccb5d Mon Sep 17 00:00:00 2001 From: Artem Nistratov Date: Wed, 8 Jul 2015 17:42:00 +0300 Subject: [PATCH 146/436] fix #671 --- orm/models_boot.go | 5 ++++- orm/models_info_f.go | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/orm/models_boot.go b/orm/models_boot.go index cb44bc05..5232a48d 100644 --- a/orm/models_boot.go +++ b/orm/models_boot.go @@ -269,7 +269,10 @@ func bootStrap() { if found == false { mForC: for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] { - if ffi.relModelInfo == mi { + conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough || + fi.relTable != "" && fi.relTable == ffi.relTable || + fi.relThrough == "" && fi.relTable == "" + if ffi.relModelInfo == mi && conditions { found = true fi.reverseField = ffi.reverseFieldInfoTwo.name diff --git a/orm/models_info_f.go b/orm/models_info_f.go index 84a0c024..01863aaa 100644 --- a/orm/models_info_f.go +++ b/orm/models_info_f.go @@ -223,6 +223,11 @@ checkType: break checkType case "many": fieldType = RelReverseMany + if tv := tags["rel_table"]; tv != "" { + fi.relTable = tv + } else if tv := tags["rel_through"]; tv != "" { + fi.relThrough = tv + } break checkType default: err = fmt.Errorf("error") From 59b903d557532abc228860fbf5fb93641fd1f834 Mon Sep 17 00:00:00 2001 From: Hubery Date: Wed, 15 Jul 2015 17:00:48 +0800 Subject: [PATCH 147/436] set DoRotate fname like xx.2013-01-01.2.log fix fname,by extension to identify the file type on mac and windows. --- logs/file.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/logs/file.go b/logs/file.go index 2d3449ce..84a8c4f3 100644 --- a/logs/file.go +++ b/logs/file.go @@ -205,15 +205,20 @@ func (w *FileLogWriter) lines() (int, error) { } // DoRotate means it need to write file in new file. -// new file name like xx.log.2013-01-01.2 +// new file name like xx.2013-01-01.2.log func (w *FileLogWriter) DoRotate() error { _, err := os.Lstat(w.Filename) if err == nil { // file exists // Find the next available number num := 1 fname := "" + suffix := filepath.Ext(w.Filename) + filenameOnly := strings.TrimSuffix(w.Filename, suffix) + if suffix == "" { + suffix = ".log" + } for ; err == nil && num <= 999; num++ { - fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num) + fname = filenameOnly + fmt.Sprintf(".%s.%03d%s", time.Now().Format("2006-01-02"), num, suffix) _, err = os.Lstat(fname) } // return error if the last file checked still existed From 0564956fd6462393ed8d023591b687b4d064da95 Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Wed, 15 Jul 2015 12:04:18 +0000 Subject: [PATCH 148/436] Fix for #1256. Indent the code sample lines properly. --- beego.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/beego.go b/beego.go index cfebfbea..e4e7f3b0 100644 --- a/beego.go +++ b/beego.go @@ -14,13 +14,13 @@ // beego is an open-source, high-performance, modularity, full-stack web framework // -// package main +// package main // -// import "github.com/astaxie/beego" +// import "github.com/astaxie/beego" // -// func main() { -// beego.Run() -// } +// func main() { +// beego.Run() +// } // // more infomation: http://beego.me package beego From 45d693e6d62c7eef3da6640d0195539c485d31c3 Mon Sep 17 00:00:00 2001 From: Mawueli Kofi Adzoe Date: Thu, 16 Jul 2015 04:32:07 +0000 Subject: [PATCH 149/436] Add quick start example from website to README --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b650887..1eb6e3b3 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,31 @@ beego is an open-source, high-performance, modular, full-stack web framework. More info [beego.me](http://beego.me) -## Installation +##Quick Start +######Download and install go get github.com/astaxie/beego +######Create file `hello.go` +```go +package main + +import "github.com/astaxie/beego" + +func main(){ + beego.Run() +} +``` +######Build and run +```bash + go build hello.go + ./hello +``` +######Congratulations! +You just built your first beego app. +Open your browser and visit `http://localhost:8000`. +Please see [Documentation](http://beego.me/docs) for more. + ## Features * RESTful support From 19d82ab62c3e96a5d7839e89fa7ebd29fd7bbb23 Mon Sep 17 00:00:00 2001 From: Pelle Johnsen Date: Wed, 22 Jul 2015 18:12:57 +0200 Subject: [PATCH 150/436] Fix #1274: Add QuerySeter.Distinct() --- orm/db.go | 6 +++++- orm/orm_queryset.go | 37 ++++++++++++++++++++++--------------- orm/types.go | 1 + 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/orm/db.go b/orm/db.go index 20dc80f2..cefd5dfc 100644 --- a/orm/db.go +++ b/orm/db.go @@ -814,7 +814,11 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi } } - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) + sqlSelect := "SELECT" + if qs.distinct { + sqlSelect += " DISTINCT" + } + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, orderBy, limit) d.ins.ReplaceMarks(&query) diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 5cc47617..106b62e5 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -61,6 +61,7 @@ type querySet struct { limit int64 offset int64 orders []string + distinct bool orm *orm } @@ -112,24 +113,30 @@ func (o querySet) OrderBy(exprs ...string) QuerySeter { return &o } +// add DISTINCT to SELECT +func (o querySet) Distinct() QuerySeter { + o.distinct = true + return &o +} + // set relation model to query together. // it will query relation models and assign to parent model. func (o querySet) RelatedSel(params ...interface{}) QuerySeter { - if len(params) == 0 { - o.relDepth = DefaultRelsDepth - } else { - for _, p := range params { - switch val := p.(type) { - case string: - o.related = append(o.related, val) - case int: - o.relDepth = val - default: - panic(fmt.Errorf(" wrong param kind: %v", val)) - } - } - } - return &o + if len(params) == 0 { + o.relDepth = DefaultRelsDepth + } else { + for _, p := range params { + switch val := p.(type) { + case string: + o.related = append(o.related, val) + case int: + o.relDepth = val + default: + panic(fmt.Errorf(" wrong param kind: %v", val)) + } + } + } + return &o } // set condition to QuerySeter. diff --git a/orm/types.go b/orm/types.go index b46be4fc..4347e46f 100644 --- a/orm/types.go +++ b/orm/types.go @@ -67,6 +67,7 @@ type QuerySeter interface { Limit(interface{}, ...interface{}) QuerySeter Offset(interface{}) QuerySeter OrderBy(...string) QuerySeter + Distinct() QuerySeter RelatedSel(...interface{}) QuerySeter Count() (int64, error) Exist() bool From b622d5d369961af4356e4d01dce78deacb0c335a Mon Sep 17 00:00:00 2001 From: Mawuli Adzoe Date: Sat, 18 Jul 2015 10:09:22 +0000 Subject: [PATCH 151/436] Fix #1269 extract documentation Fix #1271 Add description from docs on beego.me to README and also add same description to godoc --- README.md | 3 ++- beego.go | 11 ----------- doc.go | 17 +++++++++++++++++ 3 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 doc.go diff --git a/README.md b/README.md index 1eb6e3b3..b1b48e75 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ [![Build Status](https://drone.io/github.com/astaxie/beego/status.png)](https://drone.io/github.com/astaxie/beego/latest) [![GoDoc](http://godoc.org/github.com/astaxie/beego?status.svg)](http://godoc.org/github.com/astaxie/beego) -beego is an open-source, high-performance, modular, full-stack web framework. +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. More info [beego.me](http://beego.me) diff --git a/beego.go b/beego.go index 8115a558..60483764 100644 --- a/beego.go +++ b/beego.go @@ -12,17 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -// beego is an open-source, high-performance, modular, full-stack web framework -// -// package main -// -// import "github.com/astaxie/beego" -// -// func main() { -// beego.Run() -// } -// -// more infomation: http://beego.me package beego import ( diff --git a/doc.go b/doc.go new file mode 100644 index 00000000..3a5b5cb8 --- /dev/null +++ b/doc.go @@ -0,0 +1,17 @@ +/* +beego: an open-source, high-performance, modular, full-stack web framework + +It is used for rapid development of RESTful APIs, web apps and backend services in Go. +beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding. + + package main + import "github.com/astaxie/beego" + + func main() { + beego.Run() + } + +more infomation: http://beego.me +*/ +package beego + From b26ef5b2e549ea3ba00f9bdb9c2e4c8dbcffb31b Mon Sep 17 00:00:00 2001 From: JessonChan Date: Mon, 27 Jul 2015 08:44:58 +0800 Subject: [PATCH 152/436] typo fixed registor==>register innner ==> inner --- app.go | 2 +- namespace.go | 38 +++++++++++++++++----------------- router.go | 58 ++++++++++++++++++++++++++-------------------------- 3 files changed, 49 insertions(+), 49 deletions(-) diff --git a/app.go b/app.go index 8fc320ad..eddf7e21 100644 --- a/app.go +++ b/app.go @@ -28,7 +28,7 @@ import ( // App defines beego application with a new PatternServeMux. type App struct { - Handlers *ControllerRegistor + Handlers *ControllerRegister Server *http.Server } diff --git a/namespace.go b/namespace.go index ebb7c14f..86dc2ebd 100644 --- a/namespace.go +++ b/namespace.go @@ -23,16 +23,16 @@ import ( type namespaceCond func(*beecontext.Context) bool -type innnerNamespace func(*Namespace) +type innerNamespace func(*Namespace) // Namespace is store all the info type Namespace struct { prefix string - handlers *ControllerRegistor + handlers *ControllerRegister } // get new Namespace -func NewNamespace(prefix string, params ...innnerNamespace) *Namespace { +func NewNamespace(prefix string, params ...innerNamespace) *Namespace { ns := &Namespace{ prefix: prefix, handlers: NewControllerRegister(), @@ -276,112 +276,112 @@ func addPrefix(t *Tree, prefix string) { } // Namespace Condition -func NSCond(cond namespaceCond) innnerNamespace { +func NSCond(cond namespaceCond) innerNamespace { return func(ns *Namespace) { ns.Cond(cond) } } // Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) innnerNamespace { +func NSBefore(filiterList ...FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Filter("before", filiterList...) } } // Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) innnerNamespace { +func NSAfter(filiterList ...FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Filter("after", filiterList...) } } // Namespace Include ControllerInterface -func NSInclude(cList ...ControllerInterface) innnerNamespace { +func NSInclude(cList ...ControllerInterface) innerNamespace { return func(ns *Namespace) { ns.Include(cList...) } } // Namespace Router -func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innnerNamespace { +func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innerNamespace { return func(ns *Namespace) { ns.Router(rootpath, c, mappingMethods...) } } // Namespace Get -func NSGet(rootpath string, f FilterFunc) innnerNamespace { +func NSGet(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Get(rootpath, f) } } // Namespace Post -func NSPost(rootpath string, f FilterFunc) innnerNamespace { +func NSPost(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Post(rootpath, f) } } // Namespace Head -func NSHead(rootpath string, f FilterFunc) innnerNamespace { +func NSHead(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Head(rootpath, f) } } // Namespace Put -func NSPut(rootpath string, f FilterFunc) innnerNamespace { +func NSPut(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Put(rootpath, f) } } // Namespace Delete -func NSDelete(rootpath string, f FilterFunc) innnerNamespace { +func NSDelete(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Delete(rootpath, f) } } // Namespace Any -func NSAny(rootpath string, f FilterFunc) innnerNamespace { +func NSAny(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Any(rootpath, f) } } // Namespace Options -func NSOptions(rootpath string, f FilterFunc) innnerNamespace { +func NSOptions(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Options(rootpath, f) } } // Namespace Patch -func NSPatch(rootpath string, f FilterFunc) innnerNamespace { +func NSPatch(rootpath string, f FilterFunc) innerNamespace { return func(ns *Namespace) { ns.Patch(rootpath, f) } } //Namespace AutoRouter -func NSAutoRouter(c ControllerInterface) innnerNamespace { +func NSAutoRouter(c ControllerInterface) innerNamespace { return func(ns *Namespace) { ns.AutoRouter(c) } } // Namespace AutoPrefix -func NSAutoPrefix(prefix string, c ControllerInterface) innnerNamespace { +func NSAutoPrefix(prefix string, c ControllerInterface) innerNamespace { return func(ns *Namespace) { ns.AutoPrefix(prefix, c) } } // Namespace add sub Namespace -func NSNamespace(prefix string, params ...innnerNamespace) innnerNamespace { +func NSNamespace(prefix string, params ...innerNamespace) innerNamespace { return func(ns *Namespace) { n := NewNamespace(prefix, params...) ns.Namespace(n) diff --git a/router.go b/router.go index 3e1ebab3..217ad65f 100644 --- a/router.go +++ b/router.go @@ -110,22 +110,22 @@ type controllerInfo struct { routerType int } -// ControllerRegistor containers registered router rules, controller handlers and filters. -type ControllerRegistor struct { +// ControllerRegister containers registered router rules, controller handlers and filters. +type ControllerRegister struct { routers map[string]*Tree enableFilter bool filters map[int][]*FilterRouter } -// NewControllerRegister returns a new ControllerRegistor. -func NewControllerRegister() *ControllerRegistor { - return &ControllerRegistor{ +// NewControllerRegister returns a new ControllerRegister. +func NewControllerRegister() *ControllerRegister { + return &ControllerRegister{ routers: make(map[string]*Tree), filters: make(map[int][]*FilterRouter), } } -// Add controller handler and pattern rules to ControllerRegistor. +// Add controller handler and pattern rules to ControllerRegister. // usage: // default methods is the same name as method // Add("/user",&UserController{}) @@ -135,7 +135,7 @@ func NewControllerRegister() *ControllerRegistor { // Add("/api/delete",&RestController{},"delete:DeleteFood") // Add("/api",&RestController{},"get,post:ApiFunc") // Add("/simple",&SimpleController{},"get:GetFunc;post:PostFunc") -func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingMethods ...string) { +func (p *ControllerRegister) Add(pattern string, c ControllerInterface, mappingMethods ...string) { reflectVal := reflect.ValueOf(c) t := reflect.Indirect(reflectVal).Type() methods := make(map[string]string) @@ -183,7 +183,7 @@ func (p *ControllerRegistor) Add(pattern string, c ControllerInterface, mappingM } } -func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) { +func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerInfo) { if !RouterCaseSensitive { pattern = strings.ToLower(pattern) } @@ -198,7 +198,7 @@ func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerIn // only when the Runmode is dev will generate router file in the router/auto.go from the controller // Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -func (p *ControllerRegistor) Include(cList ...ControllerInterface) { +func (p *ControllerRegister) Include(cList ...ControllerInterface) { if RunMode == "dev" { skip := make(map[string]bool, 10) for _, c := range cList { @@ -243,7 +243,7 @@ func (p *ControllerRegistor) Include(cList ...ControllerInterface) { // Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Get(pattern string, f FilterFunc) { +func (p *ControllerRegister) Get(pattern string, f FilterFunc) { p.AddMethod("get", pattern, f) } @@ -252,7 +252,7 @@ func (p *ControllerRegistor) Get(pattern string, f FilterFunc) { // Post("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Post(pattern string, f FilterFunc) { +func (p *ControllerRegister) Post(pattern string, f FilterFunc) { p.AddMethod("post", pattern, f) } @@ -261,7 +261,7 @@ func (p *ControllerRegistor) Post(pattern string, f FilterFunc) { // Put("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Put(pattern string, f FilterFunc) { +func (p *ControllerRegister) Put(pattern string, f FilterFunc) { p.AddMethod("put", pattern, f) } @@ -270,7 +270,7 @@ func (p *ControllerRegistor) Put(pattern string, f FilterFunc) { // Delete("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Delete(pattern string, f FilterFunc) { +func (p *ControllerRegister) Delete(pattern string, f FilterFunc) { p.AddMethod("delete", pattern, f) } @@ -279,7 +279,7 @@ func (p *ControllerRegistor) Delete(pattern string, f FilterFunc) { // Head("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Head(pattern string, f FilterFunc) { +func (p *ControllerRegister) Head(pattern string, f FilterFunc) { p.AddMethod("head", pattern, f) } @@ -288,7 +288,7 @@ func (p *ControllerRegistor) Head(pattern string, f FilterFunc) { // Patch("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Patch(pattern string, f FilterFunc) { +func (p *ControllerRegister) Patch(pattern string, f FilterFunc) { p.AddMethod("patch", pattern, f) } @@ -297,7 +297,7 @@ func (p *ControllerRegistor) Patch(pattern string, f FilterFunc) { // Options("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Options(pattern string, f FilterFunc) { +func (p *ControllerRegister) Options(pattern string, f FilterFunc) { p.AddMethod("options", pattern, f) } @@ -306,7 +306,7 @@ func (p *ControllerRegistor) Options(pattern string, f FilterFunc) { // Any("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) Any(pattern string, f FilterFunc) { +func (p *ControllerRegister) Any(pattern string, f FilterFunc) { p.AddMethod("*", pattern, f) } @@ -315,7 +315,7 @@ func (p *ControllerRegistor) Any(pattern string, f FilterFunc) { // AddMethod("get","/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) -func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { +func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok { panic("not support http method: " + method) } @@ -344,7 +344,7 @@ func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { } // add user defined Handler -func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ...interface{}) { +func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { route := &controllerInfo{} route.pattern = pattern route.routerType = routerTypeHandler @@ -359,21 +359,21 @@ func (p *ControllerRegistor) Handler(pattern string, h http.Handler, options ... } } -// Add auto router to ControllerRegistor. +// Add auto router to ControllerRegister. // example beego.AddAuto(&MainContorlller{}), // MainController has method List and Page. // visit the url /main/list to execute List function // /main/page to execute Page function. -func (p *ControllerRegistor) AddAuto(c ControllerInterface) { +func (p *ControllerRegister) AddAuto(c ControllerInterface) { p.AddAutoPrefix("/", c) } -// Add auto router to ControllerRegistor with prefix. +// Add auto router to ControllerRegister with prefix. // example beego.AddAutoPrefix("/admin",&MainContorlller{}), // MainController has method List and Page. // visit the url /admin/main/list to execute List function // /admin/main/page to execute Page function. -func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) { +func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) { reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() ct := reflect.Indirect(reflectVal).Type() @@ -401,7 +401,7 @@ func (p *ControllerRegistor) AddAutoPrefix(prefix string, c ControllerInterface) // 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 *ControllerRegistor) 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.tree = NewTree() @@ -420,7 +420,7 @@ func (p *ControllerRegistor) InsertFilter(pattern string, pos int, filter Filter } // add Filter into -func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error { +func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error { p.filters[pos] = append(p.filters[pos], mr) p.enableFilter = true return nil @@ -428,7 +428,7 @@ func (p *ControllerRegistor) insertFilterRouter(pos int, mr *FilterRouter) error // UrlFor does another controller handler in this request function. // it can access any controller method. -func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) string { +func (p *ControllerRegister) UrlFor(endpoint string, values ...interface{}) string { paths := strings.Split(endpoint, ".") if len(paths) <= 1 { Warn("urlfor endpoint must like path.controller.method") @@ -460,7 +460,7 @@ func (p *ControllerRegistor) UrlFor(endpoint string, values ...interface{}) stri return "" } -func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { +func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName string, params map[string]string, httpMethod string) (bool, string) { for k, subtree := range t.fixrouters { u := path.Join(url, k) ok, u := p.geturl(subtree, u, controllName, methodName, params, httpMethod) @@ -575,7 +575,7 @@ func (p *ControllerRegistor) geturl(t *Tree, url, controllName, methodName strin } // Implement http.Handler interface. -func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) { +func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { starttime := time.Now() var runrouter reflect.Type var findrouter bool @@ -861,7 +861,7 @@ Admin: } } -func (p *ControllerRegistor) recoverPanic(context *beecontext.Context) { +func (p *ControllerRegister) recoverPanic(context *beecontext.Context) { if err := recover(); err != nil { if err == USERSTOPRUN { return From 57fdc308e34e7b656585f1b56fb47078e10963c5 Mon Sep 17 00:00:00 2001 From: wulove Date: Thu, 6 Aug 2015 09:36:43 +0800 Subject: [PATCH 153/436] =?UTF-8?q?AutoRender=E4=B8=BA=E7=A9=BA=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=8D=E7=BC=96=E8=AF=91=E6=A8=A1=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AutoRender为空,Controller.Render()不再执行,故无需编译模版 --- beego.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/beego.go b/beego.go index 60483764..7bcc0740 100644 --- a/beego.go +++ b/beego.go @@ -306,11 +306,13 @@ func initBeforeHttpRun() { } go GlobalSessions.GC() } - - err := BuildTemplate(ViewsPath) - if err != nil { - if RunMode == "dev" { - Warn(err) + + if AutoRender { + err := BuildTemplate(ViewsPath) + if err != nil { + if RunMode == "dev" { + Warn(err) + } } } From 877b5c233e889046853b17e799fa65e7aee1ee6b Mon Sep 17 00:00:00 2001 From: wulove Date: Thu, 6 Aug 2015 10:09:34 +0800 Subject: [PATCH 154/436] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E6=A8=A1=E7=89=88=E5=87=BD=E6=95=B0BuildTemplate=E5=8F=AF?= =?UTF-8?q?=E5=8F=98=E5=8F=82=E6=95=B0=EF=BC=8C=E4=BD=BF=E4=B9=8B=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8D=95=E4=B8=AA=E6=88=96=E5=A4=9A=E4=B8=AA=E6=A8=A1?= =?UTF-8?q?=E7=89=88=E7=9A=84=E7=BC=96=E8=AF=91=EF=BC=8C=E5=90=8C=E6=97=B6?= =?UTF-8?q?=E9=92=88=E5=AF=B9=E5=BC=80=E5=8F=91=E6=A8=A1=E5=BC=8F=EF=BC=8C?= =?UTF-8?q?=E6=AF=8F=E4=B8=AA=E8=AF=B7=E6=B1=82=E5=8F=AA=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E8=AF=B7=E6=B1=82=E7=9B=B8=E5=85=B3=E6=A8=A1?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 增加编译模版函数BuildTemplate可变参数,使之支持单个或多个模版的编译,同时针对开发模式,每个请求只编译当前请求相关模版,不再每次请求都编译全部模版 --- controller.go | 15 +++++++++++++-- template.go | 14 ++++++++------ template_test.go | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/controller.go b/controller.go index a8b9e8d3..65b311b6 100644 --- a/controller.go +++ b/controller.go @@ -200,8 +200,19 @@ func (c *Controller) RenderBytes() ([]byte, error) { if c.TplNames == "" { c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt } + if RunMode == "dev" { - BuildTemplate(ViewsPath) + buildFiles := make([]string, 1) + buildFiles = append(buildFiles, c.TplNames) + if c.LayoutSections != nil { + for _, sectionTpl := range c.LayoutSections { + if sectionTpl == "" { + continue + } + buildFiles = append(buildFiles, sectionTpl) + } + } + BuildTemplate(ViewsPath, buildFiles...) } newbytes := bytes.NewBufferString("") if _, ok := BeeTemplates[c.TplNames]; !ok { @@ -246,7 +257,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt } if RunMode == "dev" { - BuildTemplate(ViewsPath) + BuildTemplate(ViewsPath, c.TplNames) } ibytes := bytes.NewBufferString("") if _, ok := BeeTemplates[c.TplNames]; !ok { diff --git a/template.go b/template.go index 64b1939e..45ecbe52 100644 --- a/template.go +++ b/template.go @@ -127,7 +127,7 @@ func AddTemplateExt(ext string) { // build all template files in a directory. // it makes beego can render any template file in view directory. -func BuildTemplate(dir string) error { +func BuildTemplate(dir string, files... string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { return nil @@ -148,11 +148,13 @@ func BuildTemplate(dir string) error { } for _, v := range self.files { for _, file := range v { - t, err := getTemplate(self.root, file, v...) - if err != nil { - Trace("parse template err:", file, err) - } else { - BeeTemplates[file] = t + if (files == nil || utils.InSlice(file, files)) { + t, err := getTemplate(self.root, file, v...) + if err != nil { + Trace("parse template err:", file, err) + } else { + BeeTemplates[file] = t + } } } } diff --git a/template_test.go b/template_test.go index b35da5ce..79740f2b 100644 --- a/template_test.go +++ b/template_test.go @@ -123,7 +123,7 @@ func TestRelativeTemplate(t *testing.T) { f.Close() } } - if err := BuildTemplate(dir); err != nil { + if err := BuildTemplate(dir, files[1]); err != nil { t.Fatal(err) } if err := BeeTemplates["easyui/rbac/user.tpl"].ExecuteTemplate(os.Stdout, "easyui/rbac/user.tpl", nil); err != nil { From d4e15c0bd0d27c5ed4f49d150e1972af9a58d6bb Mon Sep 17 00:00:00 2001 From: Sergey Shcherbina Date: Mon, 17 Aug 2015 00:08:02 +0500 Subject: [PATCH 155/436] Added MapGet template func --- template.go | 1 + templatefunc.go | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/template.go b/template.go index 64b1939e..905f0bef 100644 --- a/template.go +++ b/template.go @@ -54,6 +54,7 @@ func init() { beegoTplFuncMap["assets_js"] = AssetsJs beegoTplFuncMap["assets_css"] = AssetsCss beegoTplFuncMap["config"] = Config + beegoTplFuncMap["map_get"] = MapGet // go1.2 added template funcs // Comparisons diff --git a/templatefunc.go b/templatefunc.go index 28ed15a3..6092f71e 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -652,3 +652,76 @@ func ge(arg1, arg2 interface{}) (bool, error) { } // go1.2 added template funcs. end + +// getting value from map by keys +// usage: +// Data["m"] = map[string]interface{} { +// "a": 1, +// "1": map[string]float64{ +// "c": 4, +// }, +// } +// +// {{ map_get m "a" }} // return 1 +// {{ map_get m 1 "c" }} // return 4 +func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) { + arg1Type := reflect.TypeOf(arg1) + arg1Val := reflect.ValueOf(arg1) + + if arg1Type.Kind() == reflect.Map && len(arg2) > 0 { + // check whether arg2[0] type equals to arg1 key type + // if they are different, make convertion + arg2Val := reflect.ValueOf(arg2[0]) + arg2Type := reflect.TypeOf(arg2[0]) + if arg2Type.Kind() != arg1Type.Key().Kind() { + // convert arg2Value to string + var arg2ConvertedVal interface{} + arg2String := fmt.Sprintf("%v", arg2[0]) + + // convert string representation to any other type + switch arg1Type.Key().Kind() { + case reflect.Bool: + arg2ConvertedVal, _ = strconv.ParseBool(arg2String) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + arg2ConvertedVal, _ = strconv.ParseInt(arg2String, 0, 64) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + arg2ConvertedVal, _ = strconv.ParseUint(arg2String, 0, 64) + case reflect.Float32, reflect.Float64: + arg2ConvertedVal, _ = strconv.ParseFloat(arg2String, 64) + case reflect.String: + arg2ConvertedVal = arg2String + default: + arg2ConvertedVal = arg2Val.Interface() + } + arg2Val = reflect.ValueOf(arg2ConvertedVal) + } + + storedVal := arg1Val.MapIndex(arg2Val) + + var result interface{} + + switch arg1Type.Elem().Kind() { + case reflect.Bool: + result = storedVal.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + result = storedVal.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + result = storedVal.Uint() + case reflect.Float32, reflect.Float64: + result = storedVal.Float() + case reflect.String: + result = storedVal.String() + default: + result = storedVal.Interface() + } + + // if there is more keys, handle this recursively + if len(arg2) > 1 { + return MapGet(result, arg2[1:]...) + } else { + return result, nil + } + } else { + return nil, nil + } +} From d91840779ac5e82cfea039f1fdd7fa38c7f7d908 Mon Sep 17 00:00:00 2001 From: Hepri Date: Mon, 17 Aug 2015 01:18:29 +0500 Subject: [PATCH 156/436] Update templatefunc.go --- templatefunc.go | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/templatefunc.go b/templatefunc.go index 6092f71e..0bfdd31c 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -698,29 +698,36 @@ func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) { storedVal := arg1Val.MapIndex(arg2Val) - var result interface{} + + if storedVal.IsValid() { + var result interface{} - switch arg1Type.Elem().Kind() { - case reflect.Bool: - result = storedVal.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - result = storedVal.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - result = storedVal.Uint() - case reflect.Float32, reflect.Float64: - result = storedVal.Float() - case reflect.String: - result = storedVal.String() - default: - result = storedVal.Interface() - } + switch arg1Type.Elem().Kind() { + case reflect.Bool: + result = storedVal.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + result = storedVal.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + result = storedVal.Uint() + case reflect.Float32, reflect.Float64: + result = storedVal.Float() + case reflect.String: + result = storedVal.String() + default: + result = storedVal.Interface() + } - // if there is more keys, handle this recursively - if len(arg2) > 1 { - return MapGet(result, arg2[1:]...) + // if there is more keys, handle this recursively + if len(arg2) > 1 { + return MapGet(result, arg2[1:]...) + } else { + return result, nil + } } else { - return result, nil + return nil, nil } + + } else { return nil, nil } From 9107fd8898fd98d94a342cc1b251237619f48794 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 17 Aug 2015 22:33:28 +0800 Subject: [PATCH 157/436] remove the default timeout setting --- httplib/httplib.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 68c22d70..a160bce0 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -74,12 +74,6 @@ func SetDefaultSetting(setting BeegoHttpSettings) { settingMutex.Lock() defer settingMutex.Unlock() defaultSetting = setting - if defaultSetting.ConnectTimeout == 0 { - defaultSetting.ConnectTimeout = 60 * time.Second - } - if defaultSetting.ReadWriteTimeout == 0 { - defaultSetting.ReadWriteTimeout = 60 * time.Second - } } // return *BeegoHttpRequest with specific method From 860006bfdae293bd30944a5dd7980b8f964da5b7 Mon Sep 17 00:00:00 2001 From: Shuai Date: Tue, 18 Aug 2015 19:28:17 +0800 Subject: [PATCH 158/436] =?UTF-8?q?httplib=E8=AF=B7=E6=B1=82=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=94=AF=E6=8C=81[]string?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- httplib/httplib.go | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 68c22d70..c671cc0a 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -100,8 +100,8 @@ func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: rawurl, req: &req, - params: map[string]string{}, - files: map[string]string{}, + params: map[string]interface{}{}, + files: map[string]string{}, setting: defaultSetting, resp: &resp, } @@ -150,7 +150,7 @@ type BeegoHttpSettings struct { type BeegoHttpRequest struct { url string req *http.Request - params map[string]string + params map[string]interface{} files map[string]string setting BeegoHttpSettings resp *http.Response @@ -272,8 +272,13 @@ func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) // Param adds query param in to request. // params build query string as ?key1=value1&key2=value2... -func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { - b.params[key] = value +func (b *BeegoHttpRequest) Param(key, value interface{}) *BeegoHttpRequest { + switch value.(type) { + case string: + b.params[key.(string)] = value.(string) + case []string: + b.params[key.(string)] = value.([]string) + } return b } @@ -348,7 +353,14 @@ func (b *BeegoHttpRequest) buildUrl(paramBody string) { } } for k, v := range b.params { - bodyWriter.WriteField(k, v) + switch v.(type) { + case string: + bodyWriter.WriteField(k, v.(string)) + case []string: + for _, vv := range v.([]string) { + bodyWriter.WriteField(k, vv) + } + } } bodyWriter.Close() pw.Close() @@ -383,10 +395,20 @@ func (b *BeegoHttpRequest) SendOut() (*http.Response, error) { if len(b.params) > 0 { var buf bytes.Buffer for k, v := range b.params { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v)) - buf.WriteByte('&') + switch v.(type) { + case string: + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v.(string))) + buf.WriteByte('&') + case []string: + for _, vv := range v.([]string) { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(vv)) + buf.WriteByte('&') + } + } } paramBody = buf.String() paramBody = paramBody[0 : len(paramBody)-1] From ff92f22d841a7015958ede181d8c76164c7c84bb Mon Sep 17 00:00:00 2001 From: Shuai Date: Tue, 18 Aug 2015 23:19:24 +0800 Subject: [PATCH 159/436] =?UTF-8?q?=E5=88=A0=E9=99=A4Param=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- httplib/httplib.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index c671cc0a..1b7b207d 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -272,13 +272,8 @@ func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) // Param adds query param in to request. // params build query string as ?key1=value1&key2=value2... -func (b *BeegoHttpRequest) Param(key, value interface{}) *BeegoHttpRequest { - switch value.(type) { - case string: - b.params[key.(string)] = value.(string) - case []string: - b.params[key.(string)] = value.([]string) - } +func (b *BeegoHttpRequest) Param(key string, value interface{}) *BeegoHttpRequest { + b.params[key] = value return b } From 506f54a080ed4006676af3019d79bdee0507e4bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=B0=8F=E7=8E=89?= Date: Wed, 19 Aug 2015 15:23:50 +0800 Subject: [PATCH 160/436] Update router.go, add Flush for responseWriter --- router.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/router.go b/router.go index 217ad65f..7cce86fe 100644 --- a/router.go +++ b/router.go @@ -931,6 +931,13 @@ func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return hj.Hijack() } +func (w *responseWriter) Flush() { + f, ok := w.writer.(http.Flusher) + if ok { + f.Flush() + } +} + func tourl(params map[string]string) string { if len(params) == 0 { return "" From 0e3fe64c6922bc21f33530a8379b6406b874ba98 Mon Sep 17 00:00:00 2001 From: Sergey Shcherbina Date: Thu, 20 Aug 2015 19:04:43 +0500 Subject: [PATCH 161/436] Added TestMapGet --- templatefunc_test.go | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/templatefunc_test.go b/templatefunc_test.go index 60af5bf5..cc467d95 100644 --- a/templatefunc_test.go +++ b/templatefunc_test.go @@ -244,3 +244,80 @@ func TestParseFormTag(t *testing.T) { t.Errorf("Form Tag that should be ignored was not correctly parsed.") } } + +func TestMapGet(t *testing.T) { + // test one level map + m1 := map[string]int64{ + "a": 1, + "1": 2, + } + + if res, err := MapGet(m1, "a"); err == nil { + if res.(int64) != 1 { + t.Errorf("Should return 1, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } + + if res, err := MapGet(m1, "1"); err == nil { + if res.(int64) != 2 { + t.Errorf("Should return 2, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } + + if res, err := MapGet(m1, 1); err == nil { + if res.(int64) != 2 { + t.Errorf("Should return 2, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } + + // test 2 level map + m2 := map[string]interface{}{ + "1": map[string]float64{ + "2": 3.5, + }, + } + + if res, err := MapGet(m2, 1, 2); err == nil { + if res.(float64) != 3.5 { + t.Errorf("Should return 3.5, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } + + // test 5 level map + m5 := map[string]interface{}{ + "1": map[string]interface{}{ + "2": map[string]interface{}{ + "3": map[string]interface{}{ + "4": map[string]interface{}{ + "5": 1.2, + }, + }, + }, + }, + } + + if res, err := MapGet(m5, 1, 2, 3, 4, 5); err == nil { + if res.(float64) != 1.2 { + t.Errorf("Should return 1.2, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } + + // check whether element not exists in map + if res, err := MapGet(m5, 5, 4, 3, 2, 1); err == nil { + if res != nil { + t.Errorf("Should return nil, but return %v", res) + } + } else { + t.Errorf("Error happens %v", err) + } +} From 38ddb61199f7ab90bfa7265a3b6cd68ecfb81c76 Mon Sep 17 00:00:00 2001 From: wulove Date: Fri, 21 Aug 2015 10:32:53 +0800 Subject: [PATCH 162/436] files is []string, use len(files)==0 --- template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/template.go b/template.go index 45ecbe52..32468d77 100644 --- a/template.go +++ b/template.go @@ -148,7 +148,7 @@ func BuildTemplate(dir string, files... string) error { } for _, v := range self.files { for _, file := range v { - if (files == nil || utils.InSlice(file, files)) { + if (len(files) == 0 || utils.InSlice(file, files)) { t, err := getTemplate(self.root, file, v...) if err != nil { Trace("parse template err:", file, err) From 99b1c5c54b72b0096a6a27b06d670a0738b8f8c2 Mon Sep 17 00:00:00 2001 From: tabalt <245291359@qq.com> Date: Fri, 21 Aug 2015 10:53:59 +0800 Subject: [PATCH 163/436] fixed mux1 to mux --- grace/grace.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grace/grace.go b/grace/grace.go index e5577267..33bf196c 100644 --- a/grace/grace.go +++ b/grace/grace.go @@ -32,7 +32,7 @@ // mux := http.NewServeMux() // mux.HandleFunc("/hello", handler) // -// err := grace.ListenAndServe("localhost:8080", mux1) +// err := grace.ListenAndServe("localhost:8080", mux) // if err != nil { // log.Println(err) // } From 862ea226e5c8b2099c9d3a4e7ed5df90ceae5a56 Mon Sep 17 00:00:00 2001 From: Shuai Date: Mon, 24 Aug 2015 00:16:56 +0800 Subject: [PATCH 164/436] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- httplib/httplib.go | 45 ++++++++++++++++++--------------------------- 1 file changed, 18 insertions(+), 27 deletions(-) diff --git a/httplib/httplib.go b/httplib/httplib.go index 1b7b207d..b10b770a 100644 --- a/httplib/httplib.go +++ b/httplib/httplib.go @@ -100,8 +100,8 @@ func NewBeegoRequest(rawurl, method string) *BeegoHttpRequest { return &BeegoHttpRequest{ url: rawurl, req: &req, - params: map[string]interface{}{}, - files: map[string]string{}, + params: map[string][]string{}, + files: map[string]string{}, setting: defaultSetting, resp: &resp, } @@ -150,7 +150,7 @@ type BeegoHttpSettings struct { type BeegoHttpRequest struct { url string req *http.Request - params map[string]interface{} + params map[string][]string files map[string]string setting BeegoHttpSettings resp *http.Response @@ -272,8 +272,12 @@ func (b *BeegoHttpRequest) SetProxy(proxy func(*http.Request) (*url.URL, error)) // Param adds query param in to request. // params build query string as ?key1=value1&key2=value2... -func (b *BeegoHttpRequest) Param(key string, value interface{}) *BeegoHttpRequest { - b.params[key] = value +func (b *BeegoHttpRequest) Param(key, value string) *BeegoHttpRequest { + if param, ok := b.params[key]; ok { + b.params[key] = append(param, value) + } else { + b.params[key] = []string{value} + } return b } @@ -348,14 +352,9 @@ func (b *BeegoHttpRequest) buildUrl(paramBody string) { } } for k, v := range b.params { - switch v.(type) { - case string: - bodyWriter.WriteField(k, v.(string)) - case []string: - for _, vv := range v.([]string) { - bodyWriter.WriteField(k, vv) - } - } + for _, vv := range v { + bodyWriter.WriteField(k, vv) + } } bodyWriter.Close() pw.Close() @@ -390,20 +389,12 @@ func (b *BeegoHttpRequest) SendOut() (*http.Response, error) { if len(b.params) > 0 { var buf bytes.Buffer for k, v := range b.params { - switch v.(type) { - case string: - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(v.(string))) - buf.WriteByte('&') - case []string: - for _, vv := range v.([]string) { - buf.WriteString(url.QueryEscape(k)) - buf.WriteByte('=') - buf.WriteString(url.QueryEscape(vv)) - buf.WriteByte('&') - } - } + for _, vv := range v { + buf.WriteString(url.QueryEscape(k)) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(vv)) + buf.WriteByte('&') + } } paramBody = buf.String() paramBody = paramBody[0 : len(paramBody)-1] From 0c33673197f1ecdba5a77418798565db9e68a378 Mon Sep 17 00:00:00 2001 From: Pelle Johnsen Date: Mon, 24 Aug 2015 09:41:10 +0200 Subject: [PATCH 165/436] Revert spaces > tabs change --- orm/orm_queryset.go | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index 106b62e5..ea236964 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -122,21 +122,21 @@ func (o querySet) Distinct() QuerySeter { // set relation model to query together. // it will query relation models and assign to parent model. func (o querySet) RelatedSel(params ...interface{}) QuerySeter { - if len(params) == 0 { - o.relDepth = DefaultRelsDepth - } else { - for _, p := range params { - switch val := p.(type) { - case string: - o.related = append(o.related, val) - case int: - o.relDepth = val - default: - panic(fmt.Errorf(" wrong param kind: %v", val)) - } - } - } - return &o + if len(params) == 0 { + o.relDepth = DefaultRelsDepth + } else { + for _, p := range params { + switch val := p.(type) { + case string: + o.related = append(o.related, val) + case int: + o.relDepth = val + default: + panic(fmt.Errorf(" wrong param kind: %v", val)) + } + } + } + return &o } // set condition to QuerySeter. From cbb6591bdb8e14b2b87d6ffed3f79dd75c586eae Mon Sep 17 00:00:00 2001 From: Viet Hung Nguyen Date: Wed, 26 Aug 2015 15:57:28 +0700 Subject: [PATCH 166/436] fix typo --- config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index 8d9261b8..d0eca747 100644 --- a/config/config.go +++ b/config/config.go @@ -88,12 +88,12 @@ func Register(name string, adapter Config) { // adapterName is ini/json/xml/yaml. // filename is the config file path. -func NewConfig(adapterName, fileaname string) (ConfigContainer, error) { +func NewConfig(adapterName, filename string) (ConfigContainer, error) { adapter, ok := adapters[adapterName] if !ok { return nil, fmt.Errorf("config: unknown adaptername %q (forgotten import?)", adapterName) } - return adapter.Parse(fileaname) + return adapter.Parse(filename) } // adapterName is ini/json/xml/yaml. From c516819c56fac85abd82a2991b0b699c196b07ee Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Fri, 28 Aug 2015 16:54:49 +0800 Subject: [PATCH 167/436] Update error.go this caused `http: multiple response.WriteHeader calls` when using method `CustomAbort` or `Abort` when status is already in errMap like 404. --- error.go | 1 - 1 file changed, 1 deletion(-) diff --git a/error.go b/error.go index 99a1fcf3..21768cdb 100644 --- a/error.go +++ b/error.go @@ -453,7 +453,6 @@ func exception(errcode string, ctx *context.Context) { func executeError(err *errorInfo, ctx *context.Context, code int) { if err.errorType == errorTypeHandler { - ctx.ResponseWriter.WriteHeader(code) err.handler(ctx.ResponseWriter, ctx.Request) return } From ad6547936e038759488f5a24823fba4baba9a077 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 28 Aug 2015 23:08:00 +0800 Subject: [PATCH 168/436] fix the http: multiple response.WriteHeader calls --- plugins/apiauth/apiauth.go | 14 +++++++------- plugins/cors/cors.go | 5 +---- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/plugins/apiauth/apiauth.go b/plugins/apiauth/apiauth.go index bbae7def..56a92d25 100644 --- a/plugins/apiauth/apiauth.go +++ b/plugins/apiauth/apiauth.go @@ -83,41 +83,41 @@ func APIBaiscAuth(appid, appkey string) beego.FilterFunc { func APIAuthWithFunc(f AppIdToAppSecret, timeout int) beego.FilterFunc { return func(ctx *context.Context) { if ctx.Input.Query("appid") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: appid") return } appsecret := f(ctx.Input.Query("appid")) if appsecret == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("not exist this appid") return } if ctx.Input.Query("signature") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: signature") return } if ctx.Input.Query("timestamp") == "" { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("miss query param: timestamp") return } u, err := time.Parse("2006-01-02 15:04:05", ctx.Input.Query("timestamp")) if err != nil { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("timestamp format is error, should 2006-01-02 15:04:05") return } t := time.Now() if t.Sub(u).Seconds() > float64(timeout) { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("timeout! the request time is long ago, please try again") return } if ctx.Input.Query("signature") != Signature(appsecret, ctx.Input.Method(), ctx.Request.Form, ctx.Input.Uri()) { - ctx.Output.SetStatus(403) + ctx.ResponseWriter.WriteHeader(403) ctx.WriteString("auth failed") } } diff --git a/plugins/cors/cors.go b/plugins/cors/cors.go index 052d3bc6..1e973a40 100644 --- a/plugins/cors/cors.go +++ b/plugins/cors/cors.go @@ -24,7 +24,7 @@ // // - PUT and PATCH methods // // - Origin header // // - Credentials share -// beego.InsertFilter("*", beego.BeforeRouter,cors.Allow(&cors.Options{ +// beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ // AllowOrigins: []string{"https://*.foo.com"}, // AllowMethods: []string{"PUT", "PATCH"}, // AllowHeaders: []string{"Origin"}, @@ -36,7 +36,6 @@ package cors import ( - "net/http" "regexp" "strconv" "strings" @@ -216,8 +215,6 @@ func Allow(opts *Options) beego.FilterFunc { for key, value := range headers { ctx.Output.Header(key, value) } - ctx.Output.SetStatus(http.StatusOK) - ctx.WriteString("") return } headers = opts.Header(origin) From dd4cbdda66af752f229fb0a6bf8ac9900df5e482 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 31 Aug 2015 11:58:11 +0800 Subject: [PATCH 169/436] update the gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 39ae5706..9806457b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ .DS_Store *.swp *.swo +beego.iml From 9fd571830d62fce13a06743512b00c8582ebb2da Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Tue, 1 Sep 2015 17:52:44 +0800 Subject: [PATCH 170/436] Update beego.go Maybe the `Hard Coding` should have a higher priority --- beego.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/beego.go b/beego.go index cfebfbea..516e3116 100644 --- a/beego.go +++ b/beego.go @@ -259,6 +259,8 @@ 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] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { @@ -268,7 +270,6 @@ func Run(params ...string) { HttpPort, _ = strconv.Atoi(strs[1]) } } - initBeforeHttpRun() if EnableAdmin { go beeAdminApp.Run() From adca4558044e9eb5bc64492021f2128773930826 Mon Sep 17 00:00:00 2001 From: onealtang Date: Wed, 2 Sep 2015 15:50:40 +0800 Subject: [PATCH 171/436] always use server's locale to parse date When parsing the date without time, it's always using UTC date, which is unexpected. If we want to use UTC date, it's recommend to set the server's timezone as UTC, and keep the code flexible. --- templatefunc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templatefunc.go b/templatefunc.go index 28ed15a3..39ada253 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -352,7 +352,7 @@ func ParseForm(form url.Values, obj interface{}) error { if len(tags) > 1 { format = tags[1] } - t, err := time.Parse(format, value) + t, err := time.ParseInLocation(format, value, time.Local) if err != nil { return err } From a074df9c2e9d25ffdb0050b68488b2484497cee4 Mon Sep 17 00:00:00 2001 From: f0r Date: Thu, 3 Sep 2015 00:45:09 +0800 Subject: [PATCH 172/436] =?UTF-8?q?=E4=B8=BAquerySeter=E6=B7=BB=E5=8A=A0Gr?= =?UTF-8?q?oupBy=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- orm/db.go | 6 ++++-- orm/db_tables.go | 24 ++++++++++++++++++++++++ orm/orm_queryset.go | 7 +++++++ orm/types.go | 1 + 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/orm/db.go b/orm/db.go index cefd5dfc..1803db4f 100644 --- a/orm/db.go +++ b/orm/db.go @@ -802,6 +802,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi tables.parseRelated(qs.related, qs.relDepth) where, args := tables.getCondSql(cond, false, tz) + groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, offset, rlimit) join := tables.getJoinSql() @@ -818,7 +819,7 @@ func (d *dbBase) ReadBatch(q dbQuerier, qs *querySet, mi *modelInfo, cond *Condi if qs.distinct { sqlSelect += " DISTINCT" } - query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, orderBy, limit) + query := fmt.Sprintf("%s %s FROM %s%s%s T0 %s%s%s%s%s", sqlSelect, sels, Q, mi.table, Q, join, where, groupBy, orderBy, limit) d.ins.ReplaceMarks(&query) @@ -1448,13 +1449,14 @@ func (d *dbBase) ReadValues(q dbQuerier, qs *querySet, mi *modelInfo, cond *Cond } where, args := tables.getCondSql(cond, false, tz) + groupBy := tables.getGroupSql(qs.groups) orderBy := tables.getOrderSql(qs.orders) limit := tables.getLimitSql(mi, qs.offset, qs.limit) join := tables.getJoinSql() sels := strings.Join(cols, ", ") - query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s", sels, Q, mi.table, Q, join, where, orderBy, limit) + query := fmt.Sprintf("SELECT %s FROM %s%s%s T0 %s%s%s%s%s", sels, Q, mi.table, Q, join, where, groupBy,orderBy, limit) d.ins.ReplaceMarks(&query) diff --git a/orm/db_tables.go b/orm/db_tables.go index a9aa10ab..245be891 100644 --- a/orm/db_tables.go +++ b/orm/db_tables.go @@ -390,6 +390,30 @@ func (t *dbTables) getCondSql(cond *Condition, sub bool, tz *time.Location) (whe return } +// generate group sql. +func (t *dbTables) getGroupSql(groups []string) (groupSql string) { + if len(groups) == 0 { + return + } + + Q := t.base.TableQuote() + + groupSqls := make([]string, 0, len(groups)) + for _, group := range groups { + exprs := strings.Split(group, ExprSep) + + index, _, fi, suc := t.parseExprs(t.mi, exprs) + if suc == false { + panic(fmt.Errorf("unknown field/column name `%s`", strings.Join(exprs, ExprSep))) + } + + groupSqls = append(groupSqls, fmt.Sprintf("%s.%s%s%s", index, Q, fi.column, Q)) + } + + groupSql = fmt.Sprintf("GROUP BY %s ", strings.Join(groupSqls, ", ")) + return +} + // generate order sql. func (t *dbTables) getOrderSql(orders []string) (orderSql string) { if len(orders) == 0 { diff --git a/orm/orm_queryset.go b/orm/orm_queryset.go index ea236964..af16b694 100644 --- a/orm/orm_queryset.go +++ b/orm/orm_queryset.go @@ -60,6 +60,7 @@ type querySet struct { relDepth int limit int64 offset int64 + groups []string orders []string distinct bool orm *orm @@ -106,6 +107,12 @@ func (o querySet) Offset(offset interface{}) QuerySeter { return &o } +// add GROUP expression +func (o querySet) GroupBy(exprs ...string) QuerySeter { + o.groups = exprs + return &o +} + // add ORDER expression. // "column" means ASC, "-column" means DESC. func (o querySet) OrderBy(exprs ...string) QuerySeter { diff --git a/orm/types.go b/orm/types.go index 4347e46f..95a3fee3 100644 --- a/orm/types.go +++ b/orm/types.go @@ -66,6 +66,7 @@ type QuerySeter interface { SetCond(*Condition) QuerySeter Limit(interface{}, ...interface{}) QuerySeter Offset(interface{}) QuerySeter + GroupBy(...string) QuerySeter OrderBy(...string) QuerySeter Distinct() QuerySeter RelatedSel(...interface{}) QuerySeter From 9ab7466d5c36ad08a5173cb91e885d4c02c32157 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 4 Sep 2015 22:20:55 +0800 Subject: [PATCH 173/436] fix the cappital --- logs/smtp.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logs/smtp.go b/logs/smtp.go index 95123ebf..f9b292b7 100644 --- a/logs/smtp.go +++ b/logs/smtp.go @@ -31,9 +31,9 @@ const ( // smtpWriter implements LoggerInterface and is used to send emails via given SMTP-server. type SmtpWriter struct { - Username string `json:"Username"` + Username string `json:"username"` Password string `json:"password"` - Host string `json:"Host"` + Host string `json:"host"` Subject string `json:"subject"` FromAddress string `json:"fromAddress"` RecipientAddresses []string `json:"sendTos"` From f9fe89fff0e6044468d27fb14febbc7ee38ae336 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 4 Sep 2015 22:33:03 +0800 Subject: [PATCH 174/436] fix the file rotate test case issues --- logs/file_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/file_test.go b/logs/file_test.go index c71e9bb4..97260f12 100644 --- a/logs/file_test.go +++ b/logs/file_test.go @@ -103,7 +103,7 @@ func TestFileRotate(t *testing.T) { log.Critical("critical") log.Emergency("emergency") time.Sleep(time.Second * 4) - rotatename := "test3.log" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + rotatename := "test3" + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), 1) + ".log" b, err := exists(rotatename) if !b || err != nil { t.Fatal("rotate not generated") From 34aa9002bba92457bafdace3ecb2773caeed7503 Mon Sep 17 00:00:00 2001 From: astaxie Date: Fri, 4 Sep 2015 23:03:10 +0800 Subject: [PATCH 175/436] fix the httplib test case timeout --- httplib/httplib_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/httplib/httplib_test.go b/httplib/httplib_test.go index 0b551c53..0b6b5779 100644 --- a/httplib/httplib_test.go +++ b/httplib/httplib_test.go @@ -19,6 +19,7 @@ import ( "os" "strings" "testing" + "time" ) func TestResponse(t *testing.T) { @@ -153,6 +154,7 @@ func TestWithSetting(t *testing.T) { setting.EnableCookie = true setting.UserAgent = v setting.Transport = nil + setting.ReadWriteTimeout = 5 * time.Second SetDefaultSetting(setting) str, err := Get("http://httpbin.org/get").String() From f55bbbdff41b43402fe8f4767ed0ebfcc35da46b Mon Sep 17 00:00:00 2001 From: sidbusy Date: Sat, 5 Sep 2015 10:31:31 +0800 Subject: [PATCH 176/436] allows custom the TableName of Session --- session/mysql/sess_mysql.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/session/mysql/sess_mysql.go b/session/mysql/sess_mysql.go index 76a13932..26237e95 100644 --- a/session/mysql/sess_mysql.go +++ b/session/mysql/sess_mysql.go @@ -51,7 +51,10 @@ import ( _ "github.com/go-sql-driver/mysql" ) -var mysqlpder = &MysqlProvider{} +var ( + TableName = "session" + mysqlpder = &MysqlProvider{} +) // mysql session store type MysqlSessionStore struct { @@ -110,7 +113,7 @@ func (st *MysqlSessionStore) SessionRelease(w http.ResponseWriter) { if err != nil { return } - st.c.Exec("UPDATE session set `session_data`=?, `session_expiry`=? where session_key=?", + st.c.Exec("UPDATE "+TableName+" set `session_data`=?, `session_expiry`=? where session_key=?", b, time.Now().Unix(), st.sid) } @@ -141,11 +144,11 @@ func (mp *MysqlProvider) SessionInit(maxlifetime int64, savePath string) error { // get mysql session by sid func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) { c := mp.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", sid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", + c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", sid, "", time.Now().Unix()) } var kv map[interface{}]interface{} @@ -165,7 +168,7 @@ func (mp *MysqlProvider) SessionRead(sid string) (session.SessionStore, error) { func (mp *MysqlProvider) SessionExist(sid string) bool { c := mp.connectInit() defer c.Close() - row := c.QueryRow("select session_data from session where session_key=?", sid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", sid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { @@ -178,13 +181,13 @@ func (mp *MysqlProvider) SessionExist(sid string) bool { // generate new sid for mysql session func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionStore, error) { c := mp.connectInit() - row := c.QueryRow("select session_data from session where session_key=?", oldsid) + row := c.QueryRow("select session_data from "+TableName+" where session_key=?", oldsid) var sessiondata []byte err := row.Scan(&sessiondata) if err == sql.ErrNoRows { - c.Exec("insert into session(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix()) + c.Exec("insert into "+TableName+"(`session_key`,`session_data`,`session_expiry`) values(?,?,?)", oldsid, "", time.Now().Unix()) } - c.Exec("update session set `session_key`=? where session_key=?", sid, oldsid) + c.Exec("update "+TableName+" set `session_key`=? where session_key=?", sid, oldsid) var kv map[interface{}]interface{} if len(sessiondata) == 0 { kv = make(map[interface{}]interface{}) @@ -201,7 +204,7 @@ func (mp *MysqlProvider) SessionRegenerate(oldsid, sid string) (session.SessionS // delete mysql session by sid func (mp *MysqlProvider) SessionDestroy(sid string) error { c := mp.connectInit() - c.Exec("DELETE FROM session where session_key=?", sid) + c.Exec("DELETE FROM "+TableName+" where session_key=?", sid) c.Close() return nil } @@ -209,7 +212,7 @@ func (mp *MysqlProvider) SessionDestroy(sid string) error { // delete expired values in mysql session func (mp *MysqlProvider) SessionGC() { c := mp.connectInit() - c.Exec("DELETE from session where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) + c.Exec("DELETE from "+TableName+" where session_expiry < ?", time.Now().Unix()-mp.maxlifetime) c.Close() return } @@ -219,7 +222,7 @@ func (mp *MysqlProvider) SessionAll() int { c := mp.connectInit() defer c.Close() var total int - err := c.QueryRow("SELECT count(*) as num from session").Scan(&total) + err := c.QueryRow("SELECT count(*) as num from " + TableName).Scan(&total) if err != nil { return 0 } From 5ad999a3d177832162c1c3bbc164c652208c7d85 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sun, 6 Sep 2015 12:01:50 +0800 Subject: [PATCH 177/436] Update tree.go fix routers for: ``` /topic/:id/?:auth /topic/:id/?:auth:int ``` --- tree.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tree.go b/tree.go index e0d9c1c2..f15af91a 100644 --- a/tree.go +++ b/tree.go @@ -208,6 +208,11 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, } else { t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) } + for i, v := range wildcards { + if v==":" && i!=0{ + t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards[:i+1]}) + } + } } else { seg := segments[0] iswild, params, regexpStr := splitSegment(seg) From 508a57be1efa3a4638ba4729f2f4e63c3a18d6bb Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sun, 6 Sep 2015 12:07:12 +0800 Subject: [PATCH 178/436] Update tree_test.go --- tree_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tree_test.go b/tree_test.go index 1d2890e1..40065e77 100644 --- a/tree_test.go +++ b/tree_test.go @@ -26,6 +26,10 @@ var routers []testinfo func init() { routers = make([]testinfo, 0) + routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1",":auth":"2"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}}) + routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1/123", map[string]string{":id": "1",":auth":"123"}}) routers = append(routers, testinfo{"/:id", "/123", map[string]string{":id": "123"}}) routers = append(routers, testinfo{"/hello/?:id", "/hello", map[string]string{":id": ""}}) routers = append(routers, testinfo{"/", "/", nil}) From ddd9bf1305635e499561a7dc4088acfbc9314873 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sun, 6 Sep 2015 12:16:05 +0800 Subject: [PATCH 179/436] Update tree.go --- tree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree.go b/tree.go index f15af91a..8f0df589 100644 --- a/tree.go +++ b/tree.go @@ -209,7 +209,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) } for i, v := range wildcards { - if v==":" && i!=0{ + if v==":" { t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards[:i+1]}) } } From 1377d16559e6e145958e0a0cf96cfdb614b2cd03 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sun, 6 Sep 2015 12:17:16 +0800 Subject: [PATCH 180/436] Update tree_test.go --- tree_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tree_test.go b/tree_test.go index 40065e77..7fe70f51 100644 --- a/tree_test.go +++ b/tree_test.go @@ -26,6 +26,8 @@ var routers []testinfo func init() { routers = make([]testinfo, 0) + routers = append(routers, testinfo{"/topic/?:auth:int", "/topic", nil}) + routers = append(routers, testinfo{"/topic/?:auth:int", "/topic/123", map[string]string{":auth":"123"}}) routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1", map[string]string{":id": "1"}}) routers = append(routers, testinfo{"/topic/:id/?:auth", "/topic/1/2", map[string]string{":id": "1",":auth":"2"}}) routers = append(routers, testinfo{"/topic/:id/?:auth:int", "/topic/1", map[string]string{":id": "1"}}) From 06ec3a931ddb0f19681520d798cdc5d260050df6 Mon Sep 17 00:00:00 2001 From: Kyoung-chan Lee Date: Sun, 6 Sep 2015 17:13:55 +0900 Subject: [PATCH 181/436] Fix a wrong test name. --- cache/memcache/memcache_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/memcache/memcache_test.go b/cache/memcache/memcache_test.go index 0523ae85..27b085e6 100644 --- a/cache/memcache/memcache_test.go +++ b/cache/memcache/memcache_test.go @@ -23,7 +23,7 @@ import ( "time" ) -func TestRedisCache(t *testing.T) { +func TestMemcacheCache(t *testing.T) { bm, err := cache.NewCache("memcache", `{"conn": "127.0.0.1:11211"}`) if err != nil { t.Error("init err") From 4ad743fc8b5fcbd2037cfe2a4da14d1b46c4cc27 Mon Sep 17 00:00:00 2001 From: Kyoung-chan Lee Date: Sun, 6 Sep 2015 17:20:18 +0900 Subject: [PATCH 182/436] Update a outdated information in README. --- cache/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/README.md b/cache/README.md index 72d0d1c5..893b213f 100644 --- a/cache/README.md +++ b/cache/README.md @@ -43,7 +43,7 @@ interval means the gc time. The cache will check at each time interval, whether ## Memcache adapter -Memcache adapter use the vitess's [Memcache](http://code.google.com/p/vitess/go/memcache) client. +Memcache adapter use the [gomemcache](http://github.com/bradfitz/gomemcache) client. Configure like this: From fe1ec1675f7b29f340e5f3e387a72eec64bf75ad Mon Sep 17 00:00:00 2001 From: Kyoung-chan Lee Date: Sun, 6 Sep 2015 17:22:33 +0900 Subject: [PATCH 183/436] Fix a wrong url (http 404). --- cache/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/README.md b/cache/README.md index 893b213f..4152c57e 100644 --- a/cache/README.md +++ b/cache/README.md @@ -52,7 +52,7 @@ Configure like this: ## Redis adapter -Redis adapter use the [redigo](http://github.com/garyburd/redigo/redis) client. +Redis adapter use the [redigo](http://github.com/garyburd/redigo) client. Configure like this: From a2a6ec954b5a8ccb13b8b9b4cb600c8bac509791 Mon Sep 17 00:00:00 2001 From: Yongzheng Lai Date: Sun, 6 Sep 2015 22:13:58 +0800 Subject: [PATCH 184/436] Update tree.go go fmt --- tree.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tree.go b/tree.go index 8f0df589..46d05ae7 100644 --- a/tree.go +++ b/tree.go @@ -209,7 +209,7 @@ func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) } for i, v := range wildcards { - if v==":" { + if v == ":" { t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards[:i+1]}) } } From eb3479b753986929aeaba194905b4a044283137f Mon Sep 17 00:00:00 2001 From: astaxie Date: Sun, 6 Sep 2015 23:00:42 +0800 Subject: [PATCH 185/436] optimize the app structure --- app.go | 189 ++++++++++++++++++++++++++++++++++++++++++++++++++ beego.go | 201 +++--------------------------------------------------- config.go | 4 -- 3 files changed, 198 insertions(+), 196 deletions(-) diff --git a/app.go b/app.go index eddf7e21..96e2ad2c 100644 --- a/app.go +++ b/app.go @@ -20,12 +20,22 @@ import ( "net/http" "net/http/fcgi" "os" + "path" "time" "github.com/astaxie/beego/grace" "github.com/astaxie/beego/utils" ) +var ( + BeeApp *App +) + +func init() { + // create beego application + BeeApp = NewApp() +} + // App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegister @@ -170,3 +180,182 @@ func (app *App) Run() { } <-endRunning } + +// Router adds a patterned controller handler to BeeApp. +// it's an alias method of App.Router. +// usage: +// simple router +// beego.Router("/admin", &admin.UserController{}) +// beego.Router("/admin/index", &admin.ArticleController{}) +// +// regex router +// +// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) +// +// custom rules +// beego.Router("/api/list",&RestController{},"*:ListFood") +// beego.Router("/api/create",&RestController{},"post:CreateFood") +// beego.Router("/api/update",&RestController{},"put:UpdateFood") +// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") +func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { + BeeApp.Handlers.Add(rootpath, c, mappingMethods...) + return BeeApp +} + +// Router add list from +// usage: +// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) +// type BankAccount struct{ +// beego.Controller +// } +// +// register the function +// func (b *BankAccount)Mapping(){ +// b.Mapping("ShowAccount" , b.ShowAccount) +// b.Mapping("ModifyAccount", b.ModifyAccount) +//} +// +// //@router /account/:id [get] +// func (b *BankAccount) ShowAccount(){ +// //logic +// } +// +// +// //@router /account/:id [post] +// func (b *BankAccount) ModifyAccount(){ +// //logic +// } +// +// the comments @router url methodlist +// url support all the function Router's pattern +// methodlist [get post head put delete options *] +func Include(cList ...ControllerInterface) *App { + BeeApp.Handlers.Include(cList...) + return BeeApp +} + +// RESTRouter adds a restful controller handler to BeeApp. +// its' controller implements beego.ControllerInterface and +// defines a param "pattern/:objectId" to visit each resource. +func RESTRouter(rootpath string, c ControllerInterface) *App { + Router(rootpath, c) + Router(path.Join(rootpath, ":objectId"), c) + return BeeApp +} + +// AutoRouter adds defined controller handler to BeeApp. +// it's same to App.AutoRouter. +// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, +// visit the url /main/list to exec List function or /main/page to exec Page function. +func AutoRouter(c ControllerInterface) *App { + BeeApp.Handlers.AddAuto(c) + return BeeApp +} + +// AutoPrefix adds controller handler to BeeApp with prefix. +// it's same to App.AutoRouterWithPrefix. +// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, +// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. +func AutoPrefix(prefix string, c ControllerInterface) *App { + BeeApp.Handlers.AddAutoPrefix(prefix, c) + return BeeApp +} + +// register router for Get method +// usage: +// beego.Get("/", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Get(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Get(rootpath, f) + return BeeApp +} + +// register router for Post method +// usage: +// beego.Post("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Post(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Post(rootpath, f) + return BeeApp +} + +// register router for Delete method +// usage: +// beego.Delete("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Delete(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Delete(rootpath, f) + return BeeApp +} + +// register router for Put method +// usage: +// beego.Put("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Put(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Put(rootpath, f) + return BeeApp +} + +// register router for Head method +// usage: +// beego.Head("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Head(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Head(rootpath, f) + return BeeApp +} + +// register router for Options method +// usage: +// beego.Options("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Options(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Options(rootpath, f) + return BeeApp +} + +// register router for Patch method +// usage: +// beego.Patch("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Patch(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Patch(rootpath, f) + return BeeApp +} + +// register router for all method +// usage: +// beego.Any("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Any(rootpath string, f FilterFunc) *App { + BeeApp.Handlers.Any(rootpath, f) + return BeeApp +} + +// register router for own Handler +// usage: +// beego.Handler("/api", func(ctx *context.Context){ +// ctx.Output.Body("hello world") +// }) +func Handler(rootpath string, h http.Handler, options ...interface{}) *App { + BeeApp.Handlers.Handler(rootpath, h, options...) + return BeeApp +} + +// InsertFilter adds a FilterFunc with pattern condition and action constant. +// The pos means action constant including +// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. +// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) +func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { + BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) + return BeeApp +} diff --git a/beego.go b/beego.go index 43f98755..ead5a6d4 100644 --- a/beego.go +++ b/beego.go @@ -15,9 +15,7 @@ package beego import ( - "net/http" "os" - "path" "path/filepath" "strconv" "strings" @@ -28,178 +26,12 @@ import ( // beego web framework version. const VERSION = "1.5.0" -type hookfunc func() error //hook function to run -var hooks []hookfunc //hook function slice to store the hookfunc +//hook function to run +type hookfunc func() error -// Router adds a patterned controller handler to BeeApp. -// it's an alias method of App.Router. -// usage: -// simple router -// beego.Router("/admin", &admin.UserController{}) -// beego.Router("/admin/index", &admin.ArticleController{}) -// -// regex router -// -// beego.Router("/api/:id([0-9]+)", &controllers.RController{}) -// -// custom rules -// beego.Router("/api/list",&RestController{},"*:ListFood") -// beego.Router("/api/create",&RestController{},"post:CreateFood") -// beego.Router("/api/update",&RestController{},"put:UpdateFood") -// beego.Router("/api/delete",&RestController{},"delete:DeleteFood") -func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *App { - BeeApp.Handlers.Add(rootpath, c, mappingMethods...) - return BeeApp -} - -// Router add list from -// usage: -// beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) -// type BankAccount struct{ -// beego.Controller -// } -// -// register the function -// func (b *BankAccount)Mapping(){ -// b.Mapping("ShowAccount" , b.ShowAccount) -// b.Mapping("ModifyAccount", b.ModifyAccount) -//} -// -// //@router /account/:id [get] -// func (b *BankAccount) ShowAccount(){ -// //logic -// } -// -// -// //@router /account/:id [post] -// func (b *BankAccount) ModifyAccount(){ -// //logic -// } -// -// the comments @router url methodlist -// url support all the function Router's pattern -// methodlist [get post head put delete options *] -func Include(cList ...ControllerInterface) *App { - BeeApp.Handlers.Include(cList...) - return BeeApp -} - -// RESTRouter adds a restful controller handler to BeeApp. -// its' controller implements beego.ControllerInterface and -// defines a param "pattern/:objectId" to visit each resource. -func RESTRouter(rootpath string, c ControllerInterface) *App { - Router(rootpath, c) - Router(path.Join(rootpath, ":objectId"), c) - return BeeApp -} - -// AutoRouter adds defined controller handler to BeeApp. -// it's same to App.AutoRouter. -// if beego.AddAuto(&MainContorlller{}) and MainController has methods List and Page, -// visit the url /main/list to exec List function or /main/page to exec Page function. -func AutoRouter(c ControllerInterface) *App { - BeeApp.Handlers.AddAuto(c) - return BeeApp -} - -// AutoPrefix adds controller handler to BeeApp with prefix. -// it's same to App.AutoRouterWithPrefix. -// if beego.AutoPrefix("/admin",&MainContorlller{}) and MainController has methods List and Page, -// visit the url /admin/main/list to exec List function or /admin/main/page to exec Page function. -func AutoPrefix(prefix string, c ControllerInterface) *App { - BeeApp.Handlers.AddAutoPrefix(prefix, c) - return BeeApp -} - -// register router for Get method -// usage: -// beego.Get("/", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Get(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Get(rootpath, f) - return BeeApp -} - -// register router for Post method -// usage: -// beego.Post("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Post(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Post(rootpath, f) - return BeeApp -} - -// register router for Delete method -// usage: -// beego.Delete("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Delete(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Delete(rootpath, f) - return BeeApp -} - -// register router for Put method -// usage: -// beego.Put("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Put(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Put(rootpath, f) - return BeeApp -} - -// register router for Head method -// usage: -// beego.Head("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Head(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Head(rootpath, f) - return BeeApp -} - -// register router for Options method -// usage: -// beego.Options("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Options(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Options(rootpath, f) - return BeeApp -} - -// register router for Patch method -// usage: -// beego.Patch("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Patch(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Patch(rootpath, f) - return BeeApp -} - -// register router for all method -// usage: -// beego.Any("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Any(rootpath string, f FilterFunc) *App { - BeeApp.Handlers.Any(rootpath, f) - return BeeApp -} - -// register router for own Handler -// usage: -// beego.Handler("/api", func(ctx *context.Context){ -// ctx.Output.Body("hello world") -// }) -func Handler(rootpath string, h http.Handler, options ...interface{}) *App { - BeeApp.Handlers.Handler(rootpath, h, options...) - return BeeApp -} +var ( + hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc +) // SetViewsPath sets view directory path in beego application. func SetViewsPath(path string) *App { @@ -228,15 +60,6 @@ func DelStaticPath(url string) *App { return BeeApp } -// InsertFilter adds a FilterFunc with pattern condition and action constant. -// The pos means action constant including -// beego.BeforeStatic, beego.BeforeRouter, beego.BeforeExec, beego.AfterExec and beego.FinishRouter. -// The bool params is for setting the returnOnOutput value (false allows multiple filters to execute) -func InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) *App { - BeeApp.Handlers.InsertFilter(pattern, pos, filter, params...) - return BeeApp -} - // The hookfunc will run in beego.Run() // such as sessionInit, middlerware start, buildtemplate, admin start func AddAPPStartHook(hf hookfunc) { @@ -249,7 +72,7 @@ func AddAPPStartHook(hf hookfunc) { // beego.Run("127.0.0.1:8089") func Run(params ...string) { initBeforeHttpRun() - + if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { @@ -307,13 +130,11 @@ func initBeforeHttpRun() { } go GlobalSessions.GC() } - + if AutoRender { err := BuildTemplate(ViewsPath) - if err != nil { - if RunMode == "dev" { - Warn(err) - } + if err != nil && RunMode == "dev" { + Warn(err) } } @@ -338,7 +159,3 @@ func TestBeegoInit(apppath string) { os.Chdir(AppPath) initBeforeHttpRun() } - -func init() { - hooks = make([]hookfunc, 0) -} diff --git a/config.go b/config.go index 09d0df24..2bbd14f6 100644 --- a/config.go +++ b/config.go @@ -29,7 +29,6 @@ import ( ) var ( - BeeApp *App // beego application AppName string AppPath string workPath string @@ -215,9 +214,6 @@ func (b *beegoAppConfig) SaveConfigFile(filename string) error { } func init() { - // create beego application - BeeApp = NewApp() - workPath, _ = os.Getwd() workPath, _ = filepath.Abs(workPath) // initialize default configurations From 85d8ec5ca65a1615289cdd6e80645e885556a0e6 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 19:18:04 +0800 Subject: [PATCH 186/436] optimize the beego structure --- beego.go | 138 +++++++++++++++++++++---------------------------------- error.go | 41 ----------------- hooks.go | 100 ++++++++++++++++++++++++++++++++++++++++ mime.go | 11 ----- 4 files changed, 153 insertions(+), 137 deletions(-) create mode 100644 hooks.go diff --git a/beego.go b/beego.go index ead5a6d4..042a4270 100644 --- a/beego.go +++ b/beego.go @@ -19,8 +19,6 @@ import ( "path/filepath" "strconv" "strings" - - "github.com/astaxie/beego/session" ) // beego web framework version. @@ -33,33 +31,6 @@ var ( hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc ) -// SetViewsPath sets view directory path in beego application. -func SetViewsPath(path string) *App { - ViewsPath = path - return BeeApp -} - -// SetStaticPath sets static directory path and proper url pattern in beego application. -// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". -func SetStaticPath(url string, path string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - StaticDir[url] = path - return BeeApp -} - -// DelStaticPath removes the static folder setting in this url pattern in beego application. -func DelStaticPath(url string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - delete(StaticDir, url) - return BeeApp -} - // The hookfunc will run in beego.Run() // such as sessionInit, middlerware start, buildtemplate, admin start func AddAPPStartHook(hf hookfunc) { @@ -90,62 +61,6 @@ func Run(params ...string) { BeeApp.Run() } -func initBeforeHttpRun() { - // if AppConfigPath not In the conf/app.conf reParse config - if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { - err := ParseConfig() - if err != nil && AppConfigPath != filepath.Join(workPath, "conf", "app.conf") { - // configuration is critical to app, panic here if parse failed - panic(err) - } - } - - //init mime - AddAPPStartHook(initMime) - - // do hooks function - for _, hk := range hooks { - err := hk() - if err != nil { - panic(err) - } - } - - if SessionOn { - var err error - sessionConfig := AppConfig.String("sessionConfig") - if sessionConfig == "" { - sessionConfig = `{"cookieName":"` + SessionName + `",` + - `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + - `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + - `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + - `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + - `"domain":"` + SessionDomain + `",` + - `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` - } - GlobalSessions, err = session.NewManager(SessionProvider, - sessionConfig) - if err != nil { - panic(err) - } - go GlobalSessions.GC() - } - - if AutoRender { - err := BuildTemplate(ViewsPath) - if err != nil && RunMode == "dev" { - Warn(err) - } - } - - registerDefaultErrorHandler() - - if EnableDocs { - Get("/docs", serverDocs) - Get("/docs/*", serverDocs) - } -} - // this function is for test package init func TestBeegoInit(apppath string) { AppPath = apppath @@ -159,3 +74,56 @@ func TestBeegoInit(apppath string) { os.Chdir(AppPath) initBeforeHttpRun() } + +func initBeforeHttpRun() { + // if AppConfigPath not In the conf/app.conf reParse config + if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { + err := ParseConfig() + if err != nil && AppConfigPath != filepath.Join(workPath, "conf", "app.conf") { + // configuration is critical to app, panic here if parse failed + panic(err) + } + } + + //init mime + AddAPPStartHook(registerMime) + AddAPPStartHook(registerDefaultErrorHandler) + AddAPPStartHook(registerSession) + AddAPPStartHook(registerDocs) + AddAPPStartHook(registerTemplate) + + // do hooks function + for _, hk := range hooks { + err := hk() + if err != nil { + panic(err) + } + } +} + +// SetViewsPath sets view directory path in beego application. +func SetViewsPath(path string) *App { + ViewsPath = path + return BeeApp +} + +// SetStaticPath sets static directory path and proper url pattern in beego application. +// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". +func SetStaticPath(url string, path string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + StaticDir[url] = path + return BeeApp +} + +// DelStaticPath removes the static folder setting in this url pattern in beego application. +func DelStaticPath(url string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + delete(StaticDir, url) + return BeeApp +} diff --git a/error.go b/error.go index 21768cdb..f5c0c80b 100644 --- a/error.go +++ b/error.go @@ -358,47 +358,6 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { t.Execute(rw, data) } -// register default error http handlers, 404,401,403,500 and 503. -func registerDefaultErrorHandler() { - if _, ok := ErrorMaps["401"]; !ok { - Errorhandler("401", unauthorized) - } - - if _, ok := ErrorMaps["402"]; !ok { - Errorhandler("402", paymentRequired) - } - - if _, ok := ErrorMaps["403"]; !ok { - Errorhandler("403", forbidden) - } - - if _, ok := ErrorMaps["404"]; !ok { - Errorhandler("404", notFound) - } - - if _, ok := ErrorMaps["405"]; !ok { - Errorhandler("405", methodNotAllowed) - } - - if _, ok := ErrorMaps["500"]; !ok { - Errorhandler("500", internalServerError) - } - if _, ok := ErrorMaps["501"]; !ok { - Errorhandler("501", notImplemented) - } - if _, ok := ErrorMaps["502"]; !ok { - Errorhandler("502", badGateway) - } - - if _, ok := ErrorMaps["503"]; !ok { - Errorhandler("503", serviceUnavailable) - } - - if _, ok := ErrorMaps["504"]; !ok { - Errorhandler("504", gatewayTimeout) - } -} - // ErrorHandler registers http.HandlerFunc to each http err code string. // usage: // beego.ErrorHandler("404",NotFound) diff --git a/hooks.go b/hooks.go new file mode 100644 index 00000000..ded0d794 --- /dev/null +++ b/hooks.go @@ -0,0 +1,100 @@ +package beego + +import ( + "mime" + "path/filepath" + "strconv" + + "github.com/astaxie/beego/session" +) + +// +func registerMime() error { + for k, v := range mimemaps { + mime.AddExtensionType(k, v) + } + return nil +} + +// register default error http handlers, 404,401,403,500 and 503. +func registerDefaultErrorHandler() error { + if _, ok := ErrorMaps["401"]; !ok { + Errorhandler("401", unauthorized) + } + + if _, ok := ErrorMaps["402"]; !ok { + Errorhandler("402", paymentRequired) + } + + if _, ok := ErrorMaps["403"]; !ok { + Errorhandler("403", forbidden) + } + + if _, ok := ErrorMaps["404"]; !ok { + Errorhandler("404", notFound) + } + + if _, ok := ErrorMaps["405"]; !ok { + Errorhandler("405", methodNotAllowed) + } + + if _, ok := ErrorMaps["500"]; !ok { + Errorhandler("500", internalServerError) + } + if _, ok := ErrorMaps["501"]; !ok { + Errorhandler("501", notImplemented) + } + if _, ok := ErrorMaps["502"]; !ok { + Errorhandler("502", badGateway) + } + + if _, ok := ErrorMaps["503"]; !ok { + Errorhandler("503", serviceUnavailable) + } + + if _, ok := ErrorMaps["504"]; !ok { + Errorhandler("504", gatewayTimeout) + } + return nil +} + +func registerSession() error { + if SessionOn { + var err error + sessionConfig := AppConfig.String("sessionConfig") + if sessionConfig == "" { + sessionConfig = `{"cookieName":"` + SessionName + `",` + + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + + `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + + `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + + `"domain":"` + SessionDomain + `",` + + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` + } + GlobalSessions, err = session.NewManager(SessionProvider, + sessionConfig) + if err != nil { + return err + } + go GlobalSessions.GC() + } + return nil +} + +func registerTemplate() error { + if AutoRender { + err := BuildTemplate(ViewsPath) + if err != nil && RunMode == "dev" { + Warn(err) + } + } + return nil +} + +func registerDocs() error { + if EnableDocs { + Get("/docs", serverDocs) + Get("/docs/*", serverDocs) + } + return nil +} diff --git a/mime.go b/mime.go index 20246c21..0034976e 100644 --- a/mime.go +++ b/mime.go @@ -14,10 +14,6 @@ package beego -import ( - "mime" -) - var mimemaps map[string]string = map[string]string{ ".3dm": "x-world/x-3dmf", ".3dmf": "x-world/x-3dmf", @@ -558,10 +554,3 @@ var mimemaps map[string]string = map[string]string{ ".oex": "application/x-opera-extension", ".mustache": "text/html", } - -func initMime() error { - for k, v := range mimemaps { - mime.AddExtensionType(k, v) - } - return nil -} From 284dfc08438819c57800f9998234684ccc8aa47b Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 19:21:55 +0800 Subject: [PATCH 187/436] move the template related fun to template.go --- beego.go | 27 --------------------------- template.go | 31 +++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/beego.go b/beego.go index 042a4270..b79e0e3f 100644 --- a/beego.go +++ b/beego.go @@ -100,30 +100,3 @@ func initBeforeHttpRun() { } } } - -// SetViewsPath sets view directory path in beego application. -func SetViewsPath(path string) *App { - ViewsPath = path - return BeeApp -} - -// SetStaticPath sets static directory path and proper url pattern in beego application. -// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". -func SetStaticPath(url string, path string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - StaticDir[url] = path - return BeeApp -} - -// DelStaticPath removes the static folder setting in this url pattern in beego application. -func DelStaticPath(url string) *App { - if !strings.HasPrefix(url, "/") { - url = "/" + url - } - url = strings.TrimRight(url, "/") - delete(StaticDir, url) - return BeeApp -} diff --git a/template.go b/template.go index fdd9dc37..e976886a 100644 --- a/template.go +++ b/template.go @@ -128,7 +128,7 @@ func AddTemplateExt(ext string) { // build all template files in a directory. // it makes beego can render any template file in view directory. -func BuildTemplate(dir string, files... string) error { +func BuildTemplate(dir string, files ...string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { return nil @@ -149,7 +149,7 @@ func BuildTemplate(dir string, files... string) error { } for _, v := range self.files { for _, file := range v { - if (len(files) == 0 || utils.InSlice(file, files)) { + if len(files) == 0 || utils.InSlice(file, files) { t, err := getTemplate(self.root, file, v...) if err != nil { Trace("parse template err:", file, err) @@ -263,3 +263,30 @@ func _getTemplate(t0 *template.Template, root string, submods [][]string, others } return } + +// SetViewsPath sets view directory path in beego application. +func SetViewsPath(path string) *App { + ViewsPath = path + return BeeApp +} + +// SetStaticPath sets static directory path and proper url pattern in beego application. +// if beego.SetStaticPath("static","public"), visit /static/* to load static file in folder "public". +func SetStaticPath(url string, path string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + StaticDir[url] = path + return BeeApp +} + +// DelStaticPath removes the static folder setting in this url pattern in beego application. +func DelStaticPath(url string) *App { + if !strings.HasPrefix(url, "/") { + url = "/" + url + } + url = strings.TrimRight(url, "/") + delete(StaticDir, url) + return BeeApp +} From fe9c52fb69b226b177c6ae1732038497cc60c120 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 19:27:53 +0800 Subject: [PATCH 188/436] optimize init admin --- beego.go | 36 ++++++++++++++++-------------------- hooks.go | 7 +++++++ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/beego.go b/beego.go index b79e0e3f..81df5752 100644 --- a/beego.go +++ b/beego.go @@ -54,27 +54,9 @@ func Run(params ...string) { } } - if EnableAdmin { - go beeAdminApp.Run() - } - BeeApp.Run() } -// this function is for test package init -func TestBeegoInit(apppath string) { - AppPath = apppath - os.Setenv("BEEGO_RUNMODE", "test") - AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") - err := ParseConfig() - if err != nil && !os.IsNotExist(err) { - // for init if doesn't have app.conf will not panic - Info(err) - } - os.Chdir(AppPath) - initBeforeHttpRun() -} - func initBeforeHttpRun() { // if AppConfigPath not In the conf/app.conf reParse config if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { @@ -91,12 +73,26 @@ func initBeforeHttpRun() { AddAPPStartHook(registerSession) AddAPPStartHook(registerDocs) AddAPPStartHook(registerTemplate) + AddAPPStartHook(registerAdmin) // do hooks function for _, hk := range hooks { - err := hk() - if err != nil { + if err := hk(); err != nil { panic(err) } } } + +// this function is for test package init +func TestBeegoInit(apppath string) { + AppPath = apppath + os.Setenv("BEEGO_RUNMODE", "test") + AppConfigPath = filepath.Join(AppPath, "conf", "app.conf") + err := ParseConfig() + if err != nil && !os.IsNotExist(err) { + // for init if doesn't have app.conf will not panic + Info(err) + } + os.Chdir(AppPath) + initBeforeHttpRun() +} diff --git a/hooks.go b/hooks.go index ded0d794..78aec636 100644 --- a/hooks.go +++ b/hooks.go @@ -98,3 +98,10 @@ func registerDocs() error { } return nil } + +func registerAdmin() error { + if EnableAdmin { + go beeAdminApp.Run() + } + return nil +} From 919675e7935293772a593a3bb6e37eeceb09157c Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 19:29:52 +0800 Subject: [PATCH 189/436] update the comments --- beego.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/beego.go b/beego.go index 81df5752..d5d66c13 100644 --- a/beego.go +++ b/beego.go @@ -67,7 +67,7 @@ func initBeforeHttpRun() { } } - //init mime + //init hooks AddAPPStartHook(registerMime) AddAPPStartHook(registerDefaultErrorHandler) AddAPPStartHook(registerSession) @@ -75,7 +75,6 @@ func initBeforeHttpRun() { AddAPPStartHook(registerTemplate) AddAPPStartHook(registerAdmin) - // do hooks function for _, hk := range hooks { if err := hk(); err != nil { panic(err) From 152127c2af9f56f376b0e1931f14721031e7dc2d Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 21:38:53 +0800 Subject: [PATCH 190/436] make golint happy --- admin.go | 4 ++-- app.go | 21 +++++++++++---------- beego.go | 11 ++++++----- hooks.go | 3 +-- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/admin.go b/admin.go index 64d7fe34..583eadfc 100644 --- a/admin.go +++ b/admin.go @@ -314,14 +314,14 @@ func profIndex(rw http.ResponseWriter, r *http.Request) { data["Content"] = result.String() if format == "json" && command == "gc summary" { - dataJson, err := json.Marshal(data) + dataJSON, err := json.Marshal(data) if err != nil { http.Error(rw, err.Error(), http.StatusInternalServerError) return } rw.Header().Set("Content-Type", "application/json") - rw.Write(dataJson) + rw.Write(dataJSON) return } diff --git a/app.go b/app.go index 96e2ad2c..c340bcbe 100644 --- a/app.go +++ b/app.go @@ -28,6 +28,7 @@ import ( ) var ( + // BeeApp is an application instance BeeApp *App ) @@ -202,7 +203,7 @@ func Router(rootpath string, c ControllerInterface, mappingMethods ...string) *A return BeeApp } -// Router add list from +// Include will generate router file in the router/xxx.go from the controller's comments // usage: // beego.Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) // type BankAccount struct{ @@ -261,7 +262,7 @@ func AutoPrefix(prefix string, c ControllerInterface) *App { return BeeApp } -// register router for Get method +// Get used to register router for Get method // usage: // beego.Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -271,7 +272,7 @@ func Get(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Post method +// Post used to register router for Post method // usage: // beego.Post("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -281,7 +282,7 @@ func Post(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Delete method +// Delete used to register router for Delete method // usage: // beego.Delete("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -291,7 +292,7 @@ func Delete(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Put method +// Put used to register router for Put method // usage: // beego.Put("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -301,7 +302,7 @@ func Put(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Head method +// Head used to register router for Head method // usage: // beego.Head("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -311,7 +312,7 @@ func Head(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Options method +// Options used to register router for Options method // usage: // beego.Options("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -321,7 +322,7 @@ func Options(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for Patch method +// Patch used to register router for Patch method // usage: // beego.Patch("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -331,7 +332,7 @@ func Patch(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for all method +// Any used to register router for all methods // usage: // beego.Any("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -341,7 +342,7 @@ func Any(rootpath string, f FilterFunc) *App { return BeeApp } -// register router for own Handler +// Handler used to register a Handler router // usage: // beego.Handler("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") diff --git a/beego.go b/beego.go index d5d66c13..06d53750 100644 --- a/beego.go +++ b/beego.go @@ -31,7 +31,8 @@ var ( hooks = make([]hookfunc, 0) //hook function slice to store the hookfunc ) -// The hookfunc will run in beego.Run() +// AddAPPStartHook is used to register the hookfunc +// The hookfuncs will run in beego.Run() // such as sessionInit, middlerware start, buildtemplate, admin start func AddAPPStartHook(hf hookfunc) { hooks = append(hooks, hf) @@ -42,7 +43,7 @@ func AddAPPStartHook(hf hookfunc) { // beego.Run(":8089") // beego.Run("127.0.0.1:8089") func Run(params ...string) { - initBeforeHttpRun() + initBeforeHTTPRun() if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") @@ -57,7 +58,7 @@ func Run(params ...string) { BeeApp.Run() } -func initBeforeHttpRun() { +func initBeforeHTTPRun() { // if AppConfigPath not In the conf/app.conf reParse config if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") { err := ParseConfig() @@ -82,7 +83,7 @@ func initBeforeHttpRun() { } } -// this function is for test package init +// TestBeegoInit is for test package init func TestBeegoInit(apppath string) { AppPath = apppath os.Setenv("BEEGO_RUNMODE", "test") @@ -93,5 +94,5 @@ func TestBeegoInit(apppath string) { Info(err) } os.Chdir(AppPath) - initBeforeHttpRun() + initBeforeHTTPRun() } diff --git a/hooks.go b/hooks.go index 78aec636..526913c3 100644 --- a/hooks.go +++ b/hooks.go @@ -71,8 +71,7 @@ func registerSession() error { `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` } - GlobalSessions, err = session.NewManager(SessionProvider, - sessionConfig) + GlobalSessions, err = session.NewManager(SessionProvider, sessionConfig) if err != nil { return err } From f28a941e267d4c416eae6aaca032a1dc05a3f401 Mon Sep 17 00:00:00 2001 From: astaxie Date: Mon, 7 Sep 2015 23:19:42 +0800 Subject: [PATCH 191/436] make golint happy and also make the config readable --- admin.go | 28 +++---- app.go | 44 +++++----- beego.go | 4 +- config.go | 235 +++++++++++++++++++++++++++++++++--------------------- hooks.go | 4 +- router.go | 4 +- 6 files changed, 185 insertions(+), 134 deletions(-) diff --git a/admin.go b/admin.go index 583eadfc..93087e12 100644 --- a/admin.go +++ b/admin.go @@ -101,11 +101,11 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["AppConfigPath"] = AppConfigPath m["StaticDir"] = StaticDir m["StaticExtensionsToGzip"] = StaticExtensionsToGzip - m["HttpAddr"] = HttpAddr - m["HttpPort"] = HttpPort - m["HttpTLS"] = EnableHttpTLS - m["HttpCertFile"] = HttpCertFile - m["HttpKeyFile"] = HttpKeyFile + m["HTTPAddr"] = HTTPAddr + m["HTTPPort"] = HTTPPort + m["HTTPTLS"] = EnableHTTPTLS + m["HTTPCertFile"] = HTTPCertFile + m["HTTPKeyFile"] = HTTPKeyFile m["RecoverPanic"] = RecoverPanic m["AutoRender"] = AutoRender m["ViewsPath"] = ViewsPath @@ -114,14 +114,14 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["SessionProvider"] = SessionProvider m["SessionName"] = SessionName m["SessionGCMaxLifetime"] = SessionGCMaxLifetime - m["SessionSavePath"] = SessionSavePath + m["SessionProviderConfig"] = SessionProviderConfig m["SessionCookieLifeTime"] = SessionCookieLifeTime - m["UseFcgi"] = UseFcgi + m["EnabelFcgi"] = EnabelFcgi m["MaxMemory"] = MaxMemory m["EnableGzip"] = EnableGzip m["DirectoryIndex"] = DirectoryIndex - m["HttpServerTimeOut"] = HttpServerTimeOut - m["ErrorsShow"] = ErrorsShow + m["HTTPServerTimeOut"] = HTTPServerTimeOut + m["EnableErrorsShow"] = EnableErrorsShow m["XSRFKEY"] = XSRFKEY m["EnableXSRF"] = EnableXSRF m["XSRFExpire"] = XSRFExpire @@ -130,8 +130,8 @@ func listConf(rw http.ResponseWriter, r *http.Request) { m["TemplateRight"] = TemplateRight m["BeegoServerName"] = BeegoServerName m["EnableAdmin"] = EnableAdmin - m["AdminHttpAddr"] = AdminHttpAddr - m["AdminHttpPort"] = AdminHttpPort + m["AdminHTTPAddr"] = AdminHTTPAddr + m["AdminHTTPPort"] = AdminHTTPPort tmpl := template.Must(template.New("dashboard").Parse(dashboardTpl)) tmpl = template.Must(tmpl.Parse(configTpl)) @@ -451,10 +451,10 @@ func (admin *adminApp) Run() { if len(toolbox.AdminTaskList) > 0 { toolbox.StartTask() } - addr := AdminHttpAddr + addr := AdminHTTPAddr - if AdminHttpPort != 0 { - addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort) + if AdminHTTPPort != 0 { + addr = fmt.Sprintf("%s:%d", AdminHTTPAddr, AdminHTTPPort) } for p, f := range admin.routers { http.Handle(p, f) diff --git a/app.go b/app.go index c340bcbe..d10d436c 100644 --- a/app.go +++ b/app.go @@ -52,10 +52,10 @@ func NewApp() *App { // Run beego application. func (app *App) Run() { - addr := HttpAddr + addr := HTTPAddr - if HttpPort != 0 { - addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort) + if HTTPPort != 0 { + addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPPort) } var ( @@ -64,8 +64,8 @@ func (app *App) Run() { ) endRunning := make(chan bool, 1) - if UseFcgi { - if UseStdIo { + if EnabelFcgi { + if EnableStdIo { err = fcgi.Serve(nil, app.Handlers) // standard I/O if err == nil { BeeLogger.Info("Use FCGI via standard I/O") @@ -73,7 +73,7 @@ func (app *App) Run() { BeeLogger.Info("Cannot use FCGI via standard I/O", err) } } else { - if HttpPort == 0 { + if HTTPPort == 0 { // remove the Socket file before start if utils.FileExists(addr) { os.Remove(addr) @@ -91,18 +91,18 @@ func (app *App) Run() { if Graceful { app.Server.Addr = addr app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second - if EnableHttpTLS { + app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second + if EnableHTTPTLS { go func() { time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + if HTTPSPort != 0 { + addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort) app.Server.Addr = addr } server := grace.NewServer(addr, app.Handlers) server.Server = app.Server - err := server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + err := server.ListenAndServeTLS(HTTPCertFile, HTTPKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid())) time.Sleep(100 * time.Microsecond) @@ -110,11 +110,11 @@ func (app *App) Run() { } }() } - if EnableHttpListen { + if EnableHTTPListen { go func() { server := grace.NewServer(addr, app.Handlers) server.Server = app.Server - if ListenTCP4 && HttpAddr == "" { + if ListenTCP4 && HTTPAddr == "" { server.Network = "tcp4" } err := server.ListenAndServe() @@ -128,17 +128,17 @@ func (app *App) Run() { } else { app.Server.Addr = addr app.Server.Handler = app.Handlers - app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second - app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second + app.Server.ReadTimeout = time.Duration(HTTPServerTimeOut) * time.Second + app.Server.WriteTimeout = time.Duration(HTTPServerTimeOut) * time.Second - if EnableHttpTLS { + if EnableHTTPTLS { go func() { time.Sleep(20 * time.Microsecond) - if HttpsPort != 0 { - app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort) + if HTTPSPort != 0 { + app.Server.Addr = fmt.Sprintf("%s:%d", HTTPAddr, HTTPSPort) } BeeLogger.Info("https server Running on %s", app.Server.Addr) - err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile) + err := app.Server.ListenAndServeTLS(HTTPCertFile, HTTPKeyFile) if err != nil { BeeLogger.Critical("ListenAndServeTLS: ", err) time.Sleep(100 * time.Microsecond) @@ -147,11 +147,11 @@ func (app *App) Run() { }() } - if EnableHttpListen { + if EnableHTTPListen { go func() { app.Server.Addr = addr BeeLogger.Info("http server Running on %s", app.Server.Addr) - if ListenTCP4 && HttpAddr == "" { + if ListenTCP4 && HTTPAddr == "" { ln, err := net.Listen("tcp4", app.Server.Addr) if err != nil { BeeLogger.Critical("ListenAndServe: ", err) diff --git a/beego.go b/beego.go index 06d53750..329e0713 100644 --- a/beego.go +++ b/beego.go @@ -48,10 +48,10 @@ func Run(params ...string) { if len(params) > 0 && params[0] != "" { strs := strings.Split(params[0], ":") if len(strs) > 0 && strs[0] != "" { - HttpAddr = strs[0] + HTTPAddr = strs[0] } if len(strs) > 1 && strs[1] != "" { - HttpPort, _ = strconv.Atoi(strs[1]) + HTTPPort, _ = strconv.Atoi(strs[1]) } } diff --git a/config.go b/config.go index 2bbd14f6..ae334cd7 100644 --- a/config.go +++ b/config.go @@ -29,59 +29,115 @@ import ( ) var ( - AppName string - AppPath string - workPath string - AppConfigPath string - StaticDir map[string]string - TemplateCache map[string]*template.Template // template caching map - StaticExtensionsToGzip []string // files with should be compressed with gzip (.js,.css,etc) - EnableHttpListen bool - HttpAddr string - HttpPort int - ListenTCP4 bool - EnableHttpTLS bool - HttpsPort int - HttpCertFile string - HttpKeyFile string - RecoverPanic bool // flag of auto recover panic - AutoRender bool // flag of render template automatically - ViewsPath string - AppConfig *beegoAppConfig - RunMode string // run mode, "dev" or "prod" - GlobalSessions *session.Manager // global session mananger - SessionOn bool // flag of starting session auto. default is false. - SessionProvider string // default session provider, memory, mysql , redis ,etc. - SessionName string // the cookie name when saving session id into cookie. - SessionGCMaxLifetime int64 // session gc time for auto cleaning expired session. - SessionSavePath string // if use mysql/redis/file provider, define save path to connection info. - SessionCookieLifeTime int // the life time of session id in cookie. - SessionAutoSetCookie bool // auto setcookie - SessionDomain string // the cookie domain default is empty - UseFcgi bool - UseStdIo bool - MaxMemory int64 - EnableGzip bool // flag of enable gzip - DirectoryIndex bool // flag of display directory index. default is false. - HttpServerTimeOut int64 - ErrorsShow bool // flag of show errors in page. if true, show error and trace info in page rendered with error template. - XSRFKEY string // xsrf hash salt string. - EnableXSRF bool // flag of enable xsrf. - XSRFExpire int // the expiry of xsrf value. - CopyRequestBody bool // flag of copy raw request body in context. - TemplateLeft string - TemplateRight string - BeegoServerName string // beego server name exported in response header. - EnableAdmin bool // flag of enable admin module to log every request info. - AdminHttpAddr string // http server configurations for admin module. - AdminHttpPort int - FlashName string // name of the flash variable found in response header and cookie - FlashSeperator string // used to seperate flash key:value - AppConfigProvider string // config provider - EnableDocs bool // enable generate docs & server docs API Swagger - RouterCaseSensitive bool // router case sensitive default is true - AccessLogs bool // print access logs, default is false - Graceful bool // use graceful start the server + // AccessLogs represent whether output the access logs, default is false + AccessLogs bool + // AdminHTTPAddr is address for admin + AdminHTTPAddr string + // AdminHTTPPort is listens port for admin + AdminHTTPPort int + // AppConfig is the instance of Config, store the config information from file + AppConfig *beegoAppConfig + // AppName represent Application name, always the project folder name + AppName string + // AppPath is the path to the application + AppPath string + // AppConfigPath is the path to the config files + AppConfigPath string + // AppConfigProvider is the provider for the config, default is ini + AppConfigProvider string + // AutoRender is a flag of render template automatically. It's always turn off in API application + // default is true + AutoRender bool + // BeegoServerName exported in response header. + BeegoServerName string + // CopyRequestBody is just useful for raw request body in context. default is false + CopyRequestBody bool + // DirectoryIndex wheather display directory index. default is false. + DirectoryIndex bool + // EnableAdmin means turn on admin module to log every request info. + EnableAdmin bool + // EnableDocs enable generate docs & server docs API Swagger + EnableDocs bool + // EnableErrorsShow wheather show errors in page. if true, show error and trace info in page rendered with error template. + EnableErrorsShow bool + // EnabelFcgi turn on the fcgi Listen, default is false + EnabelFcgi bool + // EnableGzip means gzip the response + EnableGzip bool + // EnableHTTPListen represent whether turn on the HTTP, default is true + EnableHTTPListen bool + // EnableHTTPTLS represent whether turn on the HTTPS, default is true + EnableHTTPTLS bool + // EnableStdIo works with EnabelFcgi Use FCGI via standard I/O + EnableStdIo bool + // EnableXSRF whether turn on xsrf. default is false + EnableXSRF bool + // FlashName is the name of the flash variable found in response header and cookie + FlashName string + // FlashSeperator used to seperate flash key:value, default is BEEGOFLASH + FlashSeperator string + // GlobalSessions is the instance for the session manager + GlobalSessions *session.Manager + // Graceful means use graceful module to start the server + Graceful bool + // workPath is always the same as AppPath, but sometime when it started with other + // program, like supervisor + workPath string + // ListenTCP4 represent only Listen in TCP4, default is false + ListenTCP4 bool + // MaxMemory The whole request body is parsed and up to a total of maxMemory + // bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files + MaxMemory int64 + // HTTPAddr is the TCP network address addr for HTTP + HTTPAddr string + // HTTPPort is listens port for HTTP + HTTPPort int + // HTTPSPort is listens port for HTTPS + HTTPSPort int + // HTTPCertFile is the path to certificate file + HTTPCertFile string + // HTTPKeyFile is the path to private key file + HTTPKeyFile string + // HTTPServerTimeOut HTTP server timeout. default is 0, no timeout + HTTPServerTimeOut int64 + // RecoverPanic is a flag for auto recover panic, default is true + RecoverPanic bool + // RouterCaseSensitive means whether router case sensitive, default is true + RouterCaseSensitive bool + // RunMode represent the staging, "dev" or "prod" + RunMode string + // SessionOn means whether turn on the session auto when application started. default is false. + SessionOn bool + // SessionProvider means session provider, e.q memory, mysql, redis,etc. + SessionProvider string + // SessionName is the cookie name when saving session id into cookie. + SessionName string + // SessionGCMaxLifetime for auto cleaning expired session. + SessionGCMaxLifetime int64 + // SessionProviderConfig is for the provider config, define save path or connection info. + SessionProviderConfig string + // SessionCookieLifeTime means the life time of session id in cookie. + SessionCookieLifeTime int + // SessionAutoSetCookie auto setcookie + SessionAutoSetCookie bool + // SessionDomain means the cookie domain default is empty + SessionDomain string + // StaticDir store the static path, key is path, value is the folder + StaticDir map[string]string + // StaticExtensionsToGzip stores the extensions which need to gzip(.js,.css,etc) + StaticExtensionsToGzip []string + // TemplateCache store the caching template + TemplateCache map[string]*template.Template + // TemplateLeft left delimiter + TemplateLeft string + // TemplateRight right delimiter + TemplateRight string + // ViewsPath means the template folder + ViewsPath string + // XSRFKEY xsrf hash salt string. + XSRFKEY string + // XSRFExpire is the expiry of xsrf value. + XSRFExpire int ) type beegoAppConfig struct { @@ -239,12 +295,12 @@ func init() { TemplateCache = make(map[string]*template.Template) // set this to 0.0.0.0 to make this app available to externally - EnableHttpListen = true //default enable http Listen + EnableHTTPListen = true //default enable http Listen - HttpAddr = "" - HttpPort = 8080 + HTTPAddr = "" + HTTPPort = 8080 - HttpsPort = 10443 + HTTPSPort = 10443 AppName = "beego" @@ -260,20 +316,15 @@ func init() { SessionProvider = "memory" SessionName = "beegosessionID" SessionGCMaxLifetime = 3600 - SessionSavePath = "" + SessionProviderConfig = "" SessionCookieLifeTime = 0 //set cookie default is the brower life SessionAutoSetCookie = true - UseFcgi = false - UseStdIo = false - MaxMemory = 1 << 26 //64MB - EnableGzip = false + HTTPServerTimeOut = 0 - HttpServerTimeOut = 0 - - ErrorsShow = true + EnableErrorsShow = true XSRFKEY = "beegoxsrf" XSRFExpire = 0 @@ -284,8 +335,8 @@ func init() { BeegoServerName = "beegoServer:" + VERSION EnableAdmin = false - AdminHttpAddr = "127.0.0.1" - AdminHttpPort = 8088 + AdminHTTPAddr = "127.0.0.1" + AdminHTTPPort = 8088 FlashName = "BEEGO_FLASH" FlashSeperator = "BEEGOFLASH" @@ -326,18 +377,18 @@ func ParseConfig() (err error) { RunMode = runmode } - HttpAddr = AppConfig.String("HttpAddr") + HTTPAddr = AppConfig.String("HTTPAddr") - if v, err := AppConfig.Int("HttpPort"); err == nil { - HttpPort = v + if v, err := AppConfig.Int("HTTPPort"); err == nil { + HTTPPort = v } if v, err := AppConfig.Bool("ListenTCP4"); err == nil { ListenTCP4 = v } - if v, err := AppConfig.Bool("EnableHttpListen"); err == nil { - EnableHttpListen = v + if v, err := AppConfig.Bool("EnableHTTPListen"); err == nil { + EnableHTTPListen = v } if maxmemory, err := AppConfig.Int64("MaxMemory"); err == nil { @@ -372,8 +423,8 @@ func ParseConfig() (err error) { SessionName = sessName } - if sesssavepath := AppConfig.String("SessionSavePath"); sesssavepath != "" { - SessionSavePath = sesssavepath + if sessProvConfig := AppConfig.String("SessionProviderConfig"); sessProvConfig != "" { + SessionProviderConfig = sessProvConfig } if sessMaxLifeTime, err := AppConfig.Int64("SessionGCMaxLifetime"); err == nil && sessMaxLifeTime != 0 { @@ -384,8 +435,8 @@ func ParseConfig() (err error) { SessionCookieLifeTime = sesscookielifetime } - if usefcgi, err := AppConfig.Bool("UseFcgi"); err == nil { - UseFcgi = usefcgi + if enabelFcgi, err := AppConfig.Bool("EnabelFcgi"); err == nil { + EnabelFcgi = enabelFcgi } if enablegzip, err := AppConfig.Bool("EnableGzip"); err == nil { @@ -396,12 +447,12 @@ func ParseConfig() (err error) { DirectoryIndex = directoryindex } - if timeout, err := AppConfig.Int64("HttpServerTimeOut"); err == nil { - HttpServerTimeOut = timeout + if timeout, err := AppConfig.Int64("HTTPServerTimeOut"); err == nil { + HTTPServerTimeOut = timeout } - if errorsshow, err := AppConfig.Bool("ErrorsShow"); err == nil { - ErrorsShow = errorsshow + if errorsshow, err := AppConfig.Bool("EnableErrorsShow"); err == nil { + EnableErrorsShow = errorsshow } if copyrequestbody, err := AppConfig.Bool("CopyRequestBody"); err == nil { @@ -428,20 +479,20 @@ func ParseConfig() (err error) { TemplateRight = tplright } - if httptls, err := AppConfig.Bool("EnableHttpTLS"); err == nil { - EnableHttpTLS = httptls + if httptls, err := AppConfig.Bool("EnableHTTPTLS"); err == nil { + EnableHTTPTLS = httptls } - if httpsport, err := AppConfig.Int("HttpsPort"); err == nil { - HttpsPort = httpsport + if httpsport, err := AppConfig.Int("HTTPSPort"); err == nil { + HTTPSPort = httpsport } - if certfile := AppConfig.String("HttpCertFile"); certfile != "" { - HttpCertFile = certfile + if certfile := AppConfig.String("HTTPCertFile"); certfile != "" { + HTTPCertFile = certfile } - if keyfile := AppConfig.String("HttpKeyFile"); keyfile != "" { - HttpKeyFile = keyfile + if keyfile := AppConfig.String("HTTPKeyFile"); keyfile != "" { + HTTPKeyFile = keyfile } if serverName := AppConfig.String("BeegoServerName"); serverName != "" { @@ -491,12 +542,12 @@ func ParseConfig() (err error) { EnableAdmin = enableadmin } - if adminhttpaddr := AppConfig.String("AdminHttpAddr"); adminhttpaddr != "" { - AdminHttpAddr = adminhttpaddr + if adminhttpaddr := AppConfig.String("AdminHTTPAddr"); adminhttpaddr != "" { + AdminHTTPAddr = adminhttpaddr } - if adminhttpport, err := AppConfig.Int("AdminHttpPort"); err == nil { - AdminHttpPort = adminhttpport + if adminhttpport, err := AppConfig.Int("AdminHTTPPort"); err == nil { + AdminHTTPPort = adminhttpport } if enabledocs, err := AppConfig.Bool("EnableDocs"); err == nil { diff --git a/hooks.go b/hooks.go index 526913c3..3691a800 100644 --- a/hooks.go +++ b/hooks.go @@ -65,8 +65,8 @@ func registerSession() error { if sessionConfig == "" { sessionConfig = `{"cookieName":"` + SessionName + `",` + `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` + - `"providerConfig":"` + filepath.ToSlash(SessionSavePath) + `",` + - `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` + + `"providerConfig":"` + filepath.ToSlash(SessionProviderConfig) + `",` + + `"secure":` + strconv.FormatBool(EnableHTTPTLS) + `,` + `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` + `"domain":"` + SessionDomain + `",` + `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}` diff --git a/router.go b/router.go index 7cce86fe..4ac6cb5b 100644 --- a/router.go +++ b/router.go @@ -617,7 +617,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if ok, params := filterR.ValidRouter(urlPath); ok { for k, v := range params { if context.Input.Params == nil { - context.Input.Params = make(map[string]string) + context.Input.Params = make(map[string]string) } context.Input.Params[k] = v } @@ -869,7 +869,7 @@ func (p *ControllerRegister) recoverPanic(context *beecontext.Context) { if !RecoverPanic { panic(err) } else { - if ErrorsShow { + if EnableErrorsShow { if _, ok := ErrorMaps[fmt.Sprint(err)]; ok { exception(fmt.Sprint(err), context) return From 61570ac2f7b429220a9b2603bec299bb560aa8ab Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 10:43:42 +0800 Subject: [PATCH 192/436] make golint happy with controller.go --- controller.go | 132 +++++++++++++++++++++++------------------------- router.go | 2 +- template.go | 2 +- templatefunc.go | 30 +++++------ 4 files changed, 80 insertions(+), 86 deletions(-) diff --git a/controller.go b/controller.go index 65b311b6..cad2b5ac 100644 --- a/controller.go +++ b/controller.go @@ -34,18 +34,19 @@ import ( //commonly used mime-types const ( - applicationJson = "application/json" - applicationXml = "application/xml" - textXml = "text/xml" + applicationJSON = "application/json" + applicationXML = "application/xml" + textXML = "text/xml" ) var ( - // custom error when user stop request handler manually. - USERSTOPRUN = errors.New("User stop run") - GlobalControllerRouter map[string][]ControllerComments = make(map[string][]ControllerComments) //pkgpath+controller:comments + // ErrAbort custom error when user stop request handler manually. + ErrAbort = errors.New("User stop run") + // GlobalControllerRouter store comments with controller. pkgpath+controller:comments + GlobalControllerRouter = make(map[string][]ControllerComments) ) -// store the comment for the controller method +// ControllerComments store the comment for the controller method type ControllerComments struct { Method string Router string @@ -64,7 +65,7 @@ type Controller struct { Layout string LayoutSections map[string]string // the key is the section name and the value is the template name TplExt string - _xsrf_token string + _xsrfToken string gotofunc string CruSession session.SessionStore XSRFExpire int @@ -153,20 +154,20 @@ func (c *Controller) Options() { http.Error(c.Ctx.ResponseWriter, "Method Not Allowed", 405) } -// call function fn +// HandlerFunc call function with the name func (c *Controller) HandlerFunc(fnname string) bool { if v, ok := c.methodMapping[fnname]; ok { v() return true - } else { - return false } + return false } // URLMapping register the internal Controller router. func (c *Controller) URLMapping() { } +// Mapping the method to function func (c *Controller) Mapping(method string, fn func()) { c.methodMapping[method] = fn } @@ -177,13 +178,11 @@ func (c *Controller) Render() error { return nil } rb, err := c.RenderBytes() - if err != nil { return err - } else { - c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") - c.Ctx.Output.Body(rb) } + c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") + c.Ctx.Output.Body(rb) return nil } @@ -200,7 +199,7 @@ func (c *Controller) RenderBytes() ([]byte, error) { if c.TplNames == "" { c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt } - + if RunMode == "dev" { buildFiles := make([]string, 1) buildFiles = append(buildFiles, c.TplNames) @@ -252,25 +251,25 @@ func (c *Controller) RenderBytes() ([]byte, error) { } icontent, _ := ioutil.ReadAll(ibytes) return icontent, nil - } else { - if c.TplNames == "" { - c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt - } - if RunMode == "dev" { - BuildTemplate(ViewsPath, c.TplNames) - } - ibytes := bytes.NewBufferString("") - if _, ok := BeeTemplates[c.TplNames]; !ok { - panic("can't find templatefile in the path:" + c.TplNames) - } - err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data) - if err != nil { - Trace("template Execute err:", err) - return nil, err - } - icontent, _ := ioutil.ReadAll(ibytes) - return icontent, nil } + + if c.TplNames == "" { + c.TplNames = strings.ToLower(c.controllerName) + "/" + strings.ToLower(c.actionName) + "." + c.TplExt + } + if RunMode == "dev" { + BuildTemplate(ViewsPath, c.TplNames) + } + ibytes := bytes.NewBufferString("") + if _, ok := BeeTemplates[c.TplNames]; !ok { + panic("can't find templatefile in the path:" + c.TplNames) + } + err := BeeTemplates[c.TplNames].ExecuteTemplate(ibytes, c.TplNames, c.Data) + if err != nil { + Trace("template Execute err:", err) + return nil, err + } + icontent, _ := ioutil.ReadAll(ibytes) + return icontent, nil } // Redirect sends the redirection response to url with status code. @@ -278,7 +277,7 @@ func (c *Controller) Redirect(url string, code int) { c.Ctx.Redirect(code, url) } -// Aborts stops controller handler and show the error data if code is defined in ErrorMap or code string. +// Abort stops controller handler and show the error data if code is defined in ErrorMap or code string. func (c *Controller) Abort(code string) { status, err := strconv.Atoi(code) if err != nil { @@ -296,29 +295,28 @@ func (c *Controller) CustomAbort(status int, body string) { } // last panic user string c.Ctx.ResponseWriter.Write([]byte(body)) - panic(USERSTOPRUN) + panic(ErrAbort) } // StopRun makes panic of USERSTOPRUN error and go to recover function if defined. func (c *Controller) StopRun() { - panic(USERSTOPRUN) + panic(ErrAbort) } -// UrlFor does another controller handler in this request function. +// URLFor does another controller handler in this request function. // it goes to this controller method if endpoint is not clear. -func (c *Controller) UrlFor(endpoint string, values ...interface{}) string { +func (c *Controller) URLFor(endpoint string, values ...interface{}) string { if len(endpoint) <= 0 { return "" } if endpoint[0] == '.' { - return UrlFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) - } else { - return UrlFor(endpoint, values...) + return URLFor(reflect.Indirect(reflect.ValueOf(c.AppController)).Type().Name()+endpoint, values...) } + return URLFor(endpoint, values...) } -// ServeJson sends a json response with encoding charset. -func (c *Controller) ServeJson(encoding ...bool) { +// ServeJSON sends a json response with encoding charset. +func (c *Controller) ServeJSON(encoding ...bool) { var hasIndent bool var hasencoding bool if RunMode == "prod" { @@ -332,8 +330,8 @@ func (c *Controller) ServeJson(encoding ...bool) { c.Ctx.Output.Json(c.Data["json"], hasIndent, hasencoding) } -// ServeJsonp sends a jsonp response. -func (c *Controller) ServeJsonp() { +// ServeJSONP sends a jsonp response. +func (c *Controller) ServeJSONP() { var hasIndent bool if RunMode == "prod" { hasIndent = false @@ -343,8 +341,8 @@ func (c *Controller) ServeJsonp() { c.Ctx.Output.Jsonp(c.Data["jsonp"], hasIndent) } -// ServeXml sends xml response. -func (c *Controller) ServeXml() { +// ServeXML sends xml response. +func (c *Controller) ServeXML() { var hasIndent bool if RunMode == "prod" { hasIndent = false @@ -358,12 +356,12 @@ func (c *Controller) ServeXml() { func (c *Controller) ServeFormatted() { accept := c.Ctx.Input.Header("Accept") switch accept { - case applicationJson: - c.ServeJson() - case applicationXml, textXml: - c.ServeXml() + case applicationJSON: + c.ServeJSON() + case applicationXML, textXML: + c.ServeXML() default: - c.ServeJson() + c.ServeJSON() } } @@ -389,9 +387,8 @@ func (c *Controller) GetString(key string, def ...string) string { if v := c.Ctx.Input.Query(key); v != "" { return v - } else { - return defv } + return defv } // GetStrings returns the input string slice by key string or the default value while it's present and input is blank @@ -410,9 +407,8 @@ func (c *Controller) GetStrings(key string, def ...[]string) []string { vs := f[key] if len(vs) > 0 { return vs - } else { - return defv } + return defv } // GetInt returns input as an int or the default value while it's present and input is blank @@ -586,7 +582,7 @@ func (c *Controller) GetSession(name interface{}) interface{} { return c.CruSession.Get(name) } -// SetSession removes value from session. +// DelSession removes value from session. func (c *Controller) DelSession(name interface{}) { if c.CruSession == nil { c.StartSession() @@ -625,34 +621,34 @@ func (c *Controller) SetSecureCookie(Secret, name, value string, others ...inter c.Ctx.SetSecureCookie(Secret, name, value, others...) } -// XsrfToken creates a xsrf token string and returns. -func (c *Controller) XsrfToken() string { - if c._xsrf_token == "" { +// XSRFToken creates a CSRF token string and returns. +func (c *Controller) XSRFToken() string { + if c._xsrfToken == "" { var expire int64 if c.XSRFExpire > 0 { expire = int64(c.XSRFExpire) } else { expire = int64(XSRFExpire) } - c._xsrf_token = c.Ctx.XsrfToken(XSRFKEY, expire) + c._xsrfToken = c.Ctx.XsrfToken(XSRFKEY, expire) } - return c._xsrf_token + return c._xsrfToken } -// CheckXsrfCookie checks xsrf token in this request is valid or not. +// CheckXSRFCookie checks xsrf token in this request is valid or not. // the token can provided in request header "X-Xsrftoken" and "X-CsrfToken" // or in form field value named as "_xsrf". -func (c *Controller) CheckXsrfCookie() bool { +func (c *Controller) CheckXSRFCookie() bool { if !c.EnableXSRF { return true } return c.Ctx.CheckXsrfCookie() } -// XsrfFormHtml writes an input field contains xsrf token value. -func (c *Controller) XsrfFormHtml() string { +// XSRFFormHTML writes an input field contains xsrf token value. +func (c *Controller) XSRFFormHTML() string { return "" + c._xsrfToken + "\"/>" } // GetControllerAndAction gets the executing controller name and action name. diff --git a/router.go b/router.go index 4ac6cb5b..6fbf90ea 100644 --- a/router.go +++ b/router.go @@ -863,7 +863,7 @@ Admin: func (p *ControllerRegister) recoverPanic(context *beecontext.Context) { if err := recover(); err != nil { - if err == USERSTOPRUN { + if err == ErrAbort { return } if !RecoverPanic { diff --git a/template.go b/template.go index e976886a..9403c71f 100644 --- a/template.go +++ b/template.go @@ -65,7 +65,7 @@ func init() { beegoTplFuncMap["lt"] = lt // < beegoTplFuncMap["ne"] = ne // != - beegoTplFuncMap["urlfor"] = UrlFor // != + beegoTplFuncMap["urlfor"] = URLFor // != } // AddFuncMap let user to register a func in the template. diff --git a/templatefunc.go b/templatefunc.go index d1f64f8a..1106e4d0 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -140,11 +140,11 @@ func Compare(a, b interface{}) (equal bool) { } func CompareNot(a, b interface{}) (equal bool) { - return ! Compare(a, b) + return !Compare(a, b) } func NotNil(a interface{}) (is_nil bool) { - return CompareNot(a, nil) + return CompareNot(a, nil) } func Config(returnType, key string, defaultVal interface{}) (value interface{}, err error) { @@ -237,14 +237,14 @@ func Htmlunquote(src string) string { return strings.TrimSpace(text) } -// UrlFor returns url string with another registered controller handler with params. +// URLFor returns url string with another registered controller handler with params. // usage: // -// UrlFor(".index") -// print UrlFor("index") +// URLFor(".index") +// print URLFor("index") // router /login -// print UrlFor("login") -// print UrlFor("login", "next","/"") +// print URLFor("login") +// print URLFor("login", "next","/"") // router /profile/:username // print UrlFor("profile", ":username","John Doe") // result: @@ -254,7 +254,7 @@ func Htmlunquote(src string) string { // /user/John%20Doe // // more detail http://beego.me/docs/mvc/controller/urlbuilding.md -func UrlFor(endpoint string, values ...interface{}) string { +func URLFor(endpoint string, values ...interface{}) string { return BeeApp.Handlers.UrlFor(endpoint, values...) } @@ -698,22 +698,21 @@ func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) { storedVal := arg1Val.MapIndex(arg2Val) - if storedVal.IsValid() { var result interface{} switch arg1Type.Elem().Kind() { - case reflect.Bool: + case reflect.Bool: result = storedVal.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: result = storedVal.Int() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: result = storedVal.Uint() - case reflect.Float32, reflect.Float64: + case reflect.Float32, reflect.Float64: result = storedVal.Float() - case reflect.String: + case reflect.String: result = storedVal.String() - default: + default: result = storedVal.Interface() } @@ -727,7 +726,6 @@ func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) { return nil, nil } - } else { return nil, nil } From 67b36d7c487a163ce2496a9f7d38d6dc12090f36 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 21:41:38 +0800 Subject: [PATCH 193/436] make golint happy --- controller.go | 4 +- doc.go | 2 +- docs.go | 9 +++-- error.go | 6 +-- filter.go | 3 +- flash_test.go | 2 +- hooks.go | 20 +++++----- log.go | 15 ++++--- memzipfile.go | 6 ++- mime.go | 2 +- namespace.go | 105 +++++++++++++++++++++++++------------------------ router.go | 4 +- router_test.go | 4 +- 13 files changed, 94 insertions(+), 88 deletions(-) diff --git a/controller.go b/controller.go index cad2b5ac..b225df03 100644 --- a/controller.go +++ b/controller.go @@ -88,8 +88,8 @@ type ControllerInterface interface { Options() Finish() Render() error - XsrfToken() string - CheckXsrfCookie() bool + XSRFToken() string + CheckXSRFCookie() bool HandlerFunc(fn string) bool URLMapping() } diff --git a/doc.go b/doc.go index 3a5b5cb8..4be305b3 100644 --- a/doc.go +++ b/doc.go @@ -1,4 +1,5 @@ /* +Package beego provide a MVC framework beego: an open-source, high-performance, modular, full-stack web framework It is used for rapid development of RESTful APIs, web apps and backend services in Go. @@ -14,4 +15,3 @@ beego is inspired by Tornado, Sinatra and Flask with the added benefit of some G more infomation: http://beego.me */ package beego - diff --git a/docs.go b/docs.go index aaad205e..1dbed6cc 100644 --- a/docs.go +++ b/docs.go @@ -20,20 +20,21 @@ import ( "github.com/astaxie/beego/context" ) -var GlobalDocApi map[string]interface{} +// GlobalDocAPI store the swagger api documents +var GlobalDocAPI map[string]interface{} func init() { if EnableDocs { - GlobalDocApi = make(map[string]interface{}) + GlobalDocAPI = make(map[string]interface{}) } } func serverDocs(ctx *context.Context) { var obj interface{} if splat := ctx.Input.Param(":splat"); splat == "" { - obj = GlobalDocApi["Root"] + obj = GlobalDocAPI["Root"] } else { - if v, ok := GlobalDocApi[splat]; ok { + if v, ok := GlobalDocAPI[splat]; ok { obj = v } } diff --git a/error.go b/error.go index f5c0c80b..b4708b30 100644 --- a/error.go +++ b/error.go @@ -362,7 +362,7 @@ func gatewayTimeout(rw http.ResponseWriter, r *http.Request) { // usage: // beego.ErrorHandler("404",NotFound) // beego.ErrorHandler("500",InternalServerError) -func Errorhandler(code string, h http.HandlerFunc) *App { +func ErrorHandler(code string, h http.HandlerFunc) *App { errinfo := &errorInfo{} errinfo.errorType = errorTypeHandler errinfo.handler = h @@ -373,7 +373,7 @@ func Errorhandler(code string, h http.HandlerFunc) *App { // ErrorController registers ControllerInterface to each http err code string. // usage: -// beego.ErrorHandler(&controllers.ErrorController{}) +// beego.ErrorController(&controllers.ErrorController{}) func ErrorController(c ControllerInterface) *App { reflectVal := reflect.ValueOf(c) rt := reflectVal.Type() @@ -431,7 +431,7 @@ func executeError(err *errorInfo, ctx *context.Context, code int) { execController.URLMapping() - in := make([]reflect.Value, 0) + var in []reflect.Value method := vc.MethodByName(err.method) method.Call(in) diff --git a/filter.go b/filter.go index f673ab66..7dbc7028 100644 --- a/filter.go +++ b/filter.go @@ -39,7 +39,6 @@ func (f *FilterRouter) ValidRouter(url string) (bool, map[string]string) { } if isok, ok := isok.(bool); ok { return isok, params - } else { - return false, nil } + return false, nil } diff --git a/flash_test.go b/flash_test.go index b655f552..640d54de 100644 --- a/flash_test.go +++ b/flash_test.go @@ -30,7 +30,7 @@ func (t *TestFlashController) TestWriteFlash() { flash.Notice("TestFlashString") flash.Store(&t.Controller) // we choose to serve json because we don't want to load a template html file - t.ServeJson(true) + t.ServeJSON(true) } func TestFlashHeader(t *testing.T) { diff --git a/hooks.go b/hooks.go index 3691a800..178d8dc4 100644 --- a/hooks.go +++ b/hooks.go @@ -19,41 +19,41 @@ func registerMime() error { // register default error http handlers, 404,401,403,500 and 503. func registerDefaultErrorHandler() error { if _, ok := ErrorMaps["401"]; !ok { - Errorhandler("401", unauthorized) + ErrorHandler("401", unauthorized) } if _, ok := ErrorMaps["402"]; !ok { - Errorhandler("402", paymentRequired) + ErrorHandler("402", paymentRequired) } if _, ok := ErrorMaps["403"]; !ok { - Errorhandler("403", forbidden) + ErrorHandler("403", forbidden) } if _, ok := ErrorMaps["404"]; !ok { - Errorhandler("404", notFound) + ErrorHandler("404", notFound) } if _, ok := ErrorMaps["405"]; !ok { - Errorhandler("405", methodNotAllowed) + ErrorHandler("405", methodNotAllowed) } if _, ok := ErrorMaps["500"]; !ok { - Errorhandler("500", internalServerError) + ErrorHandler("500", internalServerError) } if _, ok := ErrorMaps["501"]; !ok { - Errorhandler("501", notImplemented) + ErrorHandler("501", notImplemented) } if _, ok := ErrorMaps["502"]; !ok { - Errorhandler("502", badGateway) + ErrorHandler("502", badGateway) } if _, ok := ErrorMaps["503"]; !ok { - Errorhandler("503", serviceUnavailable) + ErrorHandler("503", serviceUnavailable) } if _, ok := ErrorMaps["504"]; !ok { - Errorhandler("504", gatewayTimeout) + ErrorHandler("504", gatewayTimeout) } return nil } diff --git a/log.go b/log.go index 7949ed96..68252236 100644 --- a/log.go +++ b/log.go @@ -32,18 +32,18 @@ const ( LevelDebug ) -// SetLogLevel sets the global log level used by the simple -// logger. +// SetLevel sets the global log level used by the simple logger. func SetLevel(l int) { BeeLogger.SetLevel(l) } +// SetLogFuncCall set the CallDepth, default is 3 func SetLogFuncCall(b bool) { BeeLogger.EnableFuncCallDepth(b) BeeLogger.SetLogFuncCallDepth(3) } -// logger references the used application logger. +// BeeLogger references the used application logger. var BeeLogger *logs.BeeLogger // SetLogger sets a new logger. @@ -55,10 +55,12 @@ func SetLogger(adaptername string, config string) error { return nil } +// Emergency logs a message at emergency level. func Emergency(v ...interface{}) { BeeLogger.Emergency(generateFmtStr(len(v)), v...) } +// Alert logs a message at alert level. func Alert(v ...interface{}) { BeeLogger.Alert(generateFmtStr(len(v)), v...) } @@ -78,21 +80,22 @@ func Warning(v ...interface{}) { BeeLogger.Warning(generateFmtStr(len(v)), v...) } -// compatibility alias for Warning() +// Warn compatibility alias for Warning() func Warn(v ...interface{}) { BeeLogger.Warn(generateFmtStr(len(v)), v...) } +// Notice logs a message at notice level. func Notice(v ...interface{}) { BeeLogger.Notice(generateFmtStr(len(v)), v...) } -// Info logs a message at info level. +// Informational logs a message at info level. func Informational(v ...interface{}) { BeeLogger.Informational(generateFmtStr(len(v)), v...) } -// compatibility alias for Warning() +// Info compatibility alias for Warning() func Info(v ...interface{}) { BeeLogger.Info(generateFmtStr(len(v)), v...) } diff --git a/memzipfile.go b/memzipfile.go index cc5e3851..0fff44b6 100644 --- a/memzipfile.go +++ b/memzipfile.go @@ -28,8 +28,10 @@ import ( "time" ) -var gmfim map[string]*memFileInfo = make(map[string]*memFileInfo) -var lock sync.RWMutex +var ( + gmfim = make(map[string]*memFileInfo) + lock sync.RWMutex +) // OpenMemZipFile returns MemFile object with a compressed static file. // it's used for serve static file if gzip enable. diff --git a/mime.go b/mime.go index 0034976e..e85fcb2a 100644 --- a/mime.go +++ b/mime.go @@ -14,7 +14,7 @@ package beego -var mimemaps map[string]string = map[string]string{ +var mimemaps = map[string]string{ ".3dm": "x-world/x-3dmf", ".3dmf": "x-world/x-3dmf", ".7z": "application/x-7z-compressed", diff --git a/namespace.go b/namespace.go index 86dc2ebd..0dfdd7af 100644 --- a/namespace.go +++ b/namespace.go @@ -23,7 +23,8 @@ import ( type namespaceCond func(*beecontext.Context) bool -type innerNamespace func(*Namespace) +// LinkNamespace used as link action +type LinkNamespace func(*Namespace) // Namespace is store all the info type Namespace struct { @@ -31,8 +32,8 @@ type Namespace struct { handlers *ControllerRegister } -// get new Namespace -func NewNamespace(prefix string, params ...innerNamespace) *Namespace { +// NewNamespace get new Namespace +func NewNamespace(prefix string, params ...LinkNamespace) *Namespace { ns := &Namespace{ prefix: prefix, handlers: NewControllerRegister(), @@ -43,7 +44,7 @@ func NewNamespace(prefix string, params ...innerNamespace) *Namespace { return ns } -// set condtion function +// Cond set condtion function // if cond return true can run this namespace, else can't // usage: // ns.Cond(func (ctx *context.Context) bool{ @@ -72,7 +73,7 @@ func (n *Namespace) Cond(cond namespaceCond) *Namespace { return n } -// add filter in the Namespace +// Filter add filter in the Namespace // action has before & after // FilterFunc // usage: @@ -95,98 +96,98 @@ func (n *Namespace) Filter(action string, filter ...FilterFunc) *Namespace { return n } -// same as beego.Rourer +// Router same as beego.Rourer // refer: https://godoc.org/github.com/astaxie/beego#Router func (n *Namespace) Router(rootpath string, c ControllerInterface, mappingMethods ...string) *Namespace { n.handlers.Add(rootpath, c, mappingMethods...) return n } -// same as beego.AutoRouter +// AutoRouter same as beego.AutoRouter // refer: https://godoc.org/github.com/astaxie/beego#AutoRouter func (n *Namespace) AutoRouter(c ControllerInterface) *Namespace { n.handlers.AddAuto(c) return n } -// same as beego.AutoPrefix +// AutoPrefix same as beego.AutoPrefix // refer: https://godoc.org/github.com/astaxie/beego#AutoPrefix func (n *Namespace) AutoPrefix(prefix string, c ControllerInterface) *Namespace { n.handlers.AddAutoPrefix(prefix, c) return n } -// same as beego.Get +// Get same as beego.Get // refer: https://godoc.org/github.com/astaxie/beego#Get func (n *Namespace) Get(rootpath string, f FilterFunc) *Namespace { n.handlers.Get(rootpath, f) return n } -// same as beego.Post +// Post same as beego.Post // refer: https://godoc.org/github.com/astaxie/beego#Post func (n *Namespace) Post(rootpath string, f FilterFunc) *Namespace { n.handlers.Post(rootpath, f) return n } -// same as beego.Delete +// Delete same as beego.Delete // refer: https://godoc.org/github.com/astaxie/beego#Delete func (n *Namespace) Delete(rootpath string, f FilterFunc) *Namespace { n.handlers.Delete(rootpath, f) return n } -// same as beego.Put +// Put same as beego.Put // refer: https://godoc.org/github.com/astaxie/beego#Put func (n *Namespace) Put(rootpath string, f FilterFunc) *Namespace { n.handlers.Put(rootpath, f) return n } -// same as beego.Head +// Head same as beego.Head // refer: https://godoc.org/github.com/astaxie/beego#Head func (n *Namespace) Head(rootpath string, f FilterFunc) *Namespace { n.handlers.Head(rootpath, f) return n } -// same as beego.Options +// Options same as beego.Options // refer: https://godoc.org/github.com/astaxie/beego#Options func (n *Namespace) Options(rootpath string, f FilterFunc) *Namespace { n.handlers.Options(rootpath, f) return n } -// same as beego.Patch +// Patch same as beego.Patch // refer: https://godoc.org/github.com/astaxie/beego#Patch func (n *Namespace) Patch(rootpath string, f FilterFunc) *Namespace { n.handlers.Patch(rootpath, f) return n } -// same as beego.Any +// Any same as beego.Any // refer: https://godoc.org/github.com/astaxie/beego#Any func (n *Namespace) Any(rootpath string, f FilterFunc) *Namespace { n.handlers.Any(rootpath, f) return n } -// same as beego.Handler +// Handler same as beego.Handler // refer: https://godoc.org/github.com/astaxie/beego#Handler func (n *Namespace) Handler(rootpath string, h http.Handler) *Namespace { n.handlers.Handler(rootpath, h) return n } -// add include class +// Include add include class // refer: https://godoc.org/github.com/astaxie/beego#Include func (n *Namespace) Include(cList ...ControllerInterface) *Namespace { n.handlers.Include(cList...) return n } -// nest Namespace +// Namespace add nest Namespace // usage: //ns := beego.NewNamespace(“/v1”). //Namespace( @@ -230,7 +231,7 @@ func (n *Namespace) Namespace(ns ...*Namespace) *Namespace { return n } -// register Namespace into beego.Handler +// AddNamespace register Namespace into beego.Handler // support multi Namespace func AddNamespace(nl ...*Namespace) { for _, n := range nl { @@ -275,113 +276,113 @@ func addPrefix(t *Tree, prefix string) { } -// Namespace Condition -func NSCond(cond namespaceCond) innerNamespace { +// NSCond is Namespace Condition +func NSCond(cond namespaceCond) LinkNamespace { return func(ns *Namespace) { ns.Cond(cond) } } -// Namespace BeforeRouter filter -func NSBefore(filiterList ...FilterFunc) innerNamespace { +// NSBefore Namespace BeforeRouter filter +func NSBefore(filiterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Filter("before", filiterList...) } } -// Namespace FinishRouter filter -func NSAfter(filiterList ...FilterFunc) innerNamespace { +// NSAfter add Namespace FinishRouter filter +func NSAfter(filiterList ...FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Filter("after", filiterList...) } } -// Namespace Include ControllerInterface -func NSInclude(cList ...ControllerInterface) innerNamespace { +// NSInclude Namespace Include ControllerInterface +func NSInclude(cList ...ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.Include(cList...) } } -// Namespace Router -func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) innerNamespace { +// NSRouter call Namespace Router +func NSRouter(rootpath string, c ControllerInterface, mappingMethods ...string) LinkNamespace { return func(ns *Namespace) { ns.Router(rootpath, c, mappingMethods...) } } -// Namespace Get -func NSGet(rootpath string, f FilterFunc) innerNamespace { +// NSGet call Namespace Get +func NSGet(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Get(rootpath, f) } } -// Namespace Post -func NSPost(rootpath string, f FilterFunc) innerNamespace { +// NSPost call Namespace Post +func NSPost(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Post(rootpath, f) } } -// Namespace Head -func NSHead(rootpath string, f FilterFunc) innerNamespace { +// NSHead call Namespace Head +func NSHead(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Head(rootpath, f) } } -// Namespace Put -func NSPut(rootpath string, f FilterFunc) innerNamespace { +// NSPut call Namespace Put +func NSPut(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Put(rootpath, f) } } -// Namespace Delete -func NSDelete(rootpath string, f FilterFunc) innerNamespace { +// NSDelete call Namespace Delete +func NSDelete(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Delete(rootpath, f) } } -// Namespace Any -func NSAny(rootpath string, f FilterFunc) innerNamespace { +// NSAny call Namespace Any +func NSAny(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Any(rootpath, f) } } -// Namespace Options -func NSOptions(rootpath string, f FilterFunc) innerNamespace { +// NSOptions call Namespace Options +func NSOptions(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Options(rootpath, f) } } -// Namespace Patch -func NSPatch(rootpath string, f FilterFunc) innerNamespace { +// NSPatch call Namespace Patch +func NSPatch(rootpath string, f FilterFunc) LinkNamespace { return func(ns *Namespace) { ns.Patch(rootpath, f) } } -//Namespace AutoRouter -func NSAutoRouter(c ControllerInterface) innerNamespace { +// NSAutoRouter call Namespace AutoRouter +func NSAutoRouter(c ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.AutoRouter(c) } } -// Namespace AutoPrefix -func NSAutoPrefix(prefix string, c ControllerInterface) innerNamespace { +// NSAutoPrefix call Namespace AutoPrefix +func NSAutoPrefix(prefix string, c ControllerInterface) LinkNamespace { return func(ns *Namespace) { ns.AutoPrefix(prefix, c) } } -// Namespace add sub Namespace -func NSNamespace(prefix string, params ...innerNamespace) innerNamespace { +// NSNamespace add sub Namespace +func NSNamespace(prefix string, params ...LinkNamespace) LinkNamespace { return func(ns *Namespace) { n := NewNamespace(prefix, params...) ns.Namespace(n) diff --git a/router.go b/router.go index 6fbf90ea..dc4ddb0f 100644 --- a/router.go +++ b/router.go @@ -770,10 +770,10 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) //if XSRF is Enable then check cookie where there has any cookie in the request's cookie _csrf if EnableXSRF { - execController.XsrfToken() + execController.XSRFToken() if r.Method == "POST" || r.Method == "DELETE" || r.Method == "PUT" || (r.Method == "POST" && (context.Input.Query("_method") == "DELETE" || context.Input.Query("_method") == "PUT")) { - execController.CheckXsrfCookie() + execController.CheckXSRFCookie() } } diff --git a/router_test.go b/router_test.go index 005f32d6..093a1fad 100644 --- a/router_test.go +++ b/router_test.go @@ -53,7 +53,7 @@ func (tc *TestController) Myext() { } func (tc *TestController) GetUrl() { - tc.Ctx.Output.Body([]byte(tc.UrlFor(".Myext"))) + tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext"))) } func (t *TestController) GetParams() { @@ -76,7 +76,7 @@ type JsonController struct { func (this *JsonController) Prepare() { this.Data["json"] = "prepare" - this.ServeJson(true) + this.ServeJSON(true) } func (this *JsonController) Get() { From 21fffc446b4f45e87c724e63afa0f05a36ff87cf Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 21:45:45 +0800 Subject: [PATCH 194/436] make golint happy parser.go --- parser.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/parser.go b/parser.go index cf8dee22..dd468c1c 100644 --- a/parser.go +++ b/parser.go @@ -42,13 +42,13 @@ func init() { ` var ( - lastupdateFilename string = "lastupdate.tmp" + lastupdateFilename = "lastupdate.tmp" commentFilename string pkgLastupdate map[string]int64 genInfoList map[string][]ControllerComments ) -const COMMENTFL = "commentsRouter_" +const coomentPrefix = "commentsRouter_" func init() { pkgLastupdate = make(map[string]int64) @@ -56,7 +56,7 @@ func init() { func parserPkg(pkgRealpath, pkgpath string) error { rep := strings.NewReplacer("/", "_", ".", "_") - commentFilename = COMMENTFL + rep.Replace(pkgpath) + ".go" + commentFilename = coomentPrefix + rep.Replace(pkgpath) + ".go" if !compareFile(pkgRealpath) { Info(pkgRealpath + " has not changed, not reloading") return nil @@ -129,9 +129,11 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat func genRouterCode() { os.Mkdir(path.Join(workPath, "routers"), 0755) Info("generate router from comments") - var globalinfo string - sortKey := make([]string, 0) - for k, _ := range genInfoList { + var ( + globalinfo string + sortKey []string + ) + for k := range genInfoList { sortKey = append(sortKey, k) } sort.Strings(sortKey) From c11740b647e765499470a21c7baefcf20ab0fb7a Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 22:01:13 +0800 Subject: [PATCH 195/436] make golint happy router.go --- router.go | 137 ++++++++++++++++++++++++------------------------ templatefunc.go | 2 +- 2 files changed, 69 insertions(+), 70 deletions(-) diff --git a/router.go b/router.go index dc4ddb0f..12b5f6ae 100644 --- a/router.go +++ b/router.go @@ -34,8 +34,8 @@ import ( "github.com/astaxie/beego/utils" ) +// default filter execution points const ( - // default filter execution points BeforeStatic = iota BeforeRouter BeforeExec @@ -50,7 +50,7 @@ const ( ) var ( - // supported http methods. + // HTTPMETHOD list the supported http methods. HTTPMETHOD = map[string]string{ "GET": "GET", "POST": "POST", @@ -71,10 +71,12 @@ var ( "SetSecureCookie", "XsrfToken", "CheckXsrfCookie", "XsrfFormHtml", "GetControllerAndAction"} - url_placeholder = "{{placeholder}}" - DefaultLogFilter FilterHandler = &logFilter{} + urlPlaceholder = "{{placeholder}}" + // DefaultAccessLogFilter will skip the accesslog if return true + DefaultAccessLogFilter FilterHandler = &logFilter{} ) +// FilterHandler is an interface for type FilterHandler interface { Filter(*beecontext.Context) bool } @@ -96,7 +98,7 @@ func (l *logFilter) Filter(ctx *beecontext.Context) bool { return false } -// To append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter +// ExceptMethodAppend to append a slice's value into "exceptMethod", for controller's methods shouldn't reflect to AutoRouter func ExceptMethodAppend(action string) { exceptMethod = append(exceptMethod, action) } @@ -196,7 +198,7 @@ func (p *ControllerRegister) addToRouter(method, pattern string, r *controllerIn } } -// only when the Runmode is dev will generate router file in the router/auto.go from the controller +// Include only when the Runmode is dev will generate router file in the router/auto.go from the controller // Include(&BankAccount{}, &OrderController{},&RefundController{},&ReceiptController{}) func (p *ControllerRegister) Include(cList ...ControllerInterface) { if RunMode == "dev" { @@ -238,7 +240,7 @@ func (p *ControllerRegister) Include(cList ...ControllerInterface) { } } -// add get method +// Get add get method // usage: // Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -247,7 +249,7 @@ func (p *ControllerRegister) Get(pattern string, f FilterFunc) { p.AddMethod("get", pattern, f) } -// add post method +// Post add post method // usage: // Post("/api", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -256,7 +258,7 @@ func (p *ControllerRegister) Post(pattern string, f FilterFunc) { p.AddMethod("post", pattern, f) } -// add put method +// Put add put method // usage: // Put("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -265,7 +267,7 @@ func (p *ControllerRegister) Put(pattern string, f FilterFunc) { p.AddMethod("put", pattern, f) } -// add delete method +// Delete add delete method // usage: // Delete("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -274,7 +276,7 @@ func (p *ControllerRegister) Delete(pattern string, f FilterFunc) { p.AddMethod("delete", pattern, f) } -// add head method +// Head add head method // usage: // Head("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -283,7 +285,7 @@ func (p *ControllerRegister) Head(pattern string, f FilterFunc) { p.AddMethod("head", pattern, f) } -// add patch method +// Patch add patch method // usage: // Patch("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -292,7 +294,7 @@ func (p *ControllerRegister) Patch(pattern string, f FilterFunc) { p.AddMethod("patch", pattern, f) } -// add options method +// Options add options method // usage: // Options("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -301,7 +303,7 @@ func (p *ControllerRegister) Options(pattern string, f FilterFunc) { p.AddMethod("options", pattern, f) } -// add all method +// Any add all method // usage: // Any("/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -310,7 +312,7 @@ func (p *ControllerRegister) Any(pattern string, f FilterFunc) { p.AddMethod("*", pattern, f) } -// add http method router +// AddMethod add http method router // usage: // AddMethod("get","/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") @@ -343,7 +345,7 @@ func (p *ControllerRegister) AddMethod(method, pattern string, f FilterFunc) { } } -// add user defined Handler +// Handler add user defined Handler func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ...interface{}) { route := &controllerInfo{} route.pattern = pattern @@ -359,7 +361,7 @@ func (p *ControllerRegister) Handler(pattern string, h http.Handler, options ... } } -// Add auto router to ControllerRegister. +// AddAuto router to ControllerRegister. // example beego.AddAuto(&MainContorlller{}), // MainController has method List and Page. // visit the url /main/list to execute List function @@ -368,7 +370,7 @@ func (p *ControllerRegister) AddAuto(c ControllerInterface) { p.AddAutoPrefix("/", c) } -// Add auto router to ControllerRegister with prefix. +// AddAutoPrefix Add auto router to ControllerRegister with prefix. // example beego.AddAutoPrefix("/admin",&MainContorlller{}), // MainController has method List and Page. // visit the url /admin/main/list to execute List function @@ -399,7 +401,7 @@ func (p *ControllerRegister) AddAutoPrefix(prefix string, c ControllerInterface) } } -// 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) func (p *ControllerRegister) InsertFilter(pattern string, pos int, filter FilterFunc, params ...bool) error { @@ -426,9 +428,9 @@ func (p *ControllerRegister) insertFilterRouter(pos int, mr *FilterRouter) error return nil } -// UrlFor does another controller handler in this request function. +// URLFor does another controller handler in this request function. // it can access any controller method. -func (p *ControllerRegister) UrlFor(endpoint string, values ...interface{}) string { +func (p *ControllerRegister) URLFor(endpoint string, values ...interface{}) string { paths := strings.Split(endpoint, ".") if len(paths) <= 1 { Warn("urlfor endpoint must like path.controller.method") @@ -469,7 +471,7 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } } if t.wildcard != nil { - u := path.Join(url, url_placeholder) + u := path.Join(url, urlPlaceholder) ok, u := p.geturl(t.wildcard, u, controllName, methodName, params, httpMethod) if ok { return ok, u @@ -499,22 +501,21 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin if find { if l.regexps == nil { if len(l.wildcards) == 0 { - return true, strings.Replace(url, "/"+url_placeholder, "", 1) + tourl(params) + return true, strings.Replace(url, "/"+urlPlaceholder, "", 1) + tourl(params) } if len(l.wildcards) == 1 { if v, ok := params[l.wildcards[0]]; ok { delete(params, l.wildcards[0]) - return true, strings.Replace(url, url_placeholder, v, 1) + tourl(params) - } else { - return false, "" + return true, strings.Replace(url, urlPlaceholder, v, 1) + tourl(params) } + return false, "" } if len(l.wildcards) == 3 && l.wildcards[0] == "." { if p, ok := params[":path"]; ok { if e, isok := params[":ext"]; isok { delete(params, ":path") delete(params, ":ext") - return true, strings.Replace(url, url_placeholder, p+"."+e, -1) + tourl(params) + return true, strings.Replace(url, urlPlaceholder, p+"."+e, -1) + tourl(params) } } } @@ -526,45 +527,43 @@ func (p *ControllerRegister) geturl(t *Tree, url, controllName, methodName strin } if u, ok := params[v]; ok { delete(params, v) - url = strings.Replace(url, url_placeholder, u, 1) + url = strings.Replace(url, urlPlaceholder, u, 1) } else { if canskip { canskip = false continue - } else { - return false, "" } + return false, "" } } return true, url + tourl(params) - } else { - var i int - var startreg bool - regurl := "" - for _, v := range strings.Trim(l.regexps.String(), "^$") { - if v == '(' { - startreg = true - continue - } else if v == ')' { - startreg = false - if v, ok := params[l.wildcards[i]]; ok { - delete(params, l.wildcards[i]) - regurl = regurl + v - i++ - } else { - break - } - } else if !startreg { - regurl = string(append([]rune(regurl), v)) + } + var i int + var startreg bool + regurl := "" + for _, v := range strings.Trim(l.regexps.String(), "^$") { + if v == '(' { + startreg = true + continue + } else if v == ')' { + startreg = false + if v, ok := params[l.wildcards[i]]; ok { + delete(params, l.wildcards[i]) + regurl = regurl + v + i++ + } else { + break } + } else if !startreg { + regurl = string(append([]rune(regurl), v)) } - if l.regexps.MatchString(regurl) { - ps := strings.Split(regurl, "/") - for _, p := range ps { - url = strings.Replace(url, url_placeholder, p, 1) - } - return true, url + tourl(params) + } + if l.regexps.MatchString(regurl) { + ps := strings.Split(regurl, "/") + for _, p := range ps { + url = strings.Replace(url, urlPlaceholder, p, 1) } + return true, url + tourl(params) } } } @@ -607,7 +606,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) urlPath = r.URL.Path } // defined filter function - do_filter := func(pos int) (started bool) { + doFilter := func(pos int) (started bool) { if p.enableFilter { if l, ok := p.filters[pos]; ok { for _, filterR := range l { @@ -639,7 +638,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } // filter for static file - if do_filter(BeforeStatic) { + if doFilter(BeforeStatic) { goto Admin } @@ -670,7 +669,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) context.Input.ParseFormOrMulitForm(MaxMemory) } - if do_filter(BeforeRouter) { + if doFilter(BeforeRouter) { goto Admin } @@ -681,17 +680,17 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } if !findrouter { - http_method := r.Method + httpMethod := r.Method - if http_method == "POST" && context.Input.Query("_method") == "PUT" { - http_method = "PUT" + if httpMethod == "POST" && context.Input.Query("_method") == "PUT" { + httpMethod = "PUT" } - if http_method == "POST" && context.Input.Query("_method") == "DELETE" { - http_method = "DELETE" + if httpMethod == "POST" && context.Input.Query("_method") == "DELETE" { + httpMethod = "DELETE" } - if t, ok := p.routers[http_method]; ok { + if t, ok := p.routers[httpMethod]; ok { runObject, p := t.Match(urlPath) if r, ok := runObject.(*controllerInfo); ok { routerInfo = r @@ -718,7 +717,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) if findrouter { //execute middleware filters - if do_filter(BeforeExec) { + if doFilter(BeforeExec) { goto Admin } isRunable := false @@ -798,7 +797,7 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) execController.Options() default: if !execController.HandlerFunc(runMethod) { - in := make([]reflect.Value, 0) + var in []reflect.Value method := vc.MethodByName(runMethod) method.Call(in) } @@ -819,12 +818,12 @@ func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) } //execute middleware filters - if do_filter(AfterExec) { + if doFilter(AfterExec) { goto Admin } } - do_filter(FinishRouter) + doFilter(FinishRouter) Admin: timeend := time.Since(starttime) @@ -850,7 +849,7 @@ Admin: } else { devinfo = fmt.Sprintf("| % -10s | % -40s | % -16s | % -10s |", r.Method, r.URL.Path, timeend.String(), "notmatch") } - if DefaultLogFilter == nil || !DefaultLogFilter.Filter(context) { + if DefaultAccessLogFilter == nil || !DefaultAccessLogFilter.Filter(context) { Debug(devinfo) } } diff --git a/templatefunc.go b/templatefunc.go index 1106e4d0..6b77fda3 100644 --- a/templatefunc.go +++ b/templatefunc.go @@ -255,7 +255,7 @@ func Htmlunquote(src string) string { // // more detail http://beego.me/docs/mvc/controller/urlbuilding.md func URLFor(endpoint string, values ...interface{}) string { - return BeeApp.Handlers.UrlFor(endpoint, values...) + return BeeApp.Handlers.URLFor(endpoint, values...) } // returns script tag with src string. From b2048e8653545d5b9c0650e0723b95b49847986c Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 22:05:38 +0800 Subject: [PATCH 196/436] make router test passed --- router_test.go | 56 +++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/router_test.go b/router_test.go index 093a1fad..c6ec9c92 100644 --- a/router_test.go +++ b/router_test.go @@ -52,17 +52,17 @@ func (tc *TestController) Myext() { tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext"))) } -func (tc *TestController) GetUrl() { +func (tc *TestController) GetURL() { tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext"))) } -func (t *TestController) GetParams() { - t.Ctx.WriteString(t.Ctx.Input.Query(":last") + "+" + - t.Ctx.Input.Query(":first") + "+" + t.Ctx.Input.Query("learn")) +func (tc *TestController) GetParams() { + tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" + + tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn")) } -func (t *TestController) GetManyRouter() { - t.Ctx.WriteString(t.Ctx.Input.Query(":id") + t.Ctx.Input.Query(":page")) +func (tc *TestController) GetManyRouter() { + tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page")) } type ResStatus struct { @@ -70,29 +70,29 @@ type ResStatus struct { Msg string } -type JsonController struct { +type JSONController struct { Controller } -func (this *JsonController) Prepare() { - this.Data["json"] = "prepare" - this.ServeJSON(true) +func (jc *JSONController) Prepare() { + jc.Data["json"] = "prepare" + jc.ServeJSON(true) } -func (this *JsonController) Get() { - this.Data["Username"] = "astaxie" - this.Ctx.Output.Body([]byte("ok")) +func (jc *JSONController) Get() { + jc.Data["Username"] = "astaxie" + jc.Ctx.Output.Body([]byte("ok")) } func TestUrlFor(t *testing.T) { handler := NewControllerRegister() handler.Add("/api/list", &TestController{}, "*:List") handler.Add("/person/:last/:first", &TestController{}, "*:Param") - if a := handler.UrlFor("TestController.List"); a != "/api/list" { + if a := handler.URLFor("TestController.List"); a != "/api/list" { Info(a) t.Errorf("TestController.List must equal to /api/list") } - if a := handler.UrlFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" { + if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" { t.Errorf("TestController.Param must equal to /person/xie/asta, but get " + a) } } @@ -100,39 +100,39 @@ func TestUrlFor(t *testing.T) { func TestUrlFor3(t *testing.T) { handler := NewControllerRegister() handler.AddAuto(&TestController{}) - if a := handler.UrlFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" { + if a := handler.URLFor("TestController.Myext"); a != "/test/myext" && a != "/Test/Myext" { t.Errorf("TestController.Myext must equal to /test/myext, but get " + a) } - if a := handler.UrlFor("TestController.GetUrl"); a != "/test/geturl" && a != "/Test/GetUrl" { - t.Errorf("TestController.GetUrl must equal to /test/geturl, but get " + a) + if a := handler.URLFor("TestController.GetURL"); a != "/test/geturl" && a != "/Test/GetURL" { + t.Errorf("TestController.GetURL must equal to /test/geturl, but get " + a) } } func TestUrlFor2(t *testing.T) { handler := NewControllerRegister() handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, "*:List") - handler.Add("/v1/:username/edit", &TestController{}, "get:GetUrl") + handler.Add("/v1/:username/edit", &TestController{}, "get:GetURL") handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, "*:Param") handler.Add("/:year:int/:month:int/:title/:entid", &TestController{}) - if handler.UrlFor("TestController.GetUrl", ":username", "astaxie") != "/v1/astaxie/edit" { - Info(handler.UrlFor("TestController.GetUrl")) + if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" { + Info(handler.URLFor("TestController.GetURL")) t.Errorf("TestController.List must equal to /v1/astaxie/edit") } - if handler.UrlFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") != + if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") != "/v1/za/cms_12_123.html" { - Info(handler.UrlFor("TestController.List")) + Info(handler.URLFor("TestController.List")) t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html") } - if handler.UrlFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") != + if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") != "/v1/za_cms/ttt_12_123.html" { - Info(handler.UrlFor("TestController.Param")) + Info(handler.URLFor("TestController.Param")) t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html") } - if handler.UrlFor("TestController.Get", ":year", "1111", ":month", "11", + if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11", ":title", "aaaa", ":entid", "aaaa") != "/1111/11/aaaa/aaaa" { - Info(handler.UrlFor("TestController.Get")) + Info(handler.URLFor("TestController.Get")) t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa") } } @@ -270,7 +270,7 @@ func TestPrepare(t *testing.T) { w := httptest.NewRecorder() handler := NewControllerRegister() - handler.Add("/json/list", &JsonController{}) + handler.Add("/json/list", &JSONController{}) handler.ServeHTTP(w, r) if w.Body.String() != `"prepare"` { t.Errorf(w.Body.String() + "user define func can't run") From 8615f875f82a40a66baf5cbcef3efcd8b5403a60 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 22:07:44 +0800 Subject: [PATCH 197/436] make golint happy staticfile.go --- staticfile.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/staticfile.go b/staticfile.go index 7c1ed98c..c85f17e0 100644 --- a/staticfile.go +++ b/staticfile.go @@ -40,15 +40,13 @@ func serverStaticRouter(ctx *context.Context) { if utils.FileExists(file) { http.ServeFile(ctx.ResponseWriter, ctx.Request, file) return - } else { - i++ - if i == len(StaticDir) { - http.NotFound(ctx.ResponseWriter, ctx.Request) - return - } else { - continue - } } + i++ + if i == len(StaticDir) { + http.NotFound(ctx.ResponseWriter, ctx.Request) + return + } + continue } if strings.HasPrefix(requestPath, prefix) { if len(requestPath) > len(prefix) && requestPath[len(prefix)] != '/' { From 44bd3beb5ed3eb270b7d832ac7a3411d3a8980e7 Mon Sep 17 00:00:00 2001 From: astaxie Date: Tue, 8 Sep 2015 23:29:58 +0800 Subject: [PATCH 198/436] golint happy with template --- template.go | 32 ++++++++++++++------------------ template_test.go | 10 +++++----- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/template.go b/template.go index 9403c71f..6e196c0a 100644 --- a/template.go +++ b/template.go @@ -28,17 +28,14 @@ import ( ) var ( - beegoTplFuncMap template.FuncMap - // beego template caching map and supported template file extensions. - BeeTemplates map[string]*template.Template - BeeTemplateExt []string + beegoTplFuncMap = make(template.FuncMap) + // BeeTemplates caching map and supported template file extensions. + BeeTemplates = make(map[string]*template.Template) + // BeeTemplateExt stores the template extention which will build + BeeTemplateExt = []string{"tpl", "html"} ) func init() { - BeeTemplates = make(map[string]*template.Template) - beegoTplFuncMap = make(template.FuncMap) - BeeTemplateExt = make([]string, 0) - BeeTemplateExt = append(BeeTemplateExt, "tpl", "html") beegoTplFuncMap["dateformat"] = DateFormat beegoTplFuncMap["date"] = Date beegoTplFuncMap["compare"] = Compare @@ -79,7 +76,7 @@ type templatefile struct { files map[string][]string } -func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { +func (tf *templatefile) visit(paths string, f os.FileInfo, err error) error { if f == nil { return err } @@ -92,21 +89,21 @@ func (self *templatefile) visit(paths string, f os.FileInfo, err error) error { replace := strings.NewReplacer("\\", "/") a := []byte(paths) - a = a[len([]byte(self.root)):] + a = a[len([]byte(tf.root)):] file := strings.TrimLeft(replace.Replace(string(a)), "/") subdir := filepath.Dir(file) - if _, ok := self.files[subdir]; ok { - self.files[subdir] = append(self.files[subdir], file) + if _, ok := tf.files[subdir]; ok { + tf.files[subdir] = append(tf.files[subdir], file) } else { m := make([]string, 1) m[0] = file - self.files[subdir] = m + tf.files[subdir] = m } return nil } -// return this path contains supported template extension of beego or not. +// HasTemplateExt return this path contains supported template extension of beego or not. func HasTemplateExt(paths string) bool { for _, v := range BeeTemplateExt { if strings.HasSuffix(paths, "."+v) { @@ -116,7 +113,7 @@ func HasTemplateExt(paths string) bool { return false } -// add new extension for template. +// AddTemplateExt add new extension for template. func AddTemplateExt(ext string) { for _, v := range BeeTemplateExt { if v == ext { @@ -126,15 +123,14 @@ func AddTemplateExt(ext string) { BeeTemplateExt = append(BeeTemplateExt, ext) } -// build all template files in a directory. +// BuildTemplate will build all template files in a directory. // it makes beego can render any template file in view directory. func BuildTemplate(dir string, files ...string) error { if _, err := os.Stat(dir); err != nil { if os.IsNotExist(err) { return nil - } else { - return errors.New("dir open err") } + return errors.New("dir open err") } self := &templatefile{ root: dir, diff --git a/template_test.go b/template_test.go index 79740f2b..2e222efc 100644 --- a/template_test.go +++ b/template_test.go @@ -20,11 +20,11 @@ import ( "testing" ) -var header string = `{{define "header"}} +var header = `{{define "header"}}

Hello, astaxie!

{{end}}` -var index string = ` +var index = ` beego welcome template @@ -37,7 +37,7 @@ var index string = ` ` -var block string = `{{define "block"}} +var block = `{{define "block"}}

Hello, blocks!

{{end}}` @@ -82,7 +82,7 @@ func TestTemplate(t *testing.T) { os.RemoveAll(dir) } -var menu string = `