github.com/gogf/gf@v1.16.9/net/ghttp/ghttp_server_router_serve.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package ghttp 8 9 import ( 10 "fmt" 11 "github.com/gogf/gf/errors/gcode" 12 "github.com/gogf/gf/errors/gerror" 13 "github.com/gogf/gf/internal/json" 14 "strings" 15 16 "github.com/gogf/gf/container/glist" 17 "github.com/gogf/gf/text/gregex" 18 ) 19 20 // handlerCacheItem is an item just for internal router searching cache. 21 type handlerCacheItem struct { 22 parsedItems []*handlerParsedItem 23 hasHook bool 24 hasServe bool 25 } 26 27 // serveHandlerKey creates and returns a handler key for router. 28 func (s *Server) serveHandlerKey(method, path, domain string) string { 29 if len(domain) > 0 { 30 domain = "@" + domain 31 } 32 if method == "" { 33 return path + strings.ToLower(domain) 34 } 35 return strings.ToUpper(method) + ":" + path + strings.ToLower(domain) 36 } 37 38 // getHandlersWithCache searches the router item with cache feature for given request. 39 func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) { 40 method := r.Method 41 // Special http method OPTIONS handling. 42 // It searches the handler with the request method instead of OPTIONS method. 43 if method == "OPTIONS" { 44 if v := r.Request.Header.Get("Access-Control-Request-Method"); v != "" { 45 method = v 46 } 47 } 48 // Search and cache the router handlers. 49 value, _ := s.serveCache.GetOrSetFunc( 50 s.serveHandlerKey(method, r.URL.Path, r.GetHost()), 51 func() (interface{}, error) { 52 parsedItems, hasHook, hasServe = s.searchHandlers(method, r.URL.Path, r.GetHost()) 53 if parsedItems != nil { 54 return &handlerCacheItem{parsedItems, hasHook, hasServe}, nil 55 } 56 return nil, nil 57 }, routeCacheDuration) 58 if value != nil { 59 item := value.(*handlerCacheItem) 60 return item.parsedItems, item.hasHook, item.hasServe 61 } 62 return 63 } 64 65 // searchHandlers retrieves and returns the routers with given parameters. 66 // Note that the returned routers contain serving handler, middleware handlers and hook handlers. 67 func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*handlerParsedItem, hasHook, hasServe bool) { 68 if len(path) == 0 { 69 return nil, false, false 70 } 71 // In case of double '/' URI, for example: 72 // /user//index, //user/index, //user//index// 73 var previousIsSep = false 74 for i := 0; i < len(path); { 75 if path[i] == '/' { 76 if previousIsSep { 77 path = path[:i] + path[i+1:] 78 continue 79 } else { 80 previousIsSep = true 81 } 82 } else { 83 previousIsSep = false 84 } 85 i++ 86 } 87 // Split the URL.path to separate parts. 88 var array []string 89 if strings.EqualFold("/", path) { 90 array = []string{"/"} 91 } else { 92 array = strings.Split(path[1:], "/") 93 } 94 var ( 95 lastMiddlewareElem *glist.Element 96 parsedItemList = glist.New() 97 repeatHandlerCheckMap = make(map[int]struct{}, 16) 98 ) 99 100 // Default domain has the most priority when iteration. 101 for _, domain := range []string{defaultDomainName, domain} { 102 p, ok := s.serveTree[domain] 103 if !ok { 104 continue 105 } 106 // Make a list array with capacity of 16. 107 lists := make([]*glist.List, 0, 16) 108 for i, part := range array { 109 // Add all list of each node to the list array. 110 if v, ok := p.(map[string]interface{})["*list"]; ok { 111 lists = append(lists, v.(*glist.List)) 112 } 113 if v, ok := p.(map[string]interface{})[part]; ok { 114 // Loop to the next node by certain key name. 115 p = v 116 if i == len(array)-1 { 117 if v, ok := p.(map[string]interface{})["*list"]; ok { 118 lists = append(lists, v.(*glist.List)) 119 break 120 } 121 } 122 } else if v, ok := p.(map[string]interface{})["*fuzz"]; ok { 123 // Loop to the next node by fuzzy node item. 124 p = v 125 } 126 if i == len(array)-1 { 127 // It here also checks the fuzzy item, 128 // for rule case like: "/user/*action" matches to "/user". 129 if v, ok := p.(map[string]interface{})["*fuzz"]; ok { 130 p = v 131 } 132 // The leaf must have a list item. It adds the list to the list array. 133 if v, ok := p.(map[string]interface{})["*list"]; ok { 134 lists = append(lists, v.(*glist.List)) 135 } 136 } 137 } 138 139 // OK, let's loop the result list array, adding the handler item to the result handler result array. 140 // As the tail of the list array has the most priority, it iterates the list array from its tail to head. 141 for i := len(lists) - 1; i >= 0; i-- { 142 for e := lists[i].Front(); e != nil; e = e.Next() { 143 item := e.Value.(*handlerItem) 144 // Filter repeated handler item, especially the middleware and hook handlers. 145 // It is necessary, do not remove this checks logic unless you really know how it is necessary. 146 if _, ok := repeatHandlerCheckMap[item.Id]; ok { 147 continue 148 } else { 149 repeatHandlerCheckMap[item.Id] = struct{}{} 150 } 151 // Serving handler can only be added to the handler array just once. 152 if hasServe { 153 switch item.Type { 154 case handlerTypeHandler, handlerTypeObject, handlerTypeController: 155 continue 156 } 157 } 158 if item.Router.Method == defaultMethod || item.Router.Method == method { 159 // Note the rule having no fuzzy rules: len(match) == 1 160 if match, err := gregex.MatchString(item.Router.RegRule, path); err == nil && len(match) > 0 { 161 parsedItem := &handlerParsedItem{item, nil} 162 // If the rule contains fuzzy names, 163 // it needs paring the URL to retrieve the values for the names. 164 if len(item.Router.RegNames) > 0 { 165 if len(match) > len(item.Router.RegNames) { 166 parsedItem.Values = make(map[string]string) 167 // It there repeated names, it just overwrites the same one. 168 for i, name := range item.Router.RegNames { 169 parsedItem.Values[name] = match[i+1] 170 } 171 } 172 } 173 switch item.Type { 174 // The serving handler can be only added just once. 175 case handlerTypeHandler, handlerTypeObject, handlerTypeController: 176 hasServe = true 177 parsedItemList.PushBack(parsedItem) 178 179 // The middleware is inserted before the serving handler. 180 // If there are multiple middleware, they're inserted into the result list by their registering order. 181 // The middleware is also executed by their registered order. 182 case handlerTypeMiddleware: 183 if lastMiddlewareElem == nil { 184 lastMiddlewareElem = parsedItemList.PushFront(parsedItem) 185 } else { 186 lastMiddlewareElem = parsedItemList.InsertAfter(lastMiddlewareElem, parsedItem) 187 } 188 189 // HOOK handler, just push it back to the list. 190 case handlerTypeHook: 191 hasHook = true 192 parsedItemList.PushBack(parsedItem) 193 194 default: 195 panic(gerror.NewCodef(gcode.CodeInternalError, `invalid handler type %d`, item.Type)) 196 } 197 } 198 } 199 } 200 } 201 } 202 if parsedItemList.Len() > 0 { 203 index := 0 204 parsedItems = make([]*handlerParsedItem, parsedItemList.Len()) 205 for e := parsedItemList.Front(); e != nil; e = e.Next() { 206 parsedItems[index] = e.Value.(*handlerParsedItem) 207 index++ 208 } 209 } 210 return 211 } 212 213 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 214 func (item *handlerItem) MarshalJSON() ([]byte, error) { 215 switch item.Type { 216 case handlerTypeHook: 217 return json.Marshal( 218 fmt.Sprintf( 219 `%s %s:%s (%s)`, 220 item.Router.Uri, 221 item.Router.Domain, 222 item.Router.Method, 223 item.HookName, 224 ), 225 ) 226 case handlerTypeMiddleware: 227 return json.Marshal( 228 fmt.Sprintf( 229 `%s %s:%s (MIDDLEWARE)`, 230 item.Router.Uri, 231 item.Router.Domain, 232 item.Router.Method, 233 ), 234 ) 235 default: 236 return json.Marshal( 237 fmt.Sprintf( 238 `%s %s:%s`, 239 item.Router.Uri, 240 item.Router.Domain, 241 item.Router.Method, 242 ), 243 ) 244 } 245 } 246 247 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 248 func (item *handlerParsedItem) MarshalJSON() ([]byte, error) { 249 return json.Marshal(item.Handler) 250 }