123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- package mux
- import (
- "errors"
- "fmt"
- "net/http"
- "path"
- "regexp"
- )
- var (
-
-
- ErrMethodMismatch = errors.New("method is not allowed")
-
- ErrNotFound = errors.New("no matching route was found")
- )
- func NewRouter() *Router {
- return &Router{namedRoutes: make(map[string]*Route), KeepContext: false}
- }
- type Router struct {
-
- NotFoundHandler http.Handler
-
- MethodNotAllowedHandler http.Handler
-
- parent parentRoute
-
- routes []*Route
-
- namedRoutes map[string]*Route
-
- strictSlash bool
-
- skipClean bool
-
-
-
- KeepContext bool
-
- useEncodedPath bool
-
- middlewares []middleware
- }
- func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
- for _, route := range r.routes {
- if route.Match(req, match) {
-
- if match.MatchErr == nil {
- for i := len(r.middlewares) - 1; i >= 0; i-- {
- match.Handler = r.middlewares[i].Middleware(match.Handler)
- }
- }
- return true
- }
- }
- if match.MatchErr == ErrMethodMismatch {
- if r.MethodNotAllowedHandler != nil {
- match.Handler = r.MethodNotAllowedHandler
- return true
- }
- return false
- }
-
- if r.NotFoundHandler != nil {
- match.Handler = r.NotFoundHandler
- match.MatchErr = ErrNotFound
- return true
- }
- match.MatchErr = ErrNotFound
- return false
- }
- func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- if !r.skipClean {
- path := req.URL.Path
- if r.useEncodedPath {
- path = req.URL.EscapedPath()
- }
-
- if p := cleanPath(path); p != path {
-
-
-
- url := *req.URL
- url.Path = p
- p = url.String()
- w.Header().Set("Location", p)
- w.WriteHeader(http.StatusMovedPermanently)
- return
- }
- }
- var match RouteMatch
- var handler http.Handler
- if r.Match(req, &match) {
- handler = match.Handler
- req = setVars(req, match.Vars)
- req = setCurrentRoute(req, match.Route)
- }
- if handler == nil && match.MatchErr == ErrMethodMismatch {
- handler = methodNotAllowedHandler()
- }
- if handler == nil {
- handler = http.NotFoundHandler()
- }
- if !r.KeepContext {
- defer contextClear(req)
- }
- handler.ServeHTTP(w, req)
- }
- func (r *Router) Get(name string) *Route {
- return r.getNamedRoutes()[name]
- }
- func (r *Router) GetRoute(name string) *Route {
- return r.getNamedRoutes()[name]
- }
- func (r *Router) StrictSlash(value bool) *Router {
- r.strictSlash = value
- return r
- }
- func (r *Router) SkipClean(value bool) *Router {
- r.skipClean = value
- return r
- }
- func (r *Router) UseEncodedPath() *Router {
- r.useEncodedPath = true
- return r
- }
- func (r *Router) getBuildScheme() string {
- if r.parent != nil {
- return r.parent.getBuildScheme()
- }
- return ""
- }
- func (r *Router) getNamedRoutes() map[string]*Route {
- if r.namedRoutes == nil {
- if r.parent != nil {
- r.namedRoutes = r.parent.getNamedRoutes()
- } else {
- r.namedRoutes = make(map[string]*Route)
- }
- }
- return r.namedRoutes
- }
- func (r *Router) getRegexpGroup() *routeRegexpGroup {
- if r.parent != nil {
- return r.parent.getRegexpGroup()
- }
- return nil
- }
- func (r *Router) buildVars(m map[string]string) map[string]string {
- if r.parent != nil {
- m = r.parent.buildVars(m)
- }
- return m
- }
- func (r *Router) NewRoute() *Route {
- route := &Route{parent: r, strictSlash: r.strictSlash, skipClean: r.skipClean, useEncodedPath: r.useEncodedPath}
- r.routes = append(r.routes, route)
- return route
- }
- func (r *Router) Handle(path string, handler http.Handler) *Route {
- return r.NewRoute().Path(path).Handler(handler)
- }
- func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
- *http.Request)) *Route {
- return r.NewRoute().Path(path).HandlerFunc(f)
- }
- func (r *Router) Headers(pairs ...string) *Route {
- return r.NewRoute().Headers(pairs...)
- }
- func (r *Router) Host(tpl string) *Route {
- return r.NewRoute().Host(tpl)
- }
- func (r *Router) MatcherFunc(f MatcherFunc) *Route {
- return r.NewRoute().MatcherFunc(f)
- }
- func (r *Router) Methods(methods ...string) *Route {
- return r.NewRoute().Methods(methods...)
- }
- func (r *Router) Path(tpl string) *Route {
- return r.NewRoute().Path(tpl)
- }
- func (r *Router) PathPrefix(tpl string) *Route {
- return r.NewRoute().PathPrefix(tpl)
- }
- func (r *Router) Queries(pairs ...string) *Route {
- return r.NewRoute().Queries(pairs...)
- }
- func (r *Router) Schemes(schemes ...string) *Route {
- return r.NewRoute().Schemes(schemes...)
- }
- func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
- return r.NewRoute().BuildVarsFunc(f)
- }
- func (r *Router) Walk(walkFn WalkFunc) error {
- return r.walk(walkFn, []*Route{})
- }
- var SkipRouter = errors.New("skip this router")
- type WalkFunc func(route *Route, router *Router, ancestors []*Route) error
- func (r *Router) walk(walkFn WalkFunc, ancestors []*Route) error {
- for _, t := range r.routes {
- err := walkFn(t, r, ancestors)
- if err == SkipRouter {
- continue
- }
- if err != nil {
- return err
- }
- for _, sr := range t.matchers {
- if h, ok := sr.(*Router); ok {
- ancestors = append(ancestors, t)
- err := h.walk(walkFn, ancestors)
- if err != nil {
- return err
- }
- ancestors = ancestors[:len(ancestors)-1]
- }
- }
- if h, ok := t.handler.(*Router); ok {
- ancestors = append(ancestors, t)
- err := h.walk(walkFn, ancestors)
- if err != nil {
- return err
- }
- ancestors = ancestors[:len(ancestors)-1]
- }
- }
- return nil
- }
- type RouteMatch struct {
- Route *Route
- Handler http.Handler
- Vars map[string]string
-
-
-
- MatchErr error
- }
- type contextKey int
- const (
- varsKey contextKey = iota
- routeKey
- )
- func Vars(r *http.Request) map[string]string {
- if rv := contextGet(r, varsKey); rv != nil {
- return rv.(map[string]string)
- }
- return nil
- }
- func CurrentRoute(r *http.Request) *Route {
- if rv := contextGet(r, routeKey); rv != nil {
- return rv.(*Route)
- }
- return nil
- }
- func setVars(r *http.Request, val interface{}) *http.Request {
- return contextSet(r, varsKey, val)
- }
- func setCurrentRoute(r *http.Request, val interface{}) *http.Request {
- return contextSet(r, routeKey, val)
- }
- func cleanPath(p string) string {
- if p == "" {
- return "/"
- }
- if p[0] != '/' {
- p = "/" + p
- }
- np := path.Clean(p)
-
-
- if p[len(p)-1] == '/' && np != "/" {
- np += "/"
- }
- return np
- }
- func uniqueVars(s1, s2 []string) error {
- for _, v1 := range s1 {
- for _, v2 := range s2 {
- if v1 == v2 {
- return fmt.Errorf("mux: duplicated route variable %q", v2)
- }
- }
- }
- return nil
- }
- func checkPairs(pairs ...string) (int, error) {
- length := len(pairs)
- if length%2 != 0 {
- return length, fmt.Errorf(
- "mux: number of parameters must be multiple of 2, got %v", pairs)
- }
- return length, nil
- }
- func mapFromPairsToString(pairs ...string) (map[string]string, error) {
- length, err := checkPairs(pairs...)
- if err != nil {
- return nil, err
- }
- m := make(map[string]string, length/2)
- for i := 0; i < length; i += 2 {
- m[pairs[i]] = pairs[i+1]
- }
- return m, nil
- }
- func mapFromPairsToRegex(pairs ...string) (map[string]*regexp.Regexp, error) {
- length, err := checkPairs(pairs...)
- if err != nil {
- return nil, err
- }
- m := make(map[string]*regexp.Regexp, length/2)
- for i := 0; i < length; i += 2 {
- regex, err := regexp.Compile(pairs[i+1])
- if err != nil {
- return nil, err
- }
- m[pairs[i]] = regex
- }
- return m, nil
- }
- func matchInArray(arr []string, value string) bool {
- for _, v := range arr {
- if v == value {
- return true
- }
- }
- return false
- }
- func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
- for k, v := range toCheck {
-
- if canonicalKey {
- k = http.CanonicalHeaderKey(k)
- }
- if values := toMatch[k]; values == nil {
- return false
- } else if v != "" {
-
-
- valueExists := false
- for _, value := range values {
- if v == value {
- valueExists = true
- break
- }
- }
- if !valueExists {
- return false
- }
- }
- }
- return true
- }
- func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
- for k, v := range toCheck {
-
- if canonicalKey {
- k = http.CanonicalHeaderKey(k)
- }
- if values := toMatch[k]; values == nil {
- return false
- } else if v != nil {
-
-
- valueExists := false
- for _, value := range values {
- if v.MatchString(value) {
- valueExists = true
- break
- }
- }
- if !valueExists {
- return false
- }
- }
- }
- return true
- }
- func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
- w.WriteHeader(http.StatusMethodNotAllowed)
- }
- func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }
|