github.com/wangyougui/gf/v2@v2.6.5/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/wangyougui/gf. 6 7 package ghttp 8 9 import ( 10 "context" 11 "fmt" 12 "net/http" 13 "strings" 14 15 "github.com/wangyougui/gf/v2/container/glist" 16 "github.com/wangyougui/gf/v2/encoding/gurl" 17 "github.com/wangyougui/gf/v2/errors/gerror" 18 "github.com/wangyougui/gf/v2/internal/intlog" 19 "github.com/wangyougui/gf/v2/internal/json" 20 "github.com/wangyougui/gf/v2/text/gregex" 21 ) 22 23 // handlerCacheItem is an item just for internal router searching cache. 24 type handlerCacheItem struct { 25 parsedItems []*HandlerItemParsed 26 serveItem *HandlerItemParsed 27 hasHook bool 28 hasServe bool 29 } 30 31 // serveHandlerKey creates and returns a handler key for router. 32 func (s *Server) serveHandlerKey(method, path, domain string) string { 33 if len(domain) > 0 { 34 domain = "@" + domain 35 } 36 if method == "" { 37 return path + strings.ToLower(domain) 38 } 39 return strings.ToUpper(method) + ":" + path + strings.ToLower(domain) 40 } 41 42 // getHandlersWithCache searches the router item with cache feature for a given request. 43 func (s *Server) getHandlersWithCache(r *Request) (parsedItems []*HandlerItemParsed, serveItem *HandlerItemParsed, hasHook, hasServe bool) { 44 var ( 45 ctx = r.Context() 46 method = r.Method 47 path = r.URL.Path 48 host = r.GetHost() 49 ) 50 // In case of, eg: 51 // Case 1: 52 // GET /net/http 53 // r.URL.Path : /net/http 54 // r.URL.RawPath : (empty string) 55 // Case 2: 56 // GET /net%2Fhttp 57 // r.URL.Path : /net/http 58 // r.URL.RawPath : /net%2Fhttp 59 if r.URL.RawPath != "" { 60 path = r.URL.RawPath 61 } 62 // Special http method OPTIONS handling. 63 // It searches the handler with the request method instead of OPTIONS method. 64 if method == http.MethodOptions { 65 if v := r.Request.Header.Get("Access-Control-Request-Method"); v != "" { 66 method = v 67 } 68 } 69 // Search and cache the router handlers. 70 if xUrlPath := r.Header.Get(HeaderXUrlPath); xUrlPath != "" { 71 path = xUrlPath 72 } 73 var handlerCacheKey = s.serveHandlerKey(method, path, host) 74 value, err := s.serveCache.GetOrSetFunc(ctx, handlerCacheKey, func(ctx context.Context) (interface{}, error) { 75 parsedItems, serveItem, hasHook, hasServe = s.searchHandlers(method, path, host) 76 if parsedItems != nil { 77 return &handlerCacheItem{parsedItems, serveItem, hasHook, hasServe}, nil 78 } 79 return nil, nil 80 }, routeCacheDuration) 81 if err != nil { 82 intlog.Errorf(ctx, `%+v`, err) 83 } 84 if value != nil { 85 item := value.Val().(*handlerCacheItem) 86 return item.parsedItems, item.serveItem, item.hasHook, item.hasServe 87 } 88 return 89 } 90 91 // searchHandlers retrieve and returns the routers with given parameters. 92 // Note that the returned routers contain serving handler, middleware handlers and hook handlers. 93 func (s *Server) searchHandlers(method, path, domain string) (parsedItems []*HandlerItemParsed, serveItem *HandlerItemParsed, hasHook, hasServe bool) { 94 if len(path) == 0 { 95 return nil, nil, false, false 96 } 97 // In case of double '/' URI, for example: 98 // /user//index, //user/index, //user//index// 99 var previousIsSep = false 100 for i := 0; i < len(path); { 101 if path[i] == '/' { 102 if previousIsSep { 103 path = path[:i] + path[i+1:] 104 continue 105 } else { 106 previousIsSep = true 107 } 108 } else { 109 previousIsSep = false 110 } 111 i++ 112 } 113 // Split the URL.path to separate parts. 114 var array []string 115 if strings.EqualFold("/", path) { 116 array = []string{"/"} 117 } else { 118 array = strings.Split(path[1:], "/") 119 } 120 var ( 121 lastMiddlewareElem *glist.Element 122 parsedItemList = glist.New() 123 repeatHandlerCheckMap = make(map[int]struct{}, 16) 124 ) 125 126 // The default domain has the most priority when iteration. 127 // Please see doSetHandler if you want to get known about the structure of serveTree. 128 for _, domainItem := range []string{DefaultDomainName, domain} { 129 p, ok := s.serveTree[domainItem] 130 if !ok { 131 continue 132 } 133 // Make a list array with a capacity of 16. 134 lists := make([]*glist.List, 0, 16) 135 for i, part := range array { 136 // Add all lists of each node to the list array. 137 if v, ok := p.(map[string]interface{})["*list"]; ok { 138 lists = append(lists, v.(*glist.List)) 139 } 140 if v, ok := p.(map[string]interface{})[part]; ok { 141 // Loop to the next node by certain key name. 142 p = v 143 if i == len(array)-1 { 144 if v, ok := p.(map[string]interface{})["*list"]; ok { 145 lists = append(lists, v.(*glist.List)) 146 break 147 } 148 } 149 } else if v, ok := p.(map[string]interface{})["*fuzz"]; ok { 150 // Loop to the next node by fuzzy node item. 151 p = v 152 } 153 if i == len(array)-1 { 154 // It here also checks the fuzzy item, 155 // for rule case like: "/user/*action" matches to "/user". 156 if v, ok := p.(map[string]interface{})["*fuzz"]; ok { 157 p = v 158 } 159 // The leaf must have a list item. It adds the list to the list array. 160 if v, ok := p.(map[string]interface{})["*list"]; ok { 161 lists = append(lists, v.(*glist.List)) 162 } 163 } 164 } 165 166 // OK, let's loop the result list array, adding the handler item to the result handler result array. 167 // As the tail of the list array has the most priority, it iterates the list array from its tail to head. 168 for i := len(lists) - 1; i >= 0; i-- { 169 for e := lists[i].Front(); e != nil; e = e.Next() { 170 item := e.Value.(*HandlerItem) 171 // Filter repeated handler items, especially the middleware and hook handlers. 172 // It is necessary, do not remove this checks logic unless you really know how it is necessary. 173 // 174 // The `repeatHandlerCheckMap` is used for repeat handler filtering during handler searching. 175 // As there are fuzzy nodes, and the fuzzy nodes have both sub-nodes and sub-list nodes, there 176 // may be repeated handler items in both sub-nodes and sub-list nodes. It here uses handler item id to 177 // identify the same handler item that registered. 178 // 179 // The same handler item is the one that is registered in the same function doSetHandler. 180 // Note that, one handler function(middleware or hook function) may be registered multiple times as 181 // different handler items using function doSetHandler, and they have different handler item id. 182 // 183 // Note that twice, the handler function may be registered multiple times as different handler items. 184 if _, isRepeatedHandler := repeatHandlerCheckMap[item.Id]; isRepeatedHandler { 185 continue 186 } else { 187 repeatHandlerCheckMap[item.Id] = struct{}{} 188 } 189 // Serving handler can only be added to the handler array just once. 190 // The first route item in the list has the most priority than the rest. 191 // This ignoring can implement route overwritten feature. 192 if hasServe { 193 switch item.Type { 194 case HandlerTypeHandler, HandlerTypeObject: 195 continue 196 } 197 } 198 if item.Router.Method == defaultMethod || item.Router.Method == method { 199 // Note the rule having no fuzzy rules: len(match) == 1 200 if match, err := gregex.MatchString(item.Router.RegRule, path); err == nil && len(match) > 0 { 201 parsedItem := &HandlerItemParsed{item, nil} 202 // If the rule contains fuzzy names, 203 // it needs paring the URL to retrieve the values for the names. 204 if len(item.Router.RegNames) > 0 { 205 if len(match) > len(item.Router.RegNames) { 206 parsedItem.Values = make(map[string]string) 207 // It there repeated names, it just overwrites the same one. 208 for i, name := range item.Router.RegNames { 209 parsedItem.Values[name], _ = gurl.Decode(match[i+1]) 210 } 211 } 212 } 213 switch item.Type { 214 // The serving handler can be added just once. 215 case HandlerTypeHandler, HandlerTypeObject: 216 hasServe = true 217 serveItem = parsedItem 218 parsedItemList.PushBack(parsedItem) 219 220 // The middleware is inserted before the serving handler. 221 // If there are multiple middleware, they're inserted into the result list by their registering order. 222 // The middleware is also executed by their registered order. 223 case HandlerTypeMiddleware: 224 if lastMiddlewareElem == nil { 225 lastMiddlewareElem = parsedItemList.PushFront(parsedItem) 226 } else { 227 lastMiddlewareElem = parsedItemList.InsertAfter(lastMiddlewareElem, parsedItem) 228 } 229 230 // HOOK handler, just push it back to the list. 231 case HandlerTypeHook: 232 hasHook = true 233 parsedItemList.PushBack(parsedItem) 234 235 default: 236 panic(gerror.Newf(`invalid handler type %s`, item.Type)) 237 } 238 } 239 } 240 } 241 } 242 } 243 if parsedItemList.Len() > 0 { 244 var index = 0 245 parsedItems = make([]*HandlerItemParsed, parsedItemList.Len()) 246 for e := parsedItemList.Front(); e != nil; e = e.Next() { 247 parsedItems[index] = e.Value.(*HandlerItemParsed) 248 index++ 249 } 250 } 251 return 252 } 253 254 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 255 func (item HandlerItem) MarshalJSON() ([]byte, error) { 256 switch item.Type { 257 case HandlerTypeHook: 258 return json.Marshal( 259 fmt.Sprintf( 260 `%s %s:%s (%s)`, 261 item.Router.Uri, 262 item.Router.Domain, 263 item.Router.Method, 264 item.HookName, 265 ), 266 ) 267 case HandlerTypeMiddleware: 268 return json.Marshal( 269 fmt.Sprintf( 270 `%s %s:%s (MIDDLEWARE)`, 271 item.Router.Uri, 272 item.Router.Domain, 273 item.Router.Method, 274 ), 275 ) 276 default: 277 return json.Marshal( 278 fmt.Sprintf( 279 `%s %s:%s`, 280 item.Router.Uri, 281 item.Router.Domain, 282 item.Router.Method, 283 ), 284 ) 285 } 286 } 287 288 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 289 func (item HandlerItemParsed) MarshalJSON() ([]byte, error) { 290 return json.Marshal(item.Handler) 291 }