From 7ce4f02533ea38baadfa16e21c792a409073f433 Mon Sep 17 00:00:00 2001 From: john Date: Tue, 21 Aug 2018 21:18:56 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E5=A2=9E=E5=8A=A0gfcache=E5=8C=85=EF=BC=8C?= =?UTF-8?q?=E6=94=B9=E8=BF=9Bgcfg/gview=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TODO | 3 +- g/net/ghttp/ghttp_response.go | 42 ++++------------ g/net/ghttp/ghttp_response_gzip.go | 45 +++++++++++++++++ g/net/ghttp/ghttp_response_writer.go | 43 ++++++++++++++++ g/net/ghttp/ghttp_server_config.go | 34 +++++++------ g/net/ghttp/ghttp_server_handler.go | 6 +++ g/net/ghttp/ghttp_server_log.go | 2 +- g/os/gcfg/gcfg.go | 19 +++----- g/os/gfcache/gfcache.go | 61 +++++++++++++++++++++++ g/os/gfcache/gfcache_cache.go | 73 ++++++++++++++++++++++++++++ g/os/gfile/gfile.go | 11 +++-- g/os/gfilepool/gfilepool.go | 10 ++-- g/os/gview/gview.go | 29 ++--------- geg/os/gfcache/gfcache.go | 28 +++++++++++ geg/other/test.go | 29 ++++++++--- version.go | 2 +- 16 files changed, 336 insertions(+), 101 deletions(-) create mode 100644 g/net/ghttp/ghttp_response_gzip.go create mode 100644 g/net/ghttp/ghttp_response_writer.go create mode 100644 g/os/gfcache/gfcache.go create mode 100644 g/os/gfcache/gfcache_cache.go create mode 100644 geg/os/gfcache/gfcache.go diff --git a/TODO b/TODO index b7d4d95f8..b13e0064a 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,7 @@ ghttp.Server的Cookie及Session锁机制优化(去掉map锁机制); ghttp.Server增加Ip访问控制功能(DenyIps&AllowIps); ghttp路由功能增加分组路由特性; 解决glog串日志情况; +ghttp增加返回数据压缩机制; DONE: @@ -53,4 +54,4 @@ DONE: 29. gdb增加查询缓存特性; 30. gpage分页增加对自定义后缀的支持,如:2.html, 2.php等等; 31. gvalid包增加struct tag的校验规则、自定义错误提示信息绑定的支持特性; - +32. 增加文件缓存包,可根据fsnotify机制进行缓存更新; diff --git a/g/net/ghttp/ghttp_response.go b/g/net/ghttp/ghttp_response.go index e6f7b8ae3..9d2030dec 100644 --- a/g/net/ghttp/ghttp_response.go +++ b/g/net/ghttp/ghttp_response.go @@ -8,7 +8,6 @@ package ghttp import ( - "sync" "net/http" "gitee.com/johng/gf/g/util/gconv" "gitee.com/johng/gf/g/encoding/gparser" @@ -17,46 +16,29 @@ import ( "gitee.com/johng/gf/g/frame/gins" ) -// 服务端请求返回对象 +// 服务端请求返回对象。 +// 注意该对象并没有实现http.ResponseWriter接口,而是依靠ghttp.ResponseWriter实现。 type Response struct { ResponseWriter Server *Server - Writer *ResponseWriter // io.Writer - mu sync.RWMutex // 缓冲区互斥锁 - buffer []byte // 每个请求的返回数据缓冲区 + Writer *ResponseWriter // ResponseWriter的别名 request *Request // 关联的Request请求对象 } -// 自定义的ResponseWriter,用于写入流的控制 -type ResponseWriter struct { - http.ResponseWriter - Status int // http status - Length int // response length -} - // 创建一个ghttp.Response对象指针 func newResponse(s *Server, w http.ResponseWriter) *Response { r := &Response { Server : s, - ResponseWriter : ResponseWriter{w, http.StatusOK, 0}, + ResponseWriter : ResponseWriter{ + ResponseWriter : w, + Status : http.StatusOK, + buffer : make([]byte, 0), + }, } r.Writer = &r.ResponseWriter return r } -// 覆盖父级的WriteHeader方法 -func (w *ResponseWriter) Write(buffer []byte) (int, error) { - n, e := w.ResponseWriter.Write(buffer) - w.Length += n - return n, e -} - -// 覆盖父级的WriteHeader方法 -func (w *ResponseWriter) WriteHeader(code int) { - w.Status = code - w.ResponseWriter.WriteHeader(code) -} - // 返回信息,任何变量自动转换为bytes func (r *Response) Write(content ... interface{}) { if len(content) == 0 { @@ -225,11 +207,5 @@ func (r *Response) ClearBuffer() { // 输出缓冲区数据到客户端 func (r *Response) OutputBuffer() { r.Header().Set("Server", r.Server.config.ServerAgent) - if len(r.buffer) > 0 { - r.mu.Lock() - r.ResponseWriter.Write(r.buffer) - r.buffer = make([]byte, 0) - r.mu.Unlock() - } - + r.Writer.OutputBuffer() } diff --git a/g/net/ghttp/ghttp_response_gzip.go b/g/net/ghttp/ghttp_response_gzip.go new file mode 100644 index 000000000..a557dbebf --- /dev/null +++ b/g/net/ghttp/ghttp_response_gzip.go @@ -0,0 +1,45 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. +// + +package ghttp + +// 默认的gzip压缩文件类型 +var defaultGzipContentTypes = []string{ + "application/atom+xml", + "application/font-sfnt", + "application/javascript", + "application/json", + "application/ld+json", + "application/manifest+json", + "application/rdf+xml", + "application/rss+xml", + "application/schema+json", + "application/vnd.geo+json", + "application/vnd.ms-fontobject", + "application/x-font-ttf", + "application/x-javascript", + "application/x-web-app-manifest+json", + "application/xhtml+xml", + "application/xml", + "font/eot", + "font/opentype", + "image/bmp", + "image/svg+xml", + "image/vnd.microsoft.icon", + "image/x-icon", + "text/cache-manifest", + "text/css", + "text/html", + "text/javascript", + "text/plain", + "text/vcard", + "text/vnd.rim.location.xloc", + "text/vtt", + "text/x-component", + "text/x-cross-domain-policy", + "text/xml", +} \ No newline at end of file diff --git a/g/net/ghttp/ghttp_response_writer.go b/g/net/ghttp/ghttp_response_writer.go new file mode 100644 index 000000000..fbd0308bf --- /dev/null +++ b/g/net/ghttp/ghttp_response_writer.go @@ -0,0 +1,43 @@ +// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. +// + +package ghttp + +import ( + "net/http" + "sync" +) + +// 自定义的ResponseWriter,用于写入流的控制 +type ResponseWriter struct { + http.ResponseWriter + mu sync.RWMutex // 缓冲区互斥锁 + Status int // http status + buffer []byte // 缓冲区内容 +} + +// 覆盖父级的WriteHeader方法 +func (w *ResponseWriter) Write(buffer []byte) (int, error) { + w.buffer = append(w.buffer, buffer...) + return len(buffer), nil +} + +// 覆盖父级的WriteHeader方法 +func (w *ResponseWriter) WriteHeader(code int) { + w.Status = code + w.ResponseWriter.WriteHeader(code) +} + +// 输出buffer数据到客户端 +func (w *ResponseWriter) OutputBuffer() { + if len(w.buffer) > 0 { + w.mu.Lock() + w.ResponseWriter.Write(w.buffer) + w.buffer = make([]byte, 0) + w.mu.Unlock() + } +} diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 167749e9a..dfe267955 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -59,27 +59,31 @@ type ServerConfig struct { AllowIps []string // 仅允许访问的ip列表,支持ip前缀过滤,如: 10 将仅允许10开头的ip访问 // 路由访问控制 DenyRoutes []string // 不允许访问的路由规则列表 + // Gzip压缩文件类型 + GzipContentTypes []string // 允许进行gzip压缩的文件类型 } // 默认HTTP Server var defaultServerConfig = ServerConfig { - Addr : "", - HTTPSAddr : "", - Handler : nil, - ReadTimeout : 60 * time.Second, - WriteTimeout : 60 * time.Second, - IdleTimeout : 60 * time.Second, - MaxHeaderBytes : 1024, - IndexFiles : []string{"index.html", "index.htm"}, - IndexFolder : false, - ServerAgent : "gf", - ServerRoot : "", + Addr : "", + HTTPSAddr : "", + Handler : nil, + ReadTimeout : 60 * time.Second, + WriteTimeout : 60 * time.Second, + IdleTimeout : 60 * time.Second, + MaxHeaderBytes : 1024, + IndexFiles : []string{"index.html", "index.htm"}, + IndexFolder : false, + ServerAgent : "gf", + ServerRoot : "", - CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE, - SessionMaxAge : gDEFAULT_SESSION_MAX_AGE, - SessionIdName : gDEFAULT_SESSION_ID_NAME, + CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE, + SessionMaxAge : gDEFAULT_SESSION_MAX_AGE, + SessionIdName : gDEFAULT_SESSION_ID_NAME, - ErrorLogEnabled : true, + ErrorLogEnabled : true, + + GzipContentTypes : defaultGzipContentTypes, } // 获取默认的http server设置 diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index 583ae2c91..bc4030747 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -97,6 +97,12 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { request.Response.OutputBuffer() // 事件 - AfterOutput s.callHookHandler(HOOK_AFTER_OUTPUT, request) + + // gzip压缩处理 + encoding := request.Header.Get("Accept-Encoding") + if encoding != "" { + + } } // 初始化控制器 diff --git a/g/net/ghttp/ghttp_server_log.go b/g/net/ghttp/ghttp_server_log.go index fad5d4c47..cbc3e0287 100644 --- a/g/net/ghttp/ghttp_server_log.go +++ b/g/net/ghttp/ghttp_server_log.go @@ -26,7 +26,7 @@ func (s *Server) handleAccessLog(r *Request) { content := fmt.Sprintf(`"%s %s %s %s" %s %s`, r.Method, r.Host, r.URL.String(), r.Proto, gconv.String(r.Response.Status), - gconv.String(r.Response.Length), + gconv.String(r.Response.BufferLength()), ) content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime - r.EnterTime)/1000) content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent()) diff --git a/g/os/gcfg/gcfg.go b/g/os/gcfg/gcfg.go index 90d57a0ae..9c2835ebc 100644 --- a/g/os/gcfg/gcfg.go +++ b/g/os/gcfg/gcfg.go @@ -25,7 +25,6 @@ type Config struct { name *gtype.String // 默认配置文件名称 paths *gspath.SPath // 搜索目录路径 jsons *gmap.StringInterfaceMap // 配置文件对象 - closed *gtype.Bool // 是否已经被close vc *gtype.Bool // 层级检索是否执行分隔符冲突检测(默认为false,检测会比较影响检索效率) } @@ -41,7 +40,6 @@ func New(path string, file...string) *Config { name : gtype.NewString(name), paths : s, jsons : gmap.NewStringInterfaceMap(), - closed : gtype.NewBool(), vc : gtype.NewBool(), } } @@ -251,17 +249,14 @@ func (c *Config) Reload() { c.jsons.Clear() } -// 关闭Config对象,自动关闭异步协程 -func (c *Config) Close() { - c.closed.Set(true) -} - // 添加文件监控 func (c *Config) addMonitor(path string) { - if c.jsons.Get(path) == nil { - gfsnotify.Add(path, func(event *gfsnotify.Event) { - // 删除文件内容缓存,下一次查询会自动更新 - c.jsons.Remove(event.Path) - }) + // 防止多goroutine同时调用 + if c.jsons.Get(path) != nil { + return } + gfsnotify.Add(path, func(event *gfsnotify.Event) { + // 删除文件内容缓存,下一次查询会自动更新 + c.jsons.Remove(event.Path) + }) } diff --git a/g/os/gfcache/gfcache.go b/g/os/gfcache/gfcache.go new file mode 100644 index 000000000..2e4f4eae7 --- /dev/null +++ b/g/os/gfcache/gfcache.go @@ -0,0 +1,61 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +// 文件缓存. +package gfcache + +import ( + "gitee.com/johng/gf/g/os/gcache" + "gitee.com/johng/gf/g/container/gtype" + "gitee.com/johng/gf/g/os/gfsnotify" +) + +type Cache struct { + cap *gtype.Int // 缓存容量(byte),设置为0表示不限制 + size *gtype.Int // 缓存大小(Byte) + cache *gcache.Cache // 缓存对象 + notify *gfsnotify.Watcher // 文件监控管理对象 +} + +const ( + // 默认的缓存容量(不限制) + gDEFAULT_CACHE_CAP = 0 +) + +var ( + // 默认的文件缓存对象 + cache = New() +) + +func New(cap ... int) *Cache { + c := gDEFAULT_CACHE_CAP + if len(cap) > 0 { + c = cap[0] + } + notify, _ := gfsnotify.New() + return &Cache { + cap : gtype.NewInt(c), + size : gtype.NewInt(), + cache : gcache.New(), + notify : notify, + } +} + + +// 获得已缓存的文件大小(byte) +func GetSize() int { + return cache.GetSize() +} + +// 获得文件内容 string +func GetContents(path string) string { + return cache.GetContents(path) +} + +// 获得文件内容 []byte +func GetBinContents(path string) []byte { + return cache.GetBinContents(path) +} \ No newline at end of file diff --git a/g/os/gfcache/gfcache_cache.go b/g/os/gfcache/gfcache_cache.go new file mode 100644 index 000000000..28cd50d99 --- /dev/null +++ b/g/os/gfcache/gfcache_cache.go @@ -0,0 +1,73 @@ +// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf. + +// 文件缓存. +package gfcache + +import ( + "gitee.com/johng/gf/g/os/gfile" + "gitee.com/johng/gf/g/os/gfsnotify" +) + +// 设置容量大小(MB) +func (c *Cache) SetCap(cap int) { + c.cap.Set(cap) +} + +// 获得缓存容量大小(byte) +func (c *Cache) GetCap() int { + return c.cap.Val() +} + +// 获得已缓存的文件大小(byte) +func (c *Cache) GetSize() int { + return c.size.Val() +} + +// 获得文件内容 string +func (c *Cache) GetContents(path string) string { + return string(c.GetBinContents(path)) +} + +// 获得文件内容 []byte +func (c *Cache) GetBinContents(path string) []byte { + v := c.cache.Get(path) + if v != nil { + return v.([]byte) + } + b := gfile.GetBinContents(path) + if b != nil && (c.cap.Val() == 0 || c.size.Val() < c.cap.Val()) { + c.addMonitor(path) + c.cache.Set(path, b, 0) + c.size.Add(len(b)) + } + return b +} + +// 添加文件监控 +func (c *Cache) addMonitor(path string) { + // 防止多goroutine同时调用 + if c.cache.Get(path) != nil { + return + } + c.notify.Add(path, func(event *gfsnotify.Event) { + r := c.cache.Get(path).([]byte) + // 是否删除 + if event.IsRemove() { + c.cache.Remove(path) + c.size.Add(-len(r)) + } + // 更新缓存内容 + if c.cap.Val() == 0 || c.size.Val() < c.cap.Val() { + b := gfile.GetBinContents(path) + if b != nil { + dif := len(b) - len(r) + c.cache.Set(path, b, 0) + c.size.Add(dif) + } + } + }) +} \ No newline at end of file diff --git a/g/os/gfile/gfile.go b/g/os/gfile/gfile.go index 81c92dca1..9f7bd271f 100644 --- a/g/os/gfile/gfile.go +++ b/g/os/gfile/gfile.go @@ -28,16 +28,19 @@ import ( // 封装了常用的文件操作方法,如需更详细的文件控制,请查看官方os包 + // 文件分隔符 const ( Separator = string(filepath.Separator) ) -// 源码的main包所在目录,仅仅会设置一次 -var mainPkgPath = gtype.NewString() +var ( + // 源码的main包所在目录,仅仅会设置一次 + mainPkgPath = gtype.NewString() -// 编译时的 GOROOT 数值 -var goRootOfBuild = gtype.NewString() + // 编译时的 GOROOT 数值 + goRootOfBuild = gtype.NewString() +) // 给定文件的绝对路径创建文件 func Mkdir(path string) error { diff --git a/g/os/gfilepool/gfilepool.go b/g/os/gfilepool/gfilepool.go index 70801be5b..581e1a191 100644 --- a/g/os/gfilepool/gfilepool.go +++ b/g/os/gfilepool/gfilepool.go @@ -70,12 +70,14 @@ func (p *Pool) File() (*File, error) { } } -// 关闭指针池 -func (p *Pool) Close() { +// 关闭指针池(返回error是标准库io.ReadWriteCloser接口实现) +func (p *Pool) Close() error { p.pool.Close() + return nil } -// 获得底层文件指针 -func (f *File) Close() { +// 获得底层文件指针(返回error是标准库io.ReadWriteCloser接口实现) +func (f *File) Close() error { f.pool.pool.Put(f) + return nil } diff --git a/g/os/gview/gview.go b/g/os/gview/gview.go index a9d6e8a77..329b4eea7 100644 --- a/g/os/gview/gview.go +++ b/g/os/gview/gview.go @@ -12,12 +12,11 @@ import ( "bytes" "errors" "html/template" - "gitee.com/johng/gf/g/os/gfile" "gitee.com/johng/gf/g/container/gmap" "gitee.com/johng/gf/g/encoding/ghash" "gitee.com/johng/gf/g/util/gconv" - "gitee.com/johng/gf/g/os/gfsnotify" "gitee.com/johng/gf/g/os/gspath" + "gitee.com/johng/gf/g/os/gfcache" ) // 视图对象 @@ -25,7 +24,6 @@ type View struct { mu sync.RWMutex paths *gspath.SPath // 模板查找目录(绝对路径) funcmap map[string]interface{} // FuncMap - contents *gmap.StringStringMap // 已解析的模板文件内容 delimiters []string // 模板变量分隔符号 } @@ -62,7 +60,6 @@ func New(path string) *View { view := &View { paths : s, funcmap : make(map[string]interface{}), - contents : gmap.NewStringStringMap(), delimiters : make([]string, 2), } view.SetDelimiters("{{", "}}") @@ -82,18 +79,12 @@ func (view *View) AddPath(path string) error { // 解析模板,返回解析后的内容 func (view *View) Parse(file string, params...map[string]interface{}) ([]byte, error) { - path := view.paths.Search(file) - content := view.contents.Get(path) - if content == "" { - content = gfile.GetContents(path) - if content != "" { - view.addMonitor(path) - view.contents.Set(path, content) - } - } - if content == "" { + path := view.paths.Search(file) + if path == "" { return nil, errors.New("tpl \"" + file + "\" not found") } + content := gfcache.GetContents(path) + // 模板参数 data := (map[string]interface{})(nil) if len(params) > 0 { @@ -154,13 +145,3 @@ func (view *View) funcInclude(file string, data...map[string]interface{}) templa } return template.HTML(content) } - -// 添加模板文件监控 -func (view *View) addMonitor(path string) { - if view.contents.Get(path) == "" { - gfsnotify.Add(path, func(event *gfsnotify.Event) { - // 删除文件内容缓存,下一次查询会自动更新 - view.contents.Remove(event.Path) - }) - } -} diff --git a/geg/os/gfcache/gfcache.go b/geg/os/gfcache/gfcache.go new file mode 100644 index 000000000..364195847 --- /dev/null +++ b/geg/os/gfcache/gfcache.go @@ -0,0 +1,28 @@ +package main + +import ( + "gitee.com/johng/gf/g/os/gfcache" + "gitee.com/johng/gf/g/os/gfile" + "fmt" + "time" +) + +func main() { + s := 0 + r := "" + path := gfile.TempDir() + gfile.Separator + "temp" + gfile.PutContents(path, "hello") + + s = gfcache.GetSize() + r = gfcache.GetContents(path) + fmt.Println(s, r) + + gfile.PutContentsAppend(path, " john") + + // 等待1秒以便gfsnotify回调能处理完成 + time.Sleep(time.Second) + + s = gfcache.GetSize() + r = gfcache.GetContents(path) + fmt.Println(s, r) +} diff --git a/geg/other/test.go b/geg/other/test.go index c201d4e0f..dc8efcb6c 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,11 +1,28 @@ package main -import ( - "fmt" -) +import "fmt" + +type A struct { + S string +} + +type B struct { + A +} + + +func (a *A) editA () { + a.S += "a" +} + +func (b *B) editB () { + b.S += "b" +} func main() { - methodMap := make(map[string]bool) - methodMap["t"] = true - fmt.Println(methodMap["t"]) + b := new(B) + b.editA() + b.editB() + b.A.editA() + fmt.Println(b.S) } \ No newline at end of file diff --git a/version.go b/version.go index fd7ed2cd8..d952b4318 100644 --- a/version.go +++ b/version.go @@ -1,5 +1,5 @@ package gf -const VERSION = "0.99 beta" +const VERSION = "0.99.682 beta" const AUTHORS = "john" From 13d6fc00a26589108a70905bb491ffc5c001f916 Mon Sep 17 00:00:00 2001 From: john Date: Wed, 22 Aug 2018 19:56:01 +0800 Subject: [PATCH 2/5] =?UTF-8?q?ghttp=20gzip=E7=89=B9=E6=80=A7=E5=BC=80?= =?UTF-8?q?=E5=8F=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/ghttp_request.go | 5 +++++ g/net/ghttp/ghttp_response.go | 1 + g/net/ghttp/ghttp_response_gzip.go | 33 +++++++++++++++++++++++++++++ g/net/ghttp/ghttp_server.go | 14 +++++++++--- g/net/ghttp/ghttp_server_config.go | 13 ++++++++++-- g/net/ghttp/ghttp_server_handler.go | 6 ------ geg/net/ghttp/server/server1.go | 2 +- geg/other/test.go | 31 ++++++++------------------- 8 files changed, 71 insertions(+), 34 deletions(-) diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index 2319f65aa..3a9ac2ec1 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -153,6 +153,11 @@ func (r *Request) GetHost() string { return host } +// 判断是否为AJAX请求 +func (r *Request) IsAjax() bool { + return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest") +} + // 获取请求的客户端IP地址 func (r *Request) GetClientIp() string { ip := r.clientIp.Val() diff --git a/g/net/ghttp/ghttp_response.go b/g/net/ghttp/ghttp_response.go index 9d2030dec..a250cece2 100644 --- a/g/net/ghttp/ghttp_response.go +++ b/g/net/ghttp/ghttp_response.go @@ -207,5 +207,6 @@ func (r *Response) ClearBuffer() { // 输出缓冲区数据到客户端 func (r *Response) OutputBuffer() { r.Header().Set("Server", r.Server.config.ServerAgent) + //r.handleGzip() r.Writer.OutputBuffer() } diff --git a/g/net/ghttp/ghttp_response_gzip.go b/g/net/ghttp/ghttp_response_gzip.go index a557dbebf..8e4cf8664 100644 --- a/g/net/ghttp/ghttp_response_gzip.go +++ b/g/net/ghttp/ghttp_response_gzip.go @@ -7,6 +7,14 @@ package ghttp +import ( + "mime" + "gitee.com/johng/gf/g/os/gfile" + "gitee.com/johng/gf/g/encoding/gcompress" + "strings" + "gitee.com/johng/gf/g/util/gconv" +) + // 默认的gzip压缩文件类型 var defaultGzipContentTypes = []string{ "application/atom+xml", @@ -42,4 +50,29 @@ var defaultGzipContentTypes = []string{ "text/x-component", "text/x-cross-domain-policy", "text/xml", +} + +// 返回内容gzip检查处理 +func (r *Response) handleGzip() { + // 如果客户端支持gzip压缩,并且服务端设置开启gzip压缩特性,那么执行压缩 + encoding := r.request.Header.Get("Accept-Encoding") + if encoding != "" && strings.Contains(encoding, "gzip") { + mimeType := "" + ext := gfile.Ext(r.request.URL.Path) + if ext != "" { + mimeType = strings.Split(mime.TypeByExtension(ext), ";")[0] + } + if mimeType == "" { + contentType := r.Header().Get("Content-Type") + if contentType != "" { + mimeType = strings.Split(contentType, ";")[0] + } + } + + if _, ok := r.Server.gzipMimesMap[mimeType]; ok { + r.SetBuffer(gcompress.Gzip(r.buffer)) + r.Header().Set("Content-Length", gconv.String(len(r.buffer))) + r.Header().Set("Content-Encoding", "gzip") + } + } } \ No newline at end of file diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index 275ef183b..f7a789f33 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -62,7 +62,7 @@ type Server struct { paths *gspath.SPath // 静态文件检索对象 config ServerConfig // 配置对象 servers []*gracefulServer // 底层http.Server列表 - methodsMap map[string]bool // 所有支持的HTTP Method(初始化时自动填充) + methodsMap map[string]struct{} // 所有支持的HTTP Method(初始化时自动填充) servedCount *gtype.Int // 已经服务的请求数(4-8字节,不考虑溢出情况),同时作为请求ID closeQueue *gqueue.Queue // 请求结束的关闭队列(存放的是需要异步关闭处理的*Request对象) // 服务注册相关 @@ -90,6 +90,7 @@ type Server struct { errorLogger *glog.Logger // error log日志对象 // 其他属性 nameToUriType *gtype.Int // 服务注册时对象和方法名称转换为URI时的规则 + gzipMimesMap map[string]struct{} // 支持gzip压缩的类型 } // 路由对象 @@ -181,7 +182,7 @@ func GetServer(name...interface{}) (*Server) { name : sname, paths : gspath.New(), servers : make([]*gracefulServer, 0), - methodsMap : make(map[string]bool), + methodsMap : make(map[string]struct{}), statusHandlerMap : make(map[string]HandlerFunc), serveTree : make(map[string]interface{}), hooksTree : make(map[string]interface{}), @@ -203,6 +204,7 @@ func GetServer(name...interface{}) (*Server) { errorLogEnabled : gtype.NewBool(), logHandler : gtype.NewInterface(), nameToUriType : gtype.NewInt(), + gzipMimesMap : make(map[string]struct{}), } s.errorLogger.SetBacktraceSkip(4) s.accessLogger.SetBacktraceSkip(4) @@ -210,7 +212,7 @@ func GetServer(name...interface{}) (*Server) { s.serveCache.SetCap(gSERVE_CACHE_LRU_SIZE) s.hooksCache.SetCap(gHOOKS_CACHE_LRU_SIZE) for _, v := range strings.Split(gHTTP_METHODS, ",") { - s.methodsMap[v] = true + s.methodsMap[v] = struct{}{} } // 初始化时使用默认配置 s.SetConfig(defaultServerConfig) @@ -254,6 +256,12 @@ func (s *Server) Start() error { }) } } + // gzip压缩文件类型 + if s.config.GzipContentTypes != nil { + for _, v := range s.config.GzipContentTypes { + s.gzipMimesMap[v] = struct{}{} + } + } // 启动http server reloaded := false diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index dfe267955..b390b2017 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -112,7 +112,10 @@ func (s *Server)SetConfig(c ServerConfig) { if s.config.ServerAgent == "" { s.SetServerAgent(defaultServerConfig.ServerAgent) } - // 其他可动态设置的配置 + + // ********************** + // 可动态设置的配置处理 + // ********************** s.SetLogPath(c.LogPath) s.SetLogHandler(c.LogHandler) s.SetErrorLogEnabled(c.ErrorLogEnabled) @@ -128,7 +131,6 @@ func (s *Server)SetConfig(c ServerConfig) { s.SetSessionIdName(c.SessionIdName) } s.SetNameToUriType(c.NameToUriType) - } // 设置http server参数 - Addr @@ -287,6 +289,13 @@ func (s *Server) SetDenyRoutes(routes []string) { s.config.DenyRoutes = routes } +func (s *Server) SetGzipContentTypes(types []string) { + if s.Status() == SERVER_STATUS_RUNNING { + glog.Error("cannot be changed while running") + } + s.config.GzipContentTypes = types +} + // 设置http server参数 - CookieMaxAge func (s *Server)SetCookieMaxAge(maxage int) { s.cookieMaxAge.Set(maxage) diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index bc4030747..583ae2c91 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -97,12 +97,6 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { request.Response.OutputBuffer() // 事件 - AfterOutput s.callHookHandler(HOOK_AFTER_OUTPUT, request) - - // gzip压缩处理 - encoding := request.Header.Get("Accept-Encoding") - if encoding != "" { - - } } // 初始化控制器 diff --git a/geg/net/ghttp/server/server1.go b/geg/net/ghttp/server/server1.go index 459152731..c31fffe34 100644 --- a/geg/net/ghttp/server/server1.go +++ b/geg/net/ghttp/server/server1.go @@ -7,7 +7,7 @@ import ( func main() { s := ghttp.GetServer() s.SetIndexFolder(true) - s.SetServerRoot("/home/john/Workspace/view") + s.SetServerRoot("/home/john/Workspace/Go/gf-home/static/plugin/editor.md/css") s.SetPort(8199) s.Run() } diff --git a/geg/other/test.go b/geg/other/test.go index dc8efcb6c..d4e1af3ce 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,28 +1,15 @@ package main -import "fmt" +import ( + "gitee.com/johng/gf/g" + "fmt" +) -type A struct { - S string -} - -type B struct { - A -} - - -func (a *A) editA () { - a.S += "a" -} - -func (b *B) editB () { - b.S += "b" -} func main() { - b := new(B) - b.editA() - b.editB() - b.A.editA() - fmt.Println(b.S) + v := g.View() + v.AddPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/other") + b, e := v.Parse("index.html") + fmt.Println(e) + fmt.Println(string(b)) } \ No newline at end of file From 684e6d29403a475d1bb3af1cb55ebbac5d5f4986 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 23 Aug 2018 00:31:14 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E6=94=B9=E8=BF=9BWeb=20Server=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=87=E4=BB=B6=E4=B8=8E=E6=9C=8D=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E4=BC=98=E5=85=88=E7=BA=A7=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/ghttp_request.go | 40 +++++++++++++++++------------ g/net/ghttp/ghttp_server_handler.go | 34 +++++++++++++----------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index 3a9ac2ec1..b26109574 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -20,22 +20,23 @@ import ( // 请求对象 type Request struct { http.Request - parsedGet *gtype.Bool // GET参数是否已经解析 - parsedPost *gtype.Bool // POST参数是否已经解析 - queryVars map[string][]string // GET参数 - routerVars map[string][]string // 路由解析参数 - exit *gtype.Bool // 是否退出当前请求流程执行 - Id int // 请求id(唯一) - Server *Server // 请求关联的服务器对象 - Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) - Session *Session // 与当前请求绑定的Session对象(并发安全) - Response *Response // 对应请求的返回数据操作对象 - Router *Router // 匹配到的路由对象 - EnterTime int64 // 请求进入时间(微秒) - LeaveTime int64 // 请求完成时间(微秒) - Param interface{} // 开发者自定义参数 - parsedHost *gtype.String // 解析过后不带端口号的服务器域名名称 - clientIp *gtype.String // 解析过后的客户端IP地址 + parsedGet *gtype.Bool // GET参数是否已经解析 + parsedPost *gtype.Bool // POST参数是否已经解析 + queryVars map[string][]string // GET参数 + routerVars map[string][]string // 路由解析参数 + exit *gtype.Bool // 是否退出当前请求流程执行 + Id int // 请求id(唯一) + Server *Server // 请求关联的服务器对象 + Cookie *Cookie // 与当前请求绑定的Cookie对象(并发安全) + Session *Session // 与当前请求绑定的Session对象(并发安全) + Response *Response // 对应请求的返回数据操作对象 + Router *Router // 匹配到的路由对象 + EnterTime int64 // 请求进入时间(微秒) + LeaveTime int64 // 请求完成时间(微秒) + Param interface{} // 开发者自定义参数 + parsedHost *gtype.String // 解析过后不带端口号的服务器域名名称 + clientIp *gtype.String // 解析过后的客户端IP地址 + isFileRequest bool // 是否为静态文件请求(非服务请求,当静态文件存在时,优先级会被服务请求高,被识别为文件请求) } // 创建一个Request对象 @@ -153,8 +154,13 @@ func (r *Request) GetHost() string { return host } +// 判断是否为静态文件请求 +func (r *Request) IsFileRequest() bool { + return r.isFileRequest +} + // 判断是否为AJAX请求 -func (r *Request) IsAjax() bool { +func (r *Request) IsAjaxRequest() bool { return strings.EqualFold(r.Header.Get("X-Requested-With"), "XMLHttpRequest") } diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index 583ae2c91..f1473bb4e 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -52,28 +52,32 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { s.closeQueue.PushBack(request) }() + // 优先执行静态文件检索 + filePath := s.paths.Search(r.URL.Path) + if filePath != "" { + request.isFileRequest = true + } + // 事件 - BeforeServe s.callHookHandler(HOOK_BEFORE_SERVE, request) - // 路由注册检索 + // 服务路由信息检索 handler := (*handlerItem)(nil) if !request.exit.Val() { - parsedItem := s.getServeHandlerWithCache(request) - if parsedItem == nil { - // 如果路由不匹配,那么执行静态文件检索 - path := s.paths.Search(r.URL.Path) - if path != "" { - s.serveFile(request, path) - } else { - request.Response.WriteStatus(http.StatusNotFound) - } + if request.IsFileRequest() { + s.serveFile(request, filePath) } else { - handler = parsedItem.handler - if request.Router == nil { - for k, v := range parsedItem.values { - request.routerVars[k] = v + parsedItem := s.getServeHandlerWithCache(request) + if parsedItem == nil { + request.Response.WriteStatus(http.StatusNotFound) + } else { + handler = parsedItem.handler + if request.Router == nil { + for k, v := range parsedItem.values { + request.routerVars[k] = v + } + request.Router = parsedItem.handler.router } - request.Router = parsedItem.handler.router } } } From 453d8fd374219ae6c6ad0c667c2cd6a849594709 Mon Sep 17 00:00:00 2001 From: john Date: Thu, 23 Aug 2018 10:24:14 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dghttp.Request=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=8F=82=E6=95=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/net/ghttp/ghttp_request_post.go | 8 +++--- g/net/ghttp/ghttp_request_query.go | 8 +++--- g/net/ghttp/ghttp_request_request.go | 10 +++---- g/net/ghttp/ghttp_server_handler.go | 40 +++++++++++++++++----------- geg/net/ghttp/server/hello.go | 2 +- 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/g/net/ghttp/ghttp_request_post.go b/g/net/ghttp/ghttp_request_post.go index ebdaedc4c..00c47ef59 100644 --- a/g/net/ghttp/ghttp_request_post.go +++ b/g/net/ghttp/ghttp_request_post.go @@ -68,7 +68,7 @@ func (r *Request) GetPostBool(key string, def ... bool) bool { func (r *Request) GetPostInt(key string, def ... int) int { value := r.GetPostString(key) if value != "" { - return gconv.Int(value[0]) + return gconv.Int(value) } if len(def) > 0 { return def[0] @@ -79,7 +79,7 @@ func (r *Request) GetPostInt(key string, def ... int) int { func (r *Request) GetPostUint(key string, def ... uint) uint { value := r.GetPostString(key) if value != "" { - return gconv.Uint(value[0]) + return gconv.Uint(value) } if len(def) > 0 { return def[0] @@ -90,7 +90,7 @@ func (r *Request) GetPostUint(key string, def ... uint) uint { func (r *Request) GetPostFloat32(key string, def ... float32) float32 { value := r.GetPostString(key) if value != "" { - return gconv.Float32(value[0]) + return gconv.Float32(value) } if len(def) > 0 { return def[0] @@ -101,7 +101,7 @@ func (r *Request) GetPostFloat32(key string, def ... float32) float32 { func (r *Request) GetPostFloat64(key string, def ... float64) float64 { value := r.GetPostString(key) if value != "" { - return gconv.Float64(value[0]) + return gconv.Float64(value) } if len(def) > 0 { return def[0] diff --git a/g/net/ghttp/ghttp_request_query.go b/g/net/ghttp/ghttp_request_query.go index a9d4e314a..45826f5aa 100644 --- a/g/net/ghttp/ghttp_request_query.go +++ b/g/net/ghttp/ghttp_request_query.go @@ -67,7 +67,7 @@ func (r *Request) GetQueryBool(key string, def ... bool) bool { func (r *Request) GetQueryInt(key string, def ... int) int { value := r.GetQueryString(key) if value != "" { - return gconv.Int(value[0]) + return gconv.Int(value) } if len(def) > 0 { return def[0] @@ -78,7 +78,7 @@ func (r *Request) GetQueryInt(key string, def ... int) int { func (r *Request) GetQueryUint(key string, def ... uint) uint { value := r.GetQueryString(key) if value != "" { - return gconv.Uint(value[0]) + return gconv.Uint(value) } if len(def) > 0 { return def[0] @@ -89,7 +89,7 @@ func (r *Request) GetQueryUint(key string, def ... uint) uint { func (r *Request) GetQueryFloat32(key string, def ... float32) float32 { value := r.GetQueryString(key) if value != "" { - return gconv.Float32(value[0]) + return gconv.Float32(value) } if len(def) > 0 { return def[0] @@ -100,7 +100,7 @@ func (r *Request) GetQueryFloat32(key string, def ... float32) float32 { func (r *Request) GetQueryFloat64(key string, def ... float64) float64 { value := r.GetQueryString(key) if value != "" { - return gconv.Float64(value[0]) + return gconv.Float64(value) } if len(def) > 0 { return def[0] diff --git a/g/net/ghttp/ghttp_request_request.go b/g/net/ghttp/ghttp_request_request.go index b88fb720f..2cc525262 100644 --- a/g/net/ghttp/ghttp_request_request.go +++ b/g/net/ghttp/ghttp_request_request.go @@ -39,7 +39,7 @@ func (r *Request) GetRequestString(key string, def ... string) string { func (r *Request) GetRequestBool(key string, def ... bool) bool { value := r.GetRequestString(key) if value != "" { - return gconv.Bool(value[0]) + return gconv.Bool(value) } if len(def) > 0 { return def[0] @@ -50,7 +50,7 @@ func (r *Request) GetRequestBool(key string, def ... bool) bool { func (r *Request) GetRequestInt(key string, def ... int) int { value := r.GetRequestString(key) if value != "" { - return gconv.Int(value[0]) + return gconv.Int(value) } if len(def) > 0 { return def[0] @@ -61,7 +61,7 @@ func (r *Request) GetRequestInt(key string, def ... int) int { func (r *Request) GetRequestUint(key string, def ... uint) uint { value := r.GetRequestString(key) if value != "" { - return gconv.Uint(value[0]) + return gconv.Uint(value) } if len(def) > 0 { return def[0] @@ -72,7 +72,7 @@ func (r *Request) GetRequestUint(key string, def ... uint) uint { func (r *Request) GetRequestFloat32(key string, def ... float32) float32 { value := r.GetRequestString(key) if value != "" { - return gconv.Float32(value[0]) + return gconv.Float32(value) } if len(def) > 0 { return def[0] @@ -83,7 +83,7 @@ func (r *Request) GetRequestFloat32(key string, def ... float32) float32 { func (r *Request) GetRequestFloat64(key string, def ... float64) float64 { value := r.GetRequestString(key) if value != "" { - return gconv.Float64(value[0]) + return gconv.Float64(value) } if len(def) > 0 { return def[0] diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index f1473bb4e..e0a8cdb00 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -55,7 +55,21 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { // 优先执行静态文件检索 filePath := s.paths.Search(r.URL.Path) if filePath != "" { - request.isFileRequest = true + if gfile.IsDir(filePath) { + // 如果是目录需要处理index files + if len(s.config.IndexFiles) > 0 { + for _, file := range s.config.IndexFiles { + fpath := s.paths.Search(filePath + gfile.Separator + file) + if fpath != "" { + filePath = fpath + request.isFileRequest = true + break + } + } + } + } else { + request.isFileRequest = true + } } // 事件 - BeforeServe @@ -65,12 +79,20 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { handler := (*handlerItem)(nil) if !request.exit.Val() { if request.IsFileRequest() { + // 如果存在请求的具体文件,那么优先服务文件 s.serveFile(request, filePath) } else { + // 其次进行服务路由注册检索 parsedItem := s.getServeHandlerWithCache(request) if parsedItem == nil { - request.Response.WriteStatus(http.StatusNotFound) + // 目录是优先级最低的操作 + if filePath != "" { + s.serveFile(request, filePath) + } else { + request.Response.WriteStatus(http.StatusNotFound) + } } else { + // 执行服务 handler = parsedItem.handler if request.Router == nil { for k, v := range parsedItem.values { @@ -135,20 +157,6 @@ func (s *Server)serveFile(r *Request, path string) { } info, _ := f.Stat() if info.IsDir() { - // 处理index files - if len(s.config.IndexFiles) > 0 { - for _, file := range s.config.IndexFiles { - // 这里使用s.paths来检索,而不是直接使用path,避免多目录检索情况 - // 例如:/tmp目录及源码目录都存在的情况按照优先级会返回/tmp,但是index files只在源码目录中存在 - fpath := s.paths.Search(r.URL.Path + gfile.Separator + file) - if fpath != "" { - f.Close() - s.serveFile(r, fpath) - return - } - } - } - // 处理访问目录 if s.config.IndexFolder { s.listDir(r, f) } else { diff --git a/geg/net/ghttp/server/hello.go b/geg/net/ghttp/server/hello.go index be6bc7c4f..9f95af03d 100644 --- a/geg/net/ghttp/server/hello.go +++ b/geg/net/ghttp/server/hello.go @@ -8,7 +8,7 @@ import ( func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request){ - r.Response.Writeln("哈喽世界!") + r.Response.Writeln(r.GetInt("id")) }) s.SetPort(8199) s.Run() From 6f88fee9ab3749c4da4b3a09dea3ae124830dc19 Mon Sep 17 00:00:00 2001 From: john Date: Thu, 23 Aug 2018 15:07:56 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dgarray=E9=94=81=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- g/container/garray/garray_int.go | 12 ++++++------ geg/net/ghttp/server/hello.go | 2 +- geg/other/test.go | 12 ++++++------ 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/g/container/garray/garray_int.go b/g/container/garray/garray_int.go index ba0650506..1b464899b 100644 --- a/g/container/garray/garray_int.go +++ b/g/container/garray/garray_int.go @@ -48,14 +48,14 @@ func (a *IntArray) Get(index int) int { // 设置指定索引的数据项, 调用方注意判断数组边界 func (a *IntArray) Set(index int, value int) { a.mu.Lock() - defer a.mu.RUnlock() + defer a.mu.Unlock() a.array[index] = value } // 在当前索引位置前插入一个数据项, 调用方注意判断数组边界 func (a *IntArray) Insert(index int, value int) { a.mu.Lock() - defer a.mu.RUnlock() + defer a.mu.Unlock() rear := append([]int{}, a.array[index : ]...) a.array = append(a.array[0 : index], value) a.array = append(a.array, rear...) @@ -65,14 +65,14 @@ func (a *IntArray) Insert(index int, value int) { // 删除指定索引的数据项, 调用方注意判断数组边界 func (a *IntArray) Remove(index int) { a.mu.Lock() - defer a.mu.RUnlock() + defer a.mu.Unlock() a.array = append(a.array[ : index], a.array[index + 1 : ]...) } // 追加数据项 func (a *IntArray) Append(value int) { a.mu.Lock() - defer a.mu.RUnlock() + defer a.mu.Unlock() a.array = append(a.array, value) } @@ -95,7 +95,7 @@ func (a *IntArray) Slice() []int { // 清空数据数组 func (a *IntArray) Clear() { a.mu.Lock() - defer a.mu.RUnlock() + defer a.mu.Unlock() if a.cap > 0 { a.array = make([]int, a.size, a.cap) } else { @@ -148,6 +148,6 @@ func (a *IntArray) LockFunc(f func(array []int)) { // 使用自定义方法执行加锁读取操作 func (a *IntArray) RLockFunc(f func(array []int)) { a.mu.RLock() - defer a.mu.Unlock() + defer a.mu.RUnlock() f(a.array) } \ No newline at end of file diff --git a/geg/net/ghttp/server/hello.go b/geg/net/ghttp/server/hello.go index 9f95af03d..be6bc7c4f 100644 --- a/geg/net/ghttp/server/hello.go +++ b/geg/net/ghttp/server/hello.go @@ -8,7 +8,7 @@ import ( func main() { s := g.Server() s.BindHandler("/", func(r *ghttp.Request){ - r.Response.Writeln(r.GetInt("id")) + r.Response.Writeln("哈喽世界!") }) s.SetPort(8199) s.Run() diff --git a/geg/other/test.go b/geg/other/test.go index d4e1af3ce..8a734dcc6 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,15 +1,15 @@ package main import ( - "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/util/gregex" "fmt" ) func main() { - v := g.View() - v.AddPath("/home/john/Workspace/Go/GOPATH/src/gitee.com/johng/gf/geg/other") - b, e := v.Parse("index.html") - fmt.Println(e) - fmt.Println(string(b)) + s := `百度"` + gregex.ReplaceStringFunc(`href="(.+?)"`, s, func(s string) string { + fmt.Println(s) + return s + }) } \ No newline at end of file