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  }