debian-forge-composer/vendor/github.com/getkin/kin-openapi/routers/legacy/router.go
Chloe Kaubisch 13c79294b6 cloudapi: validate input
Validate incoming requests with openapi3. Remove unsupported
uuid format from the openapi spec. Similarly, change url to uri as
uri is a supported format and url is not.

Co-authored-by: Ondřej Budai <obudai@redhat.com>
Signed-off-by: Ondřej Budai <ondrej@budai.cz>
2022-05-16 13:20:46 +02:00

167 lines
4.4 KiB
Go

// Package legacy implements a router.
//
// It differs from the gorilla/mux router:
// * it provides granular errors: "path not found", "method not allowed", "variable missing from path"
// * it does not handle matching routes with extensions (e.g. /books/{id}.json)
// * it handles path patterns with a different syntax (e.g. /params/{x}/{y}/{z.*})
package legacy
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
"github.com/getkin/kin-openapi/openapi3"
"github.com/getkin/kin-openapi/routers"
"github.com/getkin/kin-openapi/routers/legacy/pathpattern"
)
// Routers maps a HTTP request to a Router.
type Routers []*Router
// FindRoute extracts the route and parameters of an http.Request
func (rs Routers) FindRoute(req *http.Request) (routers.Router, *routers.Route, map[string]string, error) {
for _, router := range rs {
// Skip routers that have DO NOT have servers
if len(router.doc.Servers) == 0 {
continue
}
route, pathParams, err := router.FindRoute(req)
if err == nil {
return router, route, pathParams, nil
}
}
for _, router := range rs {
// Skip routers that DO have servers
if len(router.doc.Servers) > 0 {
continue
}
route, pathParams, err := router.FindRoute(req)
if err == nil {
return router, route, pathParams, nil
}
}
return nil, nil, nil, &routers.RouteError{
Reason: "none of the routers match",
}
}
// Router maps a HTTP request to an OpenAPI operation.
type Router struct {
doc *openapi3.T
pathNode *pathpattern.Node
}
// NewRouter creates a new router.
//
// If the given OpenAPIv3 document has servers, router will use them.
// All operations of the document will be added to the router.
func NewRouter(doc *openapi3.T) (routers.Router, error) {
if err := doc.Validate(context.Background()); err != nil {
return nil, fmt.Errorf("validating OpenAPI failed: %v", err)
}
router := &Router{doc: doc}
root := router.node()
for path, pathItem := range doc.Paths {
for method, operation := range pathItem.Operations() {
method = strings.ToUpper(method)
if err := root.Add(method+" "+path, &routers.Route{
Spec: doc,
Path: path,
PathItem: pathItem,
Method: method,
Operation: operation,
}, nil); err != nil {
return nil, err
}
}
}
return router, nil
}
// AddRoute adds a route in the router.
func (router *Router) AddRoute(route *routers.Route) error {
method := route.Method
if method == "" {
return errors.New("route is missing method")
}
method = strings.ToUpper(method)
path := route.Path
if path == "" {
return errors.New("route is missing path")
}
return router.node().Add(method+" "+path, router, nil)
}
func (router *Router) node() *pathpattern.Node {
root := router.pathNode
if root == nil {
root = &pathpattern.Node{}
router.pathNode = root
}
return root
}
// FindRoute extracts the route and parameters of an http.Request
func (router *Router) FindRoute(req *http.Request) (*routers.Route, map[string]string, error) {
method, url := req.Method, req.URL
doc := router.doc
// Get server
servers := doc.Servers
var server *openapi3.Server
var remainingPath string
var pathParams map[string]string
if len(servers) == 0 {
remainingPath = url.Path
} else {
var paramValues []string
server, paramValues, remainingPath = servers.MatchURL(url)
if server == nil {
return nil, nil, &routers.RouteError{
Reason: routers.ErrPathNotFound.Error(),
}
}
pathParams = make(map[string]string, 8)
paramNames, err := server.ParameterNames()
if err != nil {
return nil, nil, err
}
for i, value := range paramValues {
name := paramNames[i]
pathParams[name] = value
}
}
// Get PathItem
root := router.node()
var route *routers.Route
node, paramValues := root.Match(method + " " + remainingPath)
if node != nil {
route, _ = node.Value.(*routers.Route)
}
if route == nil {
pathItem := doc.Paths[remainingPath]
if pathItem == nil {
return nil, nil, &routers.RouteError{Reason: routers.ErrPathNotFound.Error()}
}
if pathItem.GetOperation(method) == nil {
return nil, nil, &routers.RouteError{Reason: routers.ErrMethodNotAllowed.Error()}
}
}
if pathParams == nil {
pathParams = make(map[string]string, len(paramValues))
}
paramKeys := node.VariableNames
for i, value := range paramValues {
key := paramKeys[i]
if strings.HasSuffix(key, "*") {
key = key[:len(key)-1]
}
pathParams[key] = value
}
return route, pathParams, nil
}