refactor(netxlite): allow easy QUIC dialer chain customization (#771)
Like the previous diff, but for creating QUIC dialers. See https://github.com/ooni/probe/issues/2121.
This commit is contained in:
parent
69fd0c5119
commit
dd5655eaee
|
@ -131,28 +131,26 @@ func NewQUICDialer(config Config) model.QUICDialer {
|
||||||
if config.FullResolver == nil {
|
if config.FullResolver == nil {
|
||||||
config.FullResolver = NewResolver(config)
|
config.FullResolver = NewResolver(config)
|
||||||
}
|
}
|
||||||
var ql model.QUICListener = &netxlite.QUICListenerStdlib{}
|
ql := netxlite.NewQUICListener()
|
||||||
ql = &netxlite.ErrorWrapperQUICListener{QUICListener: ql}
|
|
||||||
if config.ReadWriteSaver != nil {
|
if config.ReadWriteSaver != nil {
|
||||||
ql = &quicdialer.QUICListenerSaver{
|
ql = &quicdialer.QUICListenerSaver{
|
||||||
QUICListener: ql,
|
QUICListener: ql,
|
||||||
Saver: config.ReadWriteSaver,
|
Saver: config.ReadWriteSaver,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var d model.QUICDialer = &netxlite.QUICDialerQUICGo{
|
var logger model.DebugLogger = model.DiscardLogger
|
||||||
QUICListener: ql,
|
if config.Logger != nil {
|
||||||
|
logger = config.Logger
|
||||||
}
|
}
|
||||||
d = &netxlite.ErrorWrapperQUICDialer{
|
extensions := []netxlite.QUICDialerWrapper{
|
||||||
QUICDialer: d,
|
func(dialer model.QUICDialer) model.QUICDialer {
|
||||||
|
if config.TLSSaver != nil {
|
||||||
|
dialer = quicdialer.HandshakeSaver{Saver: config.TLSSaver, QUICDialer: dialer}
|
||||||
|
}
|
||||||
|
return dialer
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if config.TLSSaver != nil {
|
return netxlite.NewQUICDialerWithResolver(ql, logger, config.FullResolver, extensions...)
|
||||||
d = quicdialer.HandshakeSaver{Saver: config.TLSSaver, QUICDialer: d}
|
|
||||||
}
|
|
||||||
d = &netxlite.QUICDialerResolver{
|
|
||||||
Resolver: config.FullResolver,
|
|
||||||
Dialer: d,
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTLSDialer creates a new TLSDialer from the specified config
|
// NewTLSDialer creates a new TLSDialer from the specified config
|
||||||
|
|
|
@ -135,7 +135,7 @@ func NewDialerWithoutResolver(dl model.DebugLogger, w ...DialerWrapper) model.Di
|
||||||
return NewDialerWithResolver(dl, &NullResolver{}, w...)
|
return NewDialerWithResolver(dl, &NullResolver{}, w...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialerSystem is a model.Dialer that users TProxy.NewSimplerDialer
|
// DialerSystem is a model.Dialer that uses TProxy.NewSimplerDialer
|
||||||
// to construct the new SimpleDialer used for dialing. This dialer has
|
// to construct the new SimpleDialer used for dialing. This dialer has
|
||||||
// a fixed timeout for each connect operation equal to 15 seconds.
|
// a fixed timeout for each connect operation equal to 15 seconds.
|
||||||
type DialerSystem struct {
|
type DialerSystem struct {
|
||||||
|
|
|
@ -32,39 +32,32 @@ func (qls *quicListenerStdlib) Listen(addr *net.UDPAddr) (model.UDPLikeConn, err
|
||||||
return TProxy.ListenUDP("udp", addr)
|
return TProxy.ListenUDP("udp", addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQUICDialerWithResolver returns a QUICDialer using the given
|
// QUICDialerWrapper is a function that allows you to customize the kind of QUICDialer
|
||||||
// QUICListener to create listening connections and the given Resolver
|
// returned by NewQUICDialerWithResolver and NewQUICDialerWithoutResolver.
|
||||||
// to resolve domain names (if needed).
|
type QUICDialerWrapper func(dialer model.QUICDialer) model.QUICDialer
|
||||||
|
|
||||||
|
// NewQUICDialerWithResolver is the WrapDialer equivalent for QUIC where
|
||||||
|
// we return a composed QUICDialer modified by optional wrappers.
|
||||||
//
|
//
|
||||||
// Properties of the dialer:
|
// Unlike the dialer returned by WrapDialer, this dialer MAY attempt
|
||||||
//
|
// happy eyeballs, perform parallel dial attempts, and return an error
|
||||||
// 1. logs events using the given logger;
|
// that aggregates all the errors that occurred.
|
||||||
//
|
func NewQUICDialerWithResolver(listener model.QUICListener, logger model.DebugLogger,
|
||||||
// 2. resolves domain names using the givern resolver;
|
resolver model.Resolver, wrappers ...QUICDialerWrapper) (outDialer model.QUICDialer) {
|
||||||
//
|
outDialer = &quicDialerErrWrapper{
|
||||||
// 3. when using a resolver, _may_ attempt multiple dials
|
QUICDialer: &quicDialerHandshakeCompleter{
|
||||||
// in parallel (happy eyeballs) and _may_ return an aggregate
|
Dialer: &quicDialerQUICGo{
|
||||||
// error to the caller;
|
QUICListener: listener,
|
||||||
//
|
},
|
||||||
// 4. wraps errors;
|
},
|
||||||
//
|
}
|
||||||
// 5. has a configured connect timeout;
|
for _, wrapper := range wrappers {
|
||||||
//
|
outDialer = wrapper(outDialer) // extend with user-supplied constructors
|
||||||
// 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 model.QUICListener,
|
|
||||||
logger model.DebugLogger, resolver model.Resolver) model.QUICDialer {
|
|
||||||
return &quicDialerLogger{
|
return &quicDialerLogger{
|
||||||
Dialer: &quicDialerResolver{
|
Dialer: &quicDialerResolver{
|
||||||
Dialer: &quicDialerLogger{
|
Dialer: &quicDialerLogger{
|
||||||
Dialer: &quicDialerErrWrapper{
|
Dialer: outDialer,
|
||||||
QUICDialer: &quicDialerHandshakeCompleter{
|
|
||||||
Dialer: &quicDialerQUICGo{
|
|
||||||
QUICListener: listener,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
operationSuffix: "_address",
|
operationSuffix: "_address",
|
||||||
},
|
},
|
||||||
|
@ -74,12 +67,11 @@ func NewQUICDialerWithResolver(listener model.QUICListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewQUICDialerWithoutResolver is like NewQUICDialerWithResolver
|
// NewQUICDialerWithoutResolver is equivalent to calling NewQUICDialerWithResolver
|
||||||
// except that there is no configured resolver. So, if you pass in
|
// with the resolver argument set to &NullResolver{}.
|
||||||
// an address containing a domain name, the dial will fail with
|
func NewQUICDialerWithoutResolver(listener model.QUICListener,
|
||||||
// the ErrNoResolver failure.
|
logger model.DebugLogger, wrappers ...QUICDialerWrapper) model.QUICDialer {
|
||||||
func NewQUICDialerWithoutResolver(listener model.QUICListener, logger model.DebugLogger) model.QUICDialer {
|
return NewQUICDialerWithResolver(listener, logger, &NullResolver{}, wrappers...)
|
||||||
return NewQUICDialerWithResolver(listener, logger, &NullResolver{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
// quicDialerQUICGo dials using the lucas-clemente/quic-go library.
|
||||||
|
|
|
@ -22,9 +22,25 @@ func TestNewQUICListener(t *testing.T) {
|
||||||
_ = qew.QUICListener.(*quicListenerStdlib)
|
_ = qew.QUICListener.(*quicListenerStdlib)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type extensionQUICDialerFirst struct {
|
||||||
|
model.QUICDialer
|
||||||
|
}
|
||||||
|
|
||||||
|
type extensionQUICDialerSecond struct {
|
||||||
|
model.QUICDialer
|
||||||
|
}
|
||||||
|
|
||||||
func TestNewQUICDialer(t *testing.T) {
|
func TestNewQUICDialer(t *testing.T) {
|
||||||
ql := NewQUICListener()
|
ql := NewQUICListener()
|
||||||
dlr := NewQUICDialerWithoutResolver(ql, log.Log)
|
extensions := []QUICDialerWrapper{
|
||||||
|
func(dialer model.QUICDialer) model.QUICDialer {
|
||||||
|
return &extensionQUICDialerFirst{dialer}
|
||||||
|
},
|
||||||
|
func(dialer model.QUICDialer) model.QUICDialer {
|
||||||
|
return &extensionQUICDialerSecond{dialer}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dlr := NewQUICDialerWithoutResolver(ql, log.Log, extensions...)
|
||||||
logger := dlr.(*quicDialerLogger)
|
logger := dlr.(*quicDialerLogger)
|
||||||
if logger.Logger != log.Log {
|
if logger.Logger != log.Log {
|
||||||
t.Fatal("invalid logger")
|
t.Fatal("invalid logger")
|
||||||
|
@ -37,7 +53,9 @@ func TestNewQUICDialer(t *testing.T) {
|
||||||
if logger.Logger != log.Log {
|
if logger.Logger != log.Log {
|
||||||
t.Fatal("invalid logger")
|
t.Fatal("invalid logger")
|
||||||
}
|
}
|
||||||
errWrapper := logger.Dialer.(*quicDialerErrWrapper)
|
ext2 := logger.Dialer.(*extensionQUICDialerSecond)
|
||||||
|
ext1 := ext2.QUICDialer.(*extensionQUICDialerFirst)
|
||||||
|
errWrapper := ext1.QUICDialer.(*quicDialerErrWrapper)
|
||||||
handshakeCompleter := errWrapper.QUICDialer.(*quicDialerHandshakeCompleter)
|
handshakeCompleter := errWrapper.QUICDialer.(*quicDialerHandshakeCompleter)
|
||||||
base := handshakeCompleter.Dialer.(*quicDialerQUICGo)
|
base := handshakeCompleter.Dialer.(*quicDialerQUICGo)
|
||||||
if base.QUICListener != ql {
|
if base.QUICListener != ql {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user