diff options
Diffstat (limited to 'vendor/github.com/go-zoo/bone')
-rw-r--r-- | vendor/github.com/go-zoo/bone/LICENSE | 22 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/README.md | 81 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/bone.go | 74 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/helper.go | 169 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/helper_15.go | 45 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/helper_17.go | 39 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/mux.go | 137 | ||||
-rw-r--r-- | vendor/github.com/go-zoo/bone/route.go | 245 |
8 files changed, 812 insertions, 0 deletions
diff --git a/vendor/github.com/go-zoo/bone/LICENSE b/vendor/github.com/go-zoo/bone/LICENSE new file mode 100644 index 000000000..652583b76 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 CodingFerret + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, Subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or Substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/go-zoo/bone/README.md b/vendor/github.com/go-zoo/bone/README.md new file mode 100644 index 000000000..29ea5f6de --- /dev/null +++ b/vendor/github.com/go-zoo/bone/README.md @@ -0,0 +1,81 @@ +bone [![GoDoc](https://godoc.org/github.com/squiidz/bone?status.png)](http://godoc.org/github.com/go-zoo/bone) [![Build Status](https://travis-ci.org/go-zoo/bone.svg)](https://travis-ci.org/go-zoo/bone) [![Go Report Card](https://goreportcard.com/badge/go-zoo/bone)](https://goreportcard.com/report/go-zoo/bone) [![Sourcegraph](https://sourcegraph.com/github.com/go-zoo/bone/-/badge.svg)](https://sourcegraph.com/github.com/go-zoo/bone?badge) +======= + +## What is bone ? + +Bone is a lightweight and lightning fast HTTP Multiplexer for Golang. It support : + +- URL Parameters +- REGEX Parameters +- Wildcard routes +- Router Prefix +- Sub Router, `mux.SubRoute()`, support most standard router (bone, gorilla/mux, httpRouter etc...) +- Http method declaration +- Support for `http.Handler` and `http.HandlerFunc` +- Custom NotFound handler +- Respect the Go standard `http.Handler` interface + +![alt tag](https://c2.staticflickr.com/2/1070/540747396_5542b42cca_z.jpg) + +## Speed + +``` +- BenchmarkBoneMux 10000000 118 ns/op +- BenchmarkZeusMux 100000 144 ns/op +- BenchmarkHttpRouterMux 10000000 134 ns/op +- BenchmarkNetHttpMux 3000000 580 ns/op +- BenchmarkGorillaMux 300000 3333 ns/op +- BenchmarkGorillaPatMux 1000000 1889 ns/op +``` + + These test are just for fun, all these router are great and really efficient. + Bone do not pretend to be the fastest router for every job. + +## Example + +``` go + +package main + +import( + "net/http" + + "github.com/go-zoo/bone" +) + +func main () { + mux := bone.New() + + // mux.Get, Post, etc ... takes http.Handler + mux.Get("/home/:id", http.HandlerFunc(HomeHandler)) + mux.Get("/profil/:id/:var", http.HandlerFunc(ProfilHandler)) + mux.Post("/data", http.HandlerFunc(DataHandler)) + + // Support REGEX Route params + mux.Get("/index/#id^[0-9]$", http.HandlerFunc(IndexHandler)) + + // Handle take http.Handler + mux.Handle("/", http.HandlerFunc(RootHandler)) + + // GetFunc, PostFunc etc ... takes http.HandlerFunc + mux.GetFunc("/test", Handler) + + http.ListenAndServe(":8080", mux) +} + +func Handler(rw http.ResponseWriter, req *http.Request) { + // Get the value of the "id" parameters. + val := bone.GetValue(req, "id") + + rw.Write([]byte(val)) +} + +``` + +## Blog Posts +- http://www.peterbe.com/plog/my-favorite-go-multiplexer +- https://harshladha.xyz/my-first-library-in-go-language-hasty-791b8e2b9e69 + +## Libs +- Errors dump for Go : [Trash](https://github.com/go-zoo/trash) +- Middleware Chaining module : [Claw](https://github.com/go-zoo/claw) diff --git a/vendor/github.com/go-zoo/bone/bone.go b/vendor/github.com/go-zoo/bone/bone.go new file mode 100644 index 000000000..d00a0b083 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/bone.go @@ -0,0 +1,74 @@ +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import ( + "net/http" + "strings" +) + +// Mux have routes and a notFound handler +// Route: all the registred route +// notFound: 404 handler, default http.NotFound if not provided +type Mux struct { + Routes map[string][]*Route + prefix string + notFound http.Handler + Serve func(rw http.ResponseWriter, req *http.Request) + CaseSensitive bool +} + +var ( + static = "static" + method = []string{"GET", "POST", "PUT", "DELETE", "HEAD", "PATCH", "OPTIONS"} +) + +type adapter func(*Mux) *Mux + +// New create a pointer to a Mux instance +func New(adapters ...adapter) *Mux { + m := &Mux{Routes: make(map[string][]*Route), Serve: nil, CaseSensitive: true} + for _, adap := range adapters { + adap(m) + } + if m.Serve == nil { + m.Serve = m.DefaultServe + } + return m +} + +// Prefix set a default prefix for all routes registred on the router +func (m *Mux) Prefix(p string) *Mux { + m.prefix = strings.TrimSuffix(p, "/") + return m +} + +// DefaultServe is the default http request handler +func (m *Mux) DefaultServe(rw http.ResponseWriter, req *http.Request) { + // Check if a route match + if !m.parse(rw, req) { + // Check if it's a static ressource + if !m.staticRoute(rw, req) { + // Check if the request path doesn't end with / + if !m.validate(rw, req) { + // Check if same route exists for another HTTP method + if !m.otherMethods(rw, req) { + m.HandleNotFound(rw, req) + } + } + } + } +} + +// ServeHTTP pass the request to the serve method of Mux +func (m *Mux) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if !m.CaseSensitive { + req.URL.Path = strings.ToLower(req.URL.Path) + } + m.Serve(rw, req) +} diff --git a/vendor/github.com/go-zoo/bone/helper.go b/vendor/github.com/go-zoo/bone/helper.go new file mode 100644 index 000000000..bd7b45e95 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/helper.go @@ -0,0 +1,169 @@ +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import ( + "net/http" + "net/url" + "strings" +) + +func (m *Mux) ListenAndServe(port string) error { + return http.ListenAndServe(port, m) +} + +func (m *Mux) parse(rw http.ResponseWriter, req *http.Request) bool { + for _, r := range m.Routes[req.Method] { + ok := r.parse(rw, req) + if ok { + return true + } + } + // If no HEAD method, default to GET + if req.Method == "HEAD" { + for _, r := range m.Routes["GET"] { + ok := r.parse(rw, req) + if ok { + return true + } + } + } + return false +} + +// StaticRoute check if the request path is for Static route +func (m *Mux) staticRoute(rw http.ResponseWriter, req *http.Request) bool { + for _, s := range m.Routes[static] { + if len(req.URL.Path) >= s.Size { + if req.URL.Path[:s.Size] == s.Path { + s.Handler.ServeHTTP(rw, req) + return true + } + } + } + return false +} + +// HandleNotFound handle when a request does not match a registered handler. +func (m *Mux) HandleNotFound(rw http.ResponseWriter, req *http.Request) { + if m.notFound != nil { + m.notFound.ServeHTTP(rw, req) + } else { + http.NotFound(rw, req) + } +} + +// Check if the path don't end with a / +func (m *Mux) validate(rw http.ResponseWriter, req *http.Request) bool { + plen := len(req.URL.Path) + if plen > 1 && req.URL.Path[plen-1:] == "/" { + cleanURL(&req.URL.Path) + rw.Header().Set("Location", req.URL.String()) + rw.WriteHeader(http.StatusFound) + return true + } + // Retry to find a route that match + return m.parse(rw, req) +} + +func valid(path string) bool { + plen := len(path) + if plen > 1 && path[plen-1:] == "/" { + return false + } + return true +} + +// Clean url path +func cleanURL(url *string) { + ulen := len((*url)) + if ulen > 1 { + if (*url)[ulen-1:] == "/" { + *url = (*url)[:ulen-1] + cleanURL(url) + } + } +} + +// GetValue return the key value, of the current *http.Request +func GetValue(req *http.Request, key string) string { + return GetAllValues(req)[key] +} + +// GetRequestRoute returns the route of given Request +func (m *Mux) GetRequestRoute(req *http.Request) string { + cleanURL(&req.URL.Path) + for _, r := range m.Routes[req.Method] { + if r.Atts != 0 { + if r.Atts&SUB != 0 { + return r.Handler.(*Mux).GetRequestRoute(req) + } + if r.Match(req) { + return r.Path + } + } + if req.URL.Path == r.Path { + return r.Path + } + } + + for _, s := range m.Routes[static] { + if len(req.URL.Path) >= s.Size { + if req.URL.Path[:s.Size] == s.Path { + return s.Path + } + } + } + + return "NotFound" +} + +// GetQuery return the key value, of the current *http.Request query +func GetQuery(req *http.Request, key string) []string { + if ok, value := extractQueries(req); ok { + return value[key] + } + return nil +} + +// GetAllQueries return all queries of the current *http.Request +func GetAllQueries(req *http.Request) map[string][]string { + if ok, values := extractQueries(req); ok { + return values + } + return nil +} + +func extractQueries(req *http.Request) (bool, map[string][]string) { + if q, err := url.ParseQuery(req.URL.RawQuery); err == nil { + var queries = make(map[string][]string) + for k, v := range q { + for _, item := range v { + values := strings.Split(item, ",") + queries[k] = append(queries[k], values...) + } + } + return true, queries + } + return false, nil +} + +func (m *Mux) otherMethods(rw http.ResponseWriter, req *http.Request) bool { + for _, met := range method { + if met != req.Method { + for _, r := range m.Routes[met] { + ok := r.exists(rw, req) + if ok { + rw.WriteHeader(http.StatusMethodNotAllowed) + return true + } + } + } + } + return false +} diff --git a/vendor/github.com/go-zoo/bone/helper_15.go b/vendor/github.com/go-zoo/bone/helper_15.go new file mode 100644 index 000000000..068ce18e0 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/helper_15.go @@ -0,0 +1,45 @@ +// +build !go1.7 + +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import ( + "net/http" + "sync" +) + +var globalVars = struct { + sync.RWMutex + v map[*http.Request]map[string]string +}{v: make(map[*http.Request]map[string]string)} + +// GetAllValues return the req PARAMs +func GetAllValues(req *http.Request) map[string]string { + globalVars.RLock() + values := globalVars.v[req] + globalVars.RUnlock() + return values +} + +// serveMatchedRequest is an extension point for Route which allows us to conditionally compile for +// go1.7 and <go1.7 +func (r *Route) serveMatchedRequest(rw http.ResponseWriter, req *http.Request, vars map[string]string) { + globalVars.Lock() + globalVars.v[req] = vars + globalVars.Unlock() + + // Regardless if ServeHTTP panics (and potentially recovers) we can make sure to not leak + // memory in globalVars for this request + defer func() { + globalVars.Lock() + delete(globalVars.v, req) + globalVars.Unlock() + }() + r.Handler.ServeHTTP(rw, req) +} diff --git a/vendor/github.com/go-zoo/bone/helper_17.go b/vendor/github.com/go-zoo/bone/helper_17.go new file mode 100644 index 000000000..2e41ea4be --- /dev/null +++ b/vendor/github.com/go-zoo/bone/helper_17.go @@ -0,0 +1,39 @@ +// +build go1.7 + +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import ( + "context" + "net/http" +) + +// contextKeyType is a private struct that is used for storing bone values in net.Context +type contextKeyType struct{} + +// contextKey is the key that is used to store bone values in the net.Context for each request +var contextKey = contextKeyType{} + +// GetAllValues return the req PARAMs +func GetAllValues(req *http.Request) map[string]string { + values, ok := req.Context().Value(contextKey).(map[string]string) + if ok { + return values + } + + return map[string]string{} +} + +// serveMatchedRequest is an extension point for Route which allows us to conditionally compile for +// go1.7 and <go1.7 +func (r *Route) serveMatchedRequest(rw http.ResponseWriter, req *http.Request, vars map[string]string) { + ctx := context.WithValue(req.Context(), contextKey, vars) + newReq := req.WithContext(ctx) + r.Handler.ServeHTTP(rw, newReq) +} diff --git a/vendor/github.com/go-zoo/bone/mux.go b/vendor/github.com/go-zoo/bone/mux.go new file mode 100644 index 000000000..0ec29a338 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/mux.go @@ -0,0 +1,137 @@ +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import "net/http" + +// Router is the same as a http.Handler +type Router interface { + ServeHTTP(http.ResponseWriter, *http.Request) +} + +// Register the route in the router +func (m *Mux) Register(method string, path string, handler http.Handler) *Route { + return m.register(method, path, handler) +} + +// GetFunc add a new route to the Mux with the Get method +func (m *Mux) GetFunc(path string, handler http.HandlerFunc) *Route { + return m.register("GET", path, handler) +} + +// PostFunc add a new route to the Mux with the Post method +func (m *Mux) PostFunc(path string, handler http.HandlerFunc) *Route { + return m.register("POST", path, handler) +} + +// PutFunc add a new route to the Mux with the Put method +func (m *Mux) PutFunc(path string, handler http.HandlerFunc) *Route { + return m.register("PUT", path, handler) +} + +// DeleteFunc add a new route to the Mux with the Delete method +func (m *Mux) DeleteFunc(path string, handler http.HandlerFunc) *Route { + return m.register("DELETE", path, handler) +} + +// HeadFunc add a new route to the Mux with the Head method +func (m *Mux) HeadFunc(path string, handler http.HandlerFunc) *Route { + return m.register("HEAD", path, handler) +} + +// PatchFunc add a new route to the Mux with the Patch method +func (m *Mux) PatchFunc(path string, handler http.HandlerFunc) *Route { + return m.register("PATCH", path, handler) +} + +// OptionsFunc add a new route to the Mux with the Options method +func (m *Mux) OptionsFunc(path string, handler http.HandlerFunc) *Route { + return m.register("OPTIONS", path, handler) +} + +// NotFoundFunc the mux custom 404 handler +func (m *Mux) NotFoundFunc(handler http.HandlerFunc) { + m.notFound = handler +} + +// Handle add a new route to the Mux without a HTTP method +func (m *Mux) Handle(path string, handler http.Handler) { + for _, mt := range method { + m.register(mt, path, handler) + } +} + +// HandleFunc is use to pass a func(http.ResponseWriter, *Http.Request) instead of http.Handler +func (m *Mux) HandleFunc(path string, handler http.HandlerFunc) { + m.Handle(path, handler) +} + +// Get add a new route to the Mux with the Get method +func (m *Mux) Get(path string, handler http.Handler) *Route { + return m.register("GET", path, handler) +} + +// Post add a new route to the Mux with the Post method +func (m *Mux) Post(path string, handler http.Handler) *Route { + return m.register("POST", path, handler) +} + +// Put add a new route to the Mux with the Put method +func (m *Mux) Put(path string, handler http.Handler) *Route { + return m.register("PUT", path, handler) +} + +// Delete add a new route to the Mux with the Delete method +func (m *Mux) Delete(path string, handler http.Handler) *Route { + return m.register("DELETE", path, handler) +} + +// Head add a new route to the Mux with the Head method +func (m *Mux) Head(path string, handler http.Handler) *Route { + return m.register("HEAD", path, handler) +} + +// Patch add a new route to the Mux with the Patch method +func (m *Mux) Patch(path string, handler http.Handler) *Route { + return m.register("PATCH", path, handler) +} + +// Options add a new route to the Mux with the Options method +func (m *Mux) Options(path string, handler http.Handler) *Route { + return m.register("OPTIONS", path, handler) +} + +// NotFound the mux custom 404 handler +func (m *Mux) NotFound(handler http.Handler) { + m.notFound = handler +} + +// Register the new route in the router with the provided method and handler +func (m *Mux) register(method string, path string, handler http.Handler) *Route { + r := NewRoute(m.prefix+path, handler) + r.Method = method + if valid(path) { + m.Routes[method] = append(m.Routes[method], r) + return r + } + m.Routes[static] = append(m.Routes[static], r) + return r +} + +// SubRoute register a router as a SubRouter of bone +func (m *Mux) SubRoute(path string, router Router) *Route { + r := NewRoute(m.prefix+path, router) + if valid(path) { + r.Atts += SUB + for _, mt := range method { + m.Routes[mt] = append(m.Routes[mt], r) + } + return r + } + return nil +} diff --git a/vendor/github.com/go-zoo/bone/route.go b/vendor/github.com/go-zoo/bone/route.go new file mode 100644 index 000000000..7f6c1b1d0 --- /dev/null +++ b/vendor/github.com/go-zoo/bone/route.go @@ -0,0 +1,245 @@ +/******************************** +*** Multiplexer for Go *** +*** Bone is under MIT license *** +*** Code by CodingFerret *** +*** github.com/go-zoo *** +*********************************/ + +package bone + +import ( + "net/http" + "regexp" + "strings" +) + +const ( + //PARAM value store in Atts if the route have parameters + PARAM = 2 + //SUB value store in Atts if the route is a sub router + SUB = 4 + //WC value store in Atts if the route have wildcard + WC = 8 + //REGEX value store in Atts if the route contains regex + REGEX = 16 +) + +// Route content the required information for a valid route +// Path: is the Route URL +// Size: is the length of the path +// Token: is the value of each part of the path, split by / +// Pattern: is content information about the route, if it's have a route variable +// handler: is the handler who handle this route +// Method: define HTTP method on the route +type Route struct { + Path string + Method string + Size int + Atts int + wildPos int + Token Token + Pattern map[int]string + Compile map[int]*regexp.Regexp + Tag map[int]string + Handler http.Handler +} + +// Token content all value of a spliting route path +// Tokens: string value of each token +// size: number of token +type Token struct { + raw []int + Tokens []string + Size int +} + +// NewRoute return a pointer to a Route instance and call save() on it +func NewRoute(url string, h http.Handler) *Route { + r := &Route{Path: url, Handler: h} + r.save() + return r +} + +// Save, set automatically the the Route.Size and Route.Pattern value +func (r *Route) save() { + r.Size = len(r.Path) + r.Token.Tokens = strings.Split(r.Path, "/") + for i, s := range r.Token.Tokens { + if len(s) >= 1 { + switch s[:1] { + case ":": + if r.Pattern == nil { + r.Pattern = make(map[int]string) + } + r.Pattern[i] = s[1:] + r.Atts |= PARAM + case "#": + if r.Compile == nil { + r.Compile = make(map[int]*regexp.Regexp) + r.Tag = make(map[int]string) + } + tmp := strings.Split(s, "^") + r.Tag[i] = tmp[0][1:] + r.Compile[i] = regexp.MustCompile("^" + tmp[1][:len(tmp[1])-1]) + r.Atts |= REGEX + case "*": + r.wildPos = i + r.Atts |= WC + default: + r.Token.raw = append(r.Token.raw, i) + } + } + r.Token.Size++ + } +} + +// Match check if the request match the route Pattern +func (r *Route) Match(req *http.Request) bool { + ok, _ := r.matchAndParse(req) + return ok +} + +// matchAndParse check if the request matches the route Pattern and returns a map of the parsed +// variables if it matches +func (r *Route) matchAndParse(req *http.Request) (bool, map[string]string) { + ss := strings.Split(req.URL.EscapedPath(), "/") + if r.matchRawTokens(&ss) { + if len(ss) == r.Token.Size || r.Atts&WC != 0 { + totalSize := len(r.Pattern) + if r.Atts®EX != 0 { + totalSize += len(r.Compile) + } + + vars := make(map[string]string, totalSize) + for k, v := range r.Pattern { + vars[v] = ss[k] + } + + if r.Atts®EX != 0 { + for k, v := range r.Compile { + if !v.MatchString(ss[k]) { + return false, nil + } + vars[r.Tag[k]] = ss[k] + } + } + + return true, vars + } + } + + return false, nil +} + +func (r *Route) parse(rw http.ResponseWriter, req *http.Request) bool { + if r.Atts != 0 { + if r.Atts&SUB != 0 { + if len(req.URL.Path) >= r.Size { + if req.URL.Path[:r.Size] == r.Path { + req.URL.Path = req.URL.Path[r.Size:] + r.Handler.ServeHTTP(rw, req) + return true + } + } + } + + if ok, vars := r.matchAndParse(req); ok { + r.serveMatchedRequest(rw, req, vars) + return true + } + } + if req.URL.Path == r.Path { + r.Handler.ServeHTTP(rw, req) + return true + } + return false +} + +func (r *Route) matchRawTokens(ss *[]string) bool { + if len(*ss) >= r.Token.Size { + for i, v := range r.Token.raw { + if (*ss)[v] != r.Token.Tokens[v] { + if r.Atts&WC != 0 && r.wildPos == i { + return true + } + return false + } + } + return true + } + return false +} + +func (r *Route) exists(rw http.ResponseWriter, req *http.Request) bool { + if r.Atts != 0 { + if r.Atts&SUB != 0 { + if len(req.URL.Path) >= r.Size { + if req.URL.Path[:r.Size] == r.Path { + return true + } + } + } + + if ok, _ := r.matchAndParse(req); ok { + return true + } + } + if req.URL.Path == r.Path { + return true + } + return false +} + +// Get set the route method to Get +func (r *Route) Get() *Route { + r.Method = "GET" + return r +} + +// Post set the route method to Post +func (r *Route) Post() *Route { + r.Method = "POST" + return r +} + +// Put set the route method to Put +func (r *Route) Put() *Route { + r.Method = "PUT" + return r +} + +// Delete set the route method to Delete +func (r *Route) Delete() *Route { + r.Method = "DELETE" + return r +} + +// Head set the route method to Head +func (r *Route) Head() *Route { + r.Method = "HEAD" + return r +} + +// Patch set the route method to Patch +func (r *Route) Patch() *Route { + r.Method = "PATCH" + return r +} + +// Options set the route method to Options +func (r *Route) Options() *Route { + r.Method = "OPTIONS" + return r +} + +func (r *Route) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + if r.Method != "" { + if req.Method == r.Method { + r.Handler.ServeHTTP(rw, req) + return + } + http.NotFound(rw, req) + return + } + r.Handler.ServeHTTP(rw, req) +} |