From 223f57bb4cfe27d844c565d248ba99881766dec5 Mon Sep 17 00:00:00 2001 From: Hubery Date: Fri, 6 Mar 2015 14:12:24 +0800 Subject: [PATCH 01/55] 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 02/55] =?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 03/55] 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 04/55] 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 05/55] 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 06/55] 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 07/55] 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 08/55] 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 09/55] 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 10/55] 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 11/55] 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 12/55] 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 13/55] 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 14/55] 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 15/55] 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 16/55] 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 17/55] 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 18/55] 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 19/55] 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 20/55] 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 21/55] 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 22/55] 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 23/55] 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 24/55] 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 25/55] 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 26/55] 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 27/55] 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 28/55] 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 29/55] 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 30/55] 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 31/55] =?UTF-8?q?*feature)=20=E5=A2=9E=E5=8A=A0logcalldept?= =?UTF-8?q?h=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 32/55] 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 33/55] 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 34/55] 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 35/55] 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 36/55] 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 37/55] 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 38/55] 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 39/55] 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 40/55] 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 41/55] 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 42/55] 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 43/55] 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 44/55] 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 45/55] 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 46/55] 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 47/55] 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 48/55] 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 49/55] 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 50/55] 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 51/55] 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 52/55] 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 53/55] 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 185089299cfac2cf66b90d8acfb62b0cbce1d9b1 Mon Sep 17 00:00:00 2001 From: astaxie Date: Wed, 20 May 2015 11:07:23 +0800 Subject: [PATCH 54/55] 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 55/55] 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 }