* pkg/replace: make it more efficient. Remove the map that is allocated on every write and make it more static, but just defining a function that gets called for a label and returns its value. Remove the interface definition and just implement what is needed in our case. Add benchmark test for replace as well. Extend metadata test to test multiple values (pretty sure this didn't work, but there wasn't a test for it, so can't be sure). Update all callers to use it - concurrent use should be fine as we pass everything by value. Benchmarks in replacer: new: BenchmarkReplacer-4 300000 4717 ns/op 240 B/op 8 allocs/op old: BenchmarkReplacer-4 300000 4368 ns/op 384 B/op 11 allocs/op Added benchmark function to the old code to test it. ~~~ func BenchmarkReplacer(b *testing.B) { w := dnstest.NewRecorder(&test.ResponseWriter{}) r := new(dns.Msg) r.SetQuestion("example.org.", dns.TypeHINFO) r.MsgHdr.AuthenticatedData = true b.ResetTimer() b.ReportAllocs() repl := New(context.TODO(), r, w, "") for i := 0; i < b.N; i++ { repl.Replace("{type} {name} {size}") } } ~~~ New code contains (of course a different one). The amount of ops is more, which might be good to look at some more. For all the allocations is seems it was quite performant. This looks to be 50% faster, and there is less allocations in log plugin: old: BenchmarkLogged-4 20000 70526 ns/op new: BenchmarkLogged-4 30000 57558 ns/op Signed-off-by: Miek Gieben <miek@miek.nl> * Stickler bot Signed-off-by: Miek Gieben <miek@miek.nl> * Improve test coverage Signed-off-by: Miek Gieben <miek@miek.nl> * typo Signed-off-by: Miek Gieben <miek@miek.nl> * Add test for malformed log lines Signed-off-by: Miek Gieben <miek@miek.nl>
111 lines
2.9 KiB
Go
111 lines
2.9 KiB
Go
package rewrite
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/coredns/coredns/plugin/pkg/replacer"
|
|
"github.com/coredns/coredns/request"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
// Operators that are defined.
|
|
const (
|
|
Is = "is"
|
|
Not = "not"
|
|
Has = "has"
|
|
NotHas = "not_has"
|
|
StartsWith = "starts_with"
|
|
EndsWith = "ends_with"
|
|
Match = "match"
|
|
NotMatch = "not_match"
|
|
)
|
|
|
|
var repl = replacer.New()
|
|
|
|
// condition is a rewrite condition.
|
|
type condition func(string, string) bool
|
|
|
|
var conditions = map[string]condition{
|
|
Is: isFunc,
|
|
Not: notFunc,
|
|
Has: hasFunc,
|
|
NotHas: notHasFunc,
|
|
StartsWith: startsWithFunc,
|
|
EndsWith: endsWithFunc,
|
|
Match: matchFunc,
|
|
NotMatch: notMatchFunc,
|
|
}
|
|
|
|
// isFunc is condition for Is operator. It checks for equality.
|
|
func isFunc(a, b string) bool { return a == b }
|
|
|
|
// notFunc is condition for Not operator. It checks for inequality.
|
|
func notFunc(a, b string) bool { return a != b }
|
|
|
|
// hasFunc is condition for Has operator. It checks if b is a substring of a.
|
|
func hasFunc(a, b string) bool { return strings.Contains(a, b) }
|
|
|
|
// notHasFunc is condition for NotHas operator. It checks if b is not a substring of a.
|
|
func notHasFunc(a, b string) bool { return !strings.Contains(a, b) }
|
|
|
|
// startsWithFunc is condition for StartsWith operator. It checks if b is a prefix of a.
|
|
func startsWithFunc(a, b string) bool { return strings.HasPrefix(a, b) }
|
|
|
|
// endsWithFunc is condition for EndsWith operator. It checks if b is a suffix of a.
|
|
func endsWithFunc(a, b string) bool {
|
|
// TODO(miek): IsSubDomain
|
|
return strings.HasSuffix(a, b)
|
|
}
|
|
|
|
// matchFunc is condition for Match operator. It does regexp matching of a against pattern in b
|
|
// and returns if they match.
|
|
func matchFunc(a, b string) bool {
|
|
matched, _ := regexp.MatchString(b, a)
|
|
return matched
|
|
}
|
|
|
|
// notMatchFunc is condition for NotMatch operator. It does regexp matching of a against pattern in b
|
|
// and returns if they do not match.
|
|
func notMatchFunc(a, b string) bool {
|
|
matched, _ := regexp.MatchString(b, a)
|
|
return !matched
|
|
}
|
|
|
|
// If is statement for a rewrite condition.
|
|
type If struct {
|
|
A string
|
|
Operator string
|
|
B string
|
|
}
|
|
|
|
// True returns true if the condition is true and false otherwise.
|
|
// If r is not nil, it replaces placeholders before comparison.
|
|
func (i If) True(r *dns.Msg) bool {
|
|
if c, ok := conditions[i.Operator]; ok {
|
|
a, b := i.A, i.B
|
|
if r != nil {
|
|
ctx := context.TODO()
|
|
state := request.Request{Req: r, W: nil} // hmm W nil?
|
|
a = repl.Replace(ctx, state, nil, i.A)
|
|
b = repl.Replace(ctx, state, nil, i.B)
|
|
}
|
|
return c(a, b)
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NewIf creates a new If condition.
|
|
func NewIf(a, operator, b string) (If, error) {
|
|
if _, ok := conditions[operator]; !ok {
|
|
return If{}, fmt.Errorf("invalid operator %v", operator)
|
|
}
|
|
return If{
|
|
A: a,
|
|
Operator: operator,
|
|
B: b,
|
|
}, nil
|
|
}
|