diff --git a/internal/netemx/tlsproxy.go b/internal/netemx/tlsproxy.go index e1f41559f1..fa7bcd4f0c 100644 --- a/internal/netemx/tlsproxy.go +++ b/internal/netemx/tlsproxy.go @@ -79,7 +79,6 @@ func (srv *tlsProxyServer) MustStart() { srv.logger, &netxlite.Netx{Underlying: &netxlite.NetemUnderlyingNetworkAdapter{UNet: srv.unet}}, epnt, - srv.unet, ) // track this server as something to close later diff --git a/internal/netxlite/netx.go b/internal/netxlite/netx.go index fd059d57db..ae45f8ef5d 100644 --- a/internal/netxlite/netx.go +++ b/internal/netxlite/netx.go @@ -5,7 +5,11 @@ package netxlite // network operations using a custom model.UnderlyingNetwork. // -import "github.com/ooni/probe-cli/v3/internal/model" +import ( + "net" + + "github.com/ooni/probe-cli/v3/internal/model" +) // Netx allows constructing netxlite data types using a specific [model.UnderlyingNetwork]. type Netx struct { @@ -20,3 +24,8 @@ var _ model.MeasuringNetwork = &Netx{} func (netx *Netx) maybeCustomUnderlyingNetwork() *MaybeCustomUnderlyingNetwork { return &MaybeCustomUnderlyingNetwork{netx.Underlying} } + +// ListenTCP creates a new listening TCP socket using the given address. +func (netx *Netx) ListenTCP(network string, addr *net.TCPAddr) (net.Listener, error) { + return netx.maybeCustomUnderlyingNetwork().Get().ListenTCP(network, addr) +} diff --git a/internal/netxlite/netx_test.go b/internal/netxlite/netx_test.go index 9c9fa15b1c..854817289b 100644 --- a/internal/netxlite/netx_test.go +++ b/internal/netxlite/netx_test.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/http" + "sync" "testing" "github.com/apex/log" @@ -117,3 +118,34 @@ func TestNetxWithNetem(t *testing.T) { } }) } + +// We generally do not listen here as part of other tests, since the listening +// functionality is mainly only use for testingx. So, here's a specific test for that. +func TestNetxListenTCP(t *testing.T) { + netx := &Netx{Underlying: nil} + + listener := runtimex.Try1(netx.ListenTCP("tcp", &net.TCPAddr{})) + serverEndpoint := listener.Addr().String() + + // listen in a background goroutine + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + conn := runtimex.Try1(listener.Accept()) + conn.Close() + wg.Done() + }() + + // dial in a background goroutine + wg.Add(1) + go func() { + ctx := context.Background() + dialer := netx.NewDialerWithoutResolver(log.Log) + conn := runtimex.Try1(dialer.DialContext(ctx, "tcp", serverEndpoint)) + conn.Close() + wg.Done() + }() + + // wait for the goroutines to finish + wg.Wait() +} diff --git a/internal/testingx/httptestx.go b/internal/testingx/httptestx.go index 488fe1d186..c8a4bcc2e4 100644 --- a/internal/testingx/httptestx.go +++ b/internal/testingx/httptestx.go @@ -89,6 +89,7 @@ func mustNewHTTPServer( X509CertPool: nil, // the default when not using TLS } baseURL := &url.URL{Host: listener.Addr().String()} + switch !tlsConfig.IsNone() { case true: baseURL.Scheme = "https" @@ -97,10 +98,12 @@ func mustNewHTTPServer( srv.Config.TLSConfig = srv.TLS srv.X509CertPool = runtimex.Try1(tlsConfig.Unwrap().DefaultCertPool()) go srv.Config.ServeTLS(listener, "", "") // using server.TLSConfig + default: baseURL.Scheme = "http" go srv.Config.Serve(listener) } + srv.URL = baseURL.String() return srv } diff --git a/internal/testingx/tlssniproxy.go b/internal/testingx/tlssniproxy.go index e350b0e370..42572854af 100644 --- a/internal/testingx/tlssniproxy.go +++ b/internal/testingx/tlssniproxy.go @@ -16,6 +16,7 @@ import ( // TLSSNIProxyNetx is how [TLSSNIProxy] views [*netxlite.Netx]. type TLSSNIProxyNetx interface { + ListenTCP(network string, addr *net.TCPAddr) (net.Listener, error) NewDialerWithResolver(dl model.DebugLogger, r model.Resolver, w ...model.DialerWrapper) model.Dialer NewStdlibResolver(logger model.DebugLogger) model.Resolver } @@ -38,13 +39,10 @@ type TLSSNIProxy struct { wg *sync.WaitGroup } -// TODO(bassosimone): MustNewTLSSNIProxyEx prototype would be simpler if -// netxlite.Netx was also able to create listening TCP connections - // MustNewTLSSNIProxyEx creates a new [*TLSSNIProxy]. func MustNewTLSSNIProxyEx( - logger model.Logger, netx TLSSNIProxyNetx, tcpAddr *net.TCPAddr, tcpListener TCPListener) *TLSSNIProxy { - listener := runtimex.Try1(tcpListener.ListenTCP("tcp", tcpAddr)) + logger model.Logger, netx TLSSNIProxyNetx, tcpAddr *net.TCPAddr) *TLSSNIProxy { + listener := runtimex.Try1(netx.ListenTCP("tcp", tcpAddr)) proxy := &TLSSNIProxy{ closeOnce: sync.Once{}, listener: listener, diff --git a/internal/testingx/tlssniproxy_test.go b/internal/testingx/tlssniproxy_test.go index 0ee9c5c11f..4ad9000c8c 100644 --- a/internal/testingx/tlssniproxy_test.go +++ b/internal/testingx/tlssniproxy_test.go @@ -32,9 +32,8 @@ func TestTLSSNIProxy(t *testing.T) { Underlying: nil, // use the network } tcpAddr := &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)} - tcpListener := &testingx.TCPListenerStdlib{} - proxy := testingx.MustNewTLSSNIProxyEx(log.Log, netxProxy, tcpAddr, tcpListener) + proxy := testingx.MustNewTLSSNIProxyEx(log.Log, netxProxy, tcpAddr) closers = append(closers, proxy) netxClient := &netxlite.Netx{ @@ -75,7 +74,6 @@ func TestTLSSNIProxy(t *testing.T) { log.Log, &netxlite.Netx{Underlying: &netxlite.NetemUnderlyingNetworkAdapter{UNet: proxyStack}}, &net.TCPAddr{IP: net.IPv4(10, 0, 0, 1), Port: 443}, - proxyStack, ) closers = append(closers, proxy)