ronantakizawa/codereview-bench huggingface dataset:
Repository: traefik/traefik
File: pkg/tls/cipher_test.go (Go)

Code before:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases. We do not test for their presence
		// in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
			fallthrough
		case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%v] = `%v` AND tls.CipherSuitesReversed[`%v`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%v`] = `%v` AND tls.CipherSuites[`%v`] = `%v`", k, v, v, rv)
		}
	}
}


Review comment (suggestion):
```suggestion
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
```

Code after:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
		}
	}
}
Repository: traefik/traefik
File: pkg/tls/cipher_test.go (Go)

Code before:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases. We do not test for their presence
		// in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
			fallthrough
		case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%v] = `%v` AND tls.CipherSuitesReversed[`%v`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%v`] = `%v` AND tls.CipherSuites[`%v`] = `%v`", k, v, v, rv)
		}
	}
}


Review comment (suggestion):
```suggestion
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
```

Code after:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
		}
	}
}
Repository: traefik/traefik
File: pkg/tls/cipher_test.go (Go)

Code before:

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases. We do not test for their presence
		// in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
			fallthrough
		case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%v] = `%v` AND tls.CipherSuitesReversed[`%v`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%v`] = `%v` AND tls.CipherSuites[`%v`] = `%v`", k, v, v, rv)
		}
	}
}


Review comment (suggestion):
```suggestion
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
```

Code after:

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
		}
	}
}
Repository: traefik/traefik
File: pkg/tls/cipher_test.go (Go)

Code before:
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases. We do not test for their presence
		// in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
			fallthrough
		case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%v] = `%v` AND tls.CipherSuitesReversed[`%v`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%v`] = `%v` AND tls.CipherSuites[`%v`] = `%v`", k, v, v, rv)
		}
	}
}


Review comment (suggestion):
```suggestion
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
```

Code after:
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
		}
	}
}
Repository: traefik/traefik
File: pkg/tls/cipher_test.go (Go)

Code before:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases. We do not test for their presence
		// in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":
			fallthrough
		case "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%v] = `%v` AND tls.CipherSuitesReversed[`%v`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%v` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%v`] = `%v` AND tls.CipherSuites[`%v`] = `%v`", k, v, v, rv)
		}
	}
}


Review comment (suggestion):
```suggestion
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
```

Code after:
package tls

import (
	"testing"
)

func TestCiphersMapsSync(t *testing.T) {
	for k, v := range CipherSuites {
		// Following names are legacy aliases.
		// We do not test for their presence in CipherSuitesReversed
		switch k {
		case "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":
			continue
		}

		if rv, ok := CipherSuitesReversed[v]; !ok {
			t.Errorf("Maps not in sync: `%d` key is missing in tls.CipherSuitesReversed", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuites[%s] = `%d` AND tls.CipherSuitesReversed[`%d`] = `%v`", k, v, v, rv)
		}
	}

	for k, v := range CipherSuitesReversed {
		if rv, ok := CipherSuites[v]; !ok {
			t.Errorf("Maps not in sync: `%s` key is missing in tls.CipherSuites", v)
		} else if k != rv {
			t.Errorf("Maps not in sync: tls.CipherSuitesReversed[`%d`] = `%s` AND tls.CipherSuites[`%s`] = `%d`", k, v, v, rv)
		}
	}
}
Repository: traefik/traefik
File: pkg/middlewares/metrics/metrics.go (Go)

Code before:

func getMethod(r *http.Request) string {
	if !utf8.ValidString(r.Method) {
		log.Warnf("Invalid HTTP method encoding: %s", r.Method)
		return "NON_UTF8_HTTP_METHOD"
	}
	return r.Method
}

func getRequestTLSVersion(req *http.Request) string {
	var tlsVersion string

	switch req.TLS.Version {
	case tls.VersionTLS10:
		tlsVersion = "1.0"
	case tls.VersionTLS11:
		tlsVersion = "1.1"
	case tls.VersionTLS12:
		tlsVersion = "1.2"
	case tls.VersionTLS13:
		tlsVersion = "1.3"
	default:
		tlsVersion = "unknown"
	}

	return tlsVersion
}

func getRequestTLSCipher(req *http.Request) string {
	if version, ok := traefiktls.CipherSuitesReversed[req.TLS.CipherSuite]; ok {
		return version
	}

	return "unknown"
}

type retryMetrics interface {
	ServiceRetriesCounter() gokitmetrics.Counter
}

// NewRetryListener instantiates a MetricsRetryListener with the given retryMetrics.
func NewRetryListener(retryMetrics retryMetrics, serviceName string) retry.Listener {
	return &RetryListener{retryMetrics: retryMetrics, serviceName: serviceName}
}

// RetryListener is an implementation of the RetryListener interface to
// record RequestMetrics about retry attempts.
type RetryListener struct {
	retryMetrics retryMetrics
	serviceName  string
}

Review comment (suggestion):
```suggestion
	switch req.TLS.Version {
	case tls.VersionTLS10:
		return "1.0"
	case tls.VersionTLS11:
		return "1.1"
	case tls.VersionTLS12:
		return "1.2"
	case tls.VersionTLS13:
		return "1.3"
	default:
		return "unknown"
	}
```

Code after:

func getMethod(r *http.Request) string {
	if !utf8.ValidString(r.Method) {
		log.Warnf("Invalid HTTP method encoding: %s", r.Method)
		return "NON_UTF8_HTTP_METHOD"
	}
	return r.Method
}

func getRequestTLSVersion(req *http.Request) string {
	switch req.TLS.Version {
	case tls.VersionTLS10:
		return "1.0"
	case tls.VersionTLS11:
		return "1.1"
	case tls.VersionTLS12:
		return "1.2"
	case tls.VersionTLS13:
		return "1.3"
	default:
		return "unknown"
	}
}

func getRequestTLSCipher(req *http.Request) string {
	if version, ok := traefiktls.CipherSuitesReversed[req.TLS.CipherSuite]; ok {
		return version
	}

	return "unknown"
}

type retryMetrics interface {
	ServiceRetriesCounter() gokitmetrics.Counter
}

// NewRetryListener instantiates a MetricsRetryListener with the given retryMetrics.
func NewRetryListener(retryMetrics retryMetrics, serviceName string) retry.Listener {
	return &RetryListener{retryMetrics: retryMetrics, serviceName: serviceName}
}

// RetryListener is an implementation of the RetryListener interface to
// record RequestMetrics about retry attempts.
type RetryListener struct {
	retryMetrics retryMetrics
	serviceName  string
}

// Retried tracks the retry in the RequestMetrics implementation.
func (m *RetryListener) Retried(req *http.Request, attempt int) {
	m.retryMetrics.ServiceRetriesCounter().With("service", m.serviceName).Add(1)
Repository: traefik/traefik
File: pkg/tcp/proxy.go (Go)

Code before:
package tcp

import (
	"io"
	"net"
	"time"

	"github.com/traefik/traefik/v2/pkg/log"
)

// Proxy forwards a TCP request to a TCP service.
type Proxy struct {
	target           *net.TCPAddr
	terminationDelay time.Duration
}

// NewProxy creates a new Proxy.
func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}

	return &Proxy{target: tcpAddr, terminationDelay: terminationDelay}, nil
}

// ServeTCP forwards the connection to a service.
func (p *Proxy) ServeTCP(conn WriteCloser) {
	log.Debugf("Handling connection from %s", conn.RemoteAddr())

	// needed because of e.g. server.trackedConnection
	defer conn.Close()

	connBackend, err := net.DialTCP("tcp", nil, p.target)
	if err != nil {
		log.Errorf("Error while connection to backend: %v", err)
		return
	}

	// maybe not needed, but just in case
	defer connBackend.Close()

	errChan := make(chan error)
	go p.connCopy(conn, connBackend, errChan)
	go p.connCopy(connBackend, conn, errChan)

	err = <-errChan
	if err != nil {
		log.WithoutContext().Errorf("Error during connection: %v", err)
	}


Review comment (performance):
Could we store the` target   *net.TCPAddr` in the `Proxy` struct, with a timeout time? if the timeout is reached, then re-resolve the TCP address?

That way we could "cache" the address for a short-ish period of time, reduce DNS lookups, and would probably reduce the overhead of multiple resolutions.

I was thinking maybe a 5 second timeout? just something that if 1000 requests come in at once, we still only resolve once, but if 1000 come in over the course of a minute, we are still updating the address 20 times, so it will respond to dynamic changes fairly quickly.  We may want this to be configurable, idk.

Thoughts?

Code after:
package tcp

import (
	"io"
	"net"
	"time"

	"github.com/traefik/traefik/v2/pkg/log"
)

// Proxy forwards a TCP request to a TCP service.
type Proxy struct {
	address          string
	target           *net.TCPAddr
	terminationDelay time.Duration
	refreshTarget    bool
}

// NewProxy creates a new Proxy.
func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}

	// enable the refresh of the target only if the address in an IP
	refreshTarget := false
	if host, _, err := net.SplitHostPort(address); err == nil && net.ParseIP(host) == nil {
		refreshTarget = true
	}

	return &Proxy{
		address:          address,
		target:           tcpAddr,
		refreshTarget:    refreshTarget,
		terminationDelay: terminationDelay,
	}, nil
}

// ServeTCP forwards the connection to a service.
func (p *Proxy) ServeTCP(conn WriteCloser) {
	log.Debugf("Handling connection from %s", conn.RemoteAddr())

	// needed because of e.g. server.trackedConnection
	defer conn.Close()

	if p.refreshTarget {
		tcpAddr, err := net.ResolveTCPAddr("tcp", p.address)
		if err != nil {
			log.Errorf("Error resolving tcp address: %v", err)
			return
Repository: traefik/traefik
File: pkg/tcp/proxy.go (Go)

Code before:
	"github.com/traefik/traefik/v2/pkg/log"
)

// Proxy forwards a TCP request to a TCP service.
type Proxy struct {
	target           *net.TCPAddr
	terminationDelay time.Duration
}

// NewProxy creates a new Proxy.
func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}

	return &Proxy{target: tcpAddr, terminationDelay: terminationDelay}, nil
}

// ServeTCP forwards the connection to a service.
func (p *Proxy) ServeTCP(conn WriteCloser) {
	log.Debugf("Handling connection from %s", conn.RemoteAddr())

	// needed because of e.g. server.trackedConnection
	defer conn.Close()

	connBackend, err := net.DialTCP("tcp", nil, p.target)
	if err != nil {
		log.Errorf("Error while connection to backend: %v", err)
		return
	}

	// maybe not needed, but just in case
	defer connBackend.Close()

	errChan := make(chan error)
	go p.connCopy(conn, connBackend, errChan)
	go p.connCopy(connBackend, conn, errChan)

	err = <-errChan
	if err != nil {
		log.WithoutContext().Errorf("Error during connection: %v", err)
	}

	<-errChan
}

func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) {
	_, err := io.Copy(dst, src)
	errCh <- err


Review comment (performance):
I'm wondering, couldn't we do this once and for all and avoid the `SplitHostPort` + `ParseIP` for every new connections? The impact on performance is is pretty low but it could be easily avoidable. You could rename `p.address` into `p.hostname` and in `NewProxy` check if the given address is a hostname. If not, resolve it and set it on `t.target`. Then you will just have to check `if p.hostname != ""` in `ServeTCP` before resolving the hostname.

Code after:
	"github.com/traefik/traefik/v2/pkg/log"
)

// Proxy forwards a TCP request to a TCP service.
type Proxy struct {
	address          string
	target           *net.TCPAddr
	terminationDelay time.Duration
	refreshTarget    bool
}

// NewProxy creates a new Proxy.
func NewProxy(address string, terminationDelay time.Duration) (*Proxy, error) {
	tcpAddr, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return nil, err
	}

	// enable the refresh of the target only if the address in an IP
	refreshTarget := false
	if host, _, err := net.SplitHostPort(address); err == nil && net.ParseIP(host) == nil {
		refreshTarget = true
	}

	return &Proxy{
		address:          address,
		target:           tcpAddr,
		refreshTarget:    refreshTarget,
		terminationDelay: terminationDelay,
	}, nil
}

// ServeTCP forwards the connection to a service.
func (p *Proxy) ServeTCP(conn WriteCloser) {
	log.Debugf("Handling connection from %s", conn.RemoteAddr())

	// needed because of e.g. server.trackedConnection
	defer conn.Close()

	if p.refreshTarget {
		tcpAddr, err := net.ResolveTCPAddr("tcp", p.address)
		if err != nil {
			log.Errorf("Error resolving tcp address: %v", err)
			return
		}
		p.target = tcpAddr
	}

	connBackend, err := net.DialTCP("tcp", nil, p.target)
	if err != nil {
		log.Errorf("Error while connection to backend: %v", err)
Repository: traefik/traefik
File: pkg/tcp/proxy_test.go (Go)

Code before:

	conn, err := net.Dial("tcp", ":"+port)
	require.NoError(t, err)

	_, err = conn.Write([]byte("ping\n"))
	require.NoError(t, err)

	err = conn.(*net.TCPConn).CloseWrite()
	require.NoError(t, err)

	var buf []byte
	buffer := bytes.NewBuffer(buf)
	n, err := io.Copy(buffer, conn)
	require.NoError(t, err)
	require.Equal(t, int64(4), n)
	require.Equal(t, "PONG", buffer.String())
}

func TestLookupAddress(t *testing.T) {
	testCases := []struct {
		desc       string
		address    string
		expectSame bool
	}{
		{
			desc:       "IP don't need refresh",
			address:    "8.8.4.4:53",
			expectSame: true,
		},
		{
			desc:       "Hostname needs refresh",
			address:    "dns.google:53",
			expectSame: false,
		},
	}

	for _, test := range testCases {
		test := test
		t.Run(test.desc, func(t *testing.T) {
			t.Parallel()
			proxy, err := NewProxy(test.address, 10*time.Millisecond)
			require.NoError(t, err)

			proxyListener, err := net.Listen("tcp", ":0")
			require.NoError(t, err)

			var wg sync.WaitGroup
			go func(wg *sync.WaitGroup) {
				for {
					conn, err := proxyListener.Accept()
					require.NoError(t, err)

Review comment (suggestion):
```suggestion
			desc:       "IP doesn't need refresh",
```

Code after:

	conn, err := net.Dial("tcp", ":"+port)
	require.NoError(t, err)

	_, err = conn.Write([]byte("ping\n"))
	require.NoError(t, err)

	err = conn.(*net.TCPConn).CloseWrite()
	require.NoError(t, err)

	var buf []byte
	buffer := bytes.NewBuffer(buf)
	n, err := io.Copy(buffer, conn)
	require.NoError(t, err)
	require.Equal(t, int64(4), n)
	require.Equal(t, "PONG", buffer.String())
}

func TestLookupAddress(t *testing.T) {
	testCases := []struct {
		desc       string
		address    string
		expectSame assert.ComparisonAssertionFunc
	}{
		{
			desc:       "IP doesn't need refresh",
			address:    "8.8.4.4:53",
			expectSame: assert.Same,
		},
		{
			desc:       "Hostname needs refresh",
			address:    "dns.google:53",
			expectSame: assert.NotSame,
		},
	}

	for _, test := range testCases {
		test := test
		t.Run(test.desc, func(t *testing.T) {
			t.Parallel()

			proxy, err := NewProxy(test.address, 10*time.Millisecond)
			require.NoError(t, err)

			require.NotNil(t, proxy.target)

			proxyListener, err := net.Listen("tcp", ":0")
			require.NoError(t, err)

			var wg sync.WaitGroup
			go func(wg *sync.WaitGroup) {