github.com/m3db/m3@v1.5.0/src/query/util/queryhttp/queryhttp.go (about) 1 // Copyright (c) 2020 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package queryhttp 22 23 import ( 24 "fmt" 25 "net/http" 26 27 "github.com/gorilla/mux" 28 29 "github.com/m3db/m3/src/query/api/v1/middleware" 30 ) 31 32 // NewEndpointRegistry returns a new endpoint registry. 33 func NewEndpointRegistry(router *mux.Router) *EndpointRegistry { 34 return &EndpointRegistry{ 35 router: router, 36 registeredByRoute: make(map[routeKey]*mux.Route), 37 middlewareOpts: make(map[*mux.Route]middleware.OverrideOptions), 38 } 39 } 40 41 // EndpointRegistry is an endpoint registry that can register routes 42 // and instrument them. 43 type EndpointRegistry struct { 44 router *mux.Router 45 registeredByRoute map[routeKey]*mux.Route 46 middlewareOpts map[*mux.Route]middleware.OverrideOptions 47 } 48 49 type routeKey struct { 50 path string 51 pathPrefix string 52 method string 53 } 54 55 // RegisterOptions are options for registering a handler. 56 type RegisterOptions struct { 57 Path string 58 PathPrefix string 59 Handler http.Handler 60 Methods []string 61 MiddlewareOverride middleware.OverrideOptions 62 } 63 64 // Register registers an endpoint. 65 func (r *EndpointRegistry) Register(opts RegisterOptions) error { 66 route := r.router.NewRoute() 67 r.middlewareOpts[route] = opts.MiddlewareOverride 68 handler := opts.Handler 69 70 if p := opts.Path; p != "" && len(opts.Methods) > 0 { 71 route.Path(p).Handler(handler).Methods(opts.Methods...) 72 for _, method := range opts.Methods { 73 key := routeKey{ 74 path: p, 75 method: method, 76 } 77 if _, ok := r.registeredByRoute[key]; ok { 78 return fmt.Errorf("route already exists: path=%s, method=%s", p, method) 79 } 80 r.registeredByRoute[key] = route 81 } 82 } else if p := opts.PathPrefix; p != "" { 83 key := routeKey{ 84 pathPrefix: p, 85 } 86 if _, ok := r.registeredByRoute[key]; ok { 87 return fmt.Errorf("route already exists: pathPrefix=%s", p) 88 } 89 r.registeredByRoute[key] = route.PathPrefix(p).Handler(handler) 90 } else { 91 return fmt.Errorf("no path and methods or path prefix set: +%v", opts) 92 } 93 94 return nil 95 } 96 97 // RegisterPathsOptions is options for registering multiple paths 98 // with the same handler. 99 type RegisterPathsOptions struct { 100 Handler http.Handler 101 Methods []string 102 } 103 104 // RegisterPaths registers multiple paths for the same handler. 105 func (r *EndpointRegistry) RegisterPaths( 106 paths []string, 107 opts RegisterPathsOptions) error { 108 for _, p := range paths { 109 if err := r.Register(RegisterOptions{ 110 Path: p, 111 Handler: opts.Handler, 112 Methods: opts.Methods, 113 }); err != nil { 114 return err 115 } 116 } 117 return nil 118 } 119 120 // MiddlewareOpts returns the OverrideOptions for the route, or nil if none exist. 121 func (r *EndpointRegistry) MiddlewareOpts(route *mux.Route) middleware.OverrideOptions { 122 return r.middlewareOpts[route] 123 } 124 125 // PathEntry resolves a registered route that was registered by path and method, 126 // not by path prefix. 127 func (r *EndpointRegistry) PathEntry(path, method string) (*mux.Route, bool) { 128 key := routeKey{ 129 path: path, 130 method: method, 131 } 132 e, ok := r.registeredByRoute[key] 133 return e, ok 134 } 135 136 // Walk walks the router and all its sub-routers, calling walkFn for each route 137 // in the tree. The routes are walked in the order they were added. Sub-routers 138 // are explored depth-first. 139 func (r *EndpointRegistry) Walk(walkFn mux.WalkFunc) error { 140 return r.router.Walk(walkFn) 141 }