2022-03-17 17:02:08 +00:00
|
|
|
package http
|
2016-03-27 00:05:17 +00:00
|
|
|
|
|
|
|
import (
|
2016-08-05 18:42:45 +00:00
|
|
|
"fmt"
|
2016-04-06 11:06:31 +00:00
|
|
|
"net/http"
|
2022-12-22 16:16:04 +00:00
|
|
|
"sort"
|
2017-04-17 20:47:53 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-02-03 14:24:05 +00:00
|
|
|
"github.com/traefik/traefik/v3/pkg/rules"
|
2019-01-30 15:24:07 +00:00
|
|
|
"github.com/vulcand/predicate"
|
2016-03-27 00:05:17 +00:00
|
|
|
)
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// Muxer handles routing with rules.
|
|
|
|
type Muxer struct {
|
2024-01-30 15:28:05 +00:00
|
|
|
routes routes
|
|
|
|
parser predicate.Parser
|
|
|
|
parserV2 predicate.Parser
|
|
|
|
defaultHandler http.Handler
|
2016-03-27 00:05:17 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
// NewMuxer returns a new muxer instance.
|
|
|
|
func NewMuxer() (*Muxer, error) {
|
|
|
|
var matchers []string
|
|
|
|
for matcher := range httpFuncs {
|
|
|
|
matchers = append(matchers, matcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
parser, err := rules.NewParser(matchers)
|
2019-01-30 15:24:07 +00:00
|
|
|
if err != nil {
|
2022-12-22 16:16:04 +00:00
|
|
|
return nil, fmt.Errorf("error while creating parser: %w", err)
|
2016-04-06 11:06:31 +00:00
|
|
|
}
|
2019-01-30 15:24:07 +00:00
|
|
|
|
2024-01-23 10:34:05 +00:00
|
|
|
var matchersV2 []string
|
|
|
|
for matcher := range httpFuncsV2 {
|
|
|
|
matchersV2 = append(matchersV2, matcher)
|
|
|
|
}
|
|
|
|
|
|
|
|
parserV2, err := rules.NewParser(matchersV2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while creating v2 parser: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
return &Muxer{
|
2024-01-30 15:28:05 +00:00
|
|
|
parser: parser,
|
|
|
|
parserV2: parserV2,
|
|
|
|
defaultHandler: http.NotFoundHandler(),
|
2019-01-30 15:24:07 +00:00
|
|
|
}, nil
|
2016-03-27 00:05:17 +00:00
|
|
|
}
|
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// ServeHTTP forwards the connection to the matching HTTP handler.
|
|
|
|
// Serves 404 if no handler is found.
|
|
|
|
func (m *Muxer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|
|
|
for _, route := range m.routes {
|
|
|
|
if route.matchers.match(req) {
|
|
|
|
route.handler.ServeHTTP(rw, req)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-30 15:28:05 +00:00
|
|
|
m.defaultHandler.ServeHTTP(rw, req)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetDefaultHandler sets the muxer default handler.
|
|
|
|
func (m *Muxer) SetDefaultHandler(handler http.Handler) {
|
|
|
|
m.defaultHandler = handler
|
2022-12-22 16:16:04 +00:00
|
|
|
}
|
|
|
|
|
2023-01-09 16:24:05 +00:00
|
|
|
// GetRulePriority computes the priority for a given rule.
|
|
|
|
// The priority is calculated using the length of rule.
|
|
|
|
func GetRulePriority(rule string) int {
|
|
|
|
return len(rule)
|
|
|
|
}
|
|
|
|
|
2019-01-30 15:24:07 +00:00
|
|
|
// AddRoute add a new route to the router.
|
2024-01-23 10:34:05 +00:00
|
|
|
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler http.Handler) error {
|
|
|
|
var parse interface{}
|
|
|
|
var err error
|
|
|
|
var matcherFuncs map[string]func(*matchersTree, ...string) error
|
|
|
|
|
|
|
|
switch syntax {
|
|
|
|
case "v2":
|
|
|
|
parse, err = m.parserV2.Parse(rule)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
matcherFuncs = httpFuncsV2
|
|
|
|
default:
|
|
|
|
parse, err = m.parser.Parse(rule)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
matcherFuncs = httpFuncs
|
2016-04-06 11:06:31 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 17:02:08 +00:00
|
|
|
buildTree, ok := parse.(rules.TreeBuilder)
|
2019-01-30 15:24:07 +00:00
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("error while parsing rule %s", rule)
|
2016-04-06 11:06:31 +00:00
|
|
|
}
|
2016-03-27 00:05:17 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
var matchers matchersTree
|
2024-01-23 10:34:05 +00:00
|
|
|
err = matchers.addRule(buildTree(), matcherFuncs)
|
2022-12-22 16:16:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while adding rule %s: %w", rule, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
m.routes = append(m.routes, &route{
|
|
|
|
handler: handler,
|
|
|
|
matchers: matchers,
|
|
|
|
priority: priority,
|
|
|
|
})
|
2020-12-17 09:06:03 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
sort.Sort(m.routes)
|
2020-12-17 09:06:03 +00:00
|
|
|
|
|
|
|
return nil
|
2017-12-19 16:00:12 +00:00
|
|
|
}
|
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// ParseDomains extract domains from rule.
|
|
|
|
func ParseDomains(rule string) ([]string, error) {
|
|
|
|
var matchers []string
|
|
|
|
for matcher := range httpFuncs {
|
|
|
|
matchers = append(matchers, matcher)
|
|
|
|
}
|
2024-01-23 10:34:05 +00:00
|
|
|
for matcher := range httpFuncsV2 {
|
|
|
|
matchers = append(matchers, matcher)
|
|
|
|
}
|
2018-03-23 12:30:03 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
parser, err := rules.NewParser(matchers)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while creating parser: %w", err)
|
|
|
|
}
|
2016-06-06 07:21:00 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
parse, err := parser.Parse(rule)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error while parsing rule %s: %w", rule, err)
|
|
|
|
}
|
2022-03-17 17:02:08 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
buildTree, ok := parse.(rules.TreeBuilder)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("error while parsing rule %s", rule)
|
2019-01-30 15:24:07 +00:00
|
|
|
}
|
2016-08-05 18:42:45 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
return buildTree().ParseMatchers([]string{"Host"}), nil
|
|
|
|
}
|
2016-08-05 18:42:45 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// routes implements sort.Interface.
|
|
|
|
type routes []*route
|
2018-03-23 12:30:03 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// Len implements sort.Interface.
|
|
|
|
func (r routes) Len() int { return len(r) }
|
2019-01-30 15:24:07 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// Swap implements sort.Interface.
|
|
|
|
func (r routes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
|
2018-03-23 12:30:03 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// Less implements sort.Interface.
|
|
|
|
func (r routes) Less(i, j int) bool { return r[i].priority > r[j].priority }
|
2016-08-05 18:42:45 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// route holds the matchers to match HTTP route,
|
|
|
|
// and the handler that will serve the request.
|
|
|
|
type route struct {
|
|
|
|
// matchers tree structure reflecting the rule.
|
|
|
|
matchers matchersTree
|
|
|
|
// handler responsible for handling the route.
|
|
|
|
handler http.Handler
|
|
|
|
// priority is used to disambiguate between two (or more) rules that would all match for a given request.
|
|
|
|
// Computed from the matching rule length, if not user-set.
|
|
|
|
priority int
|
2016-08-05 18:42:45 +00:00
|
|
|
}
|
2021-03-22 20:16:04 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
// matchersTree represents the matchers tree structure.
|
|
|
|
type matchersTree struct {
|
|
|
|
// matcher is a matcher func used to match HTTP request properties.
|
|
|
|
// If matcher is not nil, it means that this matcherTree is a leaf of the tree.
|
|
|
|
// It is therefore mutually exclusive with left and right.
|
|
|
|
matcher func(*http.Request) bool
|
|
|
|
// operator to combine the evaluation of left and right leaves.
|
|
|
|
operator string
|
|
|
|
// Mutually exclusive with matcher.
|
|
|
|
left *matchersTree
|
|
|
|
right *matchersTree
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
func (m *matchersTree) match(req *http.Request) bool {
|
|
|
|
if m == nil {
|
|
|
|
// This should never happen as it should have been detected during parsing.
|
|
|
|
log.Warn().Msg("Rule matcher is nil")
|
|
|
|
return false
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
if m.matcher != nil {
|
|
|
|
return m.matcher(req)
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
switch m.operator {
|
|
|
|
case "or":
|
|
|
|
return m.left.match(req) || m.right.match(req)
|
|
|
|
case "and":
|
|
|
|
return m.left.match(req) && m.right.match(req)
|
|
|
|
default:
|
|
|
|
// This should never happen as it should have been detected during parsing.
|
|
|
|
log.Warn().Str("operator", m.operator).Msg("Invalid rule operator")
|
|
|
|
return false
|
2022-11-28 14:48:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 10:34:05 +00:00
|
|
|
type matcherFuncs map[string]func(*matchersTree, ...string) error
|
|
|
|
|
|
|
|
func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error {
|
2022-12-22 16:16:04 +00:00
|
|
|
switch rule.Matcher {
|
|
|
|
case "and", "or":
|
|
|
|
m.operator = rule.Matcher
|
|
|
|
m.left = &matchersTree{}
|
2024-01-23 10:34:05 +00:00
|
|
|
err := m.left.addRule(rule.RuleLeft, funcs)
|
2022-12-22 16:16:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
m.right = &matchersTree{}
|
2024-01-23 10:34:05 +00:00
|
|
|
return m.right.addRule(rule.RuleRight, funcs)
|
2022-12-22 16:16:04 +00:00
|
|
|
default:
|
|
|
|
err := rules.CheckRule(rule)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while checking rule %s: %w", rule.Matcher, err)
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2024-01-23 10:34:05 +00:00
|
|
|
err = funcs[rule.Matcher](m, rule.Value...)
|
2022-12-22 16:16:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
|
|
|
|
}
|
2022-11-28 14:48:05 +00:00
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
if rule.Not {
|
|
|
|
matcherFunc := m.matcher
|
|
|
|
m.matcher = func(req *http.Request) bool {
|
|
|
|
return !matcherFunc(req)
|
|
|
|
}
|
|
|
|
}
|
2021-03-22 20:16:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-22 16:16:04 +00:00
|
|
|
return nil
|
2021-03-22 20:16:04 +00:00
|
|
|
}
|