feat(netxlite): add QUICDialerLogger (#410)

Part of https://github.com/ooni/probe/issues/1505
This commit is contained in:
Simone Basso 2021-06-26 16:54:02 +02:00 committed by GitHub
parent b07890af4d
commit 046dd4545d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 433 additions and 2 deletions

View File

@ -145,6 +145,8 @@ type QUICDialerResolver struct {
Resolver Resolver Resolver Resolver
} }
var _ QUICContextDialer = &QUICDialerResolver{}
// DialContext implements QUICContextDialer.DialContext. This function // DialContext implements QUICContextDialer.DialContext. This function
// will apply the following TLS defaults: // will apply the following TLS defaults:
// //
@ -195,3 +197,28 @@ func (d *QUICDialerResolver) lookupHost(ctx context.Context, hostname string) ([
} }
return d.Resolver.LookupHost(ctx, hostname) return d.Resolver.LookupHost(ctx, hostname)
} }
// QUICDialerLogger is a dialer with logging.
type QUICDialerLogger struct {
// Dialer is the underlying QUIC dialer.
Dialer QUICContextDialer
// Logger is the underlying logger.
Logger Logger
}
var _ QUICContextDialer = &QUICDialerLogger{}
// DialContext implements QUICContextDialer.DialContext.
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)
sess, err := d.Dialer.DialContext(ctx, network, address, tlsConfig, quicConfig)
if err != nil {
d.Logger.Debugf("quic %s/%s... %s", address, network, err)
return nil, err
}
d.Logger.Debugf("quic %s/%s... ok", address, network)
return sess, nil
}

View File

@ -4,11 +4,11 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"log"
"net" "net"
"strings" "strings"
"testing" "testing"
"github.com/apex/log"
"github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp"
"github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/netxmocks" "github.com/ooni/probe-cli/v3/internal/netxmocks"
@ -125,7 +125,7 @@ func TestQUICDialerQUICGoWorksAsIntended(t *testing.T) {
} }
<-sess.HandshakeComplete().Done() <-sess.HandshakeComplete().Done()
if err := sess.CloseWithError(0, ""); err != nil { if err := sess.CloseWithError(0, ""); err != nil {
log.Fatal(err) t.Fatal(err)
} }
} }
@ -331,3 +331,55 @@ func TestQUICDialerResolverApplyTLSDefaults(t *testing.T) {
t.Fatal("gotTLSConfig.ServerName has not been set") t.Fatal("gotTLSConfig.ServerName has not been set")
} }
} }
func TestQUICDialerLoggerSuccess(t *testing.T) {
d := &QUICDialerLogger{
Dialer: &netxmocks.QUICContextDialer{
MockDialContext: func(ctx context.Context, network string,
address string, tlsConfig *tls.Config,
quicConfig *quic.Config) (quic.EarlySession, error) {
return &netxmocks.QUICEarlySession{
MockCloseWithError: func(
code quic.ApplicationErrorCode, reason string) error {
return nil
},
}, nil
},
},
Logger: log.Log,
}
ctx := context.Background()
tlsConfig := &tls.Config{}
quicConfig := &quic.Config{}
sess, err := d.DialContext(ctx, "udp", "8.8.8.8:443", tlsConfig, quicConfig)
if err != nil {
t.Fatal(err)
}
if err := sess.CloseWithError(0, ""); err != nil {
t.Fatal(err)
}
}
func TestQUICDialerLoggerFailure(t *testing.T) {
expected := errors.New("mocked error")
d := &QUICDialerLogger{
Dialer: &netxmocks.QUICContextDialer{
MockDialContext: func(ctx context.Context, network string,
address string, tlsConfig *tls.Config,
quicConfig *quic.Config) (quic.EarlySession, error) {
return nil, expected
},
},
Logger: log.Log,
}
ctx := context.Background()
tlsConfig := &tls.Config{}
quicConfig := &quic.Config{}
sess, err := d.DialContext(ctx, "udp", "8.8.8.8:443", tlsConfig, quicConfig)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if sess != nil {
t.Fatal("expected nil session")
}
}

View File

@ -29,3 +29,100 @@ func (qcd *QUICContextDialer) DialContext(ctx context.Context, network, address
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) { tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
return qcd.MockDialContext(ctx, network, address, tlsConfig, quicConfig) return qcd.MockDialContext(ctx, network, address, tlsConfig, quicConfig)
} }
// 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()
}

View File

@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"net" "net"
"reflect"
"testing" "testing"
"github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go"
@ -44,3 +45,223 @@ func TestQUICContextDialerDialContext(t *testing.T) {
t.Fatal("expected nil session") t.Fatal("expected nil session")
} }
} }
func TestQUICEarlySessionAcceptStream(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")
}
}
func TestQUICEarlySessionAcceptUniStream(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")
}
}
func TestQUICEarlySessionOpenStream(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")
}
}
func TestQUICEarlySessionOpenStreamSync(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")
}
}
func TestQUICEarlySessionOpenUniStream(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")
}
}
func TestQUICEarlySessionOpenUniStreamSync(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")
}
}
func TestQUICEarlySessionLocalAddr(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")
}
}
func TestQUICEarlySessionRemoteAddr(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")
}
}
func TestQUICEarlySessionCloseWithError(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)
}
}
func TestQUICEarlySessionContext(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")
}
}
func TestQUICEarlySessionConnectionState(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")
}
}
func TestQUICEarlySessionHandshakeComplete(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")
}
}
func TestQUICEarlySessionNextSession(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")
}
}
func TestQUICEarlySessionSendMessage(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)
}
}
func TestQUICEarlySessionReceiveMessage(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")
}
}

View File

@ -0,0 +1,34 @@
package netxmocks
import (
"crypto/tls"
"errors"
"reflect"
"testing"
)
func TestTLSConnConnectionState(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")
}
}
func TestTLSConnHandshake(t *testing.T) {
expected := errors.New("mocked error")
c := &TLSConn{
MockHandshake: func() error {
return expected
},
}
err := c.Handshake()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
}