traefik/rules/parser.go
Julien Salleyron 4a68d29ce2 Add a new protocol
Co-authored-by: Gérald Croës <gerald@containo.us>
2019-03-14 09:30:04 +01:00

144 lines
2.9 KiB
Go

package rules
import (
"strings"
"github.com/pkg/errors"
"github.com/vulcand/predicate"
)
type treeBuilder func() *tree
// 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
}
// 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 {
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{
matcher: "and",
ruleLeft: left(),
ruleRight: right(),
}
}
}
func orFunc(left, right treeBuilder) treeBuilder {
return func() *tree {
return &tree{
matcher: "or",
ruleLeft: left(),
ruleRight: right(),
}
}
}
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,
},
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,
})
}