feat(netxlite): write factories for quic code (#470)

Part of https://github.com/ooni/probe/issues/1591
This commit is contained in:
Simone Basso 2021-09-06 21:34:14 +02:00 committed by GitHub
parent 3ba5626b95
commit bdad392b61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 107 additions and 20 deletions

View File

@ -22,8 +22,9 @@ func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
return &dialerLogger{
Dialer: &dialerResolver{
Dialer: &dialerLogger{
Dialer: &dialerSystem{},
Logger: logger,
Dialer: &dialerSystem{},
Logger: logger,
operationSuffix: "_address",
},
Resolver: resolver,
},
@ -120,21 +121,31 @@ type dialerLogger struct {
// Logger is the underlying 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{}
// DialContext implements Dialer.DialContext
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()
conn, err := d.Dialer.DialContext(ctx, network, address)
elapsed := time.Since(start)
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
}
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
}

View File

@ -11,6 +11,28 @@ import (
"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.
type QUICDialer interface {
// DialContext establishes a new QUIC session using the given
@ -23,20 +45,32 @@ type QUICDialer interface {
CloseIdleConnections()
}
// QUICListener listens for QUIC connections.
type QUICListener interface {
// Listen creates a new listening UDPConn.
Listen(addr *net.UDPAddr) (quicx.UDPLikeConn, error)
// NewQUICDialerWithResolver returns a QUICDialer using the given
// QUICListener to create listening connections and the given Resolver
// to resolve domain names (if needed).
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.
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)
// NewQUICDialerWithoutResolver is like NewQUICDialerWithResolver
// 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 {
return NewQUICDialerWithResolver(listener, logger, &nullResolver{})
}
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
@ -223,6 +257,14 @@ type quicDialerLogger struct {
// Logger is the underlying 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{}
@ -231,13 +273,14 @@ var _ QUICDialer = &quicDialerLogger{}
func (d *quicDialerLogger) DialContext(
ctx context.Context, network, address string,
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)
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
}
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
}

View File

@ -427,3 +427,36 @@ func TestQUICDialerLoggerFailure(t *testing.T) {
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")
}
}