2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-08-03 15:22:31 +08:00
|
|
|
|
//
|
|
|
|
|
// 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,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-08-03 15:22:31 +08:00
|
|
|
|
// 事件回调(中间件)路由控制.
|
|
|
|
|
|
|
|
|
|
package ghttp
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"container/list"
|
|
|
|
|
"fmt"
|
2019-02-02 16:18:25 +08:00
|
|
|
|
"github.com/gogf/gf/g/container/gset"
|
|
|
|
|
"github.com/gogf/gf/g/text/gregex"
|
2018-10-17 16:56:50 +08:00
|
|
|
|
"reflect"
|
2018-11-16 18:20:09 +08:00
|
|
|
|
"runtime"
|
|
|
|
|
"strings"
|
2018-08-03 15:22:31 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 绑定指定的hook回调函数, pattern参数同BindHandler,支持命名路由;hook参数的值由ghttp server设定,参数不区分大小写
|
2019-03-06 15:21:00 +08:00
|
|
|
|
func (s *Server)BindHookHandler(pattern string, hook string, handler HandlerFunc) {
|
|
|
|
|
s.setHandler(pattern, &handlerItem {
|
2018-10-17 16:56:50 +08:00
|
|
|
|
name : runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name(),
|
2018-08-03 15:22:31 +08:00
|
|
|
|
ctype : nil,
|
|
|
|
|
fname : "",
|
|
|
|
|
faddr : handler,
|
|
|
|
|
}, hook)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通过map批量绑定回调函数
|
2019-03-06 15:21:00 +08:00
|
|
|
|
func (s *Server)BindHookHandlerByMap(pattern string, hookmap map[string]HandlerFunc) {
|
2018-08-03 15:22:31 +08:00
|
|
|
|
for k, v := range hookmap {
|
2019-03-06 15:21:00 +08:00
|
|
|
|
s.BindHookHandler(pattern, k, v)
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 事件回调处理,内部使用了缓存处理.
|
|
|
|
|
// 并按照指定hook回调函数的优先级及注册顺序进行调用
|
|
|
|
|
func (s *Server) callHookHandler(hook string, r *Request) {
|
2018-11-16 18:20:09 +08:00
|
|
|
|
// 如果没有hook注册,那么不用执行后续逻辑
|
|
|
|
|
if len(s.hooksTree) == 0 {
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-08-03 15:22:31 +08:00
|
|
|
|
hookItems := s.getHookHandlerWithCache(hook, r)
|
|
|
|
|
if len(hookItems) > 0 {
|
2018-08-24 14:57:49 +08:00
|
|
|
|
// 备份原有的router变量
|
2018-08-03 15:22:31 +08:00
|
|
|
|
oldRouterVars := r.routerVars
|
|
|
|
|
for _, item := range hookItems {
|
2018-08-24 14:57:49 +08:00
|
|
|
|
// hook方法不能更改serve方法的路由参数,其匹配的路由参数只能自己使用,
|
|
|
|
|
// 且在多个hook方法之间不能共享路由参数,单可以使用匹配的serve方法路由参数。
|
|
|
|
|
// 当前回调函数的路由参数只在当前回调函数下有效。
|
2018-08-03 15:22:31 +08:00
|
|
|
|
r.routerVars = make(map[string][]string)
|
2018-08-24 14:57:49 +08:00
|
|
|
|
if len(oldRouterVars) > 0 {
|
|
|
|
|
for k, v := range oldRouterVars {
|
|
|
|
|
r.routerVars[k] = v
|
|
|
|
|
}
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
2018-08-24 14:57:49 +08:00
|
|
|
|
if len(item.values) > 0 {
|
|
|
|
|
for k, v := range item.values {
|
|
|
|
|
r.routerVars[k] = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 不使用hook的router对象,保留路由注册服务的router对象,不能覆盖
|
|
|
|
|
// r.Router = item.handler.router
|
2019-01-13 00:43:36 +08:00
|
|
|
|
if err := s.niceCallHookHandler(item.handler.faddr, r); err != nil {
|
|
|
|
|
switch err {
|
|
|
|
|
case gEXCEPTION_EXIT:
|
2019-01-16 20:27:58 +08:00
|
|
|
|
break
|
|
|
|
|
case gEXCEPTION_EXIT_ALL: fallthrough
|
2019-01-13 00:43:36 +08:00
|
|
|
|
case gEXCEPTION_EXIT_HOOK:
|
|
|
|
|
return
|
|
|
|
|
default:
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
2018-08-24 14:57:49 +08:00
|
|
|
|
// 恢复原有的router变量
|
|
|
|
|
r.routerVars = oldRouterVars
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-13 00:43:36 +08:00
|
|
|
|
// 友好地调用方法
|
|
|
|
|
func (s *Server) niceCallHookHandler(f HandlerFunc, r *Request) (err interface{}) {
|
|
|
|
|
defer func() {
|
|
|
|
|
err = recover()
|
|
|
|
|
}()
|
|
|
|
|
f(r)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 18:20:09 +08:00
|
|
|
|
// 查询请求处理方法, 带缓存机制,按照Host、Method、Path进行缓存.
|
2018-08-03 15:22:31 +08:00
|
|
|
|
func (s *Server) getHookHandlerWithCache(hook string, r *Request) []*handlerParsedItem {
|
|
|
|
|
cacheItems := ([]*handlerParsedItem)(nil)
|
2018-11-24 11:55:57 +08:00
|
|
|
|
cacheKey := s.handlerKey(hook, r.Method, r.URL.Path, r.GetHost())
|
2018-08-03 15:22:31 +08:00
|
|
|
|
if v := s.hooksCache.Get(cacheKey); v == nil {
|
|
|
|
|
cacheItems = s.searchHookHandler(r.Method, r.URL.Path, r.GetHost(), hook)
|
|
|
|
|
if cacheItems != nil {
|
2018-11-24 11:55:57 +08:00
|
|
|
|
s.hooksCache.Set(cacheKey, cacheItems, s.config.RouterCacheExpire*1000)
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
cacheItems = v.([]*handlerParsedItem)
|
|
|
|
|
}
|
|
|
|
|
return cacheItems
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 事件方法检索
|
|
|
|
|
func (s *Server) searchHookHandler(method, path, domain, hook string) []*handlerParsedItem {
|
2018-08-31 18:46:45 +08:00
|
|
|
|
if len(path) == 0 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-08-03 15:22:31 +08:00
|
|
|
|
// 遍历检索的域名列表
|
|
|
|
|
domains := []string{ gDEFAULT_DOMAIN }
|
|
|
|
|
if !strings.EqualFold(gDEFAULT_DOMAIN, domain) {
|
|
|
|
|
domains = append(domains, domain)
|
|
|
|
|
}
|
|
|
|
|
// URL.Path层级拆分
|
|
|
|
|
array := ([]string)(nil)
|
|
|
|
|
if strings.EqualFold("/", path) {
|
|
|
|
|
array = []string{"/"}
|
|
|
|
|
} else {
|
|
|
|
|
array = strings.Split(path[1:], "/")
|
|
|
|
|
}
|
|
|
|
|
parsedItems := make([]*handlerParsedItem, 0)
|
|
|
|
|
for _, domain := range domains {
|
|
|
|
|
p, ok := s.hooksTree[domain]
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
p, ok = p.(map[string]interface{})[hook]
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// 多层链表(每个节点都有一个*list链表)的目的是当叶子节点未有任何规则匹配时,让父级模糊匹配规则继续处理
|
|
|
|
|
lists := make([]*list.List, 0)
|
|
|
|
|
for k, v := range array {
|
|
|
|
|
if _, ok := p.(map[string]interface{})["*list"]; ok {
|
|
|
|
|
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
|
|
|
|
|
}
|
|
|
|
|
if _, ok := p.(map[string]interface{})[v]; ok {
|
|
|
|
|
p = p.(map[string]interface{})[v]
|
|
|
|
|
if k == len(array) - 1 {
|
|
|
|
|
if _, ok := p.(map[string]interface{})["*list"]; ok {
|
|
|
|
|
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
|
|
|
|
|
p = p.(map[string]interface{})["*fuzz"]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 如果是叶子节点,同时判断当前层级的"*fuzz"键名,解决例如:/user/*action 匹配 /user 的规则
|
|
|
|
|
if k == len(array) - 1 {
|
|
|
|
|
if _, ok := p.(map[string]interface{})["*fuzz"]; ok {
|
|
|
|
|
p = p.(map[string]interface{})["*fuzz"]
|
|
|
|
|
}
|
|
|
|
|
if _, ok := p.(map[string]interface{})["*list"]; ok {
|
|
|
|
|
lists = append(lists, p.(map[string]interface{})["*list"].(*list.List))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 多层链表遍历检索,从数组末尾的链表开始遍历,末尾的深度高优先级也高
|
2019-01-12 23:36:22 +08:00
|
|
|
|
pushedSet := gset.NewStringSet(true)
|
2018-08-03 15:22:31 +08:00
|
|
|
|
for i := len(lists) - 1; i >= 0; i-- {
|
|
|
|
|
for e := lists[i].Front(); e != nil; e = e.Next() {
|
|
|
|
|
handler := e.Value.(*handlerItem)
|
|
|
|
|
// 动态匹配规则带有gDEFAULT_METHOD的情况,不会像静态规则那样直接解析为所有的HTTP METHOD存储
|
|
|
|
|
if strings.EqualFold(handler.router.Method, gDEFAULT_METHOD) || strings.EqualFold(handler.router.Method, method) {
|
|
|
|
|
// 注意当不带任何动态路由规则时,len(match) == 1
|
|
|
|
|
if match, err := gregex.MatchString(handler.router.RegRule, path); err == nil && len(match) > 0 {
|
|
|
|
|
parsedItem := &handlerParsedItem{handler, nil}
|
|
|
|
|
// 如果需要query匹配,那么需要重新正则解析URL
|
|
|
|
|
if len(handler.router.RegNames) > 0 {
|
|
|
|
|
if len(match) > len(handler.router.RegNames) {
|
|
|
|
|
parsedItem.values = make(map[string][]string)
|
|
|
|
|
// 如果存在存在同名路由参数名称,那么执行数组追加
|
|
|
|
|
for i, name := range handler.router.RegNames {
|
|
|
|
|
if _, ok := parsedItem.values[name]; ok {
|
|
|
|
|
parsedItem.values[name] = append(parsedItem.values[name], match[i + 1])
|
|
|
|
|
} else {
|
|
|
|
|
parsedItem.values[name] = []string{match[i + 1]}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
address := fmt.Sprintf("%p", handler)
|
|
|
|
|
if !pushedSet.Contains(address) {
|
|
|
|
|
parsedItems = append(parsedItems, parsedItem)
|
|
|
|
|
pushedSet.Add(address)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return parsedItems
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-17 10:09:42 +08:00
|
|
|
|
// 生成hook key,如果是hook key,那么使用'%'符号分隔
|
2018-11-24 11:55:57 +08:00
|
|
|
|
func (s *Server) handlerKey(hook, method, path, domain string) string {
|
2018-10-17 10:09:42 +08:00
|
|
|
|
return hook + "%" + s.serveHandlerKey(method, path, domain)
|
2018-08-03 15:22:31 +08:00
|
|
|
|
}
|
|
|
|
|
|