Skip to content

Commit

Permalink
feat(netxlite): add (*Netx).ListenTCP (#1279)
Browse files Browse the repository at this point in the history
This diff adds the ListenTCP function to the *Netx struct.

With this addition, based on
#1278, we can remove some techdebt
inside the testingx package.

This is yak shaving while trying to add support for testing socks5 in
the context of ooni/probe#2531.
  • Loading branch information
bassosimone authored Sep 15, 2023
1 parent 95a766a commit b7bd9f0
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 10 deletions.
1 change: 0 additions & 1 deletion internal/netemx/tlsproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 10 additions & 1 deletion internal/netxlite/netx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
32 changes: 32 additions & 0 deletions internal/netxlite/netx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"net"
"net/http"
"sync"
"testing"

"github.com/apex/log"
Expand Down Expand Up @@ -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()
}
3 changes: 3 additions & 0 deletions internal/testingx/httptestx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
}
Expand Down
8 changes: 3 additions & 5 deletions internal/testingx/tlssniproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand All @@ -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,
Expand Down
4 changes: 1 addition & 3 deletions internal/testingx/tlssniproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit b7bd9f0

Please sign in to comment.