cachable domain matcher: step 2

pull/692/merge
Darien Raymond 2017-11-06 22:30:56 +01:00
parent 6b77e14bf6
commit 57648c145c
2 changed files with 146 additions and 9 deletions

View File

@ -4,6 +4,8 @@ import (
"context" "context"
"regexp" "regexp"
"strings" "strings"
"sync"
"time"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
@ -64,13 +66,22 @@ func (v *AnyCondition) Len() int {
return len(*v) return len(*v)
} }
type timedResult struct {
timestamp time.Time
result bool
}
type CachableDomainMatcher struct { type CachableDomainMatcher struct {
sync.Mutex
matchers []domainMatcher matchers []domainMatcher
cache map[string]timedResult
lastScan time.Time
} }
func NewCachableDomainMatcher() *CachableDomainMatcher { func NewCachableDomainMatcher() *CachableDomainMatcher {
return &CachableDomainMatcher{ return &CachableDomainMatcher{
matchers: make([]domainMatcher, 0, 64), matchers: make([]domainMatcher, 0, 64),
cache: make(map[string]timedResult, 512),
} }
} }
@ -92,6 +103,85 @@ func (m *CachableDomainMatcher) Add(domain *Domain) error {
return nil return nil
} }
func (m *CachableDomainMatcher) applyInternal(domain string) bool {
for _, matcher := range m.matchers {
if matcher.Apply(domain) {
return true
}
}
return false
}
type cacheResult int
const (
cacheMiss cacheResult = iota
cacheHitTrue
cacheHitFalse
)
func (m *CachableDomainMatcher) findInCache(domain string) cacheResult {
m.Lock()
defer m.Unlock()
r, f := m.cache[domain]
if !f {
return cacheMiss
}
r.timestamp = time.Now()
m.cache[domain] = r
if r.result {
return cacheHitTrue
}
return cacheHitFalse
}
func (m *CachableDomainMatcher) ApplyDomain(domain string) bool {
if len(m.matchers) < 64 {
return m.applyInternal(domain)
}
cr := m.findInCache(domain)
if cr == cacheHitTrue {
return true
}
if cr == cacheHitFalse {
return false
}
r := m.applyInternal(domain)
m.Lock()
defer m.Unlock()
m.cache[domain] = timedResult{
result: r,
timestamp: time.Now(),
}
now := time.Now()
if len(m.cache) > 256 && now.Sub(m.lastScan)/time.Second > 5 {
remove := make([]string, 0, 128)
now := time.Now()
for k, v := range m.cache {
if now.Sub(v.timestamp)/time.Second > 60 {
remove = append(remove, k)
}
}
for _, v := range remove {
delete(m.cache, v)
}
m.lastScan = now
}
return r
}
func (m *CachableDomainMatcher) Apply(ctx context.Context) bool { func (m *CachableDomainMatcher) Apply(ctx context.Context) bool {
dest, ok := proxy.TargetFromContext(ctx) dest, ok := proxy.TargetFromContext(ctx)
if !ok { if !ok {
@ -101,15 +191,7 @@ func (m *CachableDomainMatcher) Apply(ctx context.Context) bool {
if !dest.Address.Family().IsDomain() { if !dest.Address.Family().IsDomain() {
return false return false
} }
domain := dest.Address.Domain() return m.ApplyDomain(dest.Address.Domain())
for _, matcher := range m.matchers {
if matcher.Apply(domain) {
return true
}
}
return false
} }
type domainMatcher interface { type domainMatcher interface {

View File

@ -2,13 +2,22 @@ package router_test
import ( import (
"context" "context"
"os"
"path/filepath"
"strconv"
"testing" "testing"
"time"
proto "github.com/golang/protobuf/proto"
. "v2ray.com/core/app/router" . "v2ray.com/core/app/router"
"v2ray.com/core/common"
"v2ray.com/core/common/errors"
"v2ray.com/core/common/net" "v2ray.com/core/common/net"
"v2ray.com/core/common/platform"
"v2ray.com/core/common/protocol" "v2ray.com/core/common/protocol"
"v2ray.com/core/proxy" "v2ray.com/core/proxy"
. "v2ray.com/ext/assert" . "v2ray.com/ext/assert"
"v2ray.com/ext/sysio"
) )
func TestSubDomainMatcher(t *testing.T) { func TestSubDomainMatcher(t *testing.T) {
@ -179,3 +188,49 @@ func TestRoutingRule(t *testing.T) {
} }
} }
} }
func loadGeoSite(country string) ([]*Domain, error) {
geositeBytes, err := sysio.ReadAsset("geosite.dat")
if err != nil {
return nil, err
}
var geositeList GeoSiteList
if err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {
return nil, err
}
for _, site := range geositeList.Entry {
if site.CountryCode == country {
return site.Domain, nil
}
}
return nil, errors.New("country not found: " + country)
}
func TestChinaSites(t *testing.T) {
assert := With(t)
common.Must(sysio.CopyFile(platform.GetAssetLocation("geosite.dat"), filepath.Join(os.Getenv("GOPATH"), "src", "v2ray.com", "core", "tools", "release", "config", "geosite.dat")))
domains, err := loadGeoSite("CN")
assert(err, IsNil)
matcher := NewCachableDomainMatcher()
for _, d := range domains {
assert(matcher.Add(d), IsNil)
}
assert(matcher.ApplyDomain("163.com"), IsTrue)
assert(matcher.ApplyDomain("163.com"), IsTrue)
assert(matcher.ApplyDomain("164.com"), IsFalse)
assert(matcher.ApplyDomain("164.com"), IsFalse)
for i := 0; i < 1024; i++ {
assert(matcher.ApplyDomain(strconv.Itoa(i)+".not-exists.com"), IsFalse)
}
time.Sleep(time.Second * 10)
for i := 0; i < 1024; i++ {
assert(matcher.ApplyDomain(strconv.Itoa(i)+".not-exists2.com"), IsFalse)
}
}