traefik/pkg/rules/parser.go

174 lines
3.3 KiB
Go
Raw Normal View History

package rules
import (
2019-04-01 13:30:07 +00:00
"errors"
"strings"
"github.com/vulcand/predicate"
)
2021-05-31 16:58:05 +00:00
const (
and = "and"
or = "or"
)
type treeBuilder func() *tree
2020-05-11 10:06:07 +00:00
// ParseDomains extract domains from rule.
func ParseDomains(rule string) ([]string, error) {
parser, err := newParser()
if err != nil {
return nil, err
}
parse, err := parser.Parse(rule)
if err != nil {
return nil, err
}
buildTree, ok := parse.(treeBuilder)
if !ok {
return nil, errors.New("cannot parse")
}
return lower(parseDomain(buildTree())), nil
}
2020-05-11 10:06:07 +00:00
// ParseHostSNI extracts the HostSNIs declared in a rule.
// This is a first naive implementation used in TCP routing.
func ParseHostSNI(rule string) ([]string, error) {
parser, err := newTCPParser()
if err != nil {
return nil, err
}
parse, err := parser.Parse(rule)
if err != nil {
return nil, err
}
buildTree, ok := parse.(treeBuilder)
if !ok {
return nil, errors.New("cannot parse")
}
return lower(parseDomain(buildTree())), nil
}
func lower(slice []string) []string {
var lowerStrings []string
for _, value := range slice {
lowerStrings = append(lowerStrings, strings.ToLower(value))
}
return lowerStrings
}
func parseDomain(tree *tree) []string {
switch tree.matcher {
2021-05-31 16:58:05 +00:00
case and, or:
return append(parseDomain(tree.ruleLeft), parseDomain(tree.ruleRight)...)
case "Host", "HostSNI":
return tree.value
default:
return nil
}
}
func andFunc(left, right treeBuilder) treeBuilder {
return func() *tree {
return &tree{
2021-05-31 16:58:05 +00:00
matcher: and,
ruleLeft: left(),
ruleRight: right(),
}
}
}
func orFunc(left, right treeBuilder) treeBuilder {
return func() *tree {
return &tree{
2021-05-31 16:58:05 +00:00
matcher: or,
ruleLeft: left(),
ruleRight: right(),
}
}
}
2021-05-31 16:58:05 +00:00
func invert(t *tree) *tree {
switch t.matcher {
case or:
t.matcher = and
t.ruleLeft = invert(t.ruleLeft)
t.ruleRight = invert(t.ruleRight)
case and:
t.matcher = or
t.ruleLeft = invert(t.ruleLeft)
t.ruleRight = invert(t.ruleRight)
default:
t.not = !t.not
}
return t
}
func notFunc(elem treeBuilder) treeBuilder {
return func() *tree {
return invert(elem())
}
}
func newParser() (predicate.Parser, error) {
parserFuncs := make(map[string]interface{})
for matcherName := range funcs {
matcherName := matcherName
fn := func(value ...string) treeBuilder {
return func() *tree {
return &tree{
matcher: matcherName,
value: value,
}
}
}
parserFuncs[matcherName] = fn
parserFuncs[strings.ToLower(matcherName)] = fn
parserFuncs[strings.ToUpper(matcherName)] = fn
parserFuncs[strings.Title(strings.ToLower(matcherName))] = fn
}
return predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
AND: andFunc,
OR: orFunc,
2021-05-31 16:58:05 +00:00
NOT: notFunc,
},
Functions: parserFuncs,
})
}
func newTCPParser() (predicate.Parser, error) {
parserFuncs := make(map[string]interface{})
// FIXME quircky way of waiting for new rules
matcherName := "HostSNI"
fn := func(value ...string) treeBuilder {
return func() *tree {
return &tree{
matcher: matcherName,
value: value,
}
}
}
parserFuncs[matcherName] = fn
parserFuncs[strings.ToLower(matcherName)] = fn
parserFuncs[strings.ToUpper(matcherName)] = fn
parserFuncs[strings.Title(strings.ToLower(matcherName))] = fn
return predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
OR: orFunc,
},
Functions: parserFuncs,
})
}