github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/mux/mux.go (about) 1 // Copyright 2012 The Gorilla Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package mux 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 "path" 12 "regexp" 13 14 http "github.com/hxx258456/ccgo/gmhttp" 15 ) 16 17 var ( 18 // ErrMethodMismatch is returned when the method in the request does not match 19 // the method defined against the route. 20 ErrMethodMismatch = errors.New("method is not allowed") 21 // ErrNotFound is returned when no route match is found. 22 ErrNotFound = errors.New("no matching route was found") 23 ) 24 25 // NewRouter returns a new router instance. 26 func NewRouter() *Router { 27 return &Router{namedRoutes: make(map[string]*Route)} 28 } 29 30 // Router registers routes to be matched and dispatches a handler. 31 // 32 // It implements the http.Handler interface, so it can be registered to serve 33 // requests: 34 // 35 // var router = mux.NewRouter() 36 // 37 // func main() { 38 // http.Handle("/", router) 39 // } 40 // 41 // Or, for Google App Engine, register it in a init() function: 42 // 43 // func init() { 44 // http.Handle("/", router) 45 // } 46 // 47 // This will send all incoming requests to the router. 48 type Router struct { 49 // Configurable Handler to be used when no route matches. 50 NotFoundHandler http.Handler 51 52 // Configurable Handler to be used when the request method does not match the route. 53 MethodNotAllowedHandler http.Handler 54 55 // Routes to be matched, in order. 56 routes []*Route 57 58 // Routes by name for URL building. 59 namedRoutes map[string]*Route 60 61 // If true, do not clear the request context after handling the request. 62 // 63 // Deprecated: No effect, since the context is stored on the request itself. 64 KeepContext bool 65 66 // Slice of middlewares to be called after a match is found 67 middlewares []middleware 68 69 // configuration shared with `Route` 70 routeConf 71 } 72 73 // common route configuration shared between `Router` and `Route` 74 type routeConf struct { 75 // If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to" 76 useEncodedPath bool 77 78 // If true, when the path pattern is "/path/", accessing "/path" will 79 // redirect to the former and vice versa. 80 strictSlash bool 81 82 // If true, when the path pattern is "/path//to", accessing "/path//to" 83 // will not redirect 84 skipClean bool 85 86 // Manager for the variables from host and path. 87 regexp routeRegexpGroup 88 89 // List of matchers. 90 matchers []matcher 91 92 // The scheme used when building URLs. 93 buildScheme string 94 95 buildVarsFunc BuildVarsFunc 96 } 97 98 // returns an effective deep copy of `routeConf` 99 func copyRouteConf(r routeConf) routeConf { 100 c := r 101 102 if r.regexp.path != nil { 103 c.regexp.path = copyRouteRegexp(r.regexp.path) 104 } 105 106 if r.regexp.host != nil { 107 c.regexp.host = copyRouteRegexp(r.regexp.host) 108 } 109 110 c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries)) 111 for _, q := range r.regexp.queries { 112 c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q)) 113 } 114 115 c.matchers = make([]matcher, len(r.matchers)) 116 copy(c.matchers, r.matchers) 117 118 return c 119 } 120 121 func copyRouteRegexp(r *routeRegexp) *routeRegexp { 122 c := *r 123 return &c 124 } 125 126 // Match attempts to match the given request against the router's registered routes. 127 // 128 // If the request matches a route of this router or one of its subrouters the Route, 129 // Handler, and Vars fields of the the match argument are filled and this function 130 // returns true. 131 // 132 // If the request does not match any of this router's or its subrouters' routes 133 // then this function returns false. If available, a reason for the match failure 134 // will be filled in the match argument's MatchErr field. If the match failure type 135 // (eg: not found) has a registered handler, the handler is assigned to the Handler 136 // field of the match argument. 137 func (r *Router) Match(req *http.Request, match *RouteMatch) bool { 138 for _, route := range r.routes { 139 if route.Match(req, match) { 140 // Build middleware chain if no error was found 141 if match.MatchErr == nil { 142 for i := len(r.middlewares) - 1; i >= 0; i-- { 143 match.Handler = r.middlewares[i].Middleware(match.Handler) 144 } 145 } 146 return true 147 } 148 } 149 150 if match.MatchErr == ErrMethodMismatch { 151 if r.MethodNotAllowedHandler != nil { 152 match.Handler = r.MethodNotAllowedHandler 153 return true 154 } 155 156 return false 157 } 158 159 // Closest match for a router (includes sub-routers) 160 if r.NotFoundHandler != nil { 161 match.Handler = r.NotFoundHandler 162 match.MatchErr = ErrNotFound 163 return true 164 } 165 166 match.MatchErr = ErrNotFound 167 return false 168 } 169 170 // ServeHTTP dispatches the handler registered in the matched route. 171 // 172 // When there is a match, the route variables can be retrieved calling 173 // mux.Vars(request). 174 func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) { 175 if !r.skipClean { 176 path := req.URL.Path 177 if r.useEncodedPath { 178 path = req.URL.EscapedPath() 179 } 180 // Clean path to canonical form and redirect. 181 if p := cleanPath(path); p != path { 182 183 // Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query. 184 // This matches with fix in go 1.2 r.c. 4 for same problem. Go Issue: 185 // http://code.google.com/p/go/issues/detail?id=5252 186 url := *req.URL 187 url.Path = p 188 p = url.String() 189 190 w.Header().Set("Location", p) 191 w.WriteHeader(http.StatusMovedPermanently) 192 return 193 } 194 } 195 var match RouteMatch 196 var handler http.Handler 197 if r.Match(req, &match) { 198 handler = match.Handler 199 req = requestWithVars(req, match.Vars) 200 req = requestWithRoute(req, match.Route) 201 } 202 203 if handler == nil && match.MatchErr == ErrMethodMismatch { 204 handler = methodNotAllowedHandler() 205 } 206 207 if handler == nil { 208 handler = http.NotFoundHandler() 209 } 210 211 handler.ServeHTTP(w, req) 212 } 213 214 // Get returns a route registered with the given name. 215 func (r *Router) Get(name string) *Route { 216 return r.namedRoutes[name] 217 } 218 219 // GetRoute returns a route registered with the given name. This method 220 // was renamed to Get() and remains here for backwards compatibility. 221 func (r *Router) GetRoute(name string) *Route { 222 return r.namedRoutes[name] 223 } 224 225 // StrictSlash defines the trailing slash behavior for new routes. The initial 226 // value is false. 227 // 228 // When true, if the route path is "/path/", accessing "/path" will perform a redirect 229 // to the former and vice versa. In other words, your application will always 230 // see the path as specified in the route. 231 // 232 // When false, if the route path is "/path", accessing "/path/" will not match 233 // this route and vice versa. 234 // 235 // The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for 236 // routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed 237 // request will be made as a GET by most clients. Use middleware or client settings 238 // to modify this behaviour as needed. 239 // 240 // Special case: when a route sets a path prefix using the PathPrefix() method, 241 // strict slash is ignored for that route because the redirect behavior can't 242 // be determined from a prefix alone. However, any subrouters created from that 243 // route inherit the original StrictSlash setting. 244 func (r *Router) StrictSlash(value bool) *Router { 245 r.strictSlash = value 246 return r 247 } 248 249 // SkipClean defines the path cleaning behaviour for new routes. The initial 250 // value is false. Users should be careful about which routes are not cleaned 251 // 252 // When true, if the route path is "/path//to", it will remain with the double 253 // slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/ 254 // 255 // When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will 256 // become /fetch/http/xkcd.com/534 257 func (r *Router) SkipClean(value bool) *Router { 258 r.skipClean = value 259 return r 260 } 261 262 // UseEncodedPath tells the router to match the encoded original path 263 // to the routes. 264 // For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to". 265 // 266 // If not called, the router will match the unencoded path to the routes. 267 // For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to" 268 func (r *Router) UseEncodedPath() *Router { 269 r.useEncodedPath = true 270 return r 271 } 272 273 // ---------------------------------------------------------------------------- 274 // Route factories 275 // ---------------------------------------------------------------------------- 276 277 // NewRoute registers an empty route. 278 func (r *Router) NewRoute() *Route { 279 // initialize a route with a copy of the parent router's configuration 280 route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes} 281 r.routes = append(r.routes, route) 282 return route 283 } 284 285 // Name registers a new route with a name. 286 // See Route.Name(). 287 func (r *Router) Name(name string) *Route { 288 return r.NewRoute().Name(name) 289 } 290 291 // Handle registers a new route with a matcher for the URL path. 292 // See Route.Path() and Route.Handler(). 293 func (r *Router) Handle(path string, handler http.Handler) *Route { 294 return r.NewRoute().Path(path).Handler(handler) 295 } 296 297 // HandleFunc registers a new route with a matcher for the URL path. 298 // See Route.Path() and Route.HandlerFunc(). 299 func (r *Router) HandleFunc(path string, f func(http.ResponseWriter, 300 *http.Request)) *Route { 301 return r.NewRoute().Path(path).HandlerFunc(f) 302 } 303 304 // Headers registers a new route with a matcher for request header values. 305 // See Route.Headers(). 306 func (r *Router) Headers(pairs ...string) *Route { 307 return r.NewRoute().Headers(pairs...) 308 } 309 310 // Host registers a new route with a matcher for the URL host. 311 // See Route.Host(). 312 func (r *Router) Host(tpl string) *Route { 313 return r.NewRoute().Host(tpl) 314 } 315 316 // MatcherFunc registers a new route with a custom matcher function. 317 // See Route.MatcherFunc(). 318 func (r *Router) MatcherFunc(f MatcherFunc) *Route { 319 return r.NewRoute().MatcherFunc(f) 320 } 321 322 // Methods registers a new route with a matcher for HTTP methods. 323 // See Route.Methods(). 324 func (r *Router) Methods(methods ...string) *Route { 325 return r.NewRoute().Methods(methods...) 326 } 327 328 // Path registers a new route with a matcher for the URL path. 329 // See Route.Path(). 330 func (r *Router) Path(tpl string) *Route { 331 return r.NewRoute().Path(tpl) 332 } 333 334 // PathPrefix registers a new route with a matcher for the URL path prefix. 335 // See Route.PathPrefix(). 336 func (r *Router) PathPrefix(tpl string) *Route { 337 return r.NewRoute().PathPrefix(tpl) 338 } 339 340 // Queries registers a new route with a matcher for URL query values. 341 // See Route.Queries(). 342 func (r *Router) Queries(pairs ...string) *Route { 343 return r.NewRoute().Queries(pairs...) 344 } 345 346 // Schemes registers a new route with a matcher for URL schemes. 347 // See Route.Schemes(). 348 func (r *Router) Schemes(schemes ...string) *Route { 349 return r.NewRoute().Schemes(schemes...) 350 } 351 352 // BuildVarsFunc registers a new route with a custom function for modifying 353 // route variables before building a URL. 354 func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route { 355 return r.NewRoute().BuildVarsFunc(f) 356 } 357 358 // Walk walks the router and all its sub-routers, calling walkFn for each route 359 // in the tree. The routes are walked in the order they were added. Sub-routers 360 // are explored depth-first. 361 func (r *Router) Walk(walkFn WalkFunc) error { 362 return r.walk(walkFn, []*Route{}) 363 } 364 365 // SkipRouter is used as a return value from WalkFuncs to indicate that the 366 // router that walk is about to descend down to should be skipped. 367 var SkipRouter = errors.New("skip this router") 368 369 // WalkFunc is the type of the function called for each route visited by Walk. 370 // At every invocation, it is given the current route, and the current router, 371 // and a list of ancestor routes that lead to the current route. 372 type WalkFunc func(route *Route, router *Router, ancestors []*Route) error 373 374 func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error { 375 for _, t := range r.routes { 376 err := walkFn(t, r, ancestors) 377 if err == SkipRouter { 378 continue 379 } 380 if err != nil { 381 return err 382 } 383 for _, sr := range t.matchers { 384 if h, ok := sr.(*Router); ok { 385 ancestors = append(ancestors, t) 386 err := h.walk(walkFn, ancestors) 387 if err != nil { 388 return err 389 } 390 ancestors = ancestors[:len(ancestors)-1] 391 } 392 } 393 if h, ok := t.handler.(*Router); ok { 394 ancestors = append(ancestors, t) 395 err := h.walk(walkFn, ancestors) 396 if err != nil { 397 return err 398 } 399 ancestors = ancestors[:len(ancestors)-1] 400 } 401 } 402 return nil 403 } 404 405 // ---------------------------------------------------------------------------- 406 // Context 407 // ---------------------------------------------------------------------------- 408 409 // RouteMatch stores information about a matched route. 410 type RouteMatch struct { 411 Route *Route 412 Handler http.Handler 413 Vars map[string]string 414 415 // MatchErr is set to appropriate matching error 416 // It is set to ErrMethodMismatch if there is a mismatch in 417 // the request method and route method 418 MatchErr error 419 } 420 421 type contextKey int 422 423 const ( 424 varsKey contextKey = iota 425 routeKey 426 ) 427 428 // Vars returns the route variables for the current request, if any. 429 func Vars(r *http.Request) map[string]string { 430 if rv := r.Context().Value(varsKey); rv != nil { 431 return rv.(map[string]string) 432 } 433 return nil 434 } 435 436 // CurrentRoute returns the matched route for the current request, if any. 437 // This only works when called inside the handler of the matched route 438 // because the matched route is stored in the request context which is cleared 439 // after the handler returns. 440 func CurrentRoute(r *http.Request) *Route { 441 if rv := r.Context().Value(routeKey); rv != nil { 442 return rv.(*Route) 443 } 444 return nil 445 } 446 447 func requestWithVars(r *http.Request, vars map[string]string) *http.Request { 448 ctx := context.WithValue(r.Context(), varsKey, vars) 449 return r.WithContext(ctx) 450 } 451 452 func requestWithRoute(r *http.Request, route *Route) *http.Request { 453 ctx := context.WithValue(r.Context(), routeKey, route) 454 return r.WithContext(ctx) 455 } 456 457 // ---------------------------------------------------------------------------- 458 // Helpers 459 // ---------------------------------------------------------------------------- 460 461 // cleanPath returns the canonical path for p, eliminating . and .. elements. 462 // Borrowed from the net/http package. 463 func cleanPath(p string) string { 464 if p == "" { 465 return "/" 466 } 467 if p[0] != '/' { 468 p = "/" + p 469 } 470 np := path.Clean(p) 471 // path.Clean removes trailing slash except for root; 472 // put the trailing slash back if necessary. 473 if p[len(p)-1] == '/' && np != "/" { 474 np += "/" 475 } 476 477 return np 478 } 479 480 // uniqueVars returns an error if two slices contain duplicated strings. 481 func uniqueVars(s1, s2 []string) error { 482 for _, v1 := range s1 { 483 for _, v2 := range s2 { 484 if v1 == v2 { 485 return fmt.Errorf("mux: duplicated route variable %q", v2) 486 } 487 } 488 } 489 return nil 490 } 491 492 // checkPairs returns the count of strings passed in, and an error if 493 // the count is not an even number. 494 func checkPairs(pairs ...string) (int, error) { 495 length := len(pairs) 496 if length%2 != 0 { 497 return length, fmt.Errorf( 498 "mux: number of parameters must be multiple of 2, got %v", pairs) 499 } 500 return length, nil 501 } 502 503 // mapFromPairsToString converts variadic string parameters to a 504 // string to string map. 505 func mapFromPairsToString(pairs ...string) (map[string]string, error) { 506 length, err := checkPairs(pairs...) 507 if err != nil { 508 return nil, err 509 } 510 m := make(map[string]string, length/2) 511 for i := 0; i < length; i += 2 { 512 m[pairs[i]] = pairs[i+1] 513 } 514 return m, nil 515 } 516 517 // mapFromPairsToRegex converts variadic string parameters to a 518 // string to regex map. 519 func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) { 520 length, err := checkPairs(pairs...) 521 if err != nil { 522 return nil, err 523 } 524 m := make(map[string]*regexp.Regexp, length/2) 525 for i := 0; i < length; i += 2 { 526 regex, err := regexp.Compile(pairs[i+1]) 527 if err != nil { 528 return nil, err 529 } 530 m[pairs[i]] = regex 531 } 532 return m, nil 533 } 534 535 // matchInArray returns true if the given string value is in the array. 536 func matchInArray(arr []string, value string) bool { 537 for _, v := range arr { 538 if v == value { 539 return true 540 } 541 } 542 return false 543 } 544 545 // matchMapWithString returns true if the given key/value pairs exist in a given map. 546 func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool { 547 for k, v := range toCheck { 548 // Check if key exists. 549 if canonicalKey { 550 k = http.CanonicalHeaderKey(k) 551 } 552 if values := toMatch[k]; values == nil { 553 return false 554 } else if v != "" { 555 // If value was defined as an empty string we only check that the 556 // key exists. Otherwise we also check for equality. 557 valueExists := false 558 for _, value := range values { 559 if v == value { 560 valueExists = true 561 break 562 } 563 } 564 if !valueExists { 565 return false 566 } 567 } 568 } 569 return true 570 } 571 572 // matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against 573 // the given regex 574 func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool { 575 for k, v := range toCheck { 576 // Check if key exists. 577 if canonicalKey { 578 k = http.CanonicalHeaderKey(k) 579 } 580 if values := toMatch[k]; values == nil { 581 return false 582 } else if v != nil { 583 // If value was defined as an empty string we only check that the 584 // key exists. Otherwise we also check for equality. 585 valueExists := false 586 for _, value := range values { 587 if v.MatchString(value) { 588 valueExists = true 589 break 590 } 591 } 592 if !valueExists { 593 return false 594 } 595 } 596 } 597 return true 598 } 599 600 // methodNotAllowed replies to the request with an HTTP status code 405. 601 func methodNotAllowed(w http.ResponseWriter, r *http.Request) { 602 w.WriteHeader(http.StatusMethodNotAllowed) 603 } 604 605 // methodNotAllowedHandler returns a simple request handler 606 // that replies to each request with a status code 405. 607 func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }