From 4332580c01160f3a0c97d1feb12b1caf1075ff41 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 25 Feb 2019 15:20:57 +0800 Subject: [PATCH 1/6] disable build-in session variable in template parsing, when it's not necessary --- g/net/ghttp/ghttp_server_cookie.go | 5 ++++ g/net/ghttp/ghttp_server_session.go | 41 +++++++++++++++++++---------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/g/net/ghttp/ghttp_server_cookie.go b/g/net/ghttp/ghttp_server_cookie.go index e57d96fdf..ae608a25d 100644 --- a/g/net/ghttp/ghttp_server_cookie.go +++ b/g/net/ghttp/ghttp_server_cookie.go @@ -115,6 +115,11 @@ func (c *Cookie) SetCookie(key, value, domain, path string, maxAge int, httpOnly } } +// 获得客户端提交的SessionId +func (c *Cookie) GetSessionId() string { + return c.Get(c.server.GetSessionIdName()) +} + // 设置SessionId func (c *Cookie) SetSessionId(id string) { c.Set(c.server.GetSessionIdName(), id) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 43d5a01e3..44b0232b2 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -52,7 +52,7 @@ func (s *Session) init() { } } -// 获取SessionId +// 获取/创建SessionId func (s *Session) Id() string { s.init() return s.id @@ -60,8 +60,11 @@ func (s *Session) Id() string { // 获取当前session所有数据 func (s *Session) Data() map[string]interface{} { - s.init() - return s.data.Map() + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + return s.data.Map() + } + return nil } // 设置session @@ -84,20 +87,25 @@ func (s *Session) BatchSet(m map[string]interface{}) { // 判断键名是否存在 func (s *Session) Contains (key string) bool { - s.init() - return s.data.Contains(key) + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + return s.data.Contains(key) + } + return false } // 获取SESSION func (s *Session) Get (key string) interface{} { - s.init() - return s.data.Get(key) + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + return s.data.Get(key) + } + return nil } // 获取SESSION,建议都用该方法获取参数 func (s *Session) GetVar(key string) gvar.VarRead { - s.init() - return gvar.NewRead(s.data.Get(key), true) + return gvar.NewRead(s.Get(key), true) } @@ -128,21 +136,26 @@ func (s *Session) GetInterfaces(key string) []interface{} { return gconv.I func (s *Session) GetTime(key string, format...string) time.Time { return gconv.Time(s.Get(key), format...) } func (s *Session) GetTimeDuration(key string) time.Duration { return gconv.TimeDuration(s.Get(key)) } -// 将变量转换为对象,注意 objPointer 参数必须为struct指针 +// Deprecated, use GetVar instead. +// (已废弃, 请使用GetVar) 将变量转换为对象,注意 objPointer 参数必须为struct指针 func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error { return gconv.Struct(s.Get(key), objPointer, attrMapping...) } // 删除session func (s *Session) Remove(key string) { - s.init() - s.data.Remove(key) + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.Remove(key) + } } // 清空session func (s *Session) Clear() { - s.init() - s.data.Clear() + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + s.init() + s.data.Clear() + } } // 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) From 49a1308875d13fd73a88f55a6bd9c3cb6141474a Mon Sep 17 00:00:00 2001 From: John Date: Mon, 25 Feb 2019 23:08:26 +0800 Subject: [PATCH 2/6] ghttp.Session updates --- g/net/ghttp/ghttp_server_session.go | 156 +++++++++++++++++++++------- 1 file changed, 117 insertions(+), 39 deletions(-) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 44b0232b2..51a6e4822 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -87,7 +87,7 @@ func (s *Session) BatchSet(m map[string]interface{}) { // 判断键名是否存在 func (s *Session) Contains (key string) bool { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { s.init() return s.data.Contains(key) } @@ -96,7 +96,7 @@ func (s *Session) Contains (key string) bool { // 获取SESSION func (s *Session) Get (key string) interface{} { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { s.init() return s.data.Get(key) } @@ -108,43 +108,9 @@ func (s *Session) GetVar(key string) gvar.VarRead { return gvar.NewRead(s.Get(key), true) } - -func (s *Session) GetString (key string) string { return gconv.String(s.Get(key)) } -func (s *Session) GetBool(key string) bool { return gconv.Bool(s.Get(key)) } - -func (s *Session) GetInt(key string) int { return gconv.Int(s.Get(key)) } -func (s *Session) GetInt8(key string) int8 { return gconv.Int8(s.Get(key)) } -func (s *Session) GetInt16(key string) int16 { return gconv.Int16(s.Get(key)) } -func (s *Session) GetInt32(key string) int32 { return gconv.Int32(s.Get(key)) } -func (s *Session) GetInt64(key string) int64 { return gconv.Int64(s.Get(key)) } - -func (s *Session) GetUint(key string) uint { return gconv.Uint(s.Get(key)) } -func (s *Session) GetUint8(key string) uint8 { return gconv.Uint8(s.Get(key)) } -func (s *Session) GetUint16(key string) uint16 { return gconv.Uint16(s.Get(key)) } -func (s *Session) GetUint32(key string) uint32 { return gconv.Uint32(s.Get(key)) } -func (s *Session) GetUint64(key string) uint64 { return gconv.Uint64(s.Get(key)) } - -func (s *Session) GetFloat32 (key string) float32 { return gconv.Float32(s.Get(key)) } -func (s *Session) GetFloat64 (key string) float64 { return gconv.Float64(s.Get(key)) } - -func (s *Session) GetBytes(key string) []byte { return gconv.Bytes(s.Get(key)) } -func (s *Session) GetInts(key string) []int { return gconv.Ints(s.Get(key)) } -func (s *Session) GetFloats(key string) []float64 { return gconv.Floats(s.Get(key)) } -func (s *Session) GetStrings(key string) []string { return gconv.Strings(s.Get(key)) } -func (s *Session) GetInterfaces(key string) []interface{} { return gconv.Interfaces(s.Get(key)) } - -func (s *Session) GetTime(key string, format...string) time.Time { return gconv.Time(s.Get(key), format...) } -func (s *Session) GetTimeDuration(key string) time.Duration { return gconv.TimeDuration(s.Get(key)) } - -// Deprecated, use GetVar instead. -// (已废弃, 请使用GetVar) 将变量转换为对象,注意 objPointer 参数必须为struct指针 -func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error { - return gconv.Struct(s.Get(key), objPointer, attrMapping...) -} - // 删除session func (s *Session) Remove(key string) { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { s.init() s.data.Remove(key) } @@ -152,7 +118,7 @@ func (s *Session) Remove(key string) { // 清空session func (s *Session) Clear() { - if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { + if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { s.init() s.data.Clear() } @@ -163,4 +129,116 @@ func (s *Session) UpdateExpire() { if len(s.id) > 0 { s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) } -} \ No newline at end of file +} + +// Deprecated, use GetVar instead. +func (s *Session) GetString(key string) string { + return gconv.String(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetBool(key string) bool { + return gconv.Bool(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInt(key string) int { + return gconv.Int(s.Get(key)) } + +// Deprecated, use GetVar instead. +func (s *Session) GetInt8(key string) int8 { + return gconv.Int8(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInt16(key string) int16 { + return gconv.Int16(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInt32(key string) int32 { + return gconv.Int32(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInt64(key string) int64 { + return gconv.Int64(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetUint(key string) uint { + return gconv.Uint(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetUint8(key string) uint8 { + return gconv.Uint8(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetUint16(key string) uint16 { + return gconv.Uint16(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetUint32(key string) uint32 { + return gconv.Uint32(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetUint64(key string) uint64 { + return gconv.Uint64(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetFloat32(key string) float32 { + return gconv.Float32(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetFloat64(key string) float64 { + return gconv.Float64(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetBytes(key string) []byte { + return gconv.Bytes(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInts(key string) []int { + return gconv.Ints(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetFloats(key string) []float64 { + return gconv.Floats(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetStrings(key string) []string { + return gconv.Strings(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetInterfaces(key string) []interface{} { + return gconv.Interfaces(s.Get(key)) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetTime(key string, format...string) time.Time { + return gconv.Time(s.Get(key), format...) +} + +// Deprecated, use GetVar instead. +func (s *Session) GetTimeDuration(key string) time.Duration { + return gconv.TimeDuration(s.Get(key)) +} + +// Deprecated, use GetVar instead. +// (已废弃, 请使用GetVar) 将变量转换为对象,注意 objPointer 参数必须为struct指针 +func (s *Session) GetStruct(key string, objPointer interface{}, attrMapping...map[string]string) error { + return gconv.Struct(s.Get(key), objPointer, attrMapping...) +} + + From cb3ce71cdca23b6d269613cb062958d26c47d060 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Feb 2019 14:33:01 +0800 Subject: [PATCH 3/6] fix issue in ghttp.Session --- g/net/ghttp/ghttp_server_session.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index 51a6e4822..bbd386e8e 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -38,6 +38,7 @@ func GetSession(r *Request) *Session { } return &Session { request : r, + server : r.Server, } } @@ -45,7 +46,6 @@ func GetSession(r *Request) *Session { func (s *Session) init() { if len(s.id) == 0 { s.id = s.request.Cookie.SessionId() - s.server = s.request.Server s.data = s.server.sessions.GetOrSetFuncLock(s.id, func() interface{} { return gmap.NewStringInterfaceMap() }, s.server.GetSessionMaxAge()).(*gmap.StringInterfaceMap) From fbd4ce8c2e82864b5c58b3d583d386dd0f3655be Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Feb 2019 17:17:11 +0800 Subject: [PATCH 4/6] fulfil unit cases of ghttp.Cookie/Session --- g/net/ghttp/ghttp_client_request_client.go | 82 +++++++++++++++++++--- g/net/ghttp/ghttp_client_response.go | 18 ++++- g/net/ghttp/ghttp_server_cookie.go | 9 ++- g/net/ghttp/ghttp_server_session.go | 12 ++-- g/net/ghttp/ghttp_unit_cookie_test.go | 62 ++++++++++++++++ g/net/ghttp/ghttp_unit_session_test.go | 66 +++++++++++++++++ 6 files changed, 230 insertions(+), 19 deletions(-) create mode 100644 g/net/ghttp/ghttp_unit_cookie_test.go create mode 100644 g/net/ghttp/ghttp_unit_session_test.go diff --git a/g/net/ghttp/ghttp_client_request_client.go b/g/net/ghttp/ghttp_client_request_client.go index 35ff9c85d..567bdc869 100644 --- a/g/net/ghttp/ghttp_client_request_client.go +++ b/g/net/ghttp/ghttp_client_request_client.go @@ -24,11 +24,13 @@ import ( // http客户端 type Client struct { - http.Client // 底层http client对象 - header map[string]string // HEADER信息Map - prefix string // 设置请求的URL前缀 - authUser string // HTTP基本权限设置:名称 - authPass string // HTTP基本权限设置:密码 + http.Client // 底层http client对象 + header map[string]string // HEADER信息Map + cookies map[string]string // 自定义COOKIE + prefix string // 设置请求的URL前缀 + authUser string // HTTP基本权限设置:名称 + authPass string // HTTP基本权限设置:密码 + browserMode bool // 是否模拟浏览器模式(自动保存提交COOKIE) } // http客户端对象指针 @@ -39,10 +41,16 @@ func NewClient() (*Client) { DisableKeepAlives: true, }, }, - header : make(map[string]string), + header : make(map[string]string), + cookies : make(map[string]string), } } +// 是否模拟浏览器模式(自动保存提交COOKIE) +func (c *Client) SetBrowserMode(enabled bool) { + c.browserMode = enabled +} + // 设置HTTP Header func (c *Client) SetHeader(key, value string) { c.header[key] = value @@ -58,6 +66,18 @@ func (c *Client) SetHeaderRaw(header string) { } } +// 设置COOKIE +func (c *Client) SetCookie(key, value string) { + c.cookies[key] = value +} + +// 使用Map设置COOKIE +func (c *Client) SetCookieMap(cookieMap map[string]string) { + for k, v := range cookieMap { + c.cookies[k] = v + } +} + // 设置请求的URL前缀 func (c *Client) SetPrefix(prefix string) { c.prefix = prefix @@ -143,6 +163,19 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) { req.Header.Set(k, v) } } + // COOKIE + if len(c.cookies) > 0 { + headerCookie := "" + for k, v := range c.cookies { + if len(headerCookie) > 0 { + headerCookie += ";" + } + headerCookie += k + "=" + v + } + if len(headerCookie) > 0 { + req.Header.Set("Cookie", headerCookie) + } + } // HTTP账号密码 if len(c.authUser) > 0 { req.SetBasicAuth(c.authUser, c.authPass) @@ -152,8 +185,10 @@ func (c *Client) Post(url string, data...string) (*ClientResponse, error) { if err != nil { return nil, err } - r := &ClientResponse{} - r.Response = *resp + r := &ClientResponse{ + cookies : make(map[string]string), + } + r.Response = resp return r, nil } @@ -254,13 +289,40 @@ func (c *Client) DoRequest(method, url string, data...string) (*ClientResponse, req.Header.Set(k, v) } } + // COOKIE + if len(c.cookies) > 0 { + headerCookie := "" + for k, v := range c.cookies { + if len(headerCookie) > 0 { + headerCookie += ";" + } + headerCookie += k + "=" + v + } + if len(headerCookie) > 0 { + req.Header.Set("Cookie", headerCookie) + } + } // 执行请求 resp, err := c.Do(req) if err != nil { return nil, err } - r := &ClientResponse{} - r.Response = *resp + r := &ClientResponse{ + cookies : make(map[string]string), + } + r.Response = resp + // 浏览器模式 + if c.browserMode { + now := time.Now() + for _, v := range r.Cookies() { + if v.Expires.UnixNano() < now.UnixNano() { + delete(c.cookies, v.Name) + } else { + c.cookies[v.Name] = v.Value + } + } + } + //fmt.Println(url, c.cookies) return r, nil } diff --git a/g/net/ghttp/ghttp_client_response.go b/g/net/ghttp/ghttp_client_response.go index 21db3029f..c9dd587d4 100644 --- a/g/net/ghttp/ghttp_client_response.go +++ b/g/net/ghttp/ghttp_client_response.go @@ -10,11 +10,27 @@ package ghttp import ( "io/ioutil" "net/http" + "time" ) // 客户端请求结果对象 type ClientResponse struct { - http.Response + *http.Response + cookies map[string]string +} + +// 获得返回的指定COOKIE值 +func (r *ClientResponse) GetCookie(key string) string { + if r.cookies == nil { + now := time.Now() + for _, v := range r.Cookies() { + if v.Expires.UnixNano() < now.UnixNano() { + continue + } + r.cookies[v.Name] = v.Value + } + } + return r.cookies[key] } // 获取返回的数据(二进制). diff --git a/g/net/ghttp/ghttp_server_cookie.go b/g/net/ghttp/ghttp_server_cookie.go index ae608a25d..e01ba78f3 100644 --- a/g/net/ghttp/ghttp_server_cookie.go +++ b/g/net/ghttp/ghttp_server_cookie.go @@ -42,6 +42,7 @@ func GetCookie(r *Request) *Cookie { } return &Cookie { request : r, + server : r.Server, } } @@ -52,7 +53,6 @@ func (c *Cookie) init() { c.path = c.request.Server.GetCookiePath() c.domain = c.request.Server.GetCookieDomain() c.maxage = c.request.Server.GetCookieMaxAge() - c.server = c.request.Server c.response = c.request.Response // 如果没有设置COOKIE有效域名,那么设置HOST为默认有效域名 if c.domain == "" { @@ -138,9 +138,14 @@ func (c *Cookie) Get(key string) string { return "" } +// 删除COOKIE,使用默认的domain&path +func (c *Cookie) Remove(key string) { + c.SetCookie(key, "", c.domain, c.path, -86400) +} + // 标记该cookie在对应的域名和路径失效 // 删除cookie的重点是需要通知浏览器客户端cookie已过期 -func (c *Cookie) Remove(key, domain, path string) { +func (c *Cookie) RemoveCookie(key, domain, path string) { c.SetCookie(key, "", domain, path, -86400) } diff --git a/g/net/ghttp/ghttp_server_session.go b/g/net/ghttp/ghttp_server_session.go index bbd386e8e..f2bf95351 100644 --- a/g/net/ghttp/ghttp_server_session.go +++ b/g/net/ghttp/ghttp_server_session.go @@ -38,7 +38,6 @@ func GetSession(r *Request) *Session { } return &Session { request : r, - server : r.Server, } } @@ -46,6 +45,7 @@ func GetSession(r *Request) *Session { func (s *Session) init() { if len(s.id) == 0 { s.id = s.request.Cookie.SessionId() + s.server = s.request.Server s.data = s.server.sessions.GetOrSetFuncLock(s.id, func() interface{} { return gmap.NewStringInterfaceMap() }, s.server.GetSessionMaxAge()).(*gmap.StringInterfaceMap) @@ -87,7 +87,7 @@ func (s *Session) BatchSet(m map[string]interface{}) { // 判断键名是否存在 func (s *Session) Contains (key string) bool { - if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() return s.data.Contains(key) } @@ -96,7 +96,7 @@ func (s *Session) Contains (key string) bool { // 获取SESSION func (s *Session) Get (key string) interface{} { - if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() return s.data.Get(key) } @@ -110,7 +110,7 @@ func (s *Session) GetVar(key string) gvar.VarRead { // 删除session func (s *Session) Remove(key string) { - if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() s.data.Remove(key) } @@ -118,7 +118,7 @@ func (s *Session) Remove(key string) { // 清空session func (s *Session) Clear() { - if len(s.id) > 0 || s.request.Cookie.Contains(s.server.GetSessionIdName()) { + if len(s.id) > 0 || s.request.Cookie.GetSessionId() != "" { s.init() s.data.Clear() } @@ -126,7 +126,7 @@ func (s *Session) Clear() { // 更新过期时间(如果用在守护进程中长期使用,需要手动调用进行更新,防止超时被清除) func (s *Session) UpdateExpire() { - if len(s.id) > 0 { + if len(s.id) > 0 && s.data.Size() > 0 { s.server.sessions.Set(s.id, s.data, s.server.GetSessionMaxAge()*1000) } } diff --git a/g/net/ghttp/ghttp_unit_cookie_test.go b/g/net/ghttp/ghttp_unit_cookie_test.go new file mode 100644 index 000000000..8dc323cdb --- /dev/null +++ b/g/net/ghttp/ghttp_unit_cookie_test.go @@ -0,0 +1,62 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// COOKIE测试 +package ghttp_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +func Test_Cookie(t *testing.T) { + s := g.Server(gtime.Nanosecond()) + s.BindHandler("/set", func(r *ghttp.Request){ + r.Cookie.Set(r.Get("k"), r.Get("v")) + }) + s.BindHandler("/get", func(r *ghttp.Request){ + //fmt.Println(r.Cookie.Map()) + r.Response.Write(r.Cookie.Get(r.Get("k"))) + }) + s.BindHandler("/remove", func(r *ghttp.Request){ + r.Cookie.Remove(r.Get("k")) + }) + s.SetPort(8500) + s.SetDumpRouteMap(false) + go s.Run() + defer func() { + s.Shutdown() + time.Sleep(time.Second) + }() + // 等待启动完成 + time.Sleep(time.Second) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetBrowserMode(true) + client.SetPrefix("http://127.0.0.1:8500") + r1, e1 := client.Get("/set?k=key1&v=100") + if r1 != nil { + defer r1.Close() + } + gtest.Assert(e1, nil) + gtest.Assert(r1.ReadAllString(), "") + + gtest.Assert(client.GetContent("/set?k=key2&v=200"), "") + + gtest.Assert(client.GetContent("/get?k=key1"), "100") + gtest.Assert(client.GetContent("/get?k=key2"), "200") + gtest.Assert(client.GetContent("/get?k=key3"), "") + gtest.Assert(client.GetContent("/remove?k=key1"), "") + gtest.Assert(client.GetContent("/remove?k=key3"), "") + gtest.Assert(client.GetContent("/remove?k=key4"), "") + gtest.Assert(client.GetContent("/get?k=key1"), "") + gtest.Assert(client.GetContent("/get?k=key2"), "200") + }) +} diff --git a/g/net/ghttp/ghttp_unit_session_test.go b/g/net/ghttp/ghttp_unit_session_test.go new file mode 100644 index 000000000..6ceffd80e --- /dev/null +++ b/g/net/ghttp/ghttp_unit_session_test.go @@ -0,0 +1,66 @@ +// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// SESSION测试 +package ghttp_test + +import ( + "github.com/gogf/gf/g" + "github.com/gogf/gf/g/net/ghttp" + "github.com/gogf/gf/g/os/gtime" + "github.com/gogf/gf/g/test/gtest" + "testing" + "time" +) + +func Test_Session(t *testing.T) { + s := g.Server(gtime.Nanosecond()) + s.BindHandler("/set", func(r *ghttp.Request){ + r.Session.Set(r.Get("k"), r.Get("v")) + }) + s.BindHandler("/get", func(r *ghttp.Request){ + r.Response.Write(r.Session.Get(r.Get("k"))) + }) + s.BindHandler("/remove", func(r *ghttp.Request){ + r.Session.Remove(r.Get("k")) + }) + s.BindHandler("/clear", func(r *ghttp.Request){ + r.Session.Clear() + }) + s.SetPort(8600) + s.SetDumpRouteMap(false) + go s.Run() + defer func() { + s.Shutdown() + time.Sleep(time.Second) + }() + // 等待启动完成 + time.Sleep(time.Second) + gtest.Case(t, func() { + client := ghttp.NewClient() + client.SetBrowserMode(true) + client.SetPrefix("http://127.0.0.1:8600") + r1, e1 := client.Get("/set?k=key1&v=100") + if r1 != nil { + defer r1.Close() + } + gtest.Assert(e1, nil) + gtest.Assert(r1.ReadAllString(), "") + + gtest.Assert(client.GetContent("/set?k=key2&v=200"), "") + + gtest.Assert(client.GetContent("/get?k=key1"), "100") + gtest.Assert(client.GetContent("/get?k=key2"), "200") + gtest.Assert(client.GetContent("/get?k=key3"), "") + gtest.Assert(client.GetContent("/remove?k=key1"), "") + gtest.Assert(client.GetContent("/remove?k=key3"), "") + gtest.Assert(client.GetContent("/remove?k=key4"), "") + gtest.Assert(client.GetContent("/get?k=key1"), "") + gtest.Assert(client.GetContent("/get?k=key2"), "200") + gtest.Assert(client.GetContent("/clear"), "") + gtest.Assert(client.GetContent("/get?k=key2"), "") + }) +} From 704a5dbd732db2a91c6dee2685be9a9c057c9ddd Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Feb 2019 17:52:50 +0800 Subject: [PATCH 5/6] fix issue of "memory leaks" in gfpool --- g/os/gfpool/gfpool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g/os/gfpool/gfpool.go b/g/os/gfpool/gfpool.go index 95a1df852..da8c29d09 100644 --- a/g/os/gfpool/gfpool.go +++ b/g/os/gfpool/gfpool.go @@ -130,7 +130,7 @@ func (p *Pool) File() (*File, error) { return nil, err } } - if !p.inited.Val() || p.inited.Set(true) == false { + if p.inited.Set(true) == false { gfsnotify.Add(f.path, func(event *gfsnotify.Event) { // 如果文件被删除或者重命名,立即重建指针池 if event.IsRemove() || event.IsRename() { From 7058e4f2c4064ed8a17a745e81bc0a7e7bf57584 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 26 Feb 2019 22:21:57 +0800 Subject: [PATCH 6/6] fix issue of router group in auto-adding 'index' router for controller and object --- g/net/ghttp/ghttp_server_service_controller.go | 12 ++++-------- g/net/ghttp/ghttp_server_service_object.go | 10 ++-------- ...ttp_unit_3_test.go => ghttp_unit_param_test.go} | 0 ...t_1_test.go => ghttp_unit_router_basic_test.go} | 0 ...t_2_test.go => ghttp_unit_router_group_test.go} | 14 ++++++++++++++ g/os/gfpool/gfpool.go | 4 +++- 6 files changed, 23 insertions(+), 17 deletions(-) rename g/net/ghttp/{ghttp_unit_3_test.go => ghttp_unit_param_test.go} (100%) rename g/net/ghttp/{ghttp_unit_1_test.go => ghttp_unit_router_basic_test.go} (100%) rename g/net/ghttp/{ghttp_unit_2_test.go => ghttp_unit_router_group_test.go} (87%) diff --git a/g/net/ghttp/ghttp_server_service_controller.go b/g/net/ghttp/ghttp_server_service_controller.go index 27b6bef94..b90ea08fa 100644 --- a/g/net/ghttp/ghttp_server_service_controller.go +++ b/g/net/ghttp/ghttp_server_service_controller.go @@ -64,15 +64,11 @@ func (s *Server)BindController(pattern string, c Controller, methods...string) e faddr : nil, } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI + // 例如: pattern为/user, 那么会同时注册/user及/user/index, + // 这里处理新增/user路由绑定 if strings.EqualFold(mname, "Index") { - p := key - if strings.EqualFold(p[len(p) - 6:], "/index") { - p = p[0 : len(p) - 6] - if len(p) == 0 { - p = "/" - } - } - m[p] = &handlerItem { + p := gstr.PosR(key, "/index") + m[key[0 : p] + key[p + 6 : ]] = &handlerItem { name : fmt.Sprintf(`%s.%s.%s`, pkgPath, ctlName, mname), rtype : gROUTE_REGISTER_CONTROLLER, ctype : v.Elem().Type(), diff --git a/g/net/ghttp/ghttp_server_service_object.go b/g/net/ghttp/ghttp_server_service_object.go index f6a43bf8d..fb501c7b9 100644 --- a/g/net/ghttp/ghttp_server_service_object.go +++ b/g/net/ghttp/ghttp_server_service_object.go @@ -74,14 +74,8 @@ func (s *Server)BindObject(pattern string, obj interface{}, methods...string) er } // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI if strings.EqualFold(mname, "Index") { - p := key - if strings.EqualFold(p[len(p) - 6:], "/index") { - p = p[0 : len(p) - 6] - if len(p) == 0 { - p = "/" - } - } - m[p] = &handlerItem { + p := gstr.PosR(key, "/index") + m[key[0 : p] + key[p + 6 : ]] = &handlerItem { name : fmt.Sprintf(`%s.%s.%s`, pkgPath, objName, mname), rtype : gROUTE_REGISTER_OBJECT, ctype : nil, diff --git a/g/net/ghttp/ghttp_unit_3_test.go b/g/net/ghttp/ghttp_unit_param_test.go similarity index 100% rename from g/net/ghttp/ghttp_unit_3_test.go rename to g/net/ghttp/ghttp_unit_param_test.go diff --git a/g/net/ghttp/ghttp_unit_1_test.go b/g/net/ghttp/ghttp_unit_router_basic_test.go similarity index 100% rename from g/net/ghttp/ghttp_unit_1_test.go rename to g/net/ghttp/ghttp_unit_router_basic_test.go diff --git a/g/net/ghttp/ghttp_unit_2_test.go b/g/net/ghttp/ghttp_unit_router_group_test.go similarity index 87% rename from g/net/ghttp/ghttp_unit_2_test.go rename to g/net/ghttp/ghttp_unit_router_group_test.go index e5dfb7bde..83c53f972 100644 --- a/g/net/ghttp/ghttp_unit_2_test.go +++ b/g/net/ghttp/ghttp_unit_router_group_test.go @@ -20,6 +20,10 @@ import ( // 执行对象 type Object struct {} +func (o *Object) Index(r *ghttp.Request) { + r.Response.Write("Object Index") +} + func (o *Object) Show(r *ghttp.Request) { r.Response.Write("Object Show") } @@ -33,6 +37,10 @@ type Controller struct { gmvc.Controller } +func (c *Controller) Index() { + c.Response.Write("Controller Index") +} + func (c *Controller) Show() { c.Response.Write("Controller Show") } @@ -72,11 +80,17 @@ func Test_Router_Group1(t *testing.T) { gtest.Assert(client.GetContent ("/api/handler"), "Handler") + gtest.Assert(client.GetContent ("/api/ctl"), "Controller Index") + gtest.Assert(client.GetContent ("/api/ctl/"), "Controller Index") + gtest.Assert(client.GetContent ("/api/ctl/index"), "Controller Index") gtest.Assert(client.GetContent ("/api/ctl/my-show"), "Controller Show") gtest.Assert(client.GetContent ("/api/ctl/post"), "Controller REST Post") gtest.Assert(client.GetContent ("/api/ctl/show"), "Controller Show") gtest.Assert(client.PostContent("/api/ctl/rest"), "Controller REST Post") + gtest.Assert(client.GetContent ("/api/obj"), "Object Index") + gtest.Assert(client.GetContent ("/api/obj/"), "Object Index") + gtest.Assert(client.GetContent ("/api/obj/index"), "Object Index") gtest.Assert(client.GetContent ("/api/obj/delete"), "Object REST Delete") gtest.Assert(client.GetContent ("/api/obj/my-show"), "Object Show") gtest.Assert(client.GetContent ("/api/obj/show"), "Object Show") diff --git a/g/os/gfpool/gfpool.go b/g/os/gfpool/gfpool.go index da8c29d09..00730c0e1 100644 --- a/g/os/gfpool/gfpool.go +++ b/g/os/gfpool/gfpool.go @@ -130,7 +130,9 @@ func (p *Pool) File() (*File, error) { return nil, err } } - if p.inited.Set(true) == false { + // !p.inited.Val() 使用原子读取操作判断,保证该操作判断的效率; + // p.inited.Set(true) == false 使用原子写入操作,保证该操作的原子性; + if !p.inited.Val() && p.inited.Set(true) == false { gfsnotify.Add(f.path, func(event *gfsnotify.Event) { // 如果文件被删除或者重命名,立即重建指针池 if event.IsRemove() || event.IsRename() {