fix(netxlite): prefer composition over embedding (#733)

This diff has been extracted and adapted from 8848c8c516

The reason to prefer composition over embedding is that we want the
build to break if we add new methods to interfaces we define. If the build
does not break, we may forget about wrapping methods we should
actually be wrapping. I noticed this issue inside netxlite when I was working
on websteps-illustrated and I added support for NS and PTR queries.

See https://github.com/ooni/probe/issues/2096

While there, perform comprehensive netxlite code review
and apply minor changes and improve the docs.
This commit is contained in:
Simone Basso 2022-05-15 19:25:27 +02:00 committed by GitHub
commit c1b06a2d09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 328 additions and 92 deletions

View file

@ -1,5 +1,9 @@
package netxlite
//
// Code for dialing TCP or UDP net.Conn-like connections
//
import (
"context"
"errors"
@ -96,8 +100,8 @@ func (d *dialerSystem) CloseIdleConnections() {
// dialerResolver combines dialing with domain name resolution.
type dialerResolver struct {
model.Dialer
model.Resolver
Dialer model.Dialer
Resolver model.Resolver
}
var _ model.Dialer = &dialerResolver{}
@ -144,10 +148,10 @@ func (d *dialerResolver) CloseIdleConnections() {
// dialerLogger is a Dialer with logging.
type dialerLogger struct {
// Dialer is the underlying dialer.
model.Dialer
Dialer model.Dialer
// Logger is the underlying logger.
model.DebugLogger
// DebugLogger is the underlying logger.
DebugLogger model.DebugLogger
// operationSuffix is appended to the operation name.
//
@ -194,15 +198,15 @@ func NewSingleUseDialer(conn net.Conn) model.Dialer {
// dialerSingleUse is the Dialer returned by NewSingleDialer.
type dialerSingleUse struct {
sync.Mutex
mu sync.Mutex
conn net.Conn
}
var _ model.Dialer = &dialerSingleUse{}
func (s *dialerSingleUse) DialContext(ctx context.Context, network string, addr string) (net.Conn, error) {
defer s.Unlock()
s.Lock()
defer s.mu.Unlock()
s.mu.Lock()
if s.conn == nil {
return nil, ErrNoConnReuse
}
@ -218,7 +222,7 @@ 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 {
model.Dialer
Dialer model.Dialer
}
var _ model.Dialer = &dialerErrWrapper{}
@ -226,11 +230,15 @@ 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)
if err != nil {
return nil, NewErrWrapper(classifyGenericError, ConnectOperation, err)
return nil, newErrWrapper(classifyGenericError, ConnectOperation, err)
}
return &dialerErrWrapperConn{Conn: conn}, nil
}
func (d *dialerErrWrapper) CloseIdleConnections() {
d.Dialer.CloseIdleConnections()
}
// dialerErrWrapperConn is a net.Conn that performs error wrapping.
type dialerErrWrapperConn struct {
net.Conn
@ -241,7 +249,7 @@ var _ net.Conn = &dialerErrWrapperConn{}
func (c *dialerErrWrapperConn) Read(b []byte) (int, error) {
count, err := c.Conn.Read(b)
if err != nil {
return 0, NewErrWrapper(classifyGenericError, ReadOperation, err)
return 0, newErrWrapper(classifyGenericError, ReadOperation, err)
}
return count, nil
}
@ -249,7 +257,7 @@ func (c *dialerErrWrapperConn) Read(b []byte) (int, error) {
func (c *dialerErrWrapperConn) Write(b []byte) (int, error) {
count, err := c.Conn.Write(b)
if err != nil {
return 0, NewErrWrapper(classifyGenericError, WriteOperation, err)
return 0, newErrWrapper(classifyGenericError, WriteOperation, err)
}
return count, nil
}
@ -257,7 +265,7 @@ func (c *dialerErrWrapperConn) Write(b []byte) (int, error) {
func (c *dialerErrWrapperConn) Close() error {
err := c.Conn.Close()
if err != nil {
return NewErrWrapper(classifyGenericError, CloseOperation, err)
return newErrWrapper(classifyGenericError, CloseOperation, err)
}
return nil
}