mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			607 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			607 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2012 The Gorilla Authors. All rights reserved.
 | 
						|
// Use of this source code is governed by a BSD-style
 | 
						|
// license that can be found in the LICENSE file.
 | 
						|
 | 
						|
package mux
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"path"
 | 
						|
	"regexp"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// ErrMethodMismatch is returned when the method in the request does not match
 | 
						|
	// the method defined against the route.
 | 
						|
	ErrMethodMismatch = errors.New("method is not allowed")
 | 
						|
	// ErrNotFound is returned when no route match is found.
 | 
						|
	ErrNotFound = errors.New("no matching route was found")
 | 
						|
)
 | 
						|
 | 
						|
// NewRouter returns a new router instance.
 | 
						|
func NewRouter() *Router {
 | 
						|
	return &Router{namedRoutes: make(map[string]*Route)}
 | 
						|
}
 | 
						|
 | 
						|
// Router registers routes to be matched and dispatches a handler.
 | 
						|
//
 | 
						|
// It implements the http.Handler interface, so it can be registered to serve
 | 
						|
// requests:
 | 
						|
//
 | 
						|
//     var router = mux.NewRouter()
 | 
						|
//
 | 
						|
//     func main() {
 | 
						|
//         http.Handle("/", router)
 | 
						|
//     }
 | 
						|
//
 | 
						|
// Or, for Google App Engine, register it in a init() function:
 | 
						|
//
 | 
						|
//     func init() {
 | 
						|
//         http.Handle("/", router)
 | 
						|
//     }
 | 
						|
//
 | 
						|
// This will send all incoming requests to the router.
 | 
						|
type Router struct {
 | 
						|
	// Configurable Handler to be used when no route matches.
 | 
						|
	NotFoundHandler http.Handler
 | 
						|
 | 
						|
	// Configurable Handler to be used when the request method does not match the route.
 | 
						|
	MethodNotAllowedHandler http.Handler
 | 
						|
 | 
						|
	// Routes to be matched, in order.
 | 
						|
	routes []*Route
 | 
						|
 | 
						|
	// Routes by name for URL building.
 | 
						|
	namedRoutes map[string]*Route
 | 
						|
 | 
						|
	// If true, do not clear the request context after handling the request.
 | 
						|
	//
 | 
						|
	// Deprecated: No effect, since the context is stored on the request itself.
 | 
						|
	KeepContext bool
 | 
						|
 | 
						|
	// Slice of middlewares to be called after a match is found
 | 
						|
	middlewares []middleware
 | 
						|
 | 
						|
	// configuration shared with `Route`
 | 
						|
	routeConf
 | 
						|
}
 | 
						|
 | 
						|
// common route configuration shared between `Router` and `Route`
 | 
						|
type routeConf struct {
 | 
						|
	// If true, "/path/foo%2Fbar/to" will match the path "/path/{var}/to"
 | 
						|
	useEncodedPath bool
 | 
						|
 | 
						|
	// If true, when the path pattern is "/path/", accessing "/path" will
 | 
						|
	// redirect to the former and vice versa.
 | 
						|
	strictSlash bool
 | 
						|
 | 
						|
	// If true, when the path pattern is "/path//to", accessing "/path//to"
 | 
						|
	// will not redirect
 | 
						|
	skipClean bool
 | 
						|
 | 
						|
	// Manager for the variables from host and path.
 | 
						|
	regexp routeRegexpGroup
 | 
						|
 | 
						|
	// List of matchers.
 | 
						|
	matchers []matcher
 | 
						|
 | 
						|
	// The scheme used when building URLs.
 | 
						|
	buildScheme string
 | 
						|
 | 
						|
	buildVarsFunc BuildVarsFunc
 | 
						|
}
 | 
						|
 | 
						|
// returns an effective deep copy of `routeConf`
 | 
						|
func copyRouteConf(r routeConf) routeConf {
 | 
						|
	c := r
 | 
						|
 | 
						|
	if r.regexp.path != nil {
 | 
						|
		c.regexp.path = copyRouteRegexp(r.regexp.path)
 | 
						|
	}
 | 
						|
 | 
						|
	if r.regexp.host != nil {
 | 
						|
		c.regexp.host = copyRouteRegexp(r.regexp.host)
 | 
						|
	}
 | 
						|
 | 
						|
	c.regexp.queries = make([]*routeRegexp, 0, len(r.regexp.queries))
 | 
						|
	for _, q := range r.regexp.queries {
 | 
						|
		c.regexp.queries = append(c.regexp.queries, copyRouteRegexp(q))
 | 
						|
	}
 | 
						|
 | 
						|
	c.matchers = make([]matcher, len(r.matchers))
 | 
						|
	copy(c.matchers, r.matchers)
 | 
						|
 | 
						|
	return c
 | 
						|
}
 | 
						|
 | 
						|
func copyRouteRegexp(r *routeRegexp) *routeRegexp {
 | 
						|
	c := *r
 | 
						|
	return &c
 | 
						|
}
 | 
						|
 | 
						|
// Match attempts to match the given request against the router's registered routes.
 | 
						|
//
 | 
						|
// If the request matches a route of this router or one of its subrouters the Route,
 | 
						|
// Handler, and Vars fields of the the match argument are filled and this function
 | 
						|
// returns true.
 | 
						|
//
 | 
						|
// If the request does not match any of this router's or its subrouters' routes
 | 
						|
// then this function returns false. If available, a reason for the match failure
 | 
						|
// will be filled in the match argument's MatchErr field. If the match failure type
 | 
						|
// (eg: not found) has a registered handler, the handler is assigned to the Handler
 | 
						|
// field of the match argument.
 | 
						|
func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
 | 
						|
	for _, route := range r.routes {
 | 
						|
		if route.Match(req, match) {
 | 
						|
			// Build middleware chain if no error was found
 | 
						|
			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
 | 
						|
	}
 | 
						|
 | 
						|
	// Closest match for a router (includes sub-routers)
 | 
						|
	if r.NotFoundHandler != nil {
 | 
						|
		match.Handler = r.NotFoundHandler
 | 
						|
		match.MatchErr = ErrNotFound
 | 
						|
		return true
 | 
						|
	}
 | 
						|
 | 
						|
	match.MatchErr = ErrNotFound
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// ServeHTTP dispatches the handler registered in the matched route.
 | 
						|
//
 | 
						|
// When there is a match, the route variables can be retrieved calling
 | 
						|
// mux.Vars(request).
 | 
						|
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 | 
						|
	if !r.skipClean {
 | 
						|
		path := req.URL.Path
 | 
						|
		if r.useEncodedPath {
 | 
						|
			path = req.URL.EscapedPath()
 | 
						|
		}
 | 
						|
		// Clean path to canonical form and redirect.
 | 
						|
		if p := cleanPath(path); p != path {
 | 
						|
 | 
						|
			// Added 3 lines (Philip Schlump) - It was dropping the query string and #whatever from query.
 | 
						|
			// This matches with fix in go 1.2 r.c. 4 for same problem.  Go Issue:
 | 
						|
			// http://code.google.com/p/go/issues/detail?id=5252
 | 
						|
			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 = requestWithVars(req, match.Vars)
 | 
						|
		req = requestWithRoute(req, match.Route)
 | 
						|
	}
 | 
						|
 | 
						|
	if handler == nil && match.MatchErr == ErrMethodMismatch {
 | 
						|
		handler = methodNotAllowedHandler()
 | 
						|
	}
 | 
						|
 | 
						|
	if handler == nil {
 | 
						|
		handler = http.NotFoundHandler()
 | 
						|
	}
 | 
						|
 | 
						|
	handler.ServeHTTP(w, req)
 | 
						|
}
 | 
						|
 | 
						|
// Get returns a route registered with the given name.
 | 
						|
func (r *Router) Get(name string) *Route {
 | 
						|
	return r.namedRoutes[name]
 | 
						|
}
 | 
						|
 | 
						|
// GetRoute returns a route registered with the given name. This method
 | 
						|
// was renamed to Get() and remains here for backwards compatibility.
 | 
						|
func (r *Router) GetRoute(name string) *Route {
 | 
						|
	return r.namedRoutes[name]
 | 
						|
}
 | 
						|
 | 
						|
// StrictSlash defines the trailing slash behavior for new routes. The initial
 | 
						|
// value is false.
 | 
						|
//
 | 
						|
// When true, if the route path is "/path/", accessing "/path" will perform a redirect
 | 
						|
// to the former and vice versa. In other words, your application will always
 | 
						|
// see the path as specified in the route.
 | 
						|
//
 | 
						|
// When false, if the route path is "/path", accessing "/path/" will not match
 | 
						|
// this route and vice versa.
 | 
						|
//
 | 
						|
// The re-direct is a HTTP 301 (Moved Permanently). Note that when this is set for
 | 
						|
// routes with a non-idempotent method (e.g. POST, PUT), the subsequent re-directed
 | 
						|
// request will be made as a GET by most clients. Use middleware or client settings
 | 
						|
// to modify this behaviour as needed.
 | 
						|
//
 | 
						|
// Special case: when a route sets a path prefix using the PathPrefix() method,
 | 
						|
// strict slash is ignored for that route because the redirect behavior can't
 | 
						|
// be determined from a prefix alone. However, any subrouters created from that
 | 
						|
// route inherit the original StrictSlash setting.
 | 
						|
func (r *Router) StrictSlash(value bool) *Router {
 | 
						|
	r.strictSlash = value
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
// SkipClean defines the path cleaning behaviour for new routes. The initial
 | 
						|
// value is false. Users should be careful about which routes are not cleaned
 | 
						|
//
 | 
						|
// When true, if the route path is "/path//to", it will remain with the double
 | 
						|
// slash. This is helpful if you have a route like: /fetch/http://xkcd.com/534/
 | 
						|
//
 | 
						|
// When false, the path will be cleaned, so /fetch/http://xkcd.com/534/ will
 | 
						|
// become /fetch/http/xkcd.com/534
 | 
						|
func (r *Router) SkipClean(value bool) *Router {
 | 
						|
	r.skipClean = value
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
// UseEncodedPath tells the router to match the encoded original path
 | 
						|
// to the routes.
 | 
						|
// For eg. "/path/foo%2Fbar/to" will match the path "/path/{var}/to".
 | 
						|
//
 | 
						|
// If not called, the router will match the unencoded path to the routes.
 | 
						|
// For eg. "/path/foo%2Fbar/to" will match the path "/path/foo/bar/to"
 | 
						|
func (r *Router) UseEncodedPath() *Router {
 | 
						|
	r.useEncodedPath = true
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// Route factories
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// NewRoute registers an empty route.
 | 
						|
func (r *Router) NewRoute() *Route {
 | 
						|
	// initialize a route with a copy of the parent router's configuration
 | 
						|
	route := &Route{routeConf: copyRouteConf(r.routeConf), namedRoutes: r.namedRoutes}
 | 
						|
	r.routes = append(r.routes, route)
 | 
						|
	return route
 | 
						|
}
 | 
						|
 | 
						|
// Name registers a new route with a name.
 | 
						|
// See Route.Name().
 | 
						|
func (r *Router) Name(name string) *Route {
 | 
						|
	return r.NewRoute().Name(name)
 | 
						|
}
 | 
						|
 | 
						|
// Handle registers a new route with a matcher for the URL path.
 | 
						|
// See Route.Path() and Route.Handler().
 | 
						|
func (r *Router) Handle(path string, handler http.Handler) *Route {
 | 
						|
	return r.NewRoute().Path(path).Handler(handler)
 | 
						|
}
 | 
						|
 | 
						|
// HandleFunc registers a new route with a matcher for the URL path.
 | 
						|
// See Route.Path() and Route.HandlerFunc().
 | 
						|
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
 | 
						|
	*http.Request)) *Route {
 | 
						|
	return r.NewRoute().Path(path).HandlerFunc(f)
 | 
						|
}
 | 
						|
 | 
						|
// Headers registers a new route with a matcher for request header values.
 | 
						|
// See Route.Headers().
 | 
						|
func (r *Router) Headers(pairs ...string) *Route {
 | 
						|
	return r.NewRoute().Headers(pairs...)
 | 
						|
}
 | 
						|
 | 
						|
// Host registers a new route with a matcher for the URL host.
 | 
						|
// See Route.Host().
 | 
						|
func (r *Router) Host(tpl string) *Route {
 | 
						|
	return r.NewRoute().Host(tpl)
 | 
						|
}
 | 
						|
 | 
						|
// MatcherFunc registers a new route with a custom matcher function.
 | 
						|
// See Route.MatcherFunc().
 | 
						|
func (r *Router) MatcherFunc(f MatcherFunc) *Route {
 | 
						|
	return r.NewRoute().MatcherFunc(f)
 | 
						|
}
 | 
						|
 | 
						|
// Methods registers a new route with a matcher for HTTP methods.
 | 
						|
// See Route.Methods().
 | 
						|
func (r *Router) Methods(methods ...string) *Route {
 | 
						|
	return r.NewRoute().Methods(methods...)
 | 
						|
}
 | 
						|
 | 
						|
// Path registers a new route with a matcher for the URL path.
 | 
						|
// See Route.Path().
 | 
						|
func (r *Router) Path(tpl string) *Route {
 | 
						|
	return r.NewRoute().Path(tpl)
 | 
						|
}
 | 
						|
 | 
						|
// PathPrefix registers a new route with a matcher for the URL path prefix.
 | 
						|
// See Route.PathPrefix().
 | 
						|
func (r *Router) PathPrefix(tpl string) *Route {
 | 
						|
	return r.NewRoute().PathPrefix(tpl)
 | 
						|
}
 | 
						|
 | 
						|
// Queries registers a new route with a matcher for URL query values.
 | 
						|
// See Route.Queries().
 | 
						|
func (r *Router) Queries(pairs ...string) *Route {
 | 
						|
	return r.NewRoute().Queries(pairs...)
 | 
						|
}
 | 
						|
 | 
						|
// Schemes registers a new route with a matcher for URL schemes.
 | 
						|
// See Route.Schemes().
 | 
						|
func (r *Router) Schemes(schemes ...string) *Route {
 | 
						|
	return r.NewRoute().Schemes(schemes...)
 | 
						|
}
 | 
						|
 | 
						|
// BuildVarsFunc registers a new route with a custom function for modifying
 | 
						|
// route variables before building a URL.
 | 
						|
func (r *Router) BuildVarsFunc(f BuildVarsFunc) *Route {
 | 
						|
	return r.NewRoute().BuildVarsFunc(f)
 | 
						|
}
 | 
						|
 | 
						|
// Walk walks the router and all its sub-routers, calling walkFn for each route
 | 
						|
// in the tree. The routes are walked in the order they were added. Sub-routers
 | 
						|
// are explored depth-first.
 | 
						|
func (r *Router) Walk(walkFn WalkFunc) error {
 | 
						|
	return r.walk(walkFn, []*Route{})
 | 
						|
}
 | 
						|
 | 
						|
// SkipRouter is used as a return value from WalkFuncs to indicate that the
 | 
						|
// router that walk is about to descend down to should be skipped.
 | 
						|
var SkipRouter = errors.New("skip this router")
 | 
						|
 | 
						|
// WalkFunc is the type of the function called for each route visited by Walk.
 | 
						|
// At every invocation, it is given the current route, and the current router,
 | 
						|
// and a list of ancestor routes that lead to the current route.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// Context
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// RouteMatch stores information about a matched route.
 | 
						|
type RouteMatch struct {
 | 
						|
	Route   *Route
 | 
						|
	Handler http.Handler
 | 
						|
	Vars    map[string]string
 | 
						|
 | 
						|
	// MatchErr is set to appropriate matching error
 | 
						|
	// It is set to ErrMethodMismatch if there is a mismatch in
 | 
						|
	// the request method and route method
 | 
						|
	MatchErr error
 | 
						|
}
 | 
						|
 | 
						|
type contextKey int
 | 
						|
 | 
						|
const (
 | 
						|
	varsKey contextKey = iota
 | 
						|
	routeKey
 | 
						|
)
 | 
						|
 | 
						|
// Vars returns the route variables for the current request, if any.
 | 
						|
func Vars(r *http.Request) map[string]string {
 | 
						|
	if rv := r.Context().Value(varsKey); rv != nil {
 | 
						|
		return rv.(map[string]string)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// CurrentRoute returns the matched route for the current request, if any.
 | 
						|
// This only works when called inside the handler of the matched route
 | 
						|
// because the matched route is stored in the request context which is cleared
 | 
						|
// after the handler returns.
 | 
						|
func CurrentRoute(r *http.Request) *Route {
 | 
						|
	if rv := r.Context().Value(routeKey); rv != nil {
 | 
						|
		return rv.(*Route)
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func requestWithVars(r *http.Request, vars map[string]string) *http.Request {
 | 
						|
	ctx := context.WithValue(r.Context(), varsKey, vars)
 | 
						|
	return r.WithContext(ctx)
 | 
						|
}
 | 
						|
 | 
						|
func requestWithRoute(r *http.Request, route *Route) *http.Request {
 | 
						|
	ctx := context.WithValue(r.Context(), routeKey, route)
 | 
						|
	return r.WithContext(ctx)
 | 
						|
}
 | 
						|
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
// Helpers
 | 
						|
// ----------------------------------------------------------------------------
 | 
						|
 | 
						|
// cleanPath returns the canonical path for p, eliminating . and .. elements.
 | 
						|
// Borrowed from the net/http package.
 | 
						|
func cleanPath(p string) string {
 | 
						|
	if p == "" {
 | 
						|
		return "/"
 | 
						|
	}
 | 
						|
	if p[0] != '/' {
 | 
						|
		p = "/" + p
 | 
						|
	}
 | 
						|
	np := path.Clean(p)
 | 
						|
	// path.Clean removes trailing slash except for root;
 | 
						|
	// put the trailing slash back if necessary.
 | 
						|
	if p[len(p)-1] == '/' && np != "/" {
 | 
						|
		np += "/"
 | 
						|
	}
 | 
						|
 | 
						|
	return np
 | 
						|
}
 | 
						|
 | 
						|
// uniqueVars returns an error if two slices contain duplicated strings.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// checkPairs returns the count of strings passed in, and an error if
 | 
						|
// the count is not an even number.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// mapFromPairsToString converts variadic string parameters to a
 | 
						|
// string to string map.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// mapFromPairsToRegex converts variadic string parameters to a
 | 
						|
// string to regex map.
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
// matchInArray returns true if the given string value is in the array.
 | 
						|
func matchInArray(arr []string, value string) bool {
 | 
						|
	for _, v := range arr {
 | 
						|
		if v == value {
 | 
						|
			return true
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// matchMapWithString returns true if the given key/value pairs exist in a given map.
 | 
						|
func matchMapWithString(toCheck map[string]string, toMatch map[string][]string, canonicalKey bool) bool {
 | 
						|
	for k, v := range toCheck {
 | 
						|
		// Check if key exists.
 | 
						|
		if canonicalKey {
 | 
						|
			k = http.CanonicalHeaderKey(k)
 | 
						|
		}
 | 
						|
		if values := toMatch[k]; values == nil {
 | 
						|
			return false
 | 
						|
		} else if v != "" {
 | 
						|
			// If value was defined as an empty string we only check that the
 | 
						|
			// key exists. Otherwise we also check for equality.
 | 
						|
			valueExists := false
 | 
						|
			for _, value := range values {
 | 
						|
				if v == value {
 | 
						|
					valueExists = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if !valueExists {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// matchMapWithRegex returns true if the given key/value pairs exist in a given map compiled against
 | 
						|
// the given regex
 | 
						|
func matchMapWithRegex(toCheck map[string]*regexp.Regexp, toMatch map[string][]string, canonicalKey bool) bool {
 | 
						|
	for k, v := range toCheck {
 | 
						|
		// Check if key exists.
 | 
						|
		if canonicalKey {
 | 
						|
			k = http.CanonicalHeaderKey(k)
 | 
						|
		}
 | 
						|
		if values := toMatch[k]; values == nil {
 | 
						|
			return false
 | 
						|
		} else if v != nil {
 | 
						|
			// If value was defined as an empty string we only check that the
 | 
						|
			// key exists. Otherwise we also check for equality.
 | 
						|
			valueExists := false
 | 
						|
			for _, value := range values {
 | 
						|
				if v.MatchString(value) {
 | 
						|
					valueExists = true
 | 
						|
					break
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if !valueExists {
 | 
						|
				return false
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// methodNotAllowed replies to the request with an HTTP status code 405.
 | 
						|
func methodNotAllowed(w http.ResponseWriter, r *http.Request) {
 | 
						|
	w.WriteHeader(http.StatusMethodNotAllowed)
 | 
						|
}
 | 
						|
 | 
						|
// methodNotAllowedHandler returns a simple request handler
 | 
						|
// that replies to each request with a status code 405.
 | 
						|
func methodNotAllowedHandler() http.Handler { return http.HandlerFunc(methodNotAllowed) }
 |