refactor: interfaces and data types into the model package (#642)
## Checklist - [x] I have read the [contribution guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md) - [x] reference issue for this pull request: https://github.com/ooni/probe/issues/1885 - [x] related ooni/spec pull request: N/A Location of the issue tracker: https://github.com/ooni/probe ## Description This PR contains a set of changes to move important interfaces and data types into the `./internal/model` package. The criteria for including an interface or data type in here is roughly that the type should be important and used by several packages. We are especially interested to move more interfaces here to increase modularity. An additional side effect is that, by reading this package, one should be able to understand more quickly how different parts of the codebase interact with each other. This is what I want to move in `internal/model`: - [x] most important interfaces from `internal/netxlite` - [x] everything that was previously part of `internal/engine/model` - [x] mocks from `internal/netxlite/mocks` should also be moved in here as a subpackage
This commit is contained in:
+25
-32
@@ -6,19 +6,12 @@ import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// Dialer establishes network connections.
|
||||
type Dialer interface {
|
||||
// DialContext behaves like net.Dialer.DialContext.
|
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
|
||||
// CloseIdleConnections closes idle connections, if any.
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// NewDialerWithResolver calls WrapDialer for the stdlib dialer.
|
||||
func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
|
||||
func NewDialerWithResolver(logger model.DebugLogger, resolver model.Resolver) model.Dialer {
|
||||
return WrapDialer(logger, resolver, &dialerSystem{})
|
||||
}
|
||||
|
||||
@@ -51,26 +44,26 @@ func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
|
||||
//
|
||||
// In general, do not use WrapDialer directly but try to use
|
||||
// more high-level factories, e.g., NewDialerWithResolver.
|
||||
func WrapDialer(logger Logger, resolver Resolver, dialer Dialer) Dialer {
|
||||
func WrapDialer(logger model.DebugLogger, resolver model.Resolver, dialer model.Dialer) model.Dialer {
|
||||
return &dialerLogger{
|
||||
Dialer: &dialerResolver{
|
||||
Dialer: &dialerLogger{
|
||||
Dialer: &dialerErrWrapper{
|
||||
Dialer: dialer,
|
||||
},
|
||||
Logger: logger,
|
||||
DebugLogger: logger,
|
||||
operationSuffix: "_address",
|
||||
},
|
||||
Resolver: resolver,
|
||||
},
|
||||
Logger: logger,
|
||||
DebugLogger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDialerWithoutResolver calls NewDialerWithResolver with a "null" resolver.
|
||||
//
|
||||
// The returned dialer fails with ErrNoResolver if passed a domain name.
|
||||
func NewDialerWithoutResolver(logger Logger) Dialer {
|
||||
func NewDialerWithoutResolver(logger model.DebugLogger) model.Dialer {
|
||||
return NewDialerWithResolver(logger, &nullResolver{})
|
||||
}
|
||||
|
||||
@@ -81,16 +74,16 @@ type dialerSystem struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
var _ Dialer = &dialerSystem{}
|
||||
var _ model.Dialer = &dialerSystem{}
|
||||
|
||||
const dialerDefaultTimeout = 15 * time.Second
|
||||
|
||||
func (d *dialerSystem) newUnderlyingDialer() TProxyDialer {
|
||||
func (d *dialerSystem) newUnderlyingDialer() model.SimpleDialer {
|
||||
t := d.timeout
|
||||
if t <= 0 {
|
||||
t = dialerDefaultTimeout
|
||||
}
|
||||
return TProxy.NewTProxyDialer(t)
|
||||
return TProxy.NewSimpleDialer(t)
|
||||
}
|
||||
|
||||
func (d *dialerSystem) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
@@ -103,11 +96,11 @@ func (d *dialerSystem) CloseIdleConnections() {
|
||||
|
||||
// dialerResolver combines dialing with domain name resolution.
|
||||
type dialerResolver struct {
|
||||
Dialer
|
||||
Resolver
|
||||
model.Dialer
|
||||
model.Resolver
|
||||
}
|
||||
|
||||
var _ Dialer = &dialerResolver{}
|
||||
var _ model.Dialer = &dialerResolver{}
|
||||
|
||||
func (d *dialerResolver) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
// QUIRK: this routine and the related routines in quirks.go cannot
|
||||
@@ -151,10 +144,10 @@ func (d *dialerResolver) CloseIdleConnections() {
|
||||
// dialerLogger is a Dialer with logging.
|
||||
type dialerLogger struct {
|
||||
// Dialer is the underlying dialer.
|
||||
Dialer
|
||||
model.Dialer
|
||||
|
||||
// Logger is the underlying logger.
|
||||
Logger
|
||||
model.DebugLogger
|
||||
|
||||
// operationSuffix is appended to the operation name.
|
||||
//
|
||||
@@ -165,19 +158,19 @@ type dialerLogger struct {
|
||||
operationSuffix string
|
||||
}
|
||||
|
||||
var _ Dialer = &dialerLogger{}
|
||||
var _ model.Dialer = &dialerLogger{}
|
||||
|
||||
func (d *dialerLogger) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
d.Logger.Debugf("dial%s %s/%s...", d.operationSuffix, address, network)
|
||||
d.DebugLogger.Debugf("dial%s %s/%s...", d.operationSuffix, address, network)
|
||||
start := time.Now()
|
||||
conn, err := d.Dialer.DialContext(ctx, network, address)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
d.Logger.Debugf("dial%s %s/%s... %s in %s", d.operationSuffix,
|
||||
d.DebugLogger.Debugf("dial%s %s/%s... %s in %s", d.operationSuffix,
|
||||
address, network, err, elapsed)
|
||||
return nil, err
|
||||
}
|
||||
d.Logger.Debugf("dial%s %s/%s... ok in %s", d.operationSuffix,
|
||||
d.DebugLogger.Debugf("dial%s %s/%s... ok in %s", d.operationSuffix,
|
||||
address, network, elapsed)
|
||||
return conn, nil
|
||||
}
|
||||
@@ -195,7 +188,7 @@ var ErrNoConnReuse = errors.New("cannot reuse connection")
|
||||
// dial will succed and return conn regardless of the network
|
||||
// and address arguments passed to DialContext. Any subsequent
|
||||
// dial returns ErrNoConnReuse.
|
||||
func NewSingleUseDialer(conn net.Conn) Dialer {
|
||||
func NewSingleUseDialer(conn net.Conn) model.Dialer {
|
||||
return &dialerSingleUse{conn: conn}
|
||||
}
|
||||
|
||||
@@ -205,7 +198,7 @@ type dialerSingleUse struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
var _ Dialer = &dialerSingleUse{}
|
||||
var _ model.Dialer = &dialerSingleUse{}
|
||||
|
||||
func (s *dialerSingleUse) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
|
||||
defer s.Unlock()
|
||||
@@ -225,10 +218,10 @@ func (s *dialerSingleUse) CloseIdleConnections() {
|
||||
// dialerErrWrapper is a dialer that performs error wrapping. The connection
|
||||
// returned by the DialContext function will also perform error wrapping.
|
||||
type dialerErrWrapper struct {
|
||||
Dialer
|
||||
model.Dialer
|
||||
}
|
||||
|
||||
var _ Dialer = &dialerErrWrapper{}
|
||||
var _ model.Dialer = &dialerErrWrapper{}
|
||||
|
||||
func (d *dialerErrWrapper) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
conn, err := d.Dialer.DialContext(ctx, network, address)
|
||||
@@ -274,13 +267,13 @@ func (c *dialerErrWrapperConn) Close() error {
|
||||
var ErrNoDialer = errors.New("no configured dialer")
|
||||
|
||||
// NewNullDialer returns a dialer that always fails with ErrNoDialer.
|
||||
func NewNullDialer() Dialer {
|
||||
func NewNullDialer() model.Dialer {
|
||||
return &nullDialer{}
|
||||
}
|
||||
|
||||
type nullDialer struct{}
|
||||
|
||||
var _ Dialer = &nullDialer{}
|
||||
var _ model.Dialer = &nullDialer{}
|
||||
|
||||
func (*nullDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return nil, ErrNoDialer
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestNewDialer(t *testing.T) {
|
||||
t.Run("produces a chain with the expected types", func(t *testing.T) {
|
||||
d := NewDialerWithoutResolver(log.Log)
|
||||
logger := d.(*dialerLogger)
|
||||
if logger.Logger != log.Log {
|
||||
if logger.DebugLogger != log.Log {
|
||||
t.Fatal("invalid logger")
|
||||
}
|
||||
reso := logger.Dialer.(*dialerResolver)
|
||||
@@ -26,7 +26,7 @@ func TestNewDialer(t *testing.T) {
|
||||
t.Fatal("invalid Resolver type")
|
||||
}
|
||||
logger = reso.Dialer.(*dialerLogger)
|
||||
if logger.Logger != log.Log {
|
||||
if logger.DebugLogger != log.Log {
|
||||
t.Fatal("invalid logger")
|
||||
}
|
||||
errWrapper := logger.Dialer.(*dialerErrWrapper)
|
||||
@@ -406,7 +406,7 @@ func TestDialerLogger(t *testing.T) {
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
Logger: lo,
|
||||
DebugLogger: lo,
|
||||
}
|
||||
conn, err := d.DialContext(context.Background(), "tcp", "www.google.com:443")
|
||||
if err != nil {
|
||||
@@ -434,7 +434,7 @@ func TestDialerLogger(t *testing.T) {
|
||||
return nil, io.EOF
|
||||
},
|
||||
},
|
||||
Logger: lo,
|
||||
DebugLogger: lo,
|
||||
}
|
||||
conn, err := d.DialContext(context.Background(), "tcp", "www.google.com:443")
|
||||
if !errors.Is(err, io.EOF) {
|
||||
|
||||
@@ -1,40 +1,9 @@
|
||||
package netxlite
|
||||
|
||||
import "github.com/miekg/dns"
|
||||
|
||||
// The DNSDecoder decodes DNS replies.
|
||||
type DNSDecoder interface {
|
||||
// DecodeLookupHost decodes an A or AAAA reply.
|
||||
//
|
||||
// Arguments:
|
||||
//
|
||||
// - qtype is the query type (e.g., dns.TypeAAAA)
|
||||
//
|
||||
// - data contains the reply bytes read from a DNSTransport
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
// - on success, a list of IP addrs inside the reply and a nil error
|
||||
//
|
||||
// - on failure, a nil list and an error.
|
||||
//
|
||||
// Note that this function will return an error if there is no
|
||||
// IP address inside of the reply.
|
||||
DecodeLookupHost(qtype uint16, data []byte) ([]string, error)
|
||||
|
||||
// DecodeHTTPS decodes an HTTPS reply.
|
||||
//
|
||||
// The argument is the reply as read by the DNSTransport.
|
||||
//
|
||||
// On success, this function returns an HTTPSSvc structure and
|
||||
// a nil error. On failure, the HTTPSSvc pointer is nil and
|
||||
// the error points to the error that occurred.
|
||||
//
|
||||
// This function will return an error if the HTTPS reply does not
|
||||
// contain at least a valid ALPN entry. It will not return
|
||||
// an error, though, when there are no IPv4/IPv6 hints in the reply.
|
||||
DecodeHTTPS(data []byte) (*HTTPSSvc, error)
|
||||
}
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// DNSDecoderMiekg uses github.com/miekg/dns to implement the Decoder.
|
||||
type DNSDecoderMiekg struct{}
|
||||
@@ -58,12 +27,12 @@ func (d *DNSDecoderMiekg) parseReply(data []byte) (*dns.Msg, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte) (*HTTPSSvc, error) {
|
||||
func (d *DNSDecoderMiekg) DecodeHTTPS(data []byte) (*model.HTTPSSvc, error) {
|
||||
reply, err := d.parseReply(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := &HTTPSSvc{}
|
||||
out := &model.HTTPSSvc{}
|
||||
for _, answer := range reply.Answer {
|
||||
switch avalue := answer.(type) {
|
||||
case *dns.HTTPS:
|
||||
@@ -115,4 +84,4 @@ func (d *DNSDecoderMiekg) DecodeLookupHost(qtype uint16, data []byte) ([]string,
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
var _ DNSDecoder = &DNSDecoderMiekg{}
|
||||
var _ model.DNSDecoder = &DNSDecoderMiekg{}
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
package netxlite
|
||||
|
||||
import "github.com/miekg/dns"
|
||||
|
||||
// The DNSEncoder encodes DNS queries to bytes
|
||||
type DNSEncoder interface {
|
||||
// Encode transforms its arguments into a serialized DNS query.
|
||||
//
|
||||
// Arguments:
|
||||
//
|
||||
// - domain is the domain for the query (e.g., x.org);
|
||||
//
|
||||
// - qtype is the query type (e.g., dns.TypeA);
|
||||
//
|
||||
// - padding is whether to add padding to the query.
|
||||
//
|
||||
// On success, this function returns a valid byte array and
|
||||
// a nil error. On failure, we have an error and the byte array is nil.
|
||||
Encode(domain string, qtype uint16, padding bool) ([]byte, error)
|
||||
}
|
||||
import (
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// DNSEncoderMiekg uses github.com/miekg/dns to implement the Encoder.
|
||||
type DNSEncoderMiekg struct{}
|
||||
@@ -60,4 +46,4 @@ func (e *DNSEncoderMiekg) Encode(domain string, qtype uint16, padding bool) ([]b
|
||||
return query.Pack()
|
||||
}
|
||||
|
||||
var _ DNSEncoder = &DNSEncoderMiekg{}
|
||||
var _ model.DNSEncoder = &DNSEncoderMiekg{}
|
||||
|
||||
@@ -8,12 +8,13 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/httpheader"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// DNSOverHTTPS is a DNS-over-HTTPS DNSTransport.
|
||||
type DNSOverHTTPS struct {
|
||||
// Client is the MANDATORY http client to use.
|
||||
Client HTTPClient
|
||||
Client model.HTTPClient
|
||||
|
||||
// URL is the MANDATORY URL of the DNS-over-HTTPS server.
|
||||
URL string
|
||||
@@ -30,14 +31,14 @@ type DNSOverHTTPS struct {
|
||||
// - client in http.Client-like type (e.g., http.DefaultClient);
|
||||
//
|
||||
// - URL is the DoH resolver URL (e.g., https://1.1.1.1/dns-query).
|
||||
func NewDNSOverHTTPS(client HTTPClient, URL string) *DNSOverHTTPS {
|
||||
func NewDNSOverHTTPS(client model.HTTPClient, URL string) *DNSOverHTTPS {
|
||||
return NewDNSOverHTTPSWithHostOverride(client, URL, "")
|
||||
}
|
||||
|
||||
// NewDNSOverHTTPSWithHostOverride creates a new DNSOverHTTPS
|
||||
// with the given Host header override.
|
||||
func NewDNSOverHTTPSWithHostOverride(
|
||||
client HTTPClient, URL, hostOverride string) *DNSOverHTTPS {
|
||||
client model.HTTPClient, URL, hostOverride string) *DNSOverHTTPS {
|
||||
return &DNSOverHTTPS{Client: client, URL: URL, HostOverride: hostOverride}
|
||||
}
|
||||
|
||||
@@ -89,4 +90,4 @@ func (t *DNSOverHTTPS) CloseIdleConnections() {
|
||||
t.Client.CloseIdleConnections()
|
||||
}
|
||||
|
||||
var _ DNSTransport = &DNSOverHTTPS{}
|
||||
var _ model.DNSTransport = &DNSOverHTTPS{}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/engine/httpheader"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestDNSOverHTTPS(t *testing.T) {
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"math"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// DialContextFunc is the type of net.Dialer.DialContext.
|
||||
@@ -108,4 +110,4 @@ func (t *DNSOverTCP) CloseIdleConnections() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
var _ DNSTransport = &DNSOverTCP{}
|
||||
var _ model.DNSTransport = &DNSOverTCP{}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestDNSOverTCP(t *testing.T) {
|
||||
|
||||
@@ -3,11 +3,13 @@ package netxlite
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// DNSOverUDP is a DNS-over-UDP DNSTransport.
|
||||
type DNSOverUDP struct {
|
||||
dialer Dialer
|
||||
dialer model.Dialer
|
||||
address string
|
||||
}
|
||||
|
||||
@@ -18,7 +20,7 @@ type DNSOverUDP struct {
|
||||
// - dialer is any type that implements the Dialer interface;
|
||||
//
|
||||
// - address is the endpoint address (e.g., 8.8.8.8:53).
|
||||
func NewDNSOverUDP(dialer Dialer, address string) *DNSOverUDP {
|
||||
func NewDNSOverUDP(dialer model.Dialer, address string) *DNSOverUDP {
|
||||
return &DNSOverUDP{dialer: dialer, address: address}
|
||||
}
|
||||
|
||||
@@ -66,4 +68,4 @@ func (t *DNSOverUDP) CloseIdleConnections() {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
var _ DNSTransport = &DNSOverUDP{}
|
||||
var _ model.DNSTransport = &DNSOverUDP{}
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestDNSOverUDP(t *testing.T) {
|
||||
|
||||
@@ -1,21 +1 @@
|
||||
package netxlite
|
||||
|
||||
import "context"
|
||||
|
||||
// DNSTransport represents an abstract DNS transport.
|
||||
type DNSTransport interface {
|
||||
// RoundTrip sends a DNS query and receives the reply.
|
||||
RoundTrip(ctx context.Context, query []byte) (reply []byte, err error)
|
||||
|
||||
// RequiresPadding returns whether this transport needs padding.
|
||||
RequiresPadding() bool
|
||||
|
||||
// Network is the network of the round tripper (e.g. "dot").
|
||||
Network() string
|
||||
|
||||
// Address is the address of the round tripper (e.g. "1.1.1.1:853").
|
||||
Address() string
|
||||
|
||||
// CloseIdleConnections closes idle connections, if any.
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
// Package dnsx contains DNS extension types.
|
||||
package dnsx
|
||||
|
||||
// HTTPSSvc is the reply to an HTTPS DNS query.
|
||||
type HTTPSSvc struct {
|
||||
// ALPN contains the ALPNs inside the HTTPS reply.
|
||||
ALPN []string
|
||||
|
||||
// IPv4 contains the IPv4 hints (which may be empty).
|
||||
IPv4 []string
|
||||
|
||||
// IPv6 contains the IPv6 hints (which may be empty).
|
||||
IPv6 []string
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
// This package is the basic networking building block that you
|
||||
// should be using every time you need networking.
|
||||
//
|
||||
// It implements interfaces defined in the internal/model package.
|
||||
//
|
||||
// You should consider checking the tutorial explaining how to use this package
|
||||
// for network measurements: https://github.com/ooni/probe-cli/tree/master/internal/tutorial/netxlite.
|
||||
//
|
||||
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestDNSProxy(t *testing.T) {
|
||||
@@ -29,7 +30,7 @@ func TestDNSProxy(t *testing.T) {
|
||||
return newProxyWithCache(action, nil)
|
||||
}
|
||||
|
||||
newresolver := func(listener DNSListener) netxlite.Resolver {
|
||||
newresolver := func(listener DNSListener) model.Resolver {
|
||||
dlr := netxlite.NewDialerWithoutResolver(log.Log)
|
||||
r := netxlite.NewResolverUDP(log.Log, dlr, listener.LocalAddr().String())
|
||||
return r
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Package filtering allows to implement self-censorship.
|
||||
//
|
||||
// The top-level struct is the TProxy. It implements netxlite's
|
||||
// TProxable interface. Therefore, you can use TProxy to
|
||||
// The top-level struct is the TProxy. It implements model's
|
||||
// UnderlyingNetworkLibrary interface. Therefore, you can use TProxy to
|
||||
// implement filtering and blocking of TCP, TLS, QUIC, DNS, HTTP.
|
||||
//
|
||||
// We also expose proxies that implement filtering policies for
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package filtering
|
||||
|
||||
// Logger defines the common interface that a logger should have. It is
|
||||
// out of the box compatible with `log.Log` in `apex/log`.
|
||||
type Logger interface {
|
||||
// Debug emits a debug message.
|
||||
Debug(msg string)
|
||||
|
||||
// Debugf formats and emits a debug message.
|
||||
Debugf(format string, v ...interface{})
|
||||
|
||||
// Info emits an informational message.
|
||||
Info(msg string)
|
||||
|
||||
// Infof formats and emits an informational message.
|
||||
Infof(format string, v ...interface{})
|
||||
|
||||
// Warn emits a warning message.
|
||||
Warn(msg string)
|
||||
|
||||
// Warnf formats and emits a warning message.
|
||||
Warnf(format string, v ...interface{})
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestTLSProxy(t *testing.T) {
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
)
|
||||
|
||||
// TProxyPolicy is a policy for TPRoxy.
|
||||
@@ -100,13 +100,13 @@ func (c *TProxyConfig) CanonicalizeDNS() {
|
||||
c.DNSCache = cache
|
||||
}
|
||||
|
||||
// TProxy is a netxlite.TProxable that implements self censorship.
|
||||
// TProxy is a model.UnderlyingNetworkLibrary that implements self censorship.
|
||||
type TProxy struct {
|
||||
// config contains settings for TProxy.
|
||||
config *TProxyConfig
|
||||
|
||||
// dnsClient is the DNS client we'll internally use.
|
||||
dnsClient netxlite.Resolver
|
||||
dnsClient model.Resolver
|
||||
|
||||
// dnsListener is the DNS listener.
|
||||
dnsListener DNSListener
|
||||
@@ -115,10 +115,10 @@ type TProxy struct {
|
||||
httpListener net.Listener
|
||||
|
||||
// listenUDP allows overriding net.ListenUDP calls in tests
|
||||
listenUDP func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error)
|
||||
listenUDP func(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error)
|
||||
|
||||
// logger is the underlying logger to use.
|
||||
logger Logger
|
||||
logger model.InfoLogger
|
||||
|
||||
// tlsListener is the TLS listener.
|
||||
tlsListener net.Listener
|
||||
@@ -129,15 +129,15 @@ type TProxy struct {
|
||||
//
|
||||
|
||||
// NewTProxy creates a new TProxy instance.
|
||||
func NewTProxy(config *TProxyConfig, logger Logger) (*TProxy, error) {
|
||||
func NewTProxy(config *TProxyConfig, logger model.InfoLogger) (*TProxy, error) {
|
||||
return newTProxy(config, logger, "127.0.0.1:0", "127.0.0.1:0", "127.0.0.1:0")
|
||||
}
|
||||
|
||||
func newTProxy(config *TProxyConfig, logger Logger, dnsListenerAddr,
|
||||
func newTProxy(config *TProxyConfig, logger model.InfoLogger, dnsListenerAddr,
|
||||
tlsListenerAddr, httpListenerAddr string) (*TProxy, error) {
|
||||
p := &TProxy{
|
||||
config: config,
|
||||
listenUDP: func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
listenUDP: func(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return net.ListenUDP(network, laddr)
|
||||
},
|
||||
logger: logger,
|
||||
@@ -165,12 +165,12 @@ func (p *TProxy) newDNSListener(listenAddr string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *TProxy) newDNSClient(logger Logger) {
|
||||
func (p *TProxy) newDNSClient(logger model.DebugLogger) {
|
||||
dialer := netxlite.NewDialerWithoutResolver(logger)
|
||||
p.dnsClient = netxlite.NewResolverUDP(logger, dialer, p.dnsListener.LocalAddr().String())
|
||||
}
|
||||
|
||||
func (p *TProxy) newTLSListener(listenAddr string, logger Logger) error {
|
||||
func (p *TProxy) newTLSListener(listenAddr string, logger model.DebugLogger) error {
|
||||
var err error
|
||||
tlsProxy := &TLSProxy{OnIncomingSNI: p.onIncomingSNI}
|
||||
p.tlsListener, err = tlsProxy.Start(listenAddr)
|
||||
@@ -198,7 +198,7 @@ func (p *TProxy) Close() error {
|
||||
//
|
||||
|
||||
// ListenUDP implements netxlite.TProxy.ListenUDP.
|
||||
func (p *TProxy) ListenUDP(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
func (p *TProxy) ListenUDP(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
pconn, err := p.listenUDP(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -209,7 +209,7 @@ func (p *TProxy) ListenUDP(network string, laddr *net.UDPAddr) (quicx.UDPLikeCon
|
||||
// tProxyUDPLikeConn is a TProxy-aware UDPLikeConn.
|
||||
type tProxyUDPLikeConn struct {
|
||||
// UDPLikeConn is the underlying conn type.
|
||||
quicx.UDPLikeConn
|
||||
model.UDPLikeConn
|
||||
|
||||
// proxy refers to the TProxy.
|
||||
proxy *TProxy
|
||||
@@ -242,8 +242,8 @@ func (p *TProxy) LookupHost(ctx context.Context, domain string) ([]string, error
|
||||
// Dialer
|
||||
//
|
||||
|
||||
// NewTProxyDialer implements netxlite.TProxy.NewTProxyDialer.
|
||||
func (p *TProxy) NewTProxyDialer(timeout time.Duration) netxlite.TProxyDialer {
|
||||
// NewSimpleDialer implements netxlite.TProxy.NewTProxyDialer.
|
||||
func (p *TProxy) NewSimpleDialer(timeout time.Duration) model.SimpleDialer {
|
||||
return &tProxyDialer{
|
||||
dialer: &net.Dialer{Timeout: timeout},
|
||||
proxy: p,
|
||||
|
||||
@@ -13,14 +13,14 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
// tProxyDialerAdapter adapts a netxlite.TProxyDialer to be a netxlite.Dialer.
|
||||
type tProxyDialerAdapter struct {
|
||||
netxlite.TProxyDialer
|
||||
model.SimpleDialer
|
||||
}
|
||||
|
||||
// CloseIdleConnections implements Dialer.CloseIdleConnections.
|
||||
@@ -158,7 +158,7 @@ func TestTProxyQUIC(t *testing.T) {
|
||||
}
|
||||
defer proxy.Close()
|
||||
var called bool
|
||||
proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
proxy.listenUDP = func(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return &mocks.QUICUDPLikeConn{
|
||||
MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
|
||||
called = true
|
||||
@@ -195,7 +195,7 @@ func TestTProxyQUIC(t *testing.T) {
|
||||
}
|
||||
defer proxy.Close()
|
||||
var called bool
|
||||
proxy.listenUDP = func(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
proxy.listenUDP = func(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return &mocks.QUICUDPLikeConn{
|
||||
MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
|
||||
called = true
|
||||
@@ -280,7 +280,7 @@ func TestTProxyOnIncomingSNI(t *testing.T) {
|
||||
}
|
||||
defer proxy.Close()
|
||||
ctx := context.Background()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
conn, err := dialer.DialContext(ctx, "tcp", "8.8.8.8:443")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -308,7 +308,7 @@ func TestTProxyOnIncomingSNI(t *testing.T) {
|
||||
}
|
||||
defer proxy.Close()
|
||||
ctx := context.Background()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
conn, err := dialer.DialContext(ctx, "tcp", "8.8.8.8:443")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -337,7 +337,7 @@ func TestTProxyOnIncomingHost(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
req, err := http.NewRequest("GET", "http://130.192.16.171:80", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -369,7 +369,7 @@ func TestTProxyOnIncomingHost(t *testing.T) {
|
||||
log.Log,
|
||||
netxlite.NewResolverStdlib(log.Log),
|
||||
&tProxyDialerAdapter{
|
||||
proxy.NewTProxyDialer(10 * time.Second),
|
||||
proxy.NewSimpleDialer(10 * time.Second),
|
||||
},
|
||||
)
|
||||
req, err := http.NewRequest("GET", "http://130.192.16.171:80", nil)
|
||||
@@ -400,7 +400,7 @@ func TestTProxyDial(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", "http://130.192.16.171:80", nil)
|
||||
@@ -432,7 +432,7 @@ func TestTProxyDial(t *testing.T) {
|
||||
dialer := netxlite.WrapDialer(log.Log,
|
||||
netxlite.NewResolverStdlib(log.Log),
|
||||
&tProxyDialerAdapter{
|
||||
proxy.NewTProxyDialer(10 * time.Second)})
|
||||
proxy.NewSimpleDialer(10 * time.Second)})
|
||||
req, err := http.NewRequest("GET", "http://130.192.16.171:80", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -459,7 +459,7 @@ func TestTProxyDial(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
req, err := http.NewRequestWithContext(
|
||||
@@ -492,7 +492,7 @@ func TestTProxyDial(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
resolver := netxlite.NewResolverUDP(
|
||||
log.Log, &tProxyDialerAdapter{dialer}, "8.8.8.8:53")
|
||||
addrs, err := resolver.LookupHost(context.Background(), "example.com")
|
||||
@@ -511,7 +511,7 @@ func TestTProxyDial(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer proxy.Close()
|
||||
dialer := proxy.NewTProxyDialer(10 * time.Second)
|
||||
dialer := proxy.NewSimpleDialer(10 * time.Second)
|
||||
ctx := context.Background()
|
||||
conn, err := dialer.DialContext(ctx, "tcp", "127.0.0.1")
|
||||
if err == nil || !strings.HasSuffix(err.Error(), "missing port in address") {
|
||||
|
||||
+16
-30
@@ -8,20 +8,12 @@ import (
|
||||
"time"
|
||||
|
||||
oohttp "github.com/ooni/oohttp"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// HTTPTransport is an http.Transport-like structure.
|
||||
type HTTPTransport interface {
|
||||
// RoundTrip performs the HTTP round trip.
|
||||
RoundTrip(req *http.Request) (*http.Response, error)
|
||||
|
||||
// CloseIdleConnections closes idle connections.
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// httpTransportErrWrapper is an HTTPTransport with error wrapping.
|
||||
type httpTransportErrWrapper struct {
|
||||
HTTPTransport
|
||||
model.HTTPTransport
|
||||
}
|
||||
|
||||
func (txp *httpTransportErrWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -35,13 +27,13 @@ func (txp *httpTransportErrWrapper) RoundTrip(req *http.Request) (*http.Response
|
||||
// httpTransportLogger is an HTTPTransport with logging.
|
||||
type httpTransportLogger struct {
|
||||
// HTTPTransport is the underlying HTTP transport.
|
||||
HTTPTransport HTTPTransport
|
||||
HTTPTransport model.HTTPTransport
|
||||
|
||||
// Logger is the underlying logger.
|
||||
Logger Logger
|
||||
Logger model.DebugLogger
|
||||
}
|
||||
|
||||
var _ HTTPTransport = &httpTransportLogger{}
|
||||
var _ model.HTTPTransport = &httpTransportLogger{}
|
||||
|
||||
func (txp *httpTransportLogger) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
txp.Logger.Debugf("> %s %s", req.Method, req.URL.String())
|
||||
@@ -73,9 +65,9 @@ func (txp *httpTransportLogger) CloseIdleConnections() {
|
||||
// httpTransportConnectionsCloser is an HTTPTransport that
|
||||
// correctly forwards CloseIdleConnections calls.
|
||||
type httpTransportConnectionsCloser struct {
|
||||
HTTPTransport
|
||||
Dialer
|
||||
TLSDialer
|
||||
model.HTTPTransport
|
||||
model.Dialer
|
||||
model.TLSDialer
|
||||
}
|
||||
|
||||
// CloseIdleConnections forwards the CloseIdleConnections calls.
|
||||
@@ -89,7 +81,7 @@ func (txp *httpTransportConnectionsCloser) CloseIdleConnections() {
|
||||
//
|
||||
// This factory and NewHTTPTransportStdlib are the recommended
|
||||
// ways of creating a new HTTPTransport.
|
||||
func NewHTTPTransport(logger Logger, dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
|
||||
func NewHTTPTransport(logger model.DebugLogger, dialer model.Dialer, tlsDialer model.TLSDialer) model.HTTPTransport {
|
||||
return WrapHTTPTransport(logger, NewOOHTTPBaseTransport(dialer, tlsDialer))
|
||||
}
|
||||
|
||||
@@ -117,7 +109,7 @@ func NewHTTPTransport(logger Logger, dialer Dialer, tlsDialer TLSDialer) HTTPTra
|
||||
// way in which we perform measurements.
|
||||
//
|
||||
// This is a low level factory. Consider not using it directly.
|
||||
func NewOOHTTPBaseTransport(dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
|
||||
func NewOOHTTPBaseTransport(dialer model.Dialer, tlsDialer model.TLSDialer) model.HTTPTransport {
|
||||
// Using oohttp to support any TLS library.
|
||||
txp := oohttp.DefaultTransport.(*oohttp.Transport).Clone()
|
||||
|
||||
@@ -158,7 +150,7 @@ func NewOOHTTPBaseTransport(dialer Dialer, tlsDialer TLSDialer) HTTPTransport {
|
||||
// and guarantees that returned errors are wrapped.
|
||||
//
|
||||
// This is a low level factory. Consider not using it directly.
|
||||
func WrapHTTPTransport(logger Logger, txp HTTPTransport) HTTPTransport {
|
||||
func WrapHTTPTransport(logger model.DebugLogger, txp model.HTTPTransport) model.HTTPTransport {
|
||||
return &httpTransportLogger{
|
||||
HTTPTransport: &httpTransportErrWrapper{txp},
|
||||
Logger: logger,
|
||||
@@ -168,7 +160,7 @@ func WrapHTTPTransport(logger Logger, txp HTTPTransport) HTTPTransport {
|
||||
// httpDialerWithReadTimeout enforces a read timeout for all HTTP
|
||||
// connections. See https://github.com/ooni/probe/issues/1609.
|
||||
type httpDialerWithReadTimeout struct {
|
||||
Dialer
|
||||
model.Dialer
|
||||
}
|
||||
|
||||
// DialContext implements Dialer.DialContext.
|
||||
@@ -184,7 +176,7 @@ func (d *httpDialerWithReadTimeout) DialContext(
|
||||
// httpTLSDialerWithReadTimeout enforces a read timeout for all HTTP
|
||||
// connections. See https://github.com/ooni/probe/issues/1609.
|
||||
type httpTLSDialerWithReadTimeout struct {
|
||||
TLSDialer
|
||||
model.TLSDialer
|
||||
}
|
||||
|
||||
// ErrNotTLSConn occur when an interface accepts a net.Conn but
|
||||
@@ -259,25 +251,19 @@ func (c *httpTLSConnWithReadTimeout) Read(b []byte) (int, error) {
|
||||
//
|
||||
// This factory and NewHTTPTransport are the recommended
|
||||
// ways of creating a new HTTPTransport.
|
||||
func NewHTTPTransportStdlib(logger Logger) HTTPTransport {
|
||||
func NewHTTPTransportStdlib(logger model.DebugLogger) model.HTTPTransport {
|
||||
dialer := NewDialerWithResolver(logger, NewResolverStdlib(logger))
|
||||
tlsDialer := NewTLSDialer(dialer, NewTLSHandshakerStdlib(logger))
|
||||
return NewHTTPTransport(logger, dialer, tlsDialer)
|
||||
}
|
||||
|
||||
// HTTPClient is an http.Client-like interface.
|
||||
type HTTPClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// WrapHTTPClient wraps an HTTP client to add error wrapping capabilities.
|
||||
func WrapHTTPClient(clnt HTTPClient) HTTPClient {
|
||||
func WrapHTTPClient(clnt model.HTTPClient) model.HTTPClient {
|
||||
return &httpClientErrWrapper{clnt}
|
||||
}
|
||||
|
||||
type httpClientErrWrapper struct {
|
||||
HTTPClient
|
||||
model.HTTPClient
|
||||
}
|
||||
|
||||
func (c *httpClientErrWrapper) Do(req *http.Request) (*http.Response, error) {
|
||||
|
||||
@@ -8,13 +8,14 @@ import (
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// http3Dialer adapts a QUICContextDialer to work with
|
||||
// an http3.RoundTripper. This is necessary because the
|
||||
// http3.RoundTripper does not support DialContext.
|
||||
type http3Dialer struct {
|
||||
QUICDialer
|
||||
model.QUICDialer
|
||||
}
|
||||
|
||||
// dial is like QUICContextDialer.DialContext but without context.
|
||||
@@ -33,10 +34,10 @@ type http3RoundTripper interface {
|
||||
// http3Transport is an HTTPTransport using the http3 protocol.
|
||||
type http3Transport struct {
|
||||
child http3RoundTripper
|
||||
dialer QUICDialer
|
||||
dialer model.QUICDialer
|
||||
}
|
||||
|
||||
var _ HTTPTransport = &http3Transport{}
|
||||
var _ model.HTTPTransport = &http3Transport{}
|
||||
|
||||
// RoundTrip implements HTTPTransport.RoundTrip.
|
||||
func (txp *http3Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
@@ -53,7 +54,7 @@ func (txp *http3Transport) CloseIdleConnections() {
|
||||
// dialer argument MUST NOT be nil. If the tlsConfig argument is nil,
|
||||
// then the code will use the default TLS configuration.
|
||||
func NewHTTP3Transport(
|
||||
logger Logger, dialer QUICDialer, tlsConfig *tls.Config) HTTPTransport {
|
||||
logger model.DebugLogger, dialer model.QUICDialer, tlsConfig *tls.Config) model.HTTPTransport {
|
||||
return &httpTransportLogger{
|
||||
HTTPTransport: &http3Transport{
|
||||
child: &http3.RoundTripper{
|
||||
|
||||
@@ -10,7 +10,8 @@ import (
|
||||
"github.com/apex/log"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/lucas-clemente/quic-go/http3"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
nlmocks "github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
)
|
||||
|
||||
func TestHTTP3Dialer(t *testing.T) {
|
||||
@@ -40,7 +41,7 @@ func TestHTTP3Transport(t *testing.T) {
|
||||
calledDialer bool
|
||||
)
|
||||
txp := &http3Transport{
|
||||
child: &mocks.HTTP3RoundTripper{
|
||||
child: &nlmocks.HTTP3RoundTripper{
|
||||
MockClose: func() error {
|
||||
calledHTTP3 = true
|
||||
return nil
|
||||
@@ -61,7 +62,7 @@ func TestHTTP3Transport(t *testing.T) {
|
||||
t.Run("RoundTrip", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
txp := &http3Transport{
|
||||
child: &mocks.HTTP3RoundTripper{
|
||||
child: &nlmocks.HTTP3RoundTripper{
|
||||
MockRoundTrip: func(req *http.Request) (*http.Response, error) {
|
||||
return nil, expected
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/apex/log"
|
||||
oohttp "github.com/ooni/oohttp"
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestHTTPTransportErrWrapper(t *testing.T) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/filtering"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quictesting"
|
||||
@@ -275,7 +276,7 @@ func TestMeasureWithTLSHandshaker(t *testing.T) {
|
||||
return d.DialContext(ctx, "tcp", address)
|
||||
}
|
||||
|
||||
successFlow := func(th netxlite.TLSHandshaker) error {
|
||||
successFlow := func(th model.TLSHandshaker) error {
|
||||
ctx := context.Background()
|
||||
conn, err := dial(ctx, "8.8.4.4:443")
|
||||
if err != nil {
|
||||
@@ -295,7 +296,7 @@ func TestMeasureWithTLSHandshaker(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
connectionResetFlow := func(th netxlite.TLSHandshaker) error {
|
||||
connectionResetFlow := func(th model.TLSHandshaker) error {
|
||||
tlsProxy := &filtering.TLSProxy{
|
||||
OnIncomingSNI: func(sni string) filtering.TLSAction {
|
||||
return filtering.TLSActionReset
|
||||
@@ -330,7 +331,7 @@ func TestMeasureWithTLSHandshaker(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
timeoutFlow := func(th netxlite.TLSHandshaker) error {
|
||||
timeoutFlow := func(th model.TLSHandshaker) error {
|
||||
tlsProxy := &filtering.TLSProxy{
|
||||
OnIncomingSNI: func(sni string) filtering.TLSAction {
|
||||
return filtering.TLSActionTimeout
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestReadAllContext(t *testing.T) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// These vars export internal names to legacy ooni/probe-cli code.
|
||||
@@ -51,7 +52,7 @@ type ResolverLegacy interface {
|
||||
|
||||
// NewResolverLegacyAdapter adapts a ResolverLegacy to
|
||||
// become compatible with the Resolver definition.
|
||||
func NewResolverLegacyAdapter(reso ResolverLegacy) Resolver {
|
||||
func NewResolverLegacyAdapter(reso ResolverLegacy) model.Resolver {
|
||||
return &ResolverLegacyAdapter{reso}
|
||||
}
|
||||
|
||||
@@ -60,7 +61,7 @@ type ResolverLegacyAdapter struct {
|
||||
ResolverLegacy
|
||||
}
|
||||
|
||||
var _ Resolver = &ResolverLegacyAdapter{}
|
||||
var _ model.Resolver = &ResolverLegacyAdapter{}
|
||||
|
||||
type resolverLegacyNetworker interface {
|
||||
Network() string
|
||||
@@ -99,7 +100,7 @@ func (r *ResolverLegacyAdapter) CloseIdleConnections() {
|
||||
|
||||
// LookupHTTPS always returns ErrDNSNoTransport.
|
||||
func (r *ResolverLegacyAdapter) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, ErrNoDNSTransport
|
||||
}
|
||||
|
||||
@@ -118,7 +119,7 @@ type DialerLegacy interface {
|
||||
// become compatible with the Dialer definition.
|
||||
//
|
||||
// Deprecated: do not use this function in new code.
|
||||
func NewDialerLegacyAdapter(d DialerLegacy) Dialer {
|
||||
func NewDialerLegacyAdapter(d DialerLegacy) model.Dialer {
|
||||
return &DialerLegacyAdapter{d}
|
||||
}
|
||||
|
||||
@@ -130,7 +131,7 @@ type DialerLegacyAdapter struct {
|
||||
DialerLegacy
|
||||
}
|
||||
|
||||
var _ Dialer = &DialerLegacyAdapter{}
|
||||
var _ model.Dialer = &DialerLegacyAdapter{}
|
||||
|
||||
type dialerLegacyIdleConnectionsCloser interface {
|
||||
CloseIdleConnections()
|
||||
@@ -159,7 +160,7 @@ type QUICContextDialer interface {
|
||||
|
||||
// NewQUICDialerFromContextDialerAdapter creates a new
|
||||
// QUICDialer from a QUICContextDialer.
|
||||
func NewQUICDialerFromContextDialerAdapter(d QUICContextDialer) QUICDialer {
|
||||
func NewQUICDialerFromContextDialerAdapter(d QUICContextDialer) model.QUICDialer {
|
||||
return &QUICContextDialerAdapter{d}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
nlmocks "github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
)
|
||||
|
||||
func TestResolverLegacyAdapter(t *testing.T) {
|
||||
@@ -93,7 +94,7 @@ func TestQUICContextDialerAdapter(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("with incompatible type", func(t *testing.T) {
|
||||
d := NewQUICDialerFromContextDialerAdapter(&mocks.QUICContextDialer{})
|
||||
d := NewQUICDialerFromContextDialerAdapter(&nlmocks.QUICContextDialer{})
|
||||
d.CloseIdleConnections() // does not crash
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package netxlite
|
||||
|
||||
// Logger is the interface we expect from a logger.
|
||||
type Logger interface {
|
||||
// Debugf formats and emits a debug message.
|
||||
Debugf(format string, v ...interface{})
|
||||
|
||||
// Debug emits a debug message.
|
||||
Debug(msg string)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Dialer is a mockable Dialer.
|
||||
type Dialer struct {
|
||||
MockDialContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||
MockCloseIdleConnections func()
|
||||
}
|
||||
|
||||
// DialContext calls MockDialContext.
|
||||
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return d.MockDialContext(ctx, network, address)
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (d *Dialer) CloseIdleConnections() {
|
||||
d.MockCloseIdleConnections()
|
||||
}
|
||||
|
||||
// Conn is a mockable net.Conn.
|
||||
type Conn struct {
|
||||
MockRead func(b []byte) (int, error)
|
||||
MockWrite func(b []byte) (int, error)
|
||||
MockClose func() error
|
||||
MockLocalAddr func() net.Addr
|
||||
MockRemoteAddr func() net.Addr
|
||||
MockSetDeadline func(t time.Time) error
|
||||
MockSetReadDeadline func(t time.Time) error
|
||||
MockSetWriteDeadline func(t time.Time) error
|
||||
}
|
||||
|
||||
// Read calls MockRead.
|
||||
func (c *Conn) Read(b []byte) (int, error) {
|
||||
return c.MockRead(b)
|
||||
}
|
||||
|
||||
// Write calls MockWrite.
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
return c.MockWrite(b)
|
||||
}
|
||||
|
||||
// Close calls MockClose.
|
||||
func (c *Conn) Close() error {
|
||||
return c.MockClose()
|
||||
}
|
||||
|
||||
// LocalAddr calls MockLocalAddr.
|
||||
func (c *Conn) LocalAddr() net.Addr {
|
||||
return c.MockLocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr calls MockRemoteAddr.
|
||||
func (c *Conn) RemoteAddr() net.Addr {
|
||||
return c.MockRemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline calls MockSetDeadline.
|
||||
func (c *Conn) SetDeadline(t time.Time) error {
|
||||
return c.MockSetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline calls MockSetReadDeadline.
|
||||
func (c *Conn) SetReadDeadline(t time.Time) error {
|
||||
return c.MockSetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline calls MockSetWriteDeadline.
|
||||
func (c *Conn) SetWriteDeadline(t time.Time) error {
|
||||
return c.MockSetWriteDeadline(t)
|
||||
}
|
||||
|
||||
var _ net.Conn = &Conn{}
|
||||
@@ -1,161 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestDialer(t *testing.T) {
|
||||
t.Run("DialContext", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
d := Dialer{
|
||||
MockDialContext: func(ctx context.Context, network string, address string) (net.Conn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
conn, err := d.DialContext(ctx, "tcp", "8.8.8.8:53")
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if conn != nil {
|
||||
t.Fatal("expected nil conn")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
var called bool
|
||||
d := &Dialer{
|
||||
MockCloseIdleConnections: func() {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
d.CloseIdleConnections()
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestConn(t *testing.T) {
|
||||
t.Run("Read", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockRead: func(b []byte) (int, error) {
|
||||
return 0, expected
|
||||
},
|
||||
}
|
||||
count, err := c.Read(make([]byte, 128))
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatal("expected 0 bytes")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Write", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockWrite: func(b []byte) (int, error) {
|
||||
return 0, expected
|
||||
},
|
||||
}
|
||||
count, err := c.Write(make([]byte, 128))
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatal("expected 0 bytes")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Close", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockClose: func() error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.Close()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LocalAddr", func(t *testing.T) {
|
||||
expected := &net.TCPAddr{
|
||||
IP: net.IPv6loopback,
|
||||
Port: 1234,
|
||||
}
|
||||
c := &Conn{
|
||||
MockLocalAddr: func() net.Addr {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
out := c.LocalAddr()
|
||||
if diff := cmp.Diff(expected, out); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RemoteAddr", func(t *testing.T) {
|
||||
expected := &net.TCPAddr{
|
||||
IP: net.IPv6loopback,
|
||||
Port: 1234,
|
||||
}
|
||||
c := &Conn{
|
||||
MockRemoteAddr: func() net.Addr {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
out := c.RemoteAddr()
|
||||
if diff := cmp.Diff(expected, out); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockSetDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetReadDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockSetReadDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetReadDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetWriteDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &Conn{
|
||||
MockSetWriteDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetWriteDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "github.com/ooni/probe-cli/v3/internal/netxlite/dnsx"
|
||||
|
||||
// HTTPSSvc is the result of HTTPS queries.
|
||||
type HTTPSSvc = dnsx.HTTPSSvc
|
||||
|
||||
// DNSDecoder allows mocking dnsx.DNSDecoder.
|
||||
type DNSDecoder struct {
|
||||
MockDecodeLookupHost func(qtype uint16, reply []byte) ([]string, error)
|
||||
|
||||
MockDecodeHTTPS func(reply []byte) (*HTTPSSvc, error)
|
||||
}
|
||||
|
||||
// DecodeLookupHost calls MockDecodeLookupHost.
|
||||
func (e *DNSDecoder) DecodeLookupHost(qtype uint16, reply []byte) ([]string, error) {
|
||||
return e.MockDecodeLookupHost(qtype, reply)
|
||||
}
|
||||
|
||||
// DecodeHTTPS calls MockDecodeHTTPS.
|
||||
func (e *DNSDecoder) DecodeHTTPS(reply []byte) (*HTTPSSvc, error) {
|
||||
return e.MockDecodeHTTPS(reply)
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestDNSDecoder(t *testing.T) {
|
||||
t.Run("DecodeLookupHost", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
e := &DNSDecoder{
|
||||
MockDecodeLookupHost: func(qtype uint16, reply []byte) ([]string, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
out, err := e.DecodeLookupHost(dns.TypeA, make([]byte, 17))
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
if out != nil {
|
||||
t.Fatal("unexpected out")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DecodeHTTPS", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
e := &DNSDecoder{
|
||||
MockDecodeHTTPS: func(reply []byte) (*HTTPSSvc, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
out, err := e.DecodeHTTPS(make([]byte, 17))
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
if out != nil {
|
||||
t.Fatal("unexpected out")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package mocks
|
||||
|
||||
// DNSEncoder allows mocking dnsx.DNSEncoder.
|
||||
type DNSEncoder struct {
|
||||
MockEncode func(domain string, qtype uint16, padding bool) ([]byte, error)
|
||||
}
|
||||
|
||||
// Encode calls MockEncode.
|
||||
func (e *DNSEncoder) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
|
||||
return e.MockEncode(domain, qtype, padding)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestDNSEncoder(t *testing.T) {
|
||||
t.Run("Encode", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
e := &DNSEncoder{
|
||||
MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
out, err := e.Encode("dns.google", dns.TypeA, true)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
if out != nil {
|
||||
t.Fatal("unexpected out")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "context"
|
||||
|
||||
// DNSTransport allows mocking dnsx.DNSTransport.
|
||||
type DNSTransport struct {
|
||||
MockRoundTrip func(ctx context.Context, query []byte) (reply []byte, err error)
|
||||
|
||||
MockRequiresPadding func() bool
|
||||
|
||||
MockNetwork func() string
|
||||
|
||||
MockAddress func() string
|
||||
|
||||
MockCloseIdleConnections func()
|
||||
}
|
||||
|
||||
// RoundTrip calls MockRoundTrip.
|
||||
func (txp *DNSTransport) RoundTrip(ctx context.Context, query []byte) (reply []byte, err error) {
|
||||
return txp.MockRoundTrip(ctx, query)
|
||||
}
|
||||
|
||||
// RequiresPadding calls MockRequiresPadding.
|
||||
func (txp *DNSTransport) RequiresPadding() bool {
|
||||
return txp.MockRequiresPadding()
|
||||
}
|
||||
|
||||
// Network calls MockNetwork.
|
||||
func (txp *DNSTransport) Network() string {
|
||||
return txp.MockNetwork()
|
||||
}
|
||||
|
||||
// Address calls MockAddress.
|
||||
func (txp *DNSTransport) Address() string {
|
||||
return txp.MockAddress()
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (txp *DNSTransport) CloseIdleConnections() {
|
||||
txp.MockCloseIdleConnections()
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
)
|
||||
|
||||
func TestDNSTransport(t *testing.T) {
|
||||
t.Run("RoundTrip", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
txp := &DNSTransport{
|
||||
MockRoundTrip: func(ctx context.Context, query []byte) ([]byte, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
resp, err := txp.RoundTrip(context.Background(), make([]byte, 16))
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatal("expected nil response here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RequiresPadding", func(t *testing.T) {
|
||||
txp := &DNSTransport{
|
||||
MockRequiresPadding: func() bool {
|
||||
return true
|
||||
},
|
||||
}
|
||||
if txp.RequiresPadding() != true {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Network", func(t *testing.T) {
|
||||
txp := &DNSTransport{
|
||||
MockNetwork: func() string {
|
||||
return "antani"
|
||||
},
|
||||
}
|
||||
if txp.Network() != "antani" {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Address", func(t *testing.T) {
|
||||
txp := &DNSTransport{
|
||||
MockAddress: func() string {
|
||||
return "mascetti"
|
||||
},
|
||||
}
|
||||
if txp.Address() != "mascetti" {
|
||||
t.Fatal("unexpected result")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
called := &atomicx.Int64{}
|
||||
txp := &DNSTransport{
|
||||
MockCloseIdleConnections: func() {
|
||||
called.Add(1)
|
||||
},
|
||||
}
|
||||
txp.CloseIdleConnections()
|
||||
if called.Load() != 1 {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
// Package mocks contains mocks for netx types.
|
||||
package mocks
|
||||
@@ -1,36 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPTransport mocks netxlite.HTTPTransport.
|
||||
type HTTPTransport struct {
|
||||
MockRoundTrip func(req *http.Request) (*http.Response, error)
|
||||
MockCloseIdleConnections func()
|
||||
}
|
||||
|
||||
// RoundTrip calls MockRoundTrip.
|
||||
func (txp *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return txp.MockRoundTrip(req)
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (txp *HTTPTransport) CloseIdleConnections() {
|
||||
txp.MockCloseIdleConnections()
|
||||
}
|
||||
|
||||
// HTTPClient allows mocking an http.Client.
|
||||
type HTTPClient struct {
|
||||
MockDo func(req *http.Request) (*http.Response, error)
|
||||
|
||||
MockCloseIdleConnections func()
|
||||
}
|
||||
|
||||
// Do calls MockDo.
|
||||
func (txp *HTTPClient) Do(req *http.Request) (*http.Response, error) {
|
||||
return txp.MockDo(req)
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (txp *HTTPClient) CloseIdleConnections() {
|
||||
txp.MockCloseIdleConnections()
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
)
|
||||
|
||||
func TestHTTPTransport(t *testing.T) {
|
||||
t.Run("RoundTrip", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
txp := &HTTPTransport{
|
||||
MockRoundTrip: func(req *http.Request) (*http.Response, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
resp, err := txp.RoundTrip(&http.Request{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatal("expected nil response here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
called := &atomicx.Int64{}
|
||||
txp := &HTTPTransport{
|
||||
MockCloseIdleConnections: func() {
|
||||
called.Add(1)
|
||||
},
|
||||
}
|
||||
txp.CloseIdleConnections()
|
||||
if called.Load() != 1 {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestHTTPClient(t *testing.T) {
|
||||
t.Run("Do", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
clnt := &HTTPClient{
|
||||
MockDo: func(req *http.Request) (*http.Response, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
resp, err := clnt.Do(&http.Request{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatal("expected nil response here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
called := &atomicx.Int64{}
|
||||
clnt := &HTTPClient{
|
||||
MockCloseIdleConnections: func() {
|
||||
called.Add(1)
|
||||
},
|
||||
}
|
||||
clnt.CloseIdleConnections()
|
||||
if called.Load() != 1 {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "net"
|
||||
|
||||
// Listener allows mocking a net.Listener.
|
||||
type Listener struct {
|
||||
// Accept allows mocking Accept.
|
||||
MockAccept func() (net.Conn, error)
|
||||
|
||||
// Close allows mocking Close.
|
||||
MockClose func() error
|
||||
|
||||
// Addr allows mocking Addr.
|
||||
MockAddr func() net.Addr
|
||||
}
|
||||
|
||||
var _ net.Listener = &Listener{}
|
||||
|
||||
// Accept implements net.Listener.Accept
|
||||
func (li *Listener) Accept() (net.Conn, error) {
|
||||
return li.MockAccept()
|
||||
}
|
||||
|
||||
// Close implements net.Listener.Closer.
|
||||
func (li *Listener) Close() error {
|
||||
return li.MockClose()
|
||||
}
|
||||
|
||||
// Addr implements net.Listener.Addr
|
||||
func (li *Listener) Addr() net.Addr {
|
||||
return li.MockAddr()
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
|
||||
func TestListener(t *testing.T) {
|
||||
t.Run("Accept", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
li := &Listener{
|
||||
MockAccept: func() (net.Conn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
conn, err := li.Accept()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
if conn != nil {
|
||||
t.Fatal("expected nil conn")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Close", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
li := &Listener{
|
||||
MockClose: func() error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := li.Close()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected err", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Addr", func(t *testing.T) {
|
||||
addr := &net.TCPAddr{
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Port: 1234,
|
||||
}
|
||||
li := &Listener{
|
||||
MockAddr: func() net.Addr {
|
||||
return addr
|
||||
},
|
||||
}
|
||||
outAddr := li.Addr()
|
||||
if diff := cmp.Diff(addr, outAddr); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package mocks
|
||||
|
||||
// Logger allows mocking a logger.
|
||||
type Logger struct {
|
||||
MockDebug func(message string)
|
||||
|
||||
MockDebugf func(format string, v ...interface{})
|
||||
}
|
||||
|
||||
// Debug calls MockDebug.
|
||||
func (lo *Logger) Debug(message string) {
|
||||
lo.MockDebug(message)
|
||||
}
|
||||
|
||||
// Debugf calls MockDebugf.
|
||||
func (lo *Logger) Debugf(format string, v ...interface{}) {
|
||||
lo.MockDebugf(format, v...)
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestLogger(t *testing.T) {
|
||||
t.Run("Debug", func(t *testing.T) {
|
||||
var called bool
|
||||
lo := &Logger{
|
||||
MockDebug: func(message string) {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
lo.Debug("antani")
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Debugf", func(t *testing.T) {
|
||||
var called bool
|
||||
lo := &Logger{
|
||||
MockDebugf: func(message string, v ...interface{}) {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
lo.Debugf("antani", 1, 2, 3, 4)
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
)
|
||||
|
||||
// QUICListener is a mockable netxlite.QUICListener.
|
||||
type QUICListener struct {
|
||||
MockListen func(addr *net.UDPAddr) (quicx.UDPLikeConn, error)
|
||||
}
|
||||
|
||||
// Listen calls MockListen.
|
||||
func (ql *QUICListener) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
return ql.MockListen(addr)
|
||||
}
|
||||
|
||||
// QUICDialer is a mockable netxlite.QUICDialer.
|
||||
type QUICDialer struct {
|
||||
// MockDialContext allows mocking DialContext.
|
||||
MockDialContext func(ctx context.Context, network, address string,
|
||||
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
|
||||
|
||||
// MockCloseIdleConnections allows mocking CloseIdleConnections.
|
||||
MockCloseIdleConnections func()
|
||||
}
|
||||
|
||||
// DialContext calls MockDialContext.
|
||||
func (qcd *QUICDialer) DialContext(ctx context.Context, network, address string,
|
||||
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
|
||||
return qcd.MockDialContext(ctx, network, address, tlsConfig, quicConfig)
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (qcd *QUICDialer) CloseIdleConnections() {
|
||||
qcd.MockCloseIdleConnections()
|
||||
}
|
||||
|
||||
// QUICEarlySession is a mockable quic.EarlySession.
|
||||
type QUICEarlySession struct {
|
||||
MockAcceptStream func(context.Context) (quic.Stream, error)
|
||||
MockAcceptUniStream func(context.Context) (quic.ReceiveStream, error)
|
||||
MockOpenStream func() (quic.Stream, error)
|
||||
MockOpenStreamSync func(ctx context.Context) (quic.Stream, error)
|
||||
MockOpenUniStream func() (quic.SendStream, error)
|
||||
MockOpenUniStreamSync func(ctx context.Context) (quic.SendStream, error)
|
||||
MockLocalAddr func() net.Addr
|
||||
MockRemoteAddr func() net.Addr
|
||||
MockCloseWithError func(code quic.ApplicationErrorCode, reason string) error
|
||||
MockContext func() context.Context
|
||||
MockConnectionState func() quic.ConnectionState
|
||||
MockHandshakeComplete func() context.Context
|
||||
MockNextSession func() quic.Session
|
||||
MockSendMessage func(b []byte) error
|
||||
MockReceiveMessage func() ([]byte, error)
|
||||
}
|
||||
|
||||
var _ quic.EarlySession = &QUICEarlySession{}
|
||||
|
||||
// AcceptStream calls MockAcceptStream.
|
||||
func (s *QUICEarlySession) AcceptStream(ctx context.Context) (quic.Stream, error) {
|
||||
return s.MockAcceptStream(ctx)
|
||||
}
|
||||
|
||||
// AcceptUniStream calls MockAcceptUniStream.
|
||||
func (s *QUICEarlySession) AcceptUniStream(ctx context.Context) (quic.ReceiveStream, error) {
|
||||
return s.MockAcceptUniStream(ctx)
|
||||
}
|
||||
|
||||
// OpenStream calls MockOpenStream.
|
||||
func (s *QUICEarlySession) OpenStream() (quic.Stream, error) {
|
||||
return s.MockOpenStream()
|
||||
}
|
||||
|
||||
// OpenStreamSync calls MockOpenStreamSync.
|
||||
func (s *QUICEarlySession) OpenStreamSync(ctx context.Context) (quic.Stream, error) {
|
||||
return s.MockOpenStreamSync(ctx)
|
||||
}
|
||||
|
||||
// OpenUniStream calls MockOpenUniStream.
|
||||
func (s *QUICEarlySession) OpenUniStream() (quic.SendStream, error) {
|
||||
return s.MockOpenUniStream()
|
||||
}
|
||||
|
||||
// OpenUniStreamSync calls MockOpenUniStreamSync.
|
||||
func (s *QUICEarlySession) OpenUniStreamSync(ctx context.Context) (quic.SendStream, error) {
|
||||
return s.MockOpenUniStreamSync(ctx)
|
||||
}
|
||||
|
||||
// LocalAddr class MockLocalAddr.
|
||||
func (c *QUICEarlySession) LocalAddr() net.Addr {
|
||||
return c.MockLocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr calls MockRemoteAddr.
|
||||
func (c *QUICEarlySession) RemoteAddr() net.Addr {
|
||||
return c.MockRemoteAddr()
|
||||
}
|
||||
|
||||
// CloseWithError calls MockCloseWithError.
|
||||
func (c *QUICEarlySession) CloseWithError(
|
||||
code quic.ApplicationErrorCode, reason string) error {
|
||||
return c.MockCloseWithError(code, reason)
|
||||
}
|
||||
|
||||
// Context calls MockContext.
|
||||
func (s *QUICEarlySession) Context() context.Context {
|
||||
return s.MockContext()
|
||||
}
|
||||
|
||||
// ConnectionState calls MockConnectionState.
|
||||
func (s *QUICEarlySession) ConnectionState() quic.ConnectionState {
|
||||
return s.MockConnectionState()
|
||||
}
|
||||
|
||||
// HandshakeComplete calls MockHandshakeComplete.
|
||||
func (s *QUICEarlySession) HandshakeComplete() context.Context {
|
||||
return s.MockHandshakeComplete()
|
||||
}
|
||||
|
||||
// NextSession calls MockNextSession.
|
||||
func (s *QUICEarlySession) NextSession() quic.Session {
|
||||
return s.MockNextSession()
|
||||
}
|
||||
|
||||
// SendMessage calls MockSendMessage.
|
||||
func (s *QUICEarlySession) SendMessage(b []byte) error {
|
||||
return s.MockSendMessage(b)
|
||||
}
|
||||
|
||||
// ReceiveMessage calls MockReceiveMessage.
|
||||
func (s *QUICEarlySession) ReceiveMessage() ([]byte, error) {
|
||||
return s.MockReceiveMessage()
|
||||
}
|
||||
|
||||
// QUICUDPLikeConn is an UDP conn used by QUIC.
|
||||
type QUICUDPLikeConn struct {
|
||||
MockWriteTo func(p []byte, addr net.Addr) (int, error)
|
||||
MockClose func() error
|
||||
MockLocalAddr func() net.Addr
|
||||
MockRemoteAddr func() net.Addr
|
||||
MockSetDeadline func(t time.Time) error
|
||||
MockSetReadDeadline func(t time.Time) error
|
||||
MockSetWriteDeadline func(t time.Time) error
|
||||
MockReadFrom func(p []byte) (n int, addr net.Addr, err error)
|
||||
MockSyscallConn func() (syscall.RawConn, error)
|
||||
MockSetReadBuffer func(n int) error
|
||||
}
|
||||
|
||||
var _ quicx.UDPLikeConn = &QUICUDPLikeConn{}
|
||||
|
||||
// WriteTo calls MockWriteTo.
|
||||
func (c *QUICUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||
return c.MockWriteTo(p, addr)
|
||||
}
|
||||
|
||||
// Close calls MockClose.
|
||||
func (c *QUICUDPLikeConn) Close() error {
|
||||
return c.MockClose()
|
||||
}
|
||||
|
||||
// LocalAddr calls MockLocalAddr.
|
||||
func (c *QUICUDPLikeConn) LocalAddr() net.Addr {
|
||||
return c.MockLocalAddr()
|
||||
}
|
||||
|
||||
// RemoteAddr calls MockRemoteAddr.
|
||||
func (c *QUICUDPLikeConn) RemoteAddr() net.Addr {
|
||||
return c.MockRemoteAddr()
|
||||
}
|
||||
|
||||
// SetDeadline calls MockSetDeadline.
|
||||
func (c *QUICUDPLikeConn) SetDeadline(t time.Time) error {
|
||||
return c.MockSetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline calls MockSetReadDeadline.
|
||||
func (c *QUICUDPLikeConn) SetReadDeadline(t time.Time) error {
|
||||
return c.MockSetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline calls MockSetWriteDeadline.
|
||||
func (c *QUICUDPLikeConn) SetWriteDeadline(t time.Time) error {
|
||||
return c.MockSetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// ReadFrom calls MockReadFrom.
|
||||
func (c *QUICUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return c.MockReadFrom(b)
|
||||
}
|
||||
|
||||
// SyscallConn calls MockSyscallConn.
|
||||
func (c *QUICUDPLikeConn) SyscallConn() (syscall.RawConn, error) {
|
||||
return c.MockSyscallConn()
|
||||
}
|
||||
|
||||
// SetReadBuffer calls MockSetReadBuffer.
|
||||
func (c *QUICUDPLikeConn) SetReadBuffer(n int) error {
|
||||
return c.MockSetReadBuffer(n)
|
||||
}
|
||||
@@ -1,443 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"reflect"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
)
|
||||
|
||||
func TestQUICListenerListen(t *testing.T) {
|
||||
t.Run("Listen", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
ql := &QUICListener{
|
||||
MockListen: func(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
pconn, err := ql.Listen(&net.UDPAddr{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", expected)
|
||||
}
|
||||
if pconn != nil {
|
||||
t.Fatal("expected nil conn here")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestQUICDialer(t *testing.T) {
|
||||
t.Run("DialContext", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
qcd := &QUICDialer{
|
||||
MockDialContext: func(ctx context.Context, network string, address string, tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
tlsConfig := &tls.Config{}
|
||||
quicConfig := &quic.Config{}
|
||||
sess, err := qcd.DialContext(ctx, "udp", "dns.google:443", tlsConfig, quicConfig)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected")
|
||||
}
|
||||
if sess != nil {
|
||||
t.Fatal("expected nil session")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
var called bool
|
||||
qcd := &QUICDialer{
|
||||
MockCloseIdleConnections: func() {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
qcd.CloseIdleConnections()
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestQUICEarlySession(t *testing.T) {
|
||||
t.Run("AcceptStream", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockAcceptStream: func(ctx context.Context) (quic.Stream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
stream, err := sess.AcceptStream(ctx)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("AcceptUniStream", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockAcceptUniStream: func(ctx context.Context) (quic.ReceiveStream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
stream, err := sess.AcceptUniStream(ctx)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OpenStream", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockOpenStream: func() (quic.Stream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
stream, err := sess.OpenStream()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OpenStreamSync", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockOpenStreamSync: func(ctx context.Context) (quic.Stream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
stream, err := sess.OpenStreamSync(ctx)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OpenUniStream", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockOpenUniStream: func() (quic.SendStream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
stream, err := sess.OpenUniStream()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("OpenUniStreamSync", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockOpenUniStreamSync: func(ctx context.Context) (quic.SendStream, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
stream, err := sess.OpenUniStreamSync(ctx)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if stream != nil {
|
||||
t.Fatal("expected nil stream here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LocalAddr", func(t *testing.T) {
|
||||
sess := &QUICEarlySession{
|
||||
MockLocalAddr: func() net.Addr {
|
||||
return &net.UDPAddr{}
|
||||
},
|
||||
}
|
||||
addr := sess.LocalAddr()
|
||||
if !reflect.ValueOf(addr).Elem().IsZero() {
|
||||
t.Fatal("expected a zero address here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RemoteAddr", func(t *testing.T) {
|
||||
sess := &QUICEarlySession{
|
||||
MockRemoteAddr: func() net.Addr {
|
||||
return &net.UDPAddr{}
|
||||
},
|
||||
}
|
||||
addr := sess.RemoteAddr()
|
||||
if !reflect.ValueOf(addr).Elem().IsZero() {
|
||||
t.Fatal("expected a zero address here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseWithError", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockCloseWithError: func(
|
||||
code quic.ApplicationErrorCode, reason string) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := sess.CloseWithError(0, "")
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Context", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sess := &QUICEarlySession{
|
||||
MockContext: func() context.Context {
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
out := sess.Context()
|
||||
if !reflect.DeepEqual(ctx, out) {
|
||||
t.Fatal("not the context we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ConnectionState", func(t *testing.T) {
|
||||
state := quic.ConnectionState{SupportsDatagrams: true}
|
||||
sess := &QUICEarlySession{
|
||||
MockConnectionState: func() quic.ConnectionState {
|
||||
return state
|
||||
},
|
||||
}
|
||||
out := sess.ConnectionState()
|
||||
if !reflect.DeepEqual(state, out) {
|
||||
t.Fatal("not the context we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("HandshakeComplete", func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
sess := &QUICEarlySession{
|
||||
MockHandshakeComplete: func() context.Context {
|
||||
return ctx
|
||||
},
|
||||
}
|
||||
out := sess.HandshakeComplete()
|
||||
if !reflect.DeepEqual(ctx, out) {
|
||||
t.Fatal("not the context we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("NextSession", func(t *testing.T) {
|
||||
next := &QUICEarlySession{}
|
||||
sess := &QUICEarlySession{
|
||||
MockNextSession: func() quic.Session {
|
||||
return next
|
||||
},
|
||||
}
|
||||
out := sess.NextSession()
|
||||
if !reflect.DeepEqual(next, out) {
|
||||
t.Fatal("not the context we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SendMessage", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockSendMessage: func(b []byte) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
b := make([]byte, 17)
|
||||
err := sess.SendMessage(b)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ReceiveMessage", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
sess := &QUICEarlySession{
|
||||
MockReceiveMessage: func() ([]byte, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
b, err := sess.ReceiveMessage()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if b != nil {
|
||||
t.Fatal("expected nil buffer here")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestQUICUDPLikeConn(t *testing.T) {
|
||||
t.Run("WriteTo", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
quc := &QUICUDPLikeConn{
|
||||
MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
|
||||
return 0, expected
|
||||
},
|
||||
}
|
||||
pkt := make([]byte, 128)
|
||||
addr := &net.UDPAddr{}
|
||||
cnt, err := quc.WriteTo(pkt, addr)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if cnt != 0 {
|
||||
t.Fatal("expected zero here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ConnClose", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
quc := &QUICUDPLikeConn{
|
||||
MockClose: func() error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := quc.Close()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LocalAddr", func(t *testing.T) {
|
||||
expected := &net.TCPAddr{
|
||||
IP: net.IPv6loopback,
|
||||
Port: 1234,
|
||||
}
|
||||
c := &QUICUDPLikeConn{
|
||||
MockLocalAddr: func() net.Addr {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
out := c.LocalAddr()
|
||||
if diff := cmp.Diff(expected, out); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RemoteAddr", func(t *testing.T) {
|
||||
expected := &net.TCPAddr{
|
||||
IP: net.IPv6loopback,
|
||||
Port: 1234,
|
||||
}
|
||||
c := &QUICUDPLikeConn{
|
||||
MockRemoteAddr: func() net.Addr {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
out := c.RemoteAddr()
|
||||
if diff := cmp.Diff(expected, out); diff != "" {
|
||||
t.Fatal(diff)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &QUICUDPLikeConn{
|
||||
MockSetDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetReadDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &QUICUDPLikeConn{
|
||||
MockSetReadDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetReadDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetWriteDeadline", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &QUICUDPLikeConn{
|
||||
MockSetWriteDeadline: func(t time.Time) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.SetWriteDeadline(time.Time{})
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ConnReadFrom", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
quc := &QUICUDPLikeConn{
|
||||
MockReadFrom: func(b []byte) (int, net.Addr, error) {
|
||||
return 0, nil, expected
|
||||
},
|
||||
}
|
||||
b := make([]byte, 128)
|
||||
n, addr, err := quc.ReadFrom(b)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatal("expected zero here")
|
||||
}
|
||||
if addr != nil {
|
||||
t.Fatal("expected nil here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SyscallConn", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
quc := &QUICUDPLikeConn{
|
||||
MockSyscallConn: func() (syscall.RawConn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
conn, err := quc.SyscallConn()
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if conn != nil {
|
||||
t.Fatal("expected nil here")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("SetReadBuffer", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
quc := &QUICUDPLikeConn{
|
||||
MockSetReadBuffer: func(n int) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := quc.SetReadBuffer(1 << 10)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import "io"
|
||||
|
||||
// Reader allows to mock any io.Reader.
|
||||
type Reader struct {
|
||||
MockRead func(b []byte) (int, error)
|
||||
}
|
||||
|
||||
// MockableReader implements an io.Reader.
|
||||
var _ io.Reader = &Reader{}
|
||||
|
||||
// Read implements io.Reader.Read.
|
||||
func (r *Reader) Read(b []byte) (int, error) {
|
||||
return r.MockRead(b)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
t.Run("Read", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
r := &Reader{
|
||||
MockRead: func(b []byte) (int, error) {
|
||||
return 0, expected
|
||||
},
|
||||
}
|
||||
b := make([]byte, 128)
|
||||
count, err := r.Read(b)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected error", err)
|
||||
}
|
||||
if count != 0 {
|
||||
t.Fatal("unexpected count", count)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// Resolver is a mockable Resolver.
|
||||
type Resolver struct {
|
||||
MockLookupHost func(ctx context.Context, domain string) ([]string, error)
|
||||
MockNetwork func() string
|
||||
MockAddress func() string
|
||||
MockCloseIdleConnections func()
|
||||
MockLookupHTTPS func(ctx context.Context, domain string) (*HTTPSSvc, error)
|
||||
}
|
||||
|
||||
// LookupHost calls MockLookupHost.
|
||||
func (r *Resolver) LookupHost(ctx context.Context, domain string) ([]string, error) {
|
||||
return r.MockLookupHost(ctx, domain)
|
||||
}
|
||||
|
||||
// Address calls MockAddress.
|
||||
func (r *Resolver) Address() string {
|
||||
return r.MockAddress()
|
||||
}
|
||||
|
||||
// Network calls MockNetwork.
|
||||
func (r *Resolver) Network() string {
|
||||
return r.MockNetwork()
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (r *Resolver) CloseIdleConnections() {
|
||||
r.MockCloseIdleConnections()
|
||||
}
|
||||
|
||||
// LookupHTTPS calls MockLookupHTTPS.
|
||||
func (r *Resolver) LookupHTTPS(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
return r.MockLookupHTTPS(ctx, domain)
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestResolver(t *testing.T) {
|
||||
t.Run("LookupHost", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
r := &Resolver{
|
||||
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
addrs, err := r.LookupHost(ctx, "dns.google")
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected error", err)
|
||||
}
|
||||
if addrs != nil {
|
||||
t.Fatal("expected nil addr")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Network", func(t *testing.T) {
|
||||
r := &Resolver{
|
||||
MockNetwork: func() string {
|
||||
return "antani"
|
||||
},
|
||||
}
|
||||
if v := r.Network(); v != "antani" {
|
||||
t.Fatal("unexpected network", v)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Address", func(t *testing.T) {
|
||||
r := &Resolver{
|
||||
MockAddress: func() string {
|
||||
return "1.1.1.1"
|
||||
},
|
||||
}
|
||||
if v := r.Address(); v != "1.1.1.1" {
|
||||
t.Fatal("unexpected address", v)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
var called bool
|
||||
r := &Resolver{
|
||||
MockCloseIdleConnections: func() {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
r.CloseIdleConnections()
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("LookupHTTPS", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
r := &Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
https, err := r.LookupHTTPS(ctx, "dns.google")
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("unexpected error", err)
|
||||
}
|
||||
if https != nil {
|
||||
t.Fatal("expected nil addr")
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
)
|
||||
|
||||
// TLSHandshaker is a mockable TLS handshaker.
|
||||
type TLSHandshaker struct {
|
||||
MockHandshake func(ctx context.Context, conn net.Conn, config *tls.Config) (
|
||||
net.Conn, tls.ConnectionState, error)
|
||||
}
|
||||
|
||||
// Handshake calls MockHandshake.
|
||||
func (th *TLSHandshaker) Handshake(ctx context.Context, conn net.Conn, config *tls.Config) (
|
||||
net.Conn, tls.ConnectionState, error) {
|
||||
return th.MockHandshake(ctx, conn, config)
|
||||
}
|
||||
|
||||
// TLSConn allows to mock netxlite.TLSConn.
|
||||
type TLSConn struct {
|
||||
// Conn is the embedded mockable Conn.
|
||||
Conn
|
||||
|
||||
// MockConnectionState allows to mock the ConnectionState method.
|
||||
MockConnectionState func() tls.ConnectionState
|
||||
|
||||
// MockHandshakeContext allows to mock the HandshakeContext method.
|
||||
MockHandshakeContext func(ctx context.Context) error
|
||||
}
|
||||
|
||||
// ConnectionState calls MockConnectionState.
|
||||
func (c *TLSConn) ConnectionState() tls.ConnectionState {
|
||||
return c.MockConnectionState()
|
||||
}
|
||||
|
||||
// HandshakeContext calls MockHandshakeContext.
|
||||
func (c *TLSConn) HandshakeContext(ctx context.Context) error {
|
||||
return c.MockHandshakeContext(ctx)
|
||||
}
|
||||
|
||||
// TLSDialer allows to mock netxlite.TLSDialer.
|
||||
type TLSDialer struct {
|
||||
// MockCloseIdleConnections allows to mock the CloseIdleConnections method.
|
||||
MockCloseIdleConnections func()
|
||||
|
||||
// MockDialTLSContext allows to mock the DialTLSContext method.
|
||||
MockDialTLSContext func(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// CloseIdleConnections calls MockCloseIdleConnections.
|
||||
func (d *TLSDialer) CloseIdleConnections() {
|
||||
d.MockCloseIdleConnections()
|
||||
}
|
||||
|
||||
// DialTLSContext calls MockDialTLSContext.
|
||||
func (d *TLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return d.MockDialTLSContext(ctx, network, address)
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTLSHandshaker(t *testing.T) {
|
||||
t.Run("Handshake", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
conn := &Conn{}
|
||||
ctx := context.Background()
|
||||
config := &tls.Config{}
|
||||
th := &TLSHandshaker{
|
||||
MockHandshake: func(ctx context.Context, conn net.Conn,
|
||||
config *tls.Config) (net.Conn, tls.ConnectionState, error) {
|
||||
return nil, tls.ConnectionState{}, expected
|
||||
},
|
||||
}
|
||||
tlsConn, connState, err := th.Handshake(ctx, conn, config)
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if !reflect.ValueOf(connState).IsZero() {
|
||||
t.Fatal("expected zero ConnectionState here")
|
||||
}
|
||||
if tlsConn != nil {
|
||||
t.Fatal("expected nil conn here")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTLSConn(t *testing.T) {
|
||||
t.Run("ConnectionState", func(t *testing.T) {
|
||||
state := tls.ConnectionState{Version: tls.VersionTLS12}
|
||||
c := &TLSConn{
|
||||
MockConnectionState: func() tls.ConnectionState {
|
||||
return state
|
||||
},
|
||||
}
|
||||
out := c.ConnectionState()
|
||||
if !reflect.DeepEqual(out, state) {
|
||||
t.Fatal("not the result we expected")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("HandshakeContext", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
c := &TLSConn{
|
||||
MockHandshakeContext: func(ctx context.Context) error {
|
||||
return expected
|
||||
},
|
||||
}
|
||||
err := c.HandshakeContext(context.Background())
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTLSDialer(t *testing.T) {
|
||||
t.Run("CloseIdleConnections", func(t *testing.T) {
|
||||
var called bool
|
||||
td := &TLSDialer{
|
||||
MockCloseIdleConnections: func() {
|
||||
called = true
|
||||
},
|
||||
}
|
||||
td.CloseIdleConnections()
|
||||
if !called {
|
||||
t.Fatal("not called")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DialTLSContext", func(t *testing.T) {
|
||||
expected := errors.New("mocked error")
|
||||
td := &TLSDialer{
|
||||
MockDialTLSContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
}
|
||||
ctx := context.Background()
|
||||
conn, err := td.DialTLSContext(ctx, "", "")
|
||||
if !errors.Is(err, expected) {
|
||||
t.Fatal("not the error we expected", err)
|
||||
}
|
||||
if conn != nil {
|
||||
t.Fatal("expected nil conn here")
|
||||
}
|
||||
})
|
||||
}
|
||||
+24
-55
@@ -9,56 +9,25 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// UDPLikeConn is the kind of UDP socket used by QUIC.
|
||||
type UDPLikeConn = quicx.UDPLikeConn
|
||||
|
||||
// QUICListener listens for QUIC connections.
|
||||
type QUICListener interface {
|
||||
// Listen creates a new listening UDPLikeConn.
|
||||
Listen(addr *net.UDPAddr) (UDPLikeConn, error)
|
||||
}
|
||||
|
||||
// NewQUICListener creates a new QUICListener using the standard
|
||||
// library to create listening UDP sockets.
|
||||
func NewQUICListener() QUICListener {
|
||||
func NewQUICListener() model.QUICListener {
|
||||
return &quicListenerErrWrapper{&quicListenerStdlib{}}
|
||||
}
|
||||
|
||||
// quicListenerStdlib is a QUICListener using the standard library.
|
||||
type quicListenerStdlib struct{}
|
||||
|
||||
var _ QUICListener = &quicListenerStdlib{}
|
||||
var _ model.QUICListener = &quicListenerStdlib{}
|
||||
|
||||
// Listen implements QUICListener.Listen.
|
||||
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (UDPLikeConn, error) {
|
||||
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return TProxy.ListenUDP("udp", addr)
|
||||
}
|
||||
|
||||
// QUICDialer dials QUIC sessions.
|
||||
type QUICDialer interface {
|
||||
// DialContext establishes a new QUIC session using the given
|
||||
// network and address. The tlsConfig and the quicConfig arguments
|
||||
// MUST NOT be nil. Returns either the session or an error.
|
||||
//
|
||||
// Recommended tlsConfig setup:
|
||||
//
|
||||
// - set ServerName to be the SNI;
|
||||
//
|
||||
// - set RootCAs to NewDefaultCertPool();
|
||||
//
|
||||
// - set NextProtos to []string{"h3"}.
|
||||
//
|
||||
// Typically, you want to pass `&quic.Config{}` as quicConfig.
|
||||
DialContext(ctx context.Context, network, address string,
|
||||
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
|
||||
|
||||
// CloseIdleConnections closes idle connections, if any.
|
||||
CloseIdleConnections()
|
||||
}
|
||||
|
||||
// NewQUICDialerWithResolver returns a QUICDialer using the given
|
||||
// QUICListener to create listening connections and the given Resolver
|
||||
// to resolve domain names (if needed).
|
||||
@@ -80,8 +49,8 @@ type QUICDialer interface {
|
||||
// 6. if a dialer wraps a resolver, the dialer will forward
|
||||
// the CloseIdleConnection call to its resolver (which is
|
||||
// instrumental to manage a DoH resolver connections properly).
|
||||
func NewQUICDialerWithResolver(listener QUICListener,
|
||||
logger Logger, resolver Resolver) QUICDialer {
|
||||
func NewQUICDialerWithResolver(listener model.QUICListener,
|
||||
logger model.DebugLogger, resolver model.Resolver) model.QUICDialer {
|
||||
return &quicDialerLogger{
|
||||
Dialer: &quicDialerResolver{
|
||||
Dialer: &quicDialerLogger{
|
||||
@@ -102,14 +71,14 @@ func NewQUICDialerWithResolver(listener QUICListener,
|
||||
// except that there is no configured resolver. So, if you pass in
|
||||
// an address containing a domain name, the dial will fail with
|
||||
// the ErrNoResolver failure.
|
||||
func NewQUICDialerWithoutResolver(listener QUICListener, logger Logger) QUICDialer {
|
||||
func NewQUICDialerWithoutResolver(listener model.QUICListener, logger model.DebugLogger) model.QUICDialer {
|
||||
return NewQUICDialerWithResolver(listener, logger, &nullResolver{})
|
||||
}
|
||||
|
||||
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
||||
type quicDialerQUICGo struct {
|
||||
// QUICListener is the underlying QUICListener to use.
|
||||
QUICListener QUICListener
|
||||
QUICListener model.QUICListener
|
||||
|
||||
// mockDialEarlyContext allows to mock quic.DialEarlyContext.
|
||||
mockDialEarlyContext func(ctx context.Context, pconn net.PacketConn,
|
||||
@@ -117,7 +86,7 @@ type quicDialerQUICGo struct {
|
||||
quicConfig *quic.Config) (quic.EarlySession, error)
|
||||
}
|
||||
|
||||
var _ QUICDialer = &quicDialerQUICGo{}
|
||||
var _ model.QUICDialer = &quicDialerQUICGo{}
|
||||
|
||||
// errInvalidIP indicates that a string is not a valid IP.
|
||||
var errInvalidIP = errors.New("netxlite: invalid IP")
|
||||
@@ -201,7 +170,7 @@ type quicSessionOwnsConn struct {
|
||||
quic.EarlySession
|
||||
|
||||
// conn is the connection we own
|
||||
conn UDPLikeConn
|
||||
conn model.UDPLikeConn
|
||||
}
|
||||
|
||||
// CloseWithError implements quic.EarlySession.CloseWithError.
|
||||
@@ -216,13 +185,13 @@ func (sess *quicSessionOwnsConn) CloseWithError(
|
||||
// to resolve a domain name to IP addrs.
|
||||
type quicDialerResolver struct {
|
||||
// Dialer is the underlying QUICDialer.
|
||||
Dialer QUICDialer
|
||||
Dialer model.QUICDialer
|
||||
|
||||
// Resolver is the underlying Resolver.
|
||||
Resolver Resolver
|
||||
Resolver model.Resolver
|
||||
}
|
||||
|
||||
var _ QUICDialer = &quicDialerResolver{}
|
||||
var _ model.QUICDialer = &quicDialerResolver{}
|
||||
|
||||
// DialContext implements QUICDialer.DialContext. This function
|
||||
// will apply the following TLS defaults:
|
||||
@@ -284,10 +253,10 @@ func (d *quicDialerResolver) CloseIdleConnections() {
|
||||
// quicDialerLogger is a dialer with logging.
|
||||
type quicDialerLogger struct {
|
||||
// Dialer is the underlying QUIC dialer.
|
||||
Dialer QUICDialer
|
||||
Dialer model.QUICDialer
|
||||
|
||||
// Logger is the underlying logger.
|
||||
Logger Logger
|
||||
Logger model.DebugLogger
|
||||
|
||||
// operationSuffix is appended to the operation name.
|
||||
//
|
||||
@@ -298,7 +267,7 @@ type quicDialerLogger struct {
|
||||
operationSuffix string
|
||||
}
|
||||
|
||||
var _ QUICDialer = &quicDialerLogger{}
|
||||
var _ model.QUICDialer = &quicDialerLogger{}
|
||||
|
||||
// DialContext implements QUICContextDialer.DialContext.
|
||||
func (d *quicDialerLogger) DialContext(
|
||||
@@ -321,7 +290,7 @@ func (d *quicDialerLogger) CloseIdleConnections() {
|
||||
}
|
||||
|
||||
// NewSingleUseQUICDialer is like NewSingleUseDialer but for QUIC.
|
||||
func NewSingleUseQUICDialer(sess quic.EarlySession) QUICDialer {
|
||||
func NewSingleUseQUICDialer(sess quic.EarlySession) model.QUICDialer {
|
||||
return &quicDialerSingleUse{sess: sess}
|
||||
}
|
||||
|
||||
@@ -331,7 +300,7 @@ type quicDialerSingleUse struct {
|
||||
sess quic.EarlySession
|
||||
}
|
||||
|
||||
var _ QUICDialer = &quicDialerSingleUse{}
|
||||
var _ model.QUICDialer = &quicDialerSingleUse{}
|
||||
|
||||
// DialContext implements QUICDialer.DialContext.
|
||||
func (s *quicDialerSingleUse) DialContext(
|
||||
@@ -355,13 +324,13 @@ func (s *quicDialerSingleUse) CloseIdleConnections() {
|
||||
// quicListenerErrWrapper is a QUICListener that wraps errors.
|
||||
type quicListenerErrWrapper struct {
|
||||
// QUICListener is the underlying listener.
|
||||
QUICListener
|
||||
model.QUICListener
|
||||
}
|
||||
|
||||
var _ QUICListener = &quicListenerErrWrapper{}
|
||||
var _ model.QUICListener = &quicListenerErrWrapper{}
|
||||
|
||||
// Listen implements QUICListener.Listen.
|
||||
func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (UDPLikeConn, error) {
|
||||
func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
pconn, err := qls.QUICListener.Listen(addr)
|
||||
if err != nil {
|
||||
return nil, NewErrWrapper(ClassifyGenericError, QUICListenOperation, err)
|
||||
@@ -372,10 +341,10 @@ func (qls *quicListenerErrWrapper) Listen(addr *net.UDPAddr) (UDPLikeConn, error
|
||||
// quicErrWrapperUDPLikeConn is a UDPLikeConn that wraps errors.
|
||||
type quicErrWrapperUDPLikeConn struct {
|
||||
// UDPLikeConn is the underlying conn.
|
||||
UDPLikeConn
|
||||
model.UDPLikeConn
|
||||
}
|
||||
|
||||
var _ UDPLikeConn = &quicErrWrapperUDPLikeConn{}
|
||||
var _ model.UDPLikeConn = &quicErrWrapperUDPLikeConn{}
|
||||
|
||||
// WriteTo implements UDPLikeConn.WriteTo.
|
||||
func (c *quicErrWrapperUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
|
||||
@@ -406,7 +375,7 @@ func (c *quicErrWrapperUDPLikeConn) Close() error {
|
||||
|
||||
// quicDialerErrWrapper is a dialer that performs quic err wrapping
|
||||
type quicDialerErrWrapper struct {
|
||||
QUICDialer
|
||||
model.QUICDialer
|
||||
}
|
||||
|
||||
// DialContext implements ContextDialer.DialContext
|
||||
|
||||
@@ -12,7 +12,8 @@ import (
|
||||
"github.com/apex/log"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/lucas-clemente/quic-go"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestNewQUICListener(t *testing.T) {
|
||||
@@ -107,7 +108,7 @@ func TestQUICDialerQUICGo(t *testing.T) {
|
||||
}
|
||||
systemdialer := quicDialerQUICGo{
|
||||
QUICListener: &mocks.QUICListener{
|
||||
MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
|
||||
MockListen: func(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return nil, expected
|
||||
},
|
||||
},
|
||||
@@ -477,7 +478,7 @@ func TestQUICListenerErrWrapper(t *testing.T) {
|
||||
expectedConn := &mocks.QUICUDPLikeConn{}
|
||||
ql := &quicListenerErrWrapper{
|
||||
QUICListener: &mocks.QUICListener{
|
||||
MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
|
||||
MockListen: func(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return expectedConn, nil
|
||||
},
|
||||
},
|
||||
@@ -496,7 +497,7 @@ func TestQUICListenerErrWrapper(t *testing.T) {
|
||||
expectedErr := io.EOF
|
||||
ql := &quicListenerErrWrapper{
|
||||
QUICListener: &mocks.QUICListener{
|
||||
MockListen: func(addr *net.UDPAddr) (UDPLikeConn, error) {
|
||||
MockListen: func(addr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return nil, expectedErr
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// Package quicx contains lucas-clemente/quic-go extensions.
|
||||
package quicx
|
||||
|
||||
import (
|
||||
"net"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// UDPLikeConn is a net.PacketConn with some extra functions
|
||||
// required to convince the QUIC library (lucas-clemente/quic-go)
|
||||
// to inflate the receive buffer of the connection.
|
||||
//
|
||||
// The QUIC library will treat this connection as a "dumb"
|
||||
// net.PacketConn, calling its ReadFrom and WriteTo methods
|
||||
// as opposed to more efficient methods that are available
|
||||
// under Linux and (maybe?) FreeBSD.
|
||||
//
|
||||
// It seems fine to avoid performance optimizations, because
|
||||
// they would complicate the implementation on our side and
|
||||
// our use cases (blocking and heavy throttling) do not seem
|
||||
// to require such optimizations.
|
||||
//
|
||||
// See https://github.com/ooni/probe/issues/1754 for a more
|
||||
// comprehensive discussion of UDPLikeConn.
|
||||
type UDPLikeConn interface {
|
||||
// An UDPLikeConn is a net.PacketConn conn.
|
||||
net.PacketConn
|
||||
|
||||
// SetReadBuffer allows setting the read buffer.
|
||||
SetReadBuffer(bytes int) error
|
||||
|
||||
// SyscallConn returns a conn suitable for calling syscalls,
|
||||
// which is also instrumental to setting the read buffer.
|
||||
SyscallConn() (syscall.RawConn, error)
|
||||
}
|
||||
@@ -7,32 +7,10 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/dnsx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
// HTTPSSvc is the type returned for HTTPS queries.
|
||||
type HTTPSSvc = dnsx.HTTPSSvc
|
||||
|
||||
// Resolver performs domain name resolutions.
|
||||
type Resolver interface {
|
||||
// LookupHost behaves like net.Resolver.LookupHost.
|
||||
LookupHost(ctx context.Context, hostname string) (addrs []string, err error)
|
||||
|
||||
// Network returns the resolver type (e.g., system, dot, doh).
|
||||
Network() string
|
||||
|
||||
// Address returns the resolver address (e.g., 8.8.8.8:53).
|
||||
Address() string
|
||||
|
||||
// CloseIdleConnections closes idle connections, if any.
|
||||
CloseIdleConnections()
|
||||
|
||||
// LookupHTTPS issues an HTTPS query for a domain.
|
||||
LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error)
|
||||
}
|
||||
|
||||
// ErrNoDNSTransport is the error returned when you attempt to perform
|
||||
// a DNS operation that requires a custom DNSTransport (e.g., DNSOverHTTPS)
|
||||
// but you are using the "system" resolver instead.
|
||||
@@ -40,7 +18,7 @@ var ErrNoDNSTransport = errors.New("operation requires a DNS transport")
|
||||
|
||||
// NewResolverStdlib creates a new Resolver by combining WrapResolver
|
||||
// with an internal "system" resolver type.
|
||||
func NewResolverStdlib(logger Logger) Resolver {
|
||||
func NewResolverStdlib(logger model.DebugLogger) model.Resolver {
|
||||
return WrapResolver(logger, &resolverSystem{})
|
||||
}
|
||||
|
||||
@@ -53,7 +31,7 @@ func NewResolverStdlib(logger Logger) Resolver {
|
||||
// - dialer is the dialer to create and connect UDP conns
|
||||
//
|
||||
// - address is the server address (e.g., 1.1.1.1:53)
|
||||
func NewResolverUDP(logger Logger, dialer Dialer, address string) Resolver {
|
||||
func NewResolverUDP(logger model.DebugLogger, dialer model.Dialer, address string) model.Resolver {
|
||||
return WrapResolver(logger, NewSerialResolver(
|
||||
NewDNSOverUDP(dialer, address),
|
||||
))
|
||||
@@ -75,7 +53,7 @@ func NewResolverUDP(logger Logger, dialer Dialer, address string) Resolver {
|
||||
// see https://github.com/ooni/probe/issues/1726).
|
||||
//
|
||||
// This is a low-level factory. Use only if out of alternatives.
|
||||
func WrapResolver(logger Logger, resolver Resolver) Resolver {
|
||||
func WrapResolver(logger model.DebugLogger, resolver model.Resolver) model.Resolver {
|
||||
return &resolverIDNA{
|
||||
Resolver: &resolverLogger{
|
||||
Resolver: &resolverShortCircuitIPAddr{
|
||||
@@ -94,7 +72,7 @@ type resolverSystem struct {
|
||||
testableLookupHost func(ctx context.Context, domain string) ([]string, error)
|
||||
}
|
||||
|
||||
var _ Resolver = &resolverSystem{}
|
||||
var _ model.Resolver = &resolverSystem{}
|
||||
|
||||
func (r *resolverSystem) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||
// This code forces adding a shorter timeout to the domain name
|
||||
@@ -149,17 +127,17 @@ func (r *resolverSystem) CloseIdleConnections() {
|
||||
}
|
||||
|
||||
func (r *resolverSystem) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, ErrNoDNSTransport
|
||||
}
|
||||
|
||||
// resolverLogger is a resolver that emits events
|
||||
type resolverLogger struct {
|
||||
Resolver
|
||||
Logger Logger
|
||||
model.Resolver
|
||||
Logger model.DebugLogger
|
||||
}
|
||||
|
||||
var _ Resolver = &resolverLogger{}
|
||||
var _ model.Resolver = &resolverLogger{}
|
||||
|
||||
func (r *resolverLogger) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||
prefix := fmt.Sprintf("resolve[A,AAAA] %s with %s (%s)", hostname, r.Network(), r.Address())
|
||||
@@ -176,7 +154,7 @@ func (r *resolverLogger) LookupHost(ctx context.Context, hostname string) ([]str
|
||||
}
|
||||
|
||||
func (r *resolverLogger) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
prefix := fmt.Sprintf("resolve[HTTPS] %s with %s (%s)", domain, r.Network(), r.Address())
|
||||
r.Logger.Debugf("%s...", prefix)
|
||||
start := time.Now()
|
||||
@@ -197,7 +175,7 @@ func (r *resolverLogger) LookupHTTPS(
|
||||
//
|
||||
// See RFC3492 for more information.
|
||||
type resolverIDNA struct {
|
||||
Resolver
|
||||
model.Resolver
|
||||
}
|
||||
|
||||
func (r *resolverIDNA) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||
@@ -209,7 +187,7 @@ func (r *resolverIDNA) LookupHost(ctx context.Context, hostname string) ([]strin
|
||||
}
|
||||
|
||||
func (r *resolverIDNA) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
host, err := idna.ToASCII(domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -220,7 +198,7 @@ func (r *resolverIDNA) LookupHTTPS(
|
||||
// resolverShortCircuitIPAddr recognizes when the input hostname is an
|
||||
// IP address and returns it immediately to the caller.
|
||||
type resolverShortCircuitIPAddr struct {
|
||||
Resolver
|
||||
model.Resolver
|
||||
}
|
||||
|
||||
func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||
@@ -256,16 +234,16 @@ func (r *nullResolver) CloseIdleConnections() {
|
||||
}
|
||||
|
||||
func (r *nullResolver) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, ErrNoResolver
|
||||
}
|
||||
|
||||
// resolverErrWrapper is a Resolver that knows about wrapping errors.
|
||||
type resolverErrWrapper struct {
|
||||
Resolver
|
||||
model.Resolver
|
||||
}
|
||||
|
||||
var _ Resolver = &resolverErrWrapper{}
|
||||
var _ model.Resolver = &resolverErrWrapper{}
|
||||
|
||||
func (r *resolverErrWrapper) LookupHost(ctx context.Context, hostname string) ([]string, error) {
|
||||
addrs, err := r.Resolver.LookupHost(ctx, hostname)
|
||||
@@ -276,7 +254,7 @@ func (r *resolverErrWrapper) LookupHost(ctx context.Context, hostname string) ([
|
||||
}
|
||||
|
||||
func (r *resolverErrWrapper) LookupHTTPS(
|
||||
ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
out, err := r.Resolver.LookupHTTPS(ctx, domain)
|
||||
if err != nil {
|
||||
return nil, NewErrWrapper(ClassifyResolverError, ResolveOperation, err)
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestNewResolverSystem(t *testing.T) {
|
||||
@@ -246,14 +247,14 @@ func TestResolverLogger(t *testing.T) {
|
||||
count++
|
||||
},
|
||||
}
|
||||
expected := &HTTPSSvc{
|
||||
expected := &model.HTTPSSvc{
|
||||
ALPN: []string{"h3"},
|
||||
IPv4: []string{"1.1.1.1"},
|
||||
}
|
||||
r := &resolverLogger{
|
||||
Logger: lo,
|
||||
Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return expected, nil
|
||||
},
|
||||
MockNetwork: func() string {
|
||||
@@ -287,7 +288,7 @@ func TestResolverLogger(t *testing.T) {
|
||||
r := &resolverLogger{
|
||||
Logger: lo,
|
||||
Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, expected
|
||||
},
|
||||
MockNetwork: func() string {
|
||||
@@ -357,14 +358,14 @@ func TestResolverIDNA(t *testing.T) {
|
||||
|
||||
t.Run("LookupHTTPS", func(t *testing.T) {
|
||||
t.Run("with valid IDNA in input", func(t *testing.T) {
|
||||
expected := &HTTPSSvc{
|
||||
expected := &model.HTTPSSvc{
|
||||
ALPN: []string{"h3"},
|
||||
IPv4: []string{"1.1.1.1"},
|
||||
IPv6: []string{},
|
||||
}
|
||||
r := &resolverIDNA{
|
||||
Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
if domain != "xn--d1acpjx3f.xn--p1ai" {
|
||||
return nil, errors.New("passed invalid domain")
|
||||
}
|
||||
@@ -384,7 +385,7 @@ func TestResolverIDNA(t *testing.T) {
|
||||
|
||||
t.Run("with invalid punycode", func(t *testing.T) {
|
||||
r := &resolverIDNA{Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, errors.New("should not happen")
|
||||
},
|
||||
}}
|
||||
@@ -567,12 +568,12 @@ func TestResolverErrWrapper(t *testing.T) {
|
||||
|
||||
t.Run("LookupHTTPS", func(t *testing.T) {
|
||||
t.Run("on success", func(t *testing.T) {
|
||||
expected := &HTTPSSvc{
|
||||
expected := &model.HTTPSSvc{
|
||||
ALPN: []string{"h3"},
|
||||
}
|
||||
reso := &resolverErrWrapper{
|
||||
Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return expected, nil
|
||||
},
|
||||
},
|
||||
@@ -591,7 +592,7 @@ func TestResolverErrWrapper(t *testing.T) {
|
||||
expected := io.EOF
|
||||
reso := &resolverErrWrapper{
|
||||
Resolver: &mocks.Resolver{
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*HTTPSSvc, error) {
|
||||
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
|
||||
return nil, expected
|
||||
},
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// SerialResolver uses a transport and sends performs a LookupHost
|
||||
@@ -16,20 +17,20 @@ import (
|
||||
// You should probably use NewSerialResolver to create a new instance.
|
||||
type SerialResolver struct {
|
||||
// Encoder is the MANDATORY encoder to use.
|
||||
Encoder DNSEncoder
|
||||
Encoder model.DNSEncoder
|
||||
|
||||
// Decoder is the MANDATORY decoder to use.
|
||||
Decoder DNSDecoder
|
||||
Decoder model.DNSDecoder
|
||||
|
||||
// NumTimeouts is MANDATORY and counts the number of timeouts.
|
||||
NumTimeouts *atomicx.Int64
|
||||
|
||||
// Txp is the underlying DNS transport.
|
||||
Txp DNSTransport
|
||||
Txp model.DNSTransport
|
||||
}
|
||||
|
||||
// NewSerialResolver creates a new SerialResolver instance.
|
||||
func NewSerialResolver(t DNSTransport) *SerialResolver {
|
||||
func NewSerialResolver(t model.DNSTransport) *SerialResolver {
|
||||
return &SerialResolver{
|
||||
Encoder: &DNSEncoderMiekg{},
|
||||
Decoder: &DNSDecoderMiekg{},
|
||||
@@ -39,7 +40,7 @@ func NewSerialResolver(t DNSTransport) *SerialResolver {
|
||||
}
|
||||
|
||||
// Transport returns the transport being used.
|
||||
func (r *SerialResolver) Transport() DNSTransport {
|
||||
func (r *SerialResolver) Transport() model.DNSTransport {
|
||||
return r.Txp
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ func (r *SerialResolver) LookupHost(ctx context.Context, hostname string) ([]str
|
||||
|
||||
// LookupHTTPS implements Resolver.LookupHTTPS.
|
||||
func (r *SerialResolver) LookupHTTPS(
|
||||
ctx context.Context, hostname string) (*HTTPSSvc, error) {
|
||||
ctx context.Context, hostname string) (*model.HTTPSSvc, error) {
|
||||
querydata, err := r.Encoder.Encode(
|
||||
hostname, dns.TypeHTTPS, r.Txp.RequiresPadding())
|
||||
if err != nil {
|
||||
|
||||
@@ -9,7 +9,8 @@ import (
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
// errorWithTimeout is an error that golang will always consider
|
||||
@@ -248,7 +249,7 @@ func TestSerialResolver(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Decoder: &mocks.DNSDecoder{
|
||||
MockDecodeHTTPS: func(reply []byte) (*mocks.HTTPSSvc, error) {
|
||||
MockDecodeHTTPS: func(reply []byte) (*model.HTTPSSvc, error) {
|
||||
return nil, expected
|
||||
},
|
||||
},
|
||||
|
||||
+22
-53
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
oohttp "github.com/ooni/oohttp"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -124,28 +125,6 @@ type TLSConn = oohttp.TLSConn
|
||||
// Ensures that a tls.Conn implements the TLSConn interface.
|
||||
var _ TLSConn = &tls.Conn{}
|
||||
|
||||
// TLSHandshaker is the generic TLS handshaker.
|
||||
type TLSHandshaker interface {
|
||||
// Handshake creates a new TLS connection from the given connection and
|
||||
// the given config. This function DOES NOT take ownership of the connection
|
||||
// and it's your responsibility to close it on failure.
|
||||
//
|
||||
// Recommended tlsConfig setup:
|
||||
//
|
||||
// - set ServerName to be the SNI;
|
||||
//
|
||||
// - set RootCAs to NewDefaultCertPool();
|
||||
//
|
||||
// - set NextProtos to []string{"h2", "http/1.1"} for HTTPS
|
||||
// and []string{"dot"} for DNS-over-TLS.
|
||||
//
|
||||
// QUIRK: The returned connection will always implement the TLSConn interface
|
||||
// exposed by this package. A future version of this interface will instead
|
||||
// return directly a TLSConn to avoid unconditional castings.
|
||||
Handshake(ctx context.Context, conn net.Conn, tlsConfig *tls.Config) (
|
||||
net.Conn, tls.ConnectionState, error)
|
||||
}
|
||||
|
||||
// NewTLSHandshakerStdlib creates a new TLS handshaker using the
|
||||
// go standard library to manage TLS.
|
||||
//
|
||||
@@ -154,17 +133,17 @@ type TLSHandshaker interface {
|
||||
// 1. logging
|
||||
//
|
||||
// 2. error wrapping
|
||||
func NewTLSHandshakerStdlib(logger Logger) TLSHandshaker {
|
||||
func NewTLSHandshakerStdlib(logger model.DebugLogger) model.TLSHandshaker {
|
||||
return newTLSHandshaker(&tlsHandshakerConfigurable{}, logger)
|
||||
}
|
||||
|
||||
// newTLSHandshaker is the common factory for creating a new TLSHandshaker
|
||||
func newTLSHandshaker(th TLSHandshaker, logger Logger) TLSHandshaker {
|
||||
func newTLSHandshaker(th model.TLSHandshaker, logger model.DebugLogger) model.TLSHandshaker {
|
||||
return &tlsHandshakerLogger{
|
||||
TLSHandshaker: &tlsHandshakerErrWrapper{
|
||||
TLSHandshaker: th,
|
||||
},
|
||||
Logger: logger,
|
||||
DebugLogger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +159,7 @@ type tlsHandshakerConfigurable struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
var _ TLSHandshaker = &tlsHandshakerConfigurable{}
|
||||
var _ model.TLSHandshaker = &tlsHandshakerConfigurable{}
|
||||
|
||||
// defaultCertPool is the cert pool we use by default. We store this
|
||||
// value into a private variable to enable for unit testing.
|
||||
@@ -222,28 +201,28 @@ var defaultTLSHandshaker = &tlsHandshakerConfigurable{}
|
||||
|
||||
// tlsHandshakerLogger is a TLSHandshaker with logging.
|
||||
type tlsHandshakerLogger struct {
|
||||
TLSHandshaker
|
||||
Logger
|
||||
model.TLSHandshaker
|
||||
model.DebugLogger
|
||||
}
|
||||
|
||||
var _ TLSHandshaker = &tlsHandshakerLogger{}
|
||||
var _ model.TLSHandshaker = &tlsHandshakerLogger{}
|
||||
|
||||
// Handshake implements Handshaker.Handshake
|
||||
func (h *tlsHandshakerLogger) Handshake(
|
||||
ctx context.Context, conn net.Conn, config *tls.Config,
|
||||
) (net.Conn, tls.ConnectionState, error) {
|
||||
h.Logger.Debugf(
|
||||
h.DebugLogger.Debugf(
|
||||
"tls {sni=%s next=%+v}...", config.ServerName, config.NextProtos)
|
||||
start := time.Now()
|
||||
tlsconn, state, err := h.TLSHandshaker.Handshake(ctx, conn, config)
|
||||
elapsed := time.Since(start)
|
||||
if err != nil {
|
||||
h.Logger.Debugf(
|
||||
h.DebugLogger.Debugf(
|
||||
"tls {sni=%s next=%+v}... %s in %s", config.ServerName,
|
||||
config.NextProtos, err, elapsed)
|
||||
return nil, tls.ConnectionState{}, err
|
||||
}
|
||||
h.Logger.Debugf(
|
||||
h.DebugLogger.Debugf(
|
||||
"tls {sni=%s next=%+v}... ok in %s {next=%s cipher=%s v=%s}",
|
||||
config.ServerName, config.NextProtos, elapsed, state.NegotiatedProtocol,
|
||||
TLSCipherSuiteString(state.CipherSuite),
|
||||
@@ -251,23 +230,13 @@ func (h *tlsHandshakerLogger) Handshake(
|
||||
return tlsconn, state, nil
|
||||
}
|
||||
|
||||
// TLSDialer is a Dialer dialing TLS connections.
|
||||
type TLSDialer interface {
|
||||
// CloseIdleConnections closes idle connections, if any.
|
||||
CloseIdleConnections()
|
||||
|
||||
// DialTLSContext dials a TLS connection. This method will always
|
||||
// return to you a TLSConn, so you can always safely cast to TLSConn.
|
||||
DialTLSContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// NewTLSDialer creates a new TLS dialer using the given dialer and handshaker.
|
||||
func NewTLSDialer(dialer Dialer, handshaker TLSHandshaker) TLSDialer {
|
||||
func NewTLSDialer(dialer model.Dialer, handshaker model.TLSHandshaker) model.TLSDialer {
|
||||
return NewTLSDialerWithConfig(dialer, handshaker, &tls.Config{})
|
||||
}
|
||||
|
||||
// NewTLSDialerWithConfig is like NewTLSDialer with an optional config.
|
||||
func NewTLSDialerWithConfig(d Dialer, h TLSHandshaker, c *tls.Config) TLSDialer {
|
||||
func NewTLSDialerWithConfig(d model.Dialer, h model.TLSHandshaker, c *tls.Config) model.TLSDialer {
|
||||
return &tlsDialer{Config: c, Dialer: d, TLSHandshaker: h}
|
||||
}
|
||||
|
||||
@@ -277,13 +246,13 @@ type tlsDialer struct {
|
||||
Config *tls.Config
|
||||
|
||||
// Dialer is the MANDATORY dialer.
|
||||
Dialer Dialer
|
||||
Dialer model.Dialer
|
||||
|
||||
// TLSHandshaker is the MANDATORY TLS handshaker.
|
||||
TLSHandshaker TLSHandshaker
|
||||
TLSHandshaker model.TLSHandshaker
|
||||
}
|
||||
|
||||
var _ TLSDialer = &tlsDialer{}
|
||||
var _ model.TLSDialer = &tlsDialer{}
|
||||
|
||||
// CloseIdleConnections implements TLSDialer.CloseIdleConnections.
|
||||
func (d *tlsDialer) CloseIdleConnections() {
|
||||
@@ -337,17 +306,17 @@ func (d *tlsDialer) config(host, port string) *tls.Config {
|
||||
|
||||
// NewSingleUseTLSDialer is like NewSingleUseDialer but takes
|
||||
// in input a TLSConn rather than a net.Conn.
|
||||
func NewSingleUseTLSDialer(conn TLSConn) TLSDialer {
|
||||
func NewSingleUseTLSDialer(conn TLSConn) model.TLSDialer {
|
||||
return &tlsDialerSingleUseAdapter{NewSingleUseDialer(conn)}
|
||||
}
|
||||
|
||||
// tlsDialerSingleUseAdapter adapts dialerSingleUse to
|
||||
// be a TLSDialer type rather than a Dialer type.
|
||||
type tlsDialerSingleUseAdapter struct {
|
||||
Dialer
|
||||
model.Dialer
|
||||
}
|
||||
|
||||
var _ TLSDialer = &tlsDialerSingleUseAdapter{}
|
||||
var _ model.TLSDialer = &tlsDialerSingleUseAdapter{}
|
||||
|
||||
// DialTLSContext implements TLSDialer.DialTLSContext.
|
||||
func (d *tlsDialerSingleUseAdapter) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
@@ -356,7 +325,7 @@ func (d *tlsDialerSingleUseAdapter) DialTLSContext(ctx context.Context, network,
|
||||
|
||||
// tlsHandshakerErrWrapper wraps the returned error to be an OONI error
|
||||
type tlsHandshakerErrWrapper struct {
|
||||
TLSHandshaker
|
||||
model.TLSHandshaker
|
||||
}
|
||||
|
||||
// Handshake implements TLSHandshaker.Handshake
|
||||
@@ -376,13 +345,13 @@ func (h *tlsHandshakerErrWrapper) Handshake(
|
||||
var ErrNoTLSDialer = errors.New("no configured TLS dialer")
|
||||
|
||||
// NewNullTLSDialer returns a TLS dialer that always fails with ErrNoTLSDialer.
|
||||
func NewNullTLSDialer() TLSDialer {
|
||||
func NewNullTLSDialer() model.TLSDialer {
|
||||
return &nullTLSDialer{}
|
||||
}
|
||||
|
||||
type nullTLSDialer struct{}
|
||||
|
||||
var _ TLSDialer = &nullTLSDialer{}
|
||||
var _ model.TLSDialer = &nullTLSDialer{}
|
||||
|
||||
func (*nullTLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return nil, ErrNoTLSDialer
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
"github.com/apex/log"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/mocks"
|
||||
"github.com/ooni/probe-cli/v3/internal/model/mocks"
|
||||
)
|
||||
|
||||
func TestVersionString(t *testing.T) {
|
||||
@@ -120,7 +120,7 @@ func TestConfigureTLSVersion(t *testing.T) {
|
||||
func TestNewTLSHandshakerStdlib(t *testing.T) {
|
||||
th := NewTLSHandshakerStdlib(log.Log)
|
||||
logger := th.(*tlsHandshakerLogger)
|
||||
if logger.Logger != log.Log {
|
||||
if logger.DebugLogger != log.Log {
|
||||
t.Fatal("invalid logger")
|
||||
}
|
||||
errWrapper := logger.TLSHandshaker.(*tlsHandshakerErrWrapper)
|
||||
@@ -253,7 +253,7 @@ func TestTLSHandshakerLogger(t *testing.T) {
|
||||
return tls.Client(conn, config), tls.ConnectionState{}, nil
|
||||
},
|
||||
},
|
||||
Logger: lo,
|
||||
DebugLogger: lo,
|
||||
}
|
||||
conn := &mocks.Conn{
|
||||
MockClose: func() error {
|
||||
@@ -291,7 +291,7 @@ func TestTLSHandshakerLogger(t *testing.T) {
|
||||
return nil, tls.ConnectionState{}, expected
|
||||
},
|
||||
},
|
||||
Logger: lo,
|
||||
DebugLogger: lo,
|
||||
}
|
||||
conn := &mocks.Conn{
|
||||
MockClose: func() error {
|
||||
|
||||
@@ -5,46 +5,22 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
)
|
||||
|
||||
// TProxable is the fundamental type used by the netxlite package to perform
|
||||
// low-level network operations for which, by default, we use the stdlib.
|
||||
//
|
||||
// The t stands for transparent. By using this type as the fundamental type,
|
||||
// we can transparently intercept connections and implement censorship
|
||||
// policies. The implementation of this functionality is not part of netxlite:
|
||||
// here we only have the basic mechanism to make this possible.
|
||||
type TProxable interface {
|
||||
// ListenUDP creates a new quicx.UDPLikeConn conn.
|
||||
ListenUDP(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error)
|
||||
|
||||
// LookupHost lookups a domain using the stdlib resolver.
|
||||
LookupHost(ctx context.Context, domain string) ([]string, error)
|
||||
|
||||
// NewTProxyDialer returns a new TProxyDialer.
|
||||
NewTProxyDialer(timeout time.Duration) TProxyDialer
|
||||
}
|
||||
|
||||
// TProxyDialer is the dialer type returned by TProxable.NewDialer.
|
||||
type TProxyDialer interface {
|
||||
// DialContext behaves like net.Dialer.DialContext.
|
||||
DialContext(ctx context.Context, network, address string) (net.Conn, error)
|
||||
}
|
||||
|
||||
// TProxy is the fundamental variable controlling how netxlite creates
|
||||
// net.Conn and quicx.UDPLikeConn, as well as how it uses the stdlib
|
||||
// net.Conn and model.UDPLikeConn, as well as how it uses the stdlib
|
||||
// resolver. By modifying this variable, you can effectively transparently
|
||||
// proxy netxlite (and hence OONI) activities to other services. This is
|
||||
// quite convenient when performing quality assurance tests.
|
||||
var TProxy TProxable = &TProxyStdlib{}
|
||||
var TProxy model.UnderlyingNetworkLibrary = &TProxyStdlib{}
|
||||
|
||||
// TProxyStdlib is the default TProxable implementation that uses
|
||||
// TProxyStdlib is the default model.UnderlyingNetworkLibrary using
|
||||
// the stdlib in the most obvious way for every functionality.
|
||||
type TProxyStdlib struct{}
|
||||
|
||||
// ListenUDP calls net.ListenUDP.
|
||||
func (*TProxyStdlib) ListenUDP(network string, laddr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||
func (*TProxyStdlib) ListenUDP(network string, laddr *net.UDPAddr) (model.UDPLikeConn, error) {
|
||||
return net.ListenUDP(network, laddr)
|
||||
}
|
||||
|
||||
@@ -53,7 +29,7 @@ func (*TProxyStdlib) LookupHost(ctx context.Context, domain string) ([]string, e
|
||||
return net.DefaultResolver.LookupHost(ctx, domain)
|
||||
}
|
||||
|
||||
// NewTProxyDialer returns a &net.Dialer{Timeout: timeout} instance.
|
||||
func (*TProxyStdlib) NewTProxyDialer(timeout time.Duration) TProxyDialer {
|
||||
// NewSimpleDialer returns a &net.Dialer{Timeout: timeout} instance.
|
||||
func (*TProxyStdlib) NewSimpleDialer(timeout time.Duration) model.SimpleDialer {
|
||||
return &net.Dialer{Timeout: timeout}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/ooni/probe-cli/v3/internal/model"
|
||||
utls "gitlab.com/yawning/utls.git"
|
||||
)
|
||||
|
||||
@@ -21,7 +22,7 @@ import (
|
||||
// 2. error wrapping
|
||||
//
|
||||
// Passing a nil `id` will make this function panic.
|
||||
func NewTLSHandshakerUTLS(logger Logger, id *utls.ClientHelloID) TLSHandshaker {
|
||||
func NewTLSHandshakerUTLS(logger model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker {
|
||||
return newTLSHandshaker(&tlsHandshakerConfigurable{
|
||||
NewConn: newConnUTLS(id),
|
||||
}, logger)
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
func TestNewTLSHandshakerUTLS(t *testing.T) {
|
||||
th := NewTLSHandshakerUTLS(log.Log, &utls.HelloChrome_83)
|
||||
logger := th.(*tlsHandshakerLogger)
|
||||
if logger.Logger != log.Log {
|
||||
if logger.DebugLogger != log.Log {
|
||||
t.Fatal("invalid logger")
|
||||
}
|
||||
errWrapper := logger.TLSHandshaker.(*tlsHandshakerErrWrapper)
|
||||
|
||||
Reference in New Issue
Block a user