Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(netxlite): add dialer factory, simplify resolver factory #459

Merged
merged 1 commit into from
Sep 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions internal/netxlite/dialer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,26 @@ type Dialer interface {
CloseIdleConnections()
}

// NewDialerWithResolver creates a dialer using the given resolver and logger.
func NewDialerWithResolver(logger Logger, resolver Resolver) Dialer {
return &dialerLogger{
Dialer: &dialerResolver{
Dialer: &dialerLogger{
Dialer: &dialerSystem{},
Logger: logger,
},
Resolver: resolver,
},
Logger: logger,
}
}

// NewDialerWithoutResolver creates a dialer that uses the given
// logger and fails with ErrNoResolver when it is passed a domain name.
func NewDialerWithoutResolver(logger Logger) Dialer {
return NewDialerWithResolver(logger, &nullResolver{})
}

// underlyingDialer is the Dialer we use by default.
var underlyingDialer = &net.Dialer{
Timeout: 15 * time.Second,
Expand Down
28 changes: 28 additions & 0 deletions internal/netxlite/dialer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,31 @@ func TestUnderlyingDialerHasTimeout(t *testing.T) {
t.Fatal("unexpected timeout value")
}
}

func TestNewDialerWithoutResolverChain(t *testing.T) {
dlr := NewDialerWithoutResolver(log.Log)
dlog, okay := dlr.(*dialerLogger)
if !okay {
t.Fatal("invalid type")
}
if dlog.Logger != log.Log {
t.Fatal("invalid logger")
}
dreso, okay := dlog.Dialer.(*dialerResolver)
if !okay {
t.Fatal("invalid type")
}
if _, okay := dreso.Resolver.(*nullResolver); !okay {
t.Fatal("invalid Resolver type")
}
dlog, okay = dreso.Dialer.(*dialerLogger)
if !okay {
t.Fatal("invalid type")
}
if dlog.Logger != log.Log {
t.Fatal("invalid logger")
}
if _, okay := dlog.Dialer.(*dialerSystem); !okay {
t.Fatal("invalid type")
}
}
2 changes: 1 addition & 1 deletion internal/netxlite/http3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func TestHTTP3TransportWorks(t *testing.T) {
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
},
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
txp := NewHTTP3Transport(d, &tls.Config{})
client := &http.Client{Transport: txp}
Expand Down
4 changes: 2 additions & 2 deletions internal/netxlite/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func TestHTTPTransportLoggerCloseIdleConnections(t *testing.T) {
func TestHTTPTransportWorks(t *testing.T) {
d := &dialerResolver{
Dialer: defaultDialer,
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
th := &tlsHandshakerConfigurable{}
txp := NewHTTPTransport(d, &tls.Config{}, th)
Expand All @@ -134,7 +134,7 @@ func TestHTTPTransportWithFailingDialer(t *testing.T) {
return nil, expected
},
},
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
}
th := &tlsHandshakerConfigurable{}
txp := NewHTTPTransport(d, &tls.Config{}, th)
Expand Down
8 changes: 4 additions & 4 deletions internal/netxlite/quic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func TestQUICDialerQUICGoTLSDefaultsForDoQ(t *testing.T) {
func TestQUICDialerResolverSuccess(t *testing.T) {
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
}}
Expand All @@ -234,7 +234,7 @@ func TestQUICDialerResolverSuccess(t *testing.T) {
func TestQUICDialerResolverNoPort(t *testing.T) {
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{}}
sess, err := dialer.DialContext(
context.Background(), "udp", "www.google.com",
Expand Down Expand Up @@ -288,7 +288,7 @@ func TestQUICDialerResolverInvalidPort(t *testing.T) {
// to establish a connection leads to a failure
tlsConf := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &quicDialerQUICGo{
QUICListener: &quicListenerStdlib{},
}}
Expand All @@ -312,7 +312,7 @@ func TestQUICDialerResolverApplyTLSDefaults(t *testing.T) {
var gotTLSConfig *tls.Config
tlsConfig := &tls.Config{}
dialer := &quicDialerResolver{
Resolver: NewResolver(&ResolverConfig{Logger: log.Log}),
Resolver: NewResolverSystem(log.Log),
Dialer: &mocks.QUICContextDialer{
MockDialContext: func(ctx context.Context, network, address string,
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
Expand Down
41 changes: 32 additions & 9 deletions internal/netxlite/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package netxlite

import (
"context"
"errors"
"net"
"time"

Expand All @@ -23,20 +24,15 @@ type Resolver interface {
CloseIdleConnections()
}

// ResolverConfig contains config for creating a resolver.
type ResolverConfig struct {
// Logger is the MANDATORY logger to use.
Logger Logger
}

// NewResolver creates a new resolver.
func NewResolver(config *ResolverConfig) Resolver {
// NewResolverSystem creates a new resolver using system
// facilities for resolving domain names (e.g., getaddrinfo).
func NewResolverSystem(logger Logger) Resolver {
return &resolverIDNA{
Resolver: &resolverLogger{
Resolver: &resolverShortCircuitIPAddr{
Resolver: &resolverSystem{},
},
Logger: config.Logger,
Logger: logger,
},
}
}
Expand Down Expand Up @@ -159,3 +155,30 @@ func (r *resolverShortCircuitIPAddr) LookupHost(ctx context.Context, hostname st
}
return r.Resolver.LookupHost(ctx, hostname)
}

// ErrNoResolver indicates you are using a dialer without a resolver.
var ErrNoResolver = errors.New("no configured resolver")

// nullResolver is a resolver that is not capable of resolving
// domain names to IP addresses and always returns ErrNoResolver.
type nullResolver struct{}

// LookupHost implements Resolver.LookupHost.
func (r *nullResolver) LookupHost(ctx context.Context, hostname string) (addrs []string, err error) {
return nil, ErrNoResolver
}

// Network implements Resolver.Network.
func (r *nullResolver) Network() string {
return "null"
}

// Address implements Resolver.Address.
func (r *nullResolver) Address() string {
return ""
}

// CloseIdleConnections implements Resolver.CloseIdleConnections.
func (r *nullResolver) CloseIdleConnections() {
// nothing
}
23 changes: 20 additions & 3 deletions internal/netxlite/resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,7 @@ func TestResolverIDNAWithInvalidPunycode(t *testing.T) {
}

func TestNewResolverTypeChain(t *testing.T) {
r := NewResolver(&ResolverConfig{
Logger: log.Log,
})
r := NewResolverSystem(log.Log)
ridna, ok := r.(*resolverIDNA)
if !ok {
t.Fatal("invalid resolver")
Expand Down Expand Up @@ -238,3 +236,22 @@ func TestResolverShortCircuitIPAddrWithDomain(t *testing.T) {
t.Fatal("invalid result")
}
}

func TestNullResolverWorksAsIntended(t *testing.T) {
r := &nullResolver{}
ctx := context.Background()
addrs, err := r.LookupHost(ctx, "dns.google")
if !errors.Is(err, ErrNoResolver) {
t.Fatal("not the error we expected", err)
}
if addrs != nil {
t.Fatal("expected nil addr")
}
if r.Network() != "null" {
t.Fatal("invalid network")
}
if r.Address() != "" {
t.Fatal("invalid address")
}
r.CloseIdleConnections() // should not crash
}