mirror of
https://github.com/Snawoot/opera-proxy.git
synced 2025-09-01 18:20:23 +00:00
72 lines
1.5 KiB
Go
72 lines
1.5 KiB
Go
package resolver
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/netip"
|
|
|
|
"github.com/hashicorp/go-multierror"
|
|
)
|
|
|
|
type LookupNetIPer interface {
|
|
LookupNetIP(context.Context, string, string) ([]netip.Addr, error)
|
|
}
|
|
|
|
type FastResolver struct {
|
|
upstreams []LookupNetIPer
|
|
}
|
|
|
|
type lookupReply struct {
|
|
addrs []netip.Addr
|
|
err error
|
|
}
|
|
|
|
func FastFromURLs(urls ...string) (*FastResolver, error) {
|
|
resolvers := make([]LookupNetIPer, 0, len(urls))
|
|
for i, u := range urls {
|
|
res, err := FromURL(u)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to construct resolver #%d (%q): %w", i, u, err)
|
|
}
|
|
resolvers = append(resolvers, res)
|
|
}
|
|
return NewFastResolver(resolvers...), nil
|
|
}
|
|
|
|
func NewFastResolver(resolvers ...LookupNetIPer) *FastResolver {
|
|
return &FastResolver{
|
|
upstreams: resolvers,
|
|
}
|
|
}
|
|
|
|
func (r FastResolver) LookupNetIP(ctx context.Context, network, host string) ([]netip.Addr, error) {
|
|
ctx, cl := context.WithCancel(ctx)
|
|
drain := make(chan lookupReply, len(r.upstreams))
|
|
for _, res := range r.upstreams {
|
|
go func(res LookupNetIPer) {
|
|
addrs, err := res.LookupNetIP(ctx, network, host)
|
|
drain <- lookupReply{addrs, err}
|
|
}(res)
|
|
}
|
|
|
|
i := 0
|
|
var resAddrs []netip.Addr
|
|
var resErr error
|
|
for ; i < len(r.upstreams); i++ {
|
|
pair := <-drain
|
|
if pair.err != nil {
|
|
resErr = multierror.Append(resErr, pair.err)
|
|
} else {
|
|
cl()
|
|
resAddrs = pair.addrs
|
|
resErr = nil
|
|
break
|
|
}
|
|
}
|
|
go func() {
|
|
for i = i + 1; i < len(r.upstreams); i++ {
|
|
<-drain
|
|
}
|
|
}()
|
|
return resAddrs, resErr
|
|
}
|