refactor: move ErrorWrapperQUICDialer to errorsx (#420)

I needed to add some tests as integration tests due to circular
imports, but this is ~fine because we quite likely want many
integration tests in the errorsx package anyway.

Part of https://github.com/ooni/probe/issues/1505.
This commit is contained in:
Simone Basso 2021-07-01 20:58:15 +02:00 committed by GitHub
parent 5c52d99d57
commit ec350cba1a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 132 additions and 116 deletions

View File

@ -171,7 +171,7 @@ func NewQUICDialer(config Config) QUICDialer {
var d quicdialer.ContextDialer = &netxlite.QUICDialerQUICGo{
QUICListener: ql,
}
d = quicdialer.ErrorWrapperDialer{Dialer: d}
d = &errorsx.ErrorWrapperQUICDialer{Dialer: d}
if config.TLSSaver != nil {
d = quicdialer.HandshakeSaver{Saver: config.TLSSaver, Dialer: d}
}

View File

@ -1,30 +0,0 @@
package quicdialer
import (
"context"
"crypto/tls"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/errorsx"
)
// ErrorWrapperDialer is a dialer that performs quic err wrapping
type ErrorWrapperDialer struct {
Dialer ContextDialer
}
// DialContext implements ContextDialer.DialContext
func (d ErrorWrapperDialer) DialContext(
ctx context.Context, network string, host string,
tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) {
sess, err := d.Dialer.DialContext(ctx, network, host, tlsCfg, cfg)
err = errorsx.SafeErrWrapperBuilder{
Classifier: errorsx.ClassifyQUICFailure,
Error: err,
Operation: errorsx.QUICHandshakeOperation,
}.MaybeBuild()
if err != nil {
return nil, err
}
return sess, nil
}

View File

@ -1,85 +0,0 @@
package quicdialer_test
import (
"context"
"crypto/tls"
"errors"
"io"
"testing"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/engine/netx/quicdialer"
"github.com/ooni/probe-cli/v3/internal/errorsx"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)
func TestErrorWrapperFailure(t *testing.T) {
ctx := context.Background()
d := quicdialer.ErrorWrapperDialer{
Dialer: MockDialer{Sess: nil, Err: io.EOF}}
sess, err := d.DialContext(
ctx, "udp", "www.google.com:443", &tls.Config{}, &quic.Config{})
if sess != nil {
t.Fatal("expected a nil sess here")
}
errorWrapperCheckErr(t, err, errorsx.QUICHandshakeOperation)
}
func errorWrapperCheckErr(t *testing.T, err error, op string) {
if !errors.Is(err, io.EOF) {
t.Fatal("expected another error here")
}
var errWrapper *errorsx.ErrWrapper
if !errors.As(err, &errWrapper) {
t.Fatal("cannot cast to ErrWrapper")
}
if errWrapper.Operation != op {
t.Fatal("unexpected Operation")
}
if errWrapper.Failure != errorsx.FailureEOFError {
t.Fatal("unexpected failure")
}
}
func TestErrorWrapperInvalidCertificate(t *testing.T) {
nextprotos := []string{"h3"}
servername := "example.com"
tlsConf := &tls.Config{
NextProtos: nextprotos,
ServerName: servername,
}
dlr := quicdialer.ErrorWrapperDialer{Dialer: &netxlite.QUICDialerQUICGo{
QUICListener: &netxlite.QUICListenerStdlib{},
}}
// use Google IP
sess, err := dlr.DialContext(context.Background(), "udp",
"216.58.212.164:443", tlsConf, &quic.Config{})
if err == nil {
t.Fatal("expected an error here")
}
if sess != nil {
t.Fatal("expected nil sess here")
}
if err.Error() != errorsx.FailureSSLInvalidCertificate {
t.Fatal("unexpected failure")
}
}
func TestErrorWrapperSuccess(t *testing.T) {
ctx := context.Background()
tlsConf := &tls.Config{
NextProtos: []string{"h3"},
ServerName: "www.google.com",
}
d := quicdialer.ErrorWrapperDialer{Dialer: &netxlite.QUICDialerQUICGo{
QUICListener: &netxlite.QUICListenerStdlib{},
}}
sess, err := d.DialContext(ctx, "udp", "216.58.212.164:443", tlsConf, &quic.Config{})
if err != nil {
t.Fatal(err)
}
if sess == nil {
t.Fatal("expected non-nil sess here")
}
}

View File

@ -0,0 +1,54 @@
package errorsx_test
import (
"context"
"crypto/tls"
"testing"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/errorsx"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)
func TestErrorWrapperQUICDialerInvalidCertificate(t *testing.T) {
nextprotos := []string{"h3"}
servername := "example.com"
tlsConf := &tls.Config{
NextProtos: nextprotos,
ServerName: servername,
}
dlr := &errorsx.ErrorWrapperQUICDialer{Dialer: &netxlite.QUICDialerQUICGo{
QUICListener: &netxlite.QUICListenerStdlib{},
}}
// use Google IP
sess, err := dlr.DialContext(context.Background(), "udp",
"216.58.212.164:443", tlsConf, &quic.Config{})
if err == nil {
t.Fatal("expected an error here")
}
if sess != nil {
t.Fatal("expected nil sess here")
}
if err.Error() != errorsx.FailureSSLInvalidCertificate {
t.Fatal("unexpected failure")
}
}
func TestErrorWrapperQUICDialerSuccess(t *testing.T) {
ctx := context.Background()
tlsConf := &tls.Config{
NextProtos: []string{"h3"},
ServerName: "www.google.com",
}
d := &errorsx.ErrorWrapperQUICDialer{Dialer: &netxlite.QUICDialerQUICGo{
QUICListener: &netxlite.QUICListenerStdlib{},
}}
sess, err := d.DialContext(ctx, "udp", "216.58.212.164:443", tlsConf, &quic.Config{})
if err != nil {
t.Fatal(err)
}
if sess == nil {
t.Fatal("expected non-nil sess here")
}
}

38
internal/errorsx/quic.go Normal file
View File

@ -0,0 +1,38 @@
package errorsx
import (
"context"
"crypto/tls"
"github.com/lucas-clemente/quic-go"
)
// QUICContextDialer is a dialer for QUIC using Context.
type QUICContextDialer interface {
// DialContext establishes a new QUIC session using the given
// network and address. The tlsConfig and the quicConfig arguments
// MUST NOT be nil. Returns either the session or an error.
DialContext(ctx context.Context, network, address string,
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
}
// ErrorWrapperQUICDialer is a dialer that performs quic err wrapping
type ErrorWrapperQUICDialer struct {
Dialer QUICContextDialer
}
// DialContext implements ContextDialer.DialContext
func (d *ErrorWrapperQUICDialer) DialContext(
ctx context.Context, network string, host string,
tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) {
sess, err := d.Dialer.DialContext(ctx, network, host, tlsCfg, cfg)
err = SafeErrWrapperBuilder{
Classifier: ClassifyQUICFailure,
Error: err,
Operation: QUICHandshakeOperation,
}.MaybeBuild()
if err != nil {
return nil, err
}
return sess, nil
}

View File

@ -0,0 +1,39 @@
package errorsx
import (
"context"
"crypto/tls"
"errors"
"io"
"testing"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/netxmocks"
)
func TestErrorWrapperQUICDialerFailure(t *testing.T) {
ctx := context.Background()
d := &ErrorWrapperQUICDialer{Dialer: &netxmocks.QUICContextDialer{
MockDialContext: func(ctx context.Context, network, address string, tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
return nil, io.EOF
},
}}
sess, err := d.DialContext(
ctx, "udp", "www.google.com:443", &tls.Config{}, &quic.Config{})
if sess != nil {
t.Fatal("expected a nil sess here")
}
if !errors.Is(err, io.EOF) {
t.Fatal("expected another error here")
}
var errWrapper *ErrWrapper
if !errors.As(err, &errWrapper) {
t.Fatal("cannot cast to ErrWrapper")
}
if errWrapper.Operation != QUICHandshakeOperation {
t.Fatal("unexpected Operation")
}
if errWrapper.Failure != FailureEOFError {
t.Fatal("unexpected failure")
}
}