refactor(sessionresolver): adapt to changing network conditions (#238)
* feat(sessionresolver): try many and use what works * fix(sessionresolver): make sure we can use quic * fix: the config struct is unnecessary * fix: make kvstore optional * feat: write simple integration test * feat: start adding tests * feat: continue writing tests * fix(sessionresolver): add more unit tests * fix(sessionresolver): finish adding tests * refactor(sessionresolver): changes after code review
This commit is contained in:
parent
12e1164940
commit
034db78f94
19 changed files with 1260 additions and 66 deletions
121
internal/engine/internal/sessionresolver/resolvermaker.go
Normal file
121
internal/engine/internal/sessionresolver/resolvermaker.go
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package sessionresolver
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/netx"
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/netx/bytecounter"
|
||||
)
|
||||
|
||||
// resolvemaker contains rules for making a resolver.
|
||||
type resolvermaker struct {
|
||||
url string
|
||||
score float64
|
||||
}
|
||||
|
||||
// systemResolverURL is the URL of the system resolver.
|
||||
const systemResolverURL = "system:///"
|
||||
|
||||
// allmakers contains all the makers in a list. We use the http3
|
||||
// prefix to indicate we wanna use http3. The code will translate
|
||||
// this to https and set the proper next options.
|
||||
var allmakers = []*resolvermaker{{
|
||||
url: "https://cloudflare-dns.com/dns-query",
|
||||
}, {
|
||||
url: "http3://cloudflare-dns.com/dns-query",
|
||||
}, {
|
||||
url: "https://dns.google/dns-query",
|
||||
}, {
|
||||
url: "http3://dns.google/dns-query",
|
||||
}, {
|
||||
url: "https://dns.quad9.net/dns-query",
|
||||
}, {
|
||||
url: "https://doh.powerdns.org/",
|
||||
}, {
|
||||
url: systemResolverURL,
|
||||
}, {
|
||||
url: "https://mozilla.cloudflare-dns.com/dns-query",
|
||||
}, {
|
||||
url: "http3://mozilla.cloudflare-dns.com/dns-query",
|
||||
}}
|
||||
|
||||
// allbyurl contains all the resolvermakers by URL
|
||||
var allbyurl map[string]*resolvermaker
|
||||
|
||||
// init fills allbyname and gives a nonzero initial score
|
||||
// to all resolvers except for the system resolver. We set
|
||||
// the system resolver score to zero, so that it's less
|
||||
// likely than other resolvers in this list.
|
||||
func init() {
|
||||
allbyurl = make(map[string]*resolvermaker)
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
for _, e := range allmakers {
|
||||
allbyurl[e.url] = e
|
||||
if e.url != systemResolverURL {
|
||||
e.score = rng.Float64()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// byteCounter returns the configured byteCounter or a default
|
||||
func (r *Resolver) byteCounter() *bytecounter.Counter {
|
||||
if r.ByteCounter != nil {
|
||||
return r.ByteCounter
|
||||
}
|
||||
return bytecounter.New()
|
||||
}
|
||||
|
||||
// logger returns the configured logger or a default
|
||||
func (r *Resolver) logger() Logger {
|
||||
if r.Logger != nil {
|
||||
return r.Logger
|
||||
}
|
||||
return log.Log
|
||||
}
|
||||
|
||||
// newresolver creates a new resolver with the given config and URL. This is
|
||||
// where we expand http3 to https and set the h3 options.
|
||||
func (r *Resolver) newresolver(URL string) (childResolver, error) {
|
||||
h3 := strings.HasPrefix(URL, "http3://")
|
||||
if h3 {
|
||||
URL = strings.Replace(URL, "http3://", "https://", 1)
|
||||
}
|
||||
return r.clientmaker().Make(netx.Config{
|
||||
BogonIsError: true,
|
||||
ByteCounter: r.byteCounter(),
|
||||
HTTP3Enabled: h3,
|
||||
Logger: r.logger(),
|
||||
}, URL)
|
||||
}
|
||||
|
||||
// getresolver returns a resolver with the given URL. This function caches
|
||||
// already allocated resolvers so we only allocate them once.
|
||||
func (r *Resolver) getresolver(URL string) (childResolver, error) {
|
||||
defer r.mu.Unlock()
|
||||
r.mu.Lock()
|
||||
if re, found := r.res[URL]; found == true {
|
||||
return re, nil // already created
|
||||
}
|
||||
re, err := r.newresolver(URL)
|
||||
if err != nil {
|
||||
return nil, err // config err?
|
||||
}
|
||||
if r.res == nil {
|
||||
r.res = make(map[string]childResolver)
|
||||
}
|
||||
r.res[URL] = re
|
||||
return re, nil
|
||||
}
|
||||
|
||||
// closeall closes the cached resolvers.
|
||||
func (r *Resolver) closeall() {
|
||||
defer r.mu.Unlock()
|
||||
r.mu.Lock()
|
||||
for _, re := range r.res {
|
||||
re.CloseIdleConnections()
|
||||
}
|
||||
r.res = nil
|
||||
}
|
||||
Loading…
Reference in a new issue