2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-04-13 15:19: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-04-13 15:19:31 +08:00
|
|
|
|
|
|
|
|
|
package ghttp
|
|
|
|
|
|
|
|
|
|
import (
|
2019-06-19 09:06:52 +08:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2019-09-29 14:27:09 +08:00
|
|
|
|
"github.com/gogf/gf/container/gtype"
|
2019-06-19 09:06:52 +08:00
|
|
|
|
"strings"
|
2019-07-29 21:01:19 +08:00
|
|
|
|
|
2019-09-19 13:13:39 +08:00
|
|
|
|
"github.com/gogf/gf/debug/gdebug"
|
|
|
|
|
|
2019-08-03 17:14:54 +08:00
|
|
|
|
"github.com/gogf/gf/container/glist"
|
2019-07-29 21:01:19 +08:00
|
|
|
|
"github.com/gogf/gf/text/gregex"
|
|
|
|
|
"github.com/gogf/gf/text/gstr"
|
2018-04-13 15:19:31 +08:00
|
|
|
|
)
|
|
|
|
|
|
2019-08-03 18:08:10 +08:00
|
|
|
|
const (
|
2019-09-19 13:13:39 +08:00
|
|
|
|
gFILTER_KEY = "/net/ghttp/ghttp"
|
2019-08-03 18:08:10 +08:00
|
|
|
|
)
|
|
|
|
|
|
2019-09-29 14:27:09 +08:00
|
|
|
|
var (
|
|
|
|
|
// 用于服务函数的ID生成变量
|
|
|
|
|
handlerIdGenerator = gtype.NewInt()
|
|
|
|
|
)
|
|
|
|
|
|
2018-04-13 15:19:31 +08:00
|
|
|
|
// 解析pattern
|
2019-06-19 09:06:52 +08:00
|
|
|
|
func (s *Server) parsePattern(pattern string) (domain, method, path string, err error) {
|
|
|
|
|
path = strings.TrimSpace(pattern)
|
|
|
|
|
domain = gDEFAULT_DOMAIN
|
|
|
|
|
method = gDEFAULT_METHOD
|
|
|
|
|
if array, err := gregex.MatchString(`([a-zA-Z]+):(.+)`, pattern); len(array) > 1 && err == nil {
|
|
|
|
|
path = strings.TrimSpace(array[2])
|
|
|
|
|
if v := strings.TrimSpace(array[1]); v != "" {
|
|
|
|
|
method = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if array, err := gregex.MatchString(`(.+)@([\w\.\-]+)`, path); len(array) > 1 && err == nil {
|
|
|
|
|
path = strings.TrimSpace(array[1])
|
|
|
|
|
if v := strings.TrimSpace(array[2]); v != "" {
|
|
|
|
|
domain = v
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if path == "" {
|
|
|
|
|
err = errors.New("invalid pattern: URI should not be empty")
|
|
|
|
|
}
|
|
|
|
|
// 去掉末尾的"/"符号,与路由匹配时处理一致
|
|
|
|
|
if path != "/" {
|
|
|
|
|
path = strings.TrimRight(path, "/")
|
|
|
|
|
}
|
|
|
|
|
return
|
2018-04-13 15:19:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-27 19:03:32 +08:00
|
|
|
|
// 路由注册处理方法。
|
2019-08-03 15:54:12 +08:00
|
|
|
|
// 非叶节点为哈希表检索节点,按照URI注册的层级进行高效检索,直至到叶子链表节点;
|
|
|
|
|
// 叶子节点是链表,按照优先级进行排序,优先级高的排前面,按照遍历检索,按照哈希表层级检索后的叶子链表数据量不会很大,所以效率比较高;
|
2019-08-03 18:08:10 +08:00
|
|
|
|
func (s *Server) setHandler(pattern string, handler *handlerItem) {
|
2019-09-29 14:27:09 +08:00
|
|
|
|
handler.itemId = handlerIdGenerator.Add(1)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
domain, method, uri, err := s.parsePattern(pattern)
|
|
|
|
|
if err != nil {
|
2020-01-21 15:42:08 +08:00
|
|
|
|
s.Logger().Fatal("invalid pattern:", pattern, err)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if len(uri) == 0 || uri[0] != '/' {
|
2020-01-21 15:42:08 +08:00
|
|
|
|
s.Logger().Fatal("invalid pattern:", pattern, "URI should lead with '/'")
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// 注册地址记录及重复注册判断
|
2019-08-03 18:08:10 +08:00
|
|
|
|
regKey := s.handlerKey(handler.hookName, method, uri, domain)
|
2019-12-18 19:37:07 +08:00
|
|
|
|
if !s.config.RouteOverWrite {
|
|
|
|
|
switch handler.itemType {
|
|
|
|
|
case gHANDLER_TYPE_HANDLER, gHANDLER_TYPE_OBJECT, gHANDLER_TYPE_CONTROLLER:
|
|
|
|
|
if item, ok := s.routesMap[regKey]; ok {
|
2020-01-21 15:42:08 +08:00
|
|
|
|
s.Logger().Fatalf(`duplicated route registry "%s", already registered at %s`, pattern, item[0].file)
|
2019-12-18 19:37:07 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-03 15:54:12 +08:00
|
|
|
|
// 注册的路由信息对象
|
2019-06-19 09:06:52 +08:00
|
|
|
|
handler.router = &Router{
|
|
|
|
|
Uri: uri,
|
|
|
|
|
Domain: domain,
|
|
|
|
|
Method: method,
|
|
|
|
|
Priority: strings.Count(uri[1:], "/"),
|
|
|
|
|
}
|
|
|
|
|
handler.router.RegRule, handler.router.RegNames = s.patternToRegRule(uri)
|
2018-07-27 19:03:32 +08:00
|
|
|
|
|
2019-08-03 15:54:12 +08:00
|
|
|
|
if _, ok := s.serveTree[domain]; !ok {
|
|
|
|
|
s.serveTree[domain] = make(map[string]interface{})
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
// 当前节点的规则链表
|
2019-08-03 17:14:54 +08:00
|
|
|
|
lists := make([]*glist.List, 0)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
array := ([]string)(nil)
|
|
|
|
|
if strings.EqualFold("/", uri) {
|
|
|
|
|
array = []string{"/"}
|
|
|
|
|
} else {
|
|
|
|
|
array = strings.Split(uri[1:], "/")
|
|
|
|
|
}
|
2019-09-29 14:27:09 +08:00
|
|
|
|
// 键名"*fuzz"代表当前节点为模糊匹配节点,该节点也会有一个*list链表;
|
2019-08-03 17:14:54 +08:00
|
|
|
|
// 键名"*list"代表链表,叶子节点和模糊匹配节点都有该属性,优先级越高越排前;
|
|
|
|
|
p := s.serveTree[domain]
|
2019-06-19 09:06:52 +08:00
|
|
|
|
for k, v := range array {
|
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// 判断是否模糊匹配规则
|
|
|
|
|
if gregex.IsMatchString(`^[:\*]|\{[\w\.\-]+\}|\*`, v) {
|
|
|
|
|
v = "*fuzz"
|
|
|
|
|
// 由于是模糊规则,因此这里会有一个*list,用以将后续的路由规则加进来,
|
|
|
|
|
// 检索会从叶子节点的链表往根节点按照优先级进行检索
|
|
|
|
|
if v, ok := p.(map[string]interface{})["*list"]; !ok {
|
2019-08-03 17:14:54 +08:00
|
|
|
|
p.(map[string]interface{})["*list"] = glist.New()
|
|
|
|
|
lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
2019-08-03 17:14:54 +08:00
|
|
|
|
lists = append(lists, v.(*glist.List))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 属性层级数据写入
|
|
|
|
|
if _, ok := p.(map[string]interface{})[v]; !ok {
|
|
|
|
|
p.(map[string]interface{})[v] = make(map[string]interface{})
|
|
|
|
|
}
|
|
|
|
|
p = p.(map[string]interface{})[v]
|
|
|
|
|
// 到达叶子节点,往list中增加匹配规则(条件 v != "*fuzz" 是因为模糊节点的话在前面已经添加了*list链表)
|
|
|
|
|
if k == len(array)-1 && v != "*fuzz" {
|
|
|
|
|
if v, ok := p.(map[string]interface{})["*list"]; !ok {
|
2019-08-03 17:14:54 +08:00
|
|
|
|
p.(map[string]interface{})["*list"] = glist.New()
|
|
|
|
|
lists = append(lists, p.(map[string]interface{})["*list"].(*glist.List))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
} else {
|
2019-08-03 17:14:54 +08:00
|
|
|
|
lists = append(lists, v.(*glist.List))
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-03 23:57:20 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 上面循环后得到的lists是该路由规则一路匹配下来相关的模糊匹配链表(注意不是这棵树所有的链表)。
|
|
|
|
|
// 下面从头开始遍历每个节点的模糊匹配链表,将该路由项插入进去(按照优先级高的放在lists链表的前面)
|
|
|
|
|
item := (*handlerItem)(nil)
|
|
|
|
|
for _, l := range lists {
|
|
|
|
|
pushed := false
|
|
|
|
|
for e := l.Front(); e != nil; e = e.Next() {
|
|
|
|
|
item = e.Value.(*handlerItem)
|
2019-09-29 14:27:09 +08:00
|
|
|
|
// 判断优先级,决定当前注册项的插入顺序
|
|
|
|
|
if s.compareRouterPriority(handler, item) {
|
2019-10-01 16:35:44 +08:00
|
|
|
|
l.InsertBefore(e, handler)
|
2019-09-29 14:27:09 +08:00
|
|
|
|
pushed = true
|
|
|
|
|
goto end
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-06 23:10:37 +08:00
|
|
|
|
end:
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if !pushed {
|
|
|
|
|
l.PushBack(handler)
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-03 17:14:54 +08:00
|
|
|
|
//gutil.Dump(s.serveTree)
|
2019-08-03 18:08:10 +08:00
|
|
|
|
if _, ok := s.routesMap[regKey]; !ok {
|
|
|
|
|
s.routesMap[regKey] = make([]registeredRouteItem, 0)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|
2019-08-09 20:05:36 +08:00
|
|
|
|
_, file, line := gdebug.CallerWithFilter(gFILTER_KEY)
|
2019-08-03 18:08:10 +08:00
|
|
|
|
s.routesMap[regKey] = append(s.routesMap[regKey], registeredRouteItem{
|
|
|
|
|
file: fmt.Sprintf(`%s:%d`, file, line),
|
2019-06-19 09:06:52 +08:00
|
|
|
|
handler: handler,
|
|
|
|
|
})
|
2018-04-13 15:19:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-19 11:25:15 +08:00
|
|
|
|
// 对比两个handlerItem的优先级,需要非常注意的是,注意新老对比项的参数先后顺序。
|
2019-09-29 14:27:09 +08:00
|
|
|
|
// 返回值true表示newItem优先级比oldItem高,会被添加链表中oldRouter的前面;否则后面。
|
2018-07-24 22:00:51 +08:00
|
|
|
|
// 优先级比较规则:
|
2019-08-03 18:08:10 +08:00
|
|
|
|
// 1、中间件优先级最高,按照添加顺序优先级执行;
|
|
|
|
|
// 2、其他路由注册类型,层级越深优先级越高(对比/数量);
|
|
|
|
|
// 3、模糊规则优先级:{xxx} > :xxx > *xxx;
|
|
|
|
|
func (s *Server) compareRouterPriority(newItem *handlerItem, oldItem *handlerItem) bool {
|
|
|
|
|
// 中间件优先级最高,按照添加顺序优先级执行
|
|
|
|
|
if newItem.itemType == gHANDLER_TYPE_MIDDLEWARE && oldItem.itemType == gHANDLER_TYPE_MIDDLEWARE {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
if newItem.itemType == gHANDLER_TYPE_MIDDLEWARE && oldItem.itemType != gHANDLER_TYPE_MIDDLEWARE {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 优先比较层级,层级越深优先级越高
|
2019-08-03 18:08:10 +08:00
|
|
|
|
if newItem.router.Priority > oldItem.router.Priority {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return true
|
|
|
|
|
}
|
2019-08-03 18:08:10 +08:00
|
|
|
|
if newItem.router.Priority < oldItem.router.Priority {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// 精准匹配比模糊匹配规则优先级高,例如:/name/act 比 /{name}/:act 优先级高
|
|
|
|
|
var fuzzyCountFieldNew, fuzzyCountFieldOld int
|
|
|
|
|
var fuzzyCountNameNew, fuzzyCountNameOld int
|
|
|
|
|
var fuzzyCountAnyNew, fuzzyCountAnyOld int
|
|
|
|
|
var fuzzyCountTotalNew, fuzzyCountTotalOld int
|
2019-08-03 18:08:10 +08:00
|
|
|
|
for _, v := range newItem.router.Uri {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
switch v {
|
|
|
|
|
case '{':
|
|
|
|
|
fuzzyCountFieldNew++
|
|
|
|
|
case ':':
|
|
|
|
|
fuzzyCountNameNew++
|
|
|
|
|
case '*':
|
|
|
|
|
fuzzyCountAnyNew++
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-03 18:08:10 +08:00
|
|
|
|
for _, v := range oldItem.router.Uri {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
switch v {
|
|
|
|
|
case '{':
|
|
|
|
|
fuzzyCountFieldOld++
|
|
|
|
|
case ':':
|
|
|
|
|
fuzzyCountNameOld++
|
|
|
|
|
case '*':
|
|
|
|
|
fuzzyCountAnyOld++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fuzzyCountTotalNew = fuzzyCountFieldNew + fuzzyCountNameNew + fuzzyCountAnyNew
|
|
|
|
|
fuzzyCountTotalOld = fuzzyCountFieldOld + fuzzyCountNameOld + fuzzyCountAnyOld
|
|
|
|
|
if fuzzyCountTotalNew < fuzzyCountTotalOld {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if fuzzyCountTotalNew > fuzzyCountTotalOld {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2018-08-19 11:25:15 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
/** 如果模糊规则数量相等,那么执行分别的数量判断 **/
|
2018-08-19 11:25:15 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 例如:/name/{act} 比 /name/:act 优先级高
|
|
|
|
|
if fuzzyCountFieldNew > fuzzyCountFieldOld {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if fuzzyCountFieldNew < fuzzyCountFieldOld {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// 例如: /name/:act 比 /name/*act 优先级高
|
|
|
|
|
if fuzzyCountNameNew > fuzzyCountNameOld {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if fuzzyCountNameNew < fuzzyCountNameOld {
|
|
|
|
|
return false
|
|
|
|
|
}
|
2018-11-19 23:13:12 +08:00
|
|
|
|
|
2019-12-02 21:26:12 +08:00
|
|
|
|
/** 比较路由规则长度,越长的规则优先级越高,模糊/命名规则不算长度 **/
|
2019-12-03 17:16:52 +08:00
|
|
|
|
|
|
|
|
|
// 例如:/admin-goods-{page} 比 /admin-{page} 优先级高
|
2019-12-02 21:26:12 +08:00
|
|
|
|
var uriNew, uriOld string
|
|
|
|
|
uriNew, _ = gregex.ReplaceString(`\{[^/]+\}`, "", newItem.router.Uri)
|
|
|
|
|
uriNew, _ = gregex.ReplaceString(`:[^/]+`, "", uriNew)
|
|
|
|
|
uriNew, _ = gregex.ReplaceString(`\*[^/]+`, "", uriNew)
|
|
|
|
|
uriOld, _ = gregex.ReplaceString(`\{[^/]+\}`, "", oldItem.router.Uri)
|
|
|
|
|
uriOld, _ = gregex.ReplaceString(`:[^/]+`, "", uriOld)
|
|
|
|
|
uriOld, _ = gregex.ReplaceString(`\*[^/]+`, "", uriOld)
|
|
|
|
|
if len(uriNew) > len(uriOld) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
if len(uriNew) < len(uriOld) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
/* 模糊规则数量相等,后续不用再判断*规则的数量比较了 */
|
2018-11-19 23:13:12 +08:00
|
|
|
|
|
2019-06-19 09:06:52 +08:00
|
|
|
|
// 比较HTTP METHOD,更精准的优先级更高
|
2019-08-03 18:08:10 +08:00
|
|
|
|
if newItem.router.Method != gDEFAULT_METHOD {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return true
|
|
|
|
|
}
|
2019-08-03 18:08:10 +08:00
|
|
|
|
if oldItem.router.Method != gDEFAULT_METHOD {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
return true
|
|
|
|
|
}
|
2018-11-19 23:13:12 +08:00
|
|
|
|
|
2019-12-19 15:38:34 +08:00
|
|
|
|
// 如果是服务路由,那么新的规则比旧的规则优先级高(路由覆盖)
|
|
|
|
|
if newItem.itemType == gHANDLER_TYPE_HANDLER ||
|
|
|
|
|
newItem.itemType == gHANDLER_TYPE_OBJECT ||
|
|
|
|
|
newItem.itemType == gHANDLER_TYPE_CONTROLLER {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是其他路由(HOOK/中间件),那么新的规则比旧的规则优先级低,使得注册相同路由则顺序执行
|
|
|
|
|
return false
|
2018-04-13 15:19:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 将pattern(不带method和domain)解析成正则表达式匹配以及对应的query字符串
|
2018-07-26 10:26:34 +08:00
|
|
|
|
func (s *Server) patternToRegRule(rule string) (regrule string, names []string) {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
if len(rule) < 2 {
|
|
|
|
|
return rule, nil
|
|
|
|
|
}
|
|
|
|
|
regrule = "^"
|
|
|
|
|
array := strings.Split(rule[1:], "/")
|
|
|
|
|
for _, v := range array {
|
|
|
|
|
if len(v) == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
switch v[0] {
|
|
|
|
|
case ':':
|
|
|
|
|
if len(v) > 1 {
|
|
|
|
|
regrule += `/([^/]+)`
|
|
|
|
|
names = append(names, v[1:])
|
|
|
|
|
} else {
|
|
|
|
|
regrule += `/[^/]+`
|
|
|
|
|
}
|
|
|
|
|
case '*':
|
|
|
|
|
if len(v) > 1 {
|
|
|
|
|
regrule += `/{0,1}(.*)`
|
|
|
|
|
names = append(names, v[1:])
|
|
|
|
|
} else {
|
|
|
|
|
regrule += `/{0,1}.*`
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
// 特殊字符替换
|
|
|
|
|
v = gstr.ReplaceByMap(v, map[string]string{
|
|
|
|
|
`.`: `\.`,
|
|
|
|
|
`+`: `\+`,
|
|
|
|
|
`*`: `.*`,
|
|
|
|
|
})
|
|
|
|
|
s, _ := gregex.ReplaceStringFunc(`\{[\w\.\-]+\}`, v, func(s string) string {
|
|
|
|
|
names = append(names, s[1:len(s)-1])
|
|
|
|
|
return `([^/]+)`
|
|
|
|
|
})
|
|
|
|
|
if strings.EqualFold(s, v) {
|
|
|
|
|
regrule += "/" + v
|
|
|
|
|
} else {
|
|
|
|
|
regrule += "/" + s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
regrule += `$`
|
|
|
|
|
return
|
2018-04-13 15:19:31 +08:00
|
|
|
|
}
|