diff --git a/TODO.MD b/TODO.MD index 473307f16..fa16318a6 100644 --- a/TODO.MD +++ b/TODO.MD @@ -44,7 +44,6 @@ 1. WebServer增加可选择的路由覆盖配置,默认情况下不覆盖; 1. gkafka这个包比较重,未来从框架中剥离出来; 1. grpool性能压测结果变慢的问题; -1. glist增加Element类型的并发安全处理; 1. 改进证书打开失败时的WebServer错误提示,前置HOOK校验后关闭后续的HOOK逻辑执行; 1. 目前WebServer的HOOK是按照优先级执行的,需要增加覆盖特性; 1. 增加jumplist的数据结构容器; diff --git a/g/net/ghttp/ghttp_request.go b/g/net/ghttp/ghttp_request.go index f9422e165..e71fbdff9 100644 --- a/g/net/ghttp/ghttp_request.go +++ b/g/net/ghttp/ghttp_request.go @@ -147,12 +147,22 @@ func (r *Request) GetToStruct(object interface{}, mapping...map[string]string) { r.GetRequestToStruct(object, mapping...) } -// 退出当前请求执行,原理是在Request.exit做标记,由服务逻辑流程做判断,自行停止 +// 仅退出当前逻辑执行函数, 如:服务函数、HOOK函数 func (r *Request) Exit() { + panic(gEXCEPTION_EXIT) +} + +// 退出当前请求执行,后续所有的服务逻辑流程(包括其他的HOOK)将不会执行 +func (r *Request) ExitAll() { r.exit = true panic(gEXCEPTION_EXIT) } +// 仅针对HOOK执行,默认情况下HOOK会按照优先级进行调用,当使用ExitHook后当前类型的后续HOOK将不会被调用 +func (r *Request) ExitHook() { + panic(gEXCEPTION_EXIT_HOOK) +} + // 判断当前请求是否停止执行 func (r *Request) IsExited() bool { return r.exit diff --git a/g/net/ghttp/ghttp_server.go b/g/net/ghttp/ghttp_server.go index e09322424..dc3ad925a 100644 --- a/g/net/ghttp/ghttp_server.go +++ b/g/net/ghttp/ghttp_server.go @@ -118,6 +118,7 @@ const ( gROUTE_REGISTER_OBJECT = 2 gROUTE_REGISTER_CONTROLLER = 3 gEXCEPTION_EXIT = "exit" + gEXCEPTION_EXIT_HOOK = "exit_hook" ) var ( diff --git a/g/net/ghttp/ghttp_server_config.go b/g/net/ghttp/ghttp_server_config.go index 3dc441b84..438725f17 100644 --- a/g/net/ghttp/ghttp_server_config.go +++ b/g/net/ghttp/ghttp_server_config.go @@ -52,7 +52,7 @@ type ServerConfig struct { ServerRoot string // 服务器服务的本地目录根路径(检索优先级比StaticPaths低) SearchPaths []string // 静态文件搜索目录(包含ServerRoot,按照优先级进行排序) StaticPaths []staticPathItem // 静态文件目录映射(按照优先级进行排序) - FileServerEnabled bool // 是否允许静态文件服务(总开关,默认开启) + FileServerEnabled bool // 是否允许静态文件服务(通过静态文件服务方法调用自动识别) // COOKIE CookieMaxAge int // Cookie有效期 @@ -99,7 +99,7 @@ var defaultServerConfig = ServerConfig { ServerAgent : "gf", ServerRoot : "", StaticPaths : make([]staticPathItem, 0), - FileServerEnabled : true, + FileServerEnabled : false, CookieMaxAge : gDEFAULT_COOKIE_MAX_AGE, CookiePath : gDEFAULT_COOKIE_PATH, diff --git a/g/net/ghttp/ghttp_server_config_static.go b/g/net/ghttp/ghttp_server_config_static.go index f395ccc94..07bfc3fb6 100644 --- a/g/net/ghttp/ghttp_server_config_static.go +++ b/g/net/ghttp/ghttp_server_config_static.go @@ -64,7 +64,8 @@ func (s *Server)SetServerRoot(root string) { if path == "" { glog.Fatal(fmt.Sprintf(`[ghttp] SetServerRoot failed: path "%s" does not exist`, root)) } - s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)} + s.config.SearchPaths = []string{strings.TrimRight(path, gfile.Separator)} + s.config.FileServerEnabled = true } // 添加静态文件搜索**目录**,必须给定目录的绝对路径 @@ -81,7 +82,8 @@ func (s *Server) AddSearchPath(path string) { if realPath == "" { glog.Fatal(fmt.Sprintf(`[ghttp] AddSearchPath failed: path "%s" does not exist`, path)) } - s.config.SearchPaths = append(s.config.SearchPaths, realPath) + s.config.SearchPaths = append(s.config.SearchPaths, realPath) + s.config.FileServerEnabled = true } // 添加URI与静态**目录**的映射 @@ -132,5 +134,6 @@ func (s *Server) AddStaticPath(prefix string, path string) { } else { s.config.StaticPaths = []staticPathItem { addItem } } + s.config.FileServerEnabled = true } diff --git a/g/net/ghttp/ghttp_server_graceful.go b/g/net/ghttp/ghttp_server_graceful.go index 05017ab32..ee7fae9c9 100644 --- a/g/net/ghttp/ghttp_server_graceful.go +++ b/g/net/ghttp/ghttp_server_graceful.go @@ -7,6 +7,7 @@ package ghttp import ( + "errors" "os" "fmt" "net" @@ -92,11 +93,11 @@ func (s *gracefulServer) ListenAndServeTLS(certFile, keyFile string) error { if config.NextProtos == nil { config.NextProtos = []string{"http/1.1"} } - var err error + err := error(nil) config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return err + return errors.New(fmt.Sprintf(`open cert file "%s","%s" failed: %s`, certFile, keyFile, err.Error())) } ln, err := s.getNetListener(addr) if err != nil { diff --git a/g/net/ghttp/ghttp_server_handler.go b/g/net/ghttp/ghttp_server_handler.go index 0e3b19dcf..bc395ee1d 100644 --- a/g/net/ghttp/ghttp_server_handler.go +++ b/g/net/ghttp/ghttp_server_handler.go @@ -118,19 +118,25 @@ func (s *Server)handleRequest(w http.ResponseWriter, r *http.Request) { } // 事件 - AfterServe - s.callHookHandler(HOOK_AFTER_SERVE, request) + if !request.IsExited() { + s.callHookHandler(HOOK_AFTER_SERVE, request) + } // 设置请求完成时间 request.LeaveTime = gtime.Microsecond() // 事件 - BeforeOutput - s.callHookHandler(HOOK_BEFORE_OUTPUT, request) + if !request.IsExited() { + s.callHookHandler(HOOK_BEFORE_OUTPUT, request) + } // 输出Cookie request.Cookie.Output() // 输出缓冲区 request.Response.OutputBuffer() // 事件 - AfterOutput - s.callHookHandler(HOOK_AFTER_OUTPUT, request) + if !request.IsExited() { + s.callHookHandler(HOOK_AFTER_OUTPUT, request) + } } // 查找静态文件的绝对路径 @@ -158,38 +164,32 @@ func (s *Server) searchStaticFile(uri string) (filePath string, isDir bool) { return "", false } -// 初始化控制器 +// 调用服务接口 func (s *Server) callServeHandler(h *handlerItem, r *Request) { if h.faddr == nil { c := reflect.New(h.ctype) - s.niceCall(func() { + s.niceCallFunc(func() { c.MethodByName("Init").Call([]reflect.Value{reflect.ValueOf(r)}) }) - s.niceCall(func() { + s.niceCallFunc(func() { c.MethodByName(h.fname).Call(nil) }) - s.niceCall(func() { + s.niceCallFunc(func() { c.MethodByName("Shut").Call(nil) }) } else { if h.finit != nil { - s.niceCall(func() { - h.finit(r) - }) + s.niceCallServeHandler(h.finit, r) } - s.niceCall(func() { - h.faddr(r) - }) + s.niceCallServeHandler(h.faddr, r) if h.fshut != nil { - s.niceCall(func() { - h.fshut(r) - }) + s.niceCallServeHandler(h.fshut, r) } } } -// 友好地调用方法 -func (s *Server) niceCall(f func()) { +// 友好地调用通用方法 +func (s *Server) niceCallFunc(f func()) { defer func() { if e := recover(); e != nil && e != gEXCEPTION_EXIT { panic(e) @@ -198,6 +198,16 @@ func (s *Server) niceCall(f func()) { f() } +// 友好地调用HandlerFunc +func (s *Server) niceCallServeHandler(f HandlerFunc, r *Request) { + defer func() { + if e := recover(); e != nil && e != gEXCEPTION_EXIT { + panic(e) + } + }() + f(r) +} + // http server静态文件处理,path可以为相对路径也可以为绝对路径 func (s *Server) serveFile(r *Request, path string) { f, err := os.Open(path) diff --git a/g/net/ghttp/ghttp_server_router_hook.go b/g/net/ghttp/ghttp_server_router_hook.go index e2e5e7dac..3390d1fdb 100644 --- a/g/net/ghttp/ghttp_server_router_hook.go +++ b/g/net/ghttp/ghttp_server_router_hook.go @@ -46,11 +46,6 @@ func (s *Server) callHookHandler(hook string, r *Request) { } hookItems := s.getHookHandlerWithCache(hook, r) if len(hookItems) > 0 { - defer func() { - if e := recover(); e != nil && e != gEXCEPTION_EXIT { - panic(e) - } - }() // 备份原有的router变量 oldRouterVars := r.routerVars for _, item := range hookItems { @@ -70,13 +65,30 @@ func (s *Server) callHookHandler(hook string, r *Request) { } // 不使用hook的router对象,保留路由注册服务的router对象,不能覆盖 // r.Router = item.handler.router - item.handler.faddr(r) + if err := s.niceCallHookHandler(item.handler.faddr, r); err != nil { + switch err { + case gEXCEPTION_EXIT: + case gEXCEPTION_EXIT_HOOK: + return + default: + panic(err) + } + } } // 恢复原有的router变量 r.routerVars = oldRouterVars } } +// 友好地调用方法 +func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{}) { + defer func() { + err = recover() + }() + f(r) + return +} + // 查询请求处理方法, 带缓存机制,按照Host、Method、Path进行缓存. func (s *Server) getHookHandlerWithCache(hook string, r *Request) []*handlerParsedItem { cacheItems := ([]*handlerParsedItem)(nil) diff --git a/geg/net/ghttp/server/hooks/hooks5.go b/geg/net/ghttp/server/hooks/hooks5.go new file mode 100644 index 000000000..8e13cfe8d --- /dev/null +++ b/geg/net/ghttp/server/hooks/hooks5.go @@ -0,0 +1,19 @@ +package main + +import ( + "gitee.com/johng/gf/g" + "gitee.com/johng/gf/g/net/ghttp" +) + +func main() { + s := g.Server() + s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) { + r.Response.Writeln("/*any") + }) + s.BindHookHandler("/v1/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) { + r.Response.Writeln("/v1/*") + r.ExitHook() + }) + s.SetPort(8199) + s.Run() +} \ No newline at end of file diff --git a/geg/other/test.go b/geg/other/test.go index 89b37603b..a70f09dcc 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,12 +1,19 @@ package main -import ( - "fmt" -) +import "fmt" + +func test() (err interface{}) { + defer func() { + err = recover() + }() + panic(1) + return +} func main() { - i := int64(5) - n := int64(3) - m := i/n - fmt.Println(m) + + switch err := test(); err { + default: + fmt.Println(err) + } }