feat(netxlite): write factories for quic code (#470)
Part of https://github.com/ooni/probe/issues/1591
This commit is contained in:
parent
3ba5626b95
commit
bdad392b61
|
@ -22,8 +22,9 @@ func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
|
||||||
return &dialerLogger{
|
return &dialerLogger{
|
||||||
Dialer: &dialerResolver{
|
Dialer: &dialerResolver{
|
||||||
Dialer: &dialerLogger{
|
Dialer: &dialerLogger{
|
||||||
Dialer: &dialerSystem{},
|
Dialer: &dialerSystem{},
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
|
operationSuffix: "_address",
|
||||||
},
|
},
|
||||||
Resolver: resolver,
|
Resolver: resolver,
|
||||||
},
|
},
|
||||||
|
@ -120,21 +121,31 @@ type dialerLogger struct {
|
||||||
|
|
||||||
// Logger is the underlying logger.
|
// Logger is the underlying logger.
|
||||||
Logger Logger
|
Logger Logger
|
||||||
|
|
||||||
|
// operationSuffix is appended to the operation name.
|
||||||
|
//
|
||||||
|
// We use this suffix to distinguish the output from dialing
|
||||||
|
// with the output from dialing an IP address when we are
|
||||||
|
// using a dialer without resolver, where otherwise both lines
|
||||||
|
// would read something like `dial 8.8.8.8:443...`
|
||||||
|
operationSuffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Dialer = &dialerLogger{}
|
var _ Dialer = &dialerLogger{}
|
||||||
|
|
||||||
// DialContext implements Dialer.DialContext
|
// DialContext implements Dialer.DialContext
|
||||||
func (d *dialerLogger) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
func (d *dialerLogger) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
d.Logger.Debugf("dial %s/%s...", address, network)
|
d.Logger.Debugf("dial%s %s/%s...", d.operationSuffix, address, network)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
conn, err := d.Dialer.DialContext(ctx, network, address)
|
conn, err := d.Dialer.DialContext(ctx, network, address)
|
||||||
elapsed := time.Since(start)
|
elapsed := time.Since(start)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Logger.Debugf("dial %s/%s... %s in %s", address, network, err, elapsed)
|
d.Logger.Debugf("dial%s %s/%s... %s in %s", d.operationSuffix,
|
||||||
|
address, network, err, elapsed)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.Logger.Debugf("dial %s/%s... ok in %s", address, network, elapsed)
|
d.Logger.Debugf("dial%s %s/%s... ok in %s", d.operationSuffix,
|
||||||
|
address, network, elapsed)
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,28 @@ import (
|
||||||
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
"github.com/ooni/probe-cli/v3/internal/netxlite/quicx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// QUICListener listens for QUIC connections.
|
||||||
|
type QUICListener interface {
|
||||||
|
// Listen creates a new listening UDPConn.
|
||||||
|
Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewQUICListener creates a new QUICListener using the standard
|
||||||
|
// library to create listening UDP sockets.
|
||||||
|
func NewQUICListener() QUICListener {
|
||||||
|
return &quicListenerStdlib{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// quicListenerStdlib is a QUICListener using the standard library.
|
||||||
|
type quicListenerStdlib struct{}
|
||||||
|
|
||||||
|
var _ QUICListener = &quicListenerStdlib{}
|
||||||
|
|
||||||
|
// Listen implements QUICListener.Listen.
|
||||||
|
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
||||||
|
return net.ListenUDP("udp", addr)
|
||||||
|
}
|
||||||
|
|
||||||
// QUICDialer dials QUIC sessions.
|
// QUICDialer dials QUIC sessions.
|
||||||
type QUICDialer interface {
|
type QUICDialer interface {
|
||||||
// DialContext establishes a new QUIC session using the given
|
// DialContext establishes a new QUIC session using the given
|
||||||
|
@ -23,20 +45,32 @@ type QUICDialer interface {
|
||||||
CloseIdleConnections()
|
CloseIdleConnections()
|
||||||
}
|
}
|
||||||
|
|
||||||
// QUICListener listens for QUIC connections.
|
// NewQUICDialerWithResolver returns a QUICDialer using the given
|
||||||
type QUICListener interface {
|
// QUICListener to create listening connections and the given Resolver
|
||||||
// Listen creates a new listening UDPConn.
|
// to resolve domain names (if needed).
|
||||||
Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error)
|
func NewQUICDialerWithResolver(listener QUICListener,
|
||||||
|
logger Logger, resolver Resolver) QUICDialer {
|
||||||
|
return &quicDialerLogger{
|
||||||
|
Dialer: &quicDialerResolver{
|
||||||
|
Dialer: &quicDialerLogger{
|
||||||
|
Dialer: &quicDialerQUICGo{
|
||||||
|
QUICListener: listener,
|
||||||
|
},
|
||||||
|
Logger: logger,
|
||||||
|
operationSuffix: "_address",
|
||||||
|
},
|
||||||
|
Resolver: resolver,
|
||||||
|
},
|
||||||
|
Logger: logger,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// quicListenerStdlib is a QUICListener using the standard library.
|
// NewQUICDialerWithoutResolver is like NewQUICDialerWithResolver
|
||||||
type quicListenerStdlib struct{}
|
// except that there is no configured resolver. So, if you pass in
|
||||||
|
// an address containing a domain name, the dial will fail with
|
||||||
var _ QUICListener = &quicListenerStdlib{}
|
// the ErrNoResolver failure.
|
||||||
|
func NewQUICDialerWithoutResolver(listener QUICListener, logger Logger) QUICDialer {
|
||||||
// Listen implements QUICListener.Listen.
|
return NewQUICDialerWithResolver(listener, logger, &nullResolver{})
|
||||||
func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error) {
|
|
||||||
return net.ListenUDP("udp", addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
||||||
|
@ -223,6 +257,14 @@ type quicDialerLogger struct {
|
||||||
|
|
||||||
// Logger is the underlying logger.
|
// Logger is the underlying logger.
|
||||||
Logger Logger
|
Logger Logger
|
||||||
|
|
||||||
|
// operationSuffix is appended to the operation name.
|
||||||
|
//
|
||||||
|
// We use this suffix to distinguish the output from dialing
|
||||||
|
// with the output from dialing an IP address when we are
|
||||||
|
// using a dialer without resolver, where otherwise both lines
|
||||||
|
// would read something like `dial 8.8.8.8:443...`
|
||||||
|
operationSuffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ QUICDialer = &quicDialerLogger{}
|
var _ QUICDialer = &quicDialerLogger{}
|
||||||
|
@ -231,13 +273,14 @@ var _ QUICDialer = &quicDialerLogger{}
|
||||||
func (d *quicDialerLogger) DialContext(
|
func (d *quicDialerLogger) DialContext(
|
||||||
ctx context.Context, network, address string,
|
ctx context.Context, network, address string,
|
||||||
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
|
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
|
||||||
d.Logger.Debugf("quic %s/%s...", address, network)
|
d.Logger.Debugf("quic_dial%s %s/%s...", d.operationSuffix, address, network)
|
||||||
sess, err := d.Dialer.DialContext(ctx, network, address, tlsConfig, quicConfig)
|
sess, err := d.Dialer.DialContext(ctx, network, address, tlsConfig, quicConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Logger.Debugf("quic %s/%s... %s", address, network, err)
|
d.Logger.Debugf("quic_dial%s %s/%s... %s", d.operationSuffix,
|
||||||
|
address, network, err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.Logger.Debugf("quic %s/%s... ok", address, network)
|
d.Logger.Debugf("quic_dial%s %s/%s... ok", d.operationSuffix, address, network)
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -427,3 +427,36 @@ func TestQUICDialerLoggerFailure(t *testing.T) {
|
||||||
t.Fatal("expected nil session")
|
t.Fatal("expected nil session")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewQUICDialerWithoutResolverChain(t *testing.T) {
|
||||||
|
ql := NewQUICListener()
|
||||||
|
dlr := NewQUICDialerWithoutResolver(ql, log.Log)
|
||||||
|
dlog, okay := dlr.(*quicDialerLogger)
|
||||||
|
if !okay {
|
||||||
|
t.Fatal("invalid type")
|
||||||
|
}
|
||||||
|
if dlog.Logger != log.Log {
|
||||||
|
t.Fatal("invalid logger")
|
||||||
|
}
|
||||||
|
dr, okay := dlog.Dialer.(*quicDialerResolver)
|
||||||
|
if !okay {
|
||||||
|
t.Fatal("invalid type")
|
||||||
|
}
|
||||||
|
if _, okay := dr.Resolver.(*nullResolver); !okay {
|
||||||
|
t.Fatal("invalid resolver type")
|
||||||
|
}
|
||||||
|
dlog, okay = dr.Dialer.(*quicDialerLogger)
|
||||||
|
if !okay {
|
||||||
|
t.Fatal("invalid type")
|
||||||
|
}
|
||||||
|
if dlog.Logger != log.Log {
|
||||||
|
t.Fatal("invalid logger")
|
||||||
|
}
|
||||||
|
dgo, okay := dlog.Dialer.(*quicDialerQUICGo)
|
||||||
|
if !okay {
|
||||||
|
t.Fatal("invalid type")
|
||||||
|
}
|
||||||
|
if dgo.QUICListener != ql {
|
||||||
|
t.Fatal("invalid quic listener")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user