From f8ad90c845b901ac59e60ef73673ad2ab35278e7 Mon Sep 17 00:00:00 2001 From: 9seconds Date: Sun, 14 Mar 2021 21:42:52 +0300 Subject: [PATCH] Refactor network to a top-level module --- cli/access_test.go | 10 +-- cli/base.go | 7 +- cli/generate_secret_test.go | 5 +- cli/init_test.go | 81 +------------------ go.mod | 1 + go.sum | 11 +++ mtglib/init.go | 29 ++++++- mtglib/network/init_internal_test.go | 65 --------------- .../network => network}/circuit_breaker.go | 0 .../circuit_breaker_internal_test.go | 5 +- {mtglib/network => network}/default.go | 0 {mtglib/network => network}/default_test.go | 2 +- {mtglib/network => network}/init.go | 12 --- network/init_internal_test.go | 24 ++++++ {mtglib/network => network}/init_test.go | 2 +- .../load_balanced_socks5.go | 0 .../load_balanced_socks5_test.go | 2 +- {mtglib/network => network}/network.go | 40 ++++----- {mtglib/network => network}/proxy_dialer.go | 0 .../proxy_dialer_internal_test.go | 0 {mtglib/network => network}/socks5.go | 0 {mtglib/network => network}/socks5_test.go | 2 +- testlib/capture_output.go | 42 ++++++++++ testlib/net_conn_mock.go | 48 +++++++++++ testlib/network_mock.go | 35 ++++++++ 25 files changed, 231 insertions(+), 192 deletions(-) delete mode 100644 mtglib/network/init_internal_test.go rename {mtglib/network => network}/circuit_breaker.go (100%) rename {mtglib/network => network}/circuit_breaker_internal_test.go (96%) rename {mtglib/network => network}/default.go (100%) rename {mtglib/network => network}/default_test.go (97%) rename {mtglib/network => network}/init.go (71%) create mode 100644 network/init_internal_test.go rename {mtglib/network => network}/init_test.go (97%) rename {mtglib/network => network}/load_balanced_socks5.go (100%) rename {mtglib/network => network}/load_balanced_socks5_test.go (98%) rename {mtglib/network => network}/network.go (86%) rename {mtglib/network => network}/proxy_dialer.go (100%) rename {mtglib/network => network}/proxy_dialer_internal_test.go (100%) rename {mtglib/network => network}/socks5.go (100%) rename {mtglib/network => network}/socks5_test.go (96%) create mode 100644 testlib/capture_output.go create mode 100644 testlib/net_conn_mock.go create mode 100644 testlib/network_mock.go diff --git a/cli/access_test.go b/cli/access_test.go index f1c1435..0cf0579 100644 --- a/cli/access_test.go +++ b/cli/access_test.go @@ -7,6 +7,7 @@ import ( "github.com/9seconds/mtg/v2/config" "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/testlib" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/suite" "github.com/xeipuuv/gojsonschema" @@ -126,7 +127,7 @@ func (suite *AccessTestSuite) TestGenerateNoCalls() { suite.cli.Access.PublicIPv4 = net.ParseIP("10.0.0.10") suite.cli.Access.PublicIPv6 = net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334") - output := suite.CaptureStdout(func() { + output := testlib.CaptureStdout(func() { suite.NoError(suite.cli.Access.Execute(suite.cli)) }) @@ -150,7 +151,7 @@ func (suite *AccessTestSuite) TestGenerateIPv4Call() { httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", httpmock.NewStringResponder(http.StatusOK, "10.11.12.13")) - output := suite.CaptureStdout(func() { + output := testlib.CaptureStdout(func() { suite.NoError(suite.cli.Access.Execute(suite.cli)) }) @@ -174,7 +175,7 @@ func (suite *AccessTestSuite) TestIPv4CallFail() { httpmock.RegisterResponder(http.MethodGet, "https://ifconfig.co", httpmock.NewStringResponder(http.StatusForbidden, "")) - output := suite.CaptureStdout(func() { + output := testlib.CaptureStdout(func() { suite.NoError(suite.cli.Access.Execute(suite.cli)) }) @@ -191,7 +192,6 @@ func (suite *AccessTestSuite) TestIPv4CallFail() { suite.Contains(output, suite.cli.Access.Config.Secret.Hex()) } -func TestAccess(t *testing.T) { - t.Parallel() +func TestAccess(t *testing.T) { // nolint: paralleltest suite.Run(t, &AccessTestSuite{}) } diff --git a/cli/base.go b/cli/base.go index 40348c9..def25d8 100644 --- a/cli/base.go +++ b/cli/base.go @@ -7,11 +7,12 @@ import ( "net/url" "github.com/9seconds/mtg/v2/config" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/network" ) type base struct { - Network network.Network + Network mtglib.Network Config *config.Config } @@ -37,7 +38,7 @@ func (b *base) ReadConfig(path, version string) error { return nil } -func (b *base) makeNetwork(conf *config.Config, version string) (network.Network, error) { +func (b *base) makeNetwork(conf *config.Config, version string) (mtglib.Network, error) { tcpTimeout := conf.Network.Timeout.TCP.Value(network.DefaultTimeout) idleTimeout := conf.Network.Timeout.Idle.Value(network.DefaultIdleTimeout) httpTimeout := conf.Network.Timeout.HTTP.Value(network.DefaultHTTPTimeout) diff --git a/cli/generate_secret_test.go b/cli/generate_secret_test.go index 7e32775..5cbe18d 100644 --- a/cli/generate_secret_test.go +++ b/cli/generate_secret_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/9seconds/mtg/v2/mtglib" + "github.com/9seconds/mtg/v2/testlib" "github.com/stretchr/testify/suite" ) @@ -19,7 +20,7 @@ func (suite *GenerateSecretTestSuite) SetupTest() { } func (suite *GenerateSecretTestSuite) TestDefault() { - output := suite.CaptureStdout(func() { + output := testlib.CaptureStdout(func() { suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) }) suite.True(strings.HasPrefix(output, "7")) @@ -33,7 +34,7 @@ func (suite *GenerateSecretTestSuite) TestDefault() { func (suite *GenerateSecretTestSuite) TestHex() { suite.cli.GenerateSecret.Hex = true - output := suite.CaptureStdout(func() { + output := testlib.CaptureStdout(func() { suite.NoError(suite.cli.GenerateSecret.Run(suite.cli, "dev")) }) suite.True(strings.HasPrefix(output, "ee")) diff --git a/cli/init_test.go b/cli/init_test.go index a1894b6..197d6d6 100644 --- a/cli/init_test.go +++ b/cli/init_test.go @@ -1,66 +1,25 @@ package cli_test import ( - "bytes" - "context" - "io" - "net" "net/http" - "os" - "strings" - "time" "github.com/9seconds/mtg/v2/cli" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/testlib" "github.com/jarcoal/httpmock" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) -type NetworkMock struct { - mock.Mock -} - -func (n *NetworkMock) Dial(network, address string) (net.Conn, error) { - args := n.Called(network, address) - - return args.Get(0).(net.Conn), args.Error(1) -} - -func (n *NetworkMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - args := n.Called(ctx, network, address) - - return args.Get(0).(net.Conn), args.Error(1) -} - -func (n *NetworkMock) DNSResolve(network, hostname string) ([]string, error) { - args := n.Called(network, hostname) - - return args.Get(0).([]string), args.Error(1) -} - -func (n *NetworkMock) MakeHTTPClient(dialFunc network.DialFunc) *http.Client { - return n.Called(dialFunc).Get(0).(*http.Client) -} - -func (n *NetworkMock) IdleTimeout() time.Duration { - return n.Called().Get(0).(time.Duration) -} - -func (n *NetworkMock) HTTPTimeout() time.Duration { - return n.Called().Get(0).(time.Duration) -} - type CommonTestSuite struct { suite.Suite cli *cli.CLI - networkMock *NetworkMock + networkMock *testlib.NetworkMock httpClient *http.Client } func (suite *CommonTestSuite) SetupTest() { - suite.networkMock = &NetworkMock{} + suite.networkMock = &testlib.NetworkMock{} suite.httpClient = &http.Client{} suite.cli = &cli.CLI{} @@ -76,37 +35,3 @@ func (suite *CommonTestSuite) TearDownTest() { suite.networkMock.AssertExpectations(suite.T()) httpmock.DeactivateAndReset() } - -func (suite *CommonTestSuite) CaptureStdout(callback func()) string { - return suite.captureOutput(&os.Stdout, callback) -} - -func (suite *CommonTestSuite) CaptureStderr(callback func()) string { - return suite.captureOutput(&os.Stderr, callback) -} - -func (suite *CommonTestSuite) captureOutput(filefp **os.File, callback func()) string { - oldFp := *filefp - - defer func() { - *filefp = oldFp - }() - - reader, writer, _ := os.Pipe() - buf := &bytes.Buffer{} - closeChan := make(chan bool) - - go func() { - io.Copy(buf, reader) // nolint: errcheck - close(closeChan) - }() - - *filefp = writer - - callback() - - writer.Close() - <-closeChan - - return strings.TrimSpace(buf.String()) -} diff --git a/go.mod b/go.mod index c59047b..3f1b78b 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/libp2p/go-reuseport v0.0.2 github.com/mccutchen/go-httpbin v1.1.1 github.com/pelletier/go-toml v1.8.1 + github.com/rs/zerolog v1.20.0 // indirect github.com/stretchr/objx v0.3.0 // indirect github.com/stretchr/testify v1.7.0 github.com/xeipuuv/gojsonschema v1.2.0 diff --git a/go.sum b/go.sum index dafe96e..e6d0a7f 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6 h1:4NNbNM2Iq/k57qEu7WfL67UrbPq1uFWxW4qODCohi+0= github.com/babolivier/go-doh-client v0.0.0-20201028162107-a76cff4cb8b6/go.mod h1:J29hk+f9lJrblVIfiJOtTFk+OblBawmib4uz/VdKzlg= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,6 +28,9 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs= +github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.3.0 h1:NGXK3lHquSN08v5vWalVI/L8XU9hdzE/G6xsrze47As= github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -42,15 +46,22 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 h1:46ULzRKLh1CwgRq2dC5SlBzEqqNCi8rreOZnNrbqcIY= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/mtglib/init.go b/mtglib/init.go index e5e1a0d..1c50d61 100644 --- a/mtglib/init.go +++ b/mtglib/init.go @@ -1,5 +1,32 @@ package mtglib -import "errors" +import ( + "context" + "errors" + "net" + "net/http" + "time" +) var ErrSecretEmpty = errors.New("secret is empty") + +type Network interface { + Dial(network, address string) (net.Conn, error) + DialContext(ctx context.Context, network, address string) (net.Conn, error) + MakeHTTPClient(func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client + IdleTimeout() time.Duration +} + +type Logger interface { + Named(name string) Logger + + BindInt(name string, value int) Logger + BindStr(name, value string) Logger + + Info(msg string) + InfoError(msg string, err error) + Warning(msg string) + WarningError(msg string, err error) + Debug(msg string) + DebugError(msg string, err error) +} diff --git a/mtglib/network/init_internal_test.go b/mtglib/network/init_internal_test.go deleted file mode 100644 index d40233d..0000000 --- a/mtglib/network/init_internal_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package network - -import ( - "context" - "net" - "time" - - "github.com/stretchr/testify/mock" -) - -type ConnMock struct { - mock.Mock -} - -func (c *ConnMock) Read(b []byte) (int, error) { - args := c.Called(b) - - return args.Int(0), args.Error(1) -} - -func (c *ConnMock) Write(b []byte) (int, error) { - args := c.Called(b) - - return args.Int(0), args.Error(1) -} - -func (c *ConnMock) Close() error { - return c.Called().Error(0) -} - -func (c *ConnMock) LocalAddr() net.Addr { - return c.Called().Get(0).(net.Addr) -} - -func (c *ConnMock) RemoteAddr() net.Addr { - return c.Called().Get(0).(net.Addr) -} - -func (c *ConnMock) SetDeadline(t time.Time) error { - return c.Called(t).Error(0) -} - -func (c *ConnMock) SetReadDeadline(t time.Time) error { - return c.Called(t).Error(0) -} - -func (c *ConnMock) SetWriteDeadline(t time.Time) error { - return c.Called(t).Error(0) -} - -type DialerMock struct { - mock.Mock -} - -func (d *DialerMock) Dial(network, address string) (net.Conn, error) { - args := d.Called(network, address) - - return args.Get(0).(net.Conn), args.Error(1) -} - -func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - args := d.Called(ctx, network, address) - - return args.Get(0).(net.Conn), args.Error(1) -} diff --git a/mtglib/network/circuit_breaker.go b/network/circuit_breaker.go similarity index 100% rename from mtglib/network/circuit_breaker.go rename to network/circuit_breaker.go diff --git a/mtglib/network/circuit_breaker_internal_test.go b/network/circuit_breaker_internal_test.go similarity index 96% rename from mtglib/network/circuit_breaker_internal_test.go rename to network/circuit_breaker_internal_test.go index cc26064..a29eec3 100644 --- a/mtglib/network/circuit_breaker_internal_test.go +++ b/network/circuit_breaker_internal_test.go @@ -9,6 +9,7 @@ import ( "testing" "time" + "github.com/9seconds/mtg/v2/testlib" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) @@ -20,7 +21,7 @@ type CircuitBreakerTestSuite struct { mutex sync.Mutex ctx context.Context ctxCancel context.CancelFunc - connMock *ConnMock + connMock *testlib.NetConnMock baseDialerMock *DialerMock } @@ -28,7 +29,7 @@ func (suite *CircuitBreakerTestSuite) SetupTest() { suite.mutex = sync.Mutex{} suite.ctx, suite.ctxCancel = context.WithCancel(context.Background()) suite.baseDialerMock = &DialerMock{} - suite.connMock = &ConnMock{} + suite.connMock = &testlib.NetConnMock{} suite.d = newCircuitBreakerDialer(suite.baseDialerMock, 3, 100*time.Millisecond, 50*time.Millisecond) } diff --git a/mtglib/network/default.go b/network/default.go similarity index 100% rename from mtglib/network/default.go rename to network/default.go diff --git a/mtglib/network/default_test.go b/network/default_test.go similarity index 97% rename from mtglib/network/default_test.go rename to network/default_test.go index c954456..a6b24c7 100644 --- a/mtglib/network/default_test.go +++ b/network/default_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/network" "github.com/stretchr/testify/suite" ) diff --git a/mtglib/network/init.go b/network/init.go similarity index 71% rename from mtglib/network/init.go rename to network/init.go index 82416af..3bab936 100644 --- a/mtglib/network/init.go +++ b/network/init.go @@ -4,7 +4,6 @@ import ( "context" "errors" "net" - "net/http" "time" ) @@ -27,18 +26,7 @@ var ( ErrCannotDialWithAllProxies = errors.New("cannot dial with all proxies") ) -type DialFunc func(ctx context.Context, protocol, address string) (net.Conn, error) - type Dialer interface { Dial(network, address string) (net.Conn, error) DialContext(ctx context.Context, network, address string) (net.Conn, error) } - -type Network interface { - Dialer - - DNSResolve(network, hostname string) (ips []string, err error) - MakeHTTPClient(DialFunc) *http.Client - IdleTimeout() time.Duration - HTTPTimeout() time.Duration -} diff --git a/network/init_internal_test.go b/network/init_internal_test.go new file mode 100644 index 0000000..9335818 --- /dev/null +++ b/network/init_internal_test.go @@ -0,0 +1,24 @@ +package network + +import ( + "context" + "net" + + "github.com/stretchr/testify/mock" +) + +type DialerMock struct { + mock.Mock +} + +func (d *DialerMock) Dial(network, address string) (net.Conn, error) { + args := d.Called(network, address) + + return args.Get(0).(net.Conn), args.Error(1) +} + +func (d *DialerMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + args := d.Called(ctx, network, address) + + return args.Get(0).(net.Conn), args.Error(1) +} diff --git a/mtglib/network/init_test.go b/network/init_test.go similarity index 97% rename from mtglib/network/init_test.go rename to network/init_test.go index 8f648d9..f42d1c3 100644 --- a/mtglib/network/init_test.go +++ b/network/init_test.go @@ -8,7 +8,7 @@ import ( "net/url" "strings" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/network" socks5 "github.com/armon/go-socks5" "github.com/mccutchen/go-httpbin/httpbin" "github.com/stretchr/testify/mock" diff --git a/mtglib/network/load_balanced_socks5.go b/network/load_balanced_socks5.go similarity index 100% rename from mtglib/network/load_balanced_socks5.go rename to network/load_balanced_socks5.go diff --git a/mtglib/network/load_balanced_socks5_test.go b/network/load_balanced_socks5_test.go similarity index 98% rename from mtglib/network/load_balanced_socks5_test.go rename to network/load_balanced_socks5_test.go index 03dd6f5..b6983c8 100644 --- a/mtglib/network/load_balanced_socks5_test.go +++ b/network/load_balanced_socks5_test.go @@ -8,7 +8,7 @@ import ( "net/url" "testing" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/network" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) diff --git a/mtglib/network/network.go b/network/network.go similarity index 86% rename from mtglib/network/network.go rename to network/network.go index 1835eb0..a897899 100644 --- a/mtglib/network/network.go +++ b/network/network.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/9seconds/mtg/v2/mtglib" doh "github.com/babolivier/go-doh-client" ) @@ -38,7 +39,7 @@ func (n *network) Dial(protocol, address string) (net.Conn, error) { func (n *network) DialContext(ctx context.Context, protocol, address string) (net.Conn, error) { host, port, _ := net.SplitHostPort(address) - ips, err := n.DNSResolve(protocol, host) + ips, err := n.dnsResolve(protocol, host) if err != nil { return nil, fmt.Errorf("cannot resolve dns names: %w", err) } @@ -61,7 +62,20 @@ func (n *network) DialContext(ctx context.Context, protocol, address string) (ne return nil, fmt.Errorf("cannot dial to %s:%s: %w", protocol, address, err) } -func (n *network) DNSResolve(protocol, address string) ([]string, error) { +func (n *network) MakeHTTPClient(dialFunc func(ctx context.Context, + network, address string) (net.Conn, error)) *http.Client { + if dialFunc == nil { + dialFunc = n.DialContext + } + + return makeHTTPClient(n.userAgent, n.httpTimeout, dialFunc) +} + +func (n *network) IdleTimeout() time.Duration { + return n.idleTimeout +} + +func (n *network) dnsResolve(protocol, address string) ([]string, error) { if net.ParseIP(address) != nil { return []string{address}, nil } @@ -115,25 +129,9 @@ func (n *network) DNSResolve(protocol, address string) ([]string, error) { return ips, nil } -func (n *network) MakeHTTPClient(dialFunc DialFunc) *http.Client { - if dialFunc == nil { - dialFunc = n.DialContext - } - - return makeHTTPClient(n.userAgent, n.httpTimeout, dialFunc) -} - -func (n *network) IdleTimeout() time.Duration { - return n.idleTimeout -} - -func (n *network) HTTPTimeout() time.Duration { - return n.httpTimeout -} - func NewNetwork(dialer Dialer, userAgent, dohHostname string, - httpTimeout, idleTimeout time.Duration) (Network, error) { + httpTimeout, idleTimeout time.Duration) (mtglib.Network, error) { switch { case idleTimeout < 0: return nil, fmt.Errorf("timeout should be positive number %s", idleTimeout) @@ -158,7 +156,9 @@ func NewNetwork(dialer Dialer, }, nil } -func makeHTTPClient(userAgent string, timeout time.Duration, dialFunc DialFunc) *http.Client { +func makeHTTPClient(userAgent string, + timeout time.Duration, + dialFunc func(ctx context.Context, network, address string) (net.Conn, error)) *http.Client { return &http.Client{ Timeout: timeout, Transport: networkHTTPTransport{ diff --git a/mtglib/network/proxy_dialer.go b/network/proxy_dialer.go similarity index 100% rename from mtglib/network/proxy_dialer.go rename to network/proxy_dialer.go diff --git a/mtglib/network/proxy_dialer_internal_test.go b/network/proxy_dialer_internal_test.go similarity index 100% rename from mtglib/network/proxy_dialer_internal_test.go rename to network/proxy_dialer_internal_test.go diff --git a/mtglib/network/socks5.go b/network/socks5.go similarity index 100% rename from mtglib/network/socks5.go rename to network/socks5.go diff --git a/mtglib/network/socks5_test.go b/network/socks5_test.go similarity index 96% rename from mtglib/network/socks5_test.go rename to network/socks5_test.go index 82b471f..3460f4d 100644 --- a/mtglib/network/socks5_test.go +++ b/network/socks5_test.go @@ -4,7 +4,7 @@ import ( "net/http" "testing" - "github.com/9seconds/mtg/v2/mtglib/network" + "github.com/9seconds/mtg/v2/network" "github.com/stretchr/testify/suite" ) diff --git a/testlib/capture_output.go b/testlib/capture_output.go new file mode 100644 index 0000000..aa538c2 --- /dev/null +++ b/testlib/capture_output.go @@ -0,0 +1,42 @@ +package testlib + +import ( + "bytes" + "io" + "os" + "strings" +) + +func CaptureStdout(callback func()) string { + return captureOutput(&os.Stdout, callback) +} + +func CaptureStderr(callback func()) string { + return captureOutput(&os.Stderr, callback) +} + +func captureOutput(filefp **os.File, callback func()) string { + oldFp := *filefp + + defer func() { + *filefp = oldFp + }() + + reader, writer, _ := os.Pipe() + buf := &bytes.Buffer{} + closeChan := make(chan bool) + + go func() { + io.Copy(buf, reader) // nolint: errcheck + close(closeChan) + }() + + *filefp = writer + + callback() + + writer.Close() + <-closeChan + + return strings.TrimSpace(buf.String()) +} diff --git a/testlib/net_conn_mock.go b/testlib/net_conn_mock.go new file mode 100644 index 0000000..b2fc1a3 --- /dev/null +++ b/testlib/net_conn_mock.go @@ -0,0 +1,48 @@ +package testlib + +import ( + "net" + "time" + + "github.com/stretchr/testify/mock" +) + +type NetConnMock struct { + mock.Mock +} + +func (n *NetConnMock) Read(b []byte) (int, error) { + args := n.Called(b) + + return args.Int(0), args.Error(1) +} + +func (n *NetConnMock) Write(b []byte) (int, error) { + args := n.Called(b) + + return args.Int(0), args.Error(1) +} + +func (n *NetConnMock) Close() error { + return n.Called().Error(0) +} + +func (n *NetConnMock) LocalAddr() net.Addr { + return n.Called().Get(0).(net.Addr) +} + +func (n *NetConnMock) RemoteAddr() net.Addr { + return n.Called().Get(0).(net.Addr) +} + +func (n *NetConnMock) SetDeadline(t time.Time) error { + return n.Called(t).Error(0) +} + +func (n *NetConnMock) SetReadDeadline(t time.Time) error { + return n.Called(t).Error(0) +} + +func (n *NetConnMock) SetWriteDeadline(t time.Time) error { + return n.Called(t).Error(0) +} diff --git a/testlib/network_mock.go b/testlib/network_mock.go new file mode 100644 index 0000000..325dfd3 --- /dev/null +++ b/testlib/network_mock.go @@ -0,0 +1,35 @@ +package testlib + +import ( + "context" + "net" + "net/http" + "time" + + "github.com/stretchr/testify/mock" +) + +type NetworkMock struct { + mock.Mock +} + +func (n *NetworkMock) Dial(network, address string) (net.Conn, error) { + args := n.Called(network, address) + + return args.Get(0).(net.Conn), args.Error(1) +} + +func (n *NetworkMock) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + args := n.Called(ctx, network, address) + + return args.Get(0).(net.Conn), args.Error(1) +} + +func (n *NetworkMock) MakeHTTPClient(dialFunc func(ctx context.Context, + network, address string) (net.Conn, error)) *http.Client { + return n.Called(dialFunc).Get(0).(*http.Client) +} + +func (n *NetworkMock) IdleTimeout() time.Duration { + return n.Called().Get(0).(time.Duration) +}