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  }