Repository: grpc/grpc-go

Issue:
`NO_PROXY` environment variable no longer respected on custom resolver results
### What version of gRPC are you using?

v1.71.0

### What version of Go are you using (`go version`)?

```
go version go1.24.2 linux/amd64
```

### What operating system (Linux, Windows, …) and version?

Linux

### What did you do?

```go
package main

import (
	"context"
	"fmt"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/resolver"
)

// testResolver is a dummy resolver that always resolves to a static address.
type testResolver struct{}

func (testResolver) Scheme() string {
	return "test"
}

func (r testResolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
	cc.UpdateState(resolver.State{
		Addresses: []resolver.Address{
			{Addr: "10.1.1.1:1234"},
		},
	})
	return r, nil
}

func (testResolver) ResolveNow(_ resolver.ResolveNowOptions) {}

func (testResolver) Close() {}

func init() {
	resolver.Register(testResolver{})
}

func main() {
	cc, err := grpc.NewClient("test://",
		grpc.WithTransportCredentials(insecure.NewCredentials()),
	)
	if err != nil {
		panic(err)
	}
	defer cc.Close()

	c := grpc_health_v1.NewHealthClient(cc)

	res, err := c.Check(context.Background(), &grpc_health_v1.HealthCheckRequest{})
	if err != nil {
		panic(err)
	}

	fmt.Println(res)
}
```

Execute under the following environment:

```shell
https_proxy=http://localhost:8080 NO_PROXY=10.0.0.0/8 go run .
```

### What did you expect to see?

Behavior v1.70.0 and below was that, because the resolved address 10.1.1.1:1234 matched `NO_PROXY=10.0.0.0/8`, the proxy would not be used.

### What did you see instead?

gRPC as of v1.71.0 (specifically #7881) now attempts to use a proxy for the outbound connection to `10.1.1.1:1234`.

```
panic: rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:8080: connect: connection refused"
```

This represents a change in behavior from v1.70.0 and below. It also means there is no way to use `NO_PROXY` to bypass proxies for custom resolvers.

Discussion:
@purnesh42H: @eshitachandwani could you take a look if its related to https://github.com/grpc/grpc-go/pull/7881?

Solution patch:
diff --git a/internal/resolver/delegatingresolver/delegatingresolver.go b/internal/resolver/delegatingresolver/delegatingresolver.go
index c0e22757..20b8fb09 100644
--- a/internal/resolver/delegatingresolver/delegatingresolver.go
+++ b/internal/resolver/delegatingresolver/delegatingresolver.go
@@ -186,23 +186,15 @@ func (r *delegatingResolver) Close() {
 	r.proxyResolver = nil
 }
 
-func networkTypeFromAddr(addr resolver.Address) string {
-	networkType, ok := networktype.Get(addr)
-	if !ok {
-		networkType, _ = transport.ParseDialTarget(addr.Addr)
-	}
-	return networkType
-}
-
-func isTCPAddressPresent(state *resolver.State) bool {
+func needsProxyResolver(state *resolver.State) bool {
 	for _, addr := range state.Addresses {
-		if networkType := networkTypeFromAddr(addr); networkType == "tcp" {
+		if !skipProxy(addr) {
 			return true
 		}
 	}
 	for _, endpoint := range state.Endpoints {
 		for _, addr := range endpoint.Addresses {
-			if networktype := networkTypeFromAddr(addr); networktype == "tcp" {
+			if !skipProxy(addr) {
 				return true
 			}
 		}
@@ -210,6 +202,29 @@ func isTCPAddressPresent(state *resolver.State) bool {
 	return false
 }
 
+func skipProxy(address resolver.Address) bool {
+	// Avoid proxy when network is not tcp.
+	networkType, ok := networktype.Get(address)
+	if !ok {
+		networkType, _ = transport.ParseDialTarget(address.Addr)
+	}
+	if networkType != "tcp" {
+		return true
+	}
+
+	req := &http.Request{URL: &url.URL{
+		Scheme: "https",
+		Host:   address.Addr,
+	}}
+	// Avoid proxy when address included in `NO_PROXY` environment variable or
+	// fails to get the proxy address.
+	url, err := HTTPSProxyFromEnvironment(req)
+	if err != nil || url == nil {
+		return true
+	}
+	return false
+}
+
 // updateClientConnStateLocked constructs a combined list of addresses by
 // pairing each proxy address with every target address of type TCP. For each
 // pair, it creates a new [resolver.Address] using the proxy address and
@@ -240,8 +255,7 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
 	}
 	var addresses []resolver.Address
 	for _, targetAddr := range (*r.targetResolverState).Addresses {
-		// Avoid proxy when network is not tcp.
-		if networkType := networkTypeFromAddr(targetAddr); networkType != "tcp" {
+		if skipProxy(targetAddr) {
 			addresses = append(addresses, targetAddr)
 			continue
 		}
@@ -259,7 +273,7 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
 		var addrs []resolver.Address
 		for _, targetAddr := range endpt.Addresses {
 			// Avoid proxy when network is not tcp.
-			if networkType := networkTypeFromAddr(targetAddr); networkType != "tcp" {
+			if skipProxy(targetAddr) {
 				addrs = append(addrs, targetAddr)
 				continue
 			}
@@ -340,9 +354,10 @@ func (r *delegatingResolver) updateTargetResolverState(state resolver.State) err
 		logger.Infof("Addresses received from target resolver: %v", state.Addresses)
 	}
 	r.targetResolverState = &state
-	// If no addresses returned by resolver have network type as tcp , do not
-	// wait for proxy update.
-	if !isTCPAddressPresent(r.targetResolverState) {
+	// If all addresses returned by the target resolver have a non-TCP network
+	// type, or are listed in the `NO_PROXY` environment variable, do not wait
+	// for proxy update.
+	if !needsProxyResolver(r.targetResolverState) {
 		return r.cc.UpdateState(*r.targetResolverState)
 	}
 



Test patch:
diff --git a/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go b/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
index b7176d5f..c14d4209 100644
--- a/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
+++ b/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
@@ -164,6 +164,19 @@ func proxyAddressWithTargetAttribute(proxyAddr string, targetAddr string) resolv
 	return addr
 }
 
+func overrideTestHTTPSProxy(t *testing.T, proxyAddr string) {
+	t.Helper()
+	hpfe := func(req *http.Request) (*url.URL, error) {
+		return &url.URL{
+			Scheme: "https",
+			Host:   proxyAddr,
+		}, nil
+	}
+	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
+	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
+	t.Cleanup(func() { delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe })
+}
+
 // Tests the scenario where proxy is configured and the target URI contains the
 // "dns" scheme and target resolution is enabled. The test verifies that the
 // addresses returned by the delegating resolver combines the addresses
@@ -176,21 +189,8 @@ func (s) TestDelegatingResolverwithDNSAndProxyWithTargetResolution(t *testing.T)
 		envProxyAddr            = "proxytest.com"
 		resolvedProxyTestAddr1  = "11.11.11.11:7687"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
+
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("dns")
 	target := targetResolver.Scheme() + ":///" + targetTestAddr
@@ -257,21 +257,7 @@ func (s) TestDelegatingResolverwithDNSAndProxyWithNoTargetResolution(t *testing.
 		envProxyAddr           = "proxytest.com"
 		resolvedProxyTestAddr1 = "11.11.11.11:7687"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	targetResolver := manual.NewBuilderWithScheme("dns")
 	target := targetResolver.Scheme() + ":///" + targetTestAddr
@@ -323,21 +309,7 @@ func (s) TestDelegatingResolverwithCustomResolverAndProxy(t *testing.T) {
 		envProxyAddr            = "proxytest.com"
 		resolvedProxyTestAddr1  = "11.11.11.11:7687"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -410,21 +382,7 @@ func (s) TestDelegatingResolverForEndpointsWithProxy(t *testing.T) {
 		resolvedProxyTestAddr1  = "11.11.11.11:7687"
 		resolvedProxyTestAddr2  = "22.22.22.22:7687"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -517,21 +475,7 @@ func (s) TestDelegatingResolverForMultipleProxyAddress(t *testing.T) {
 		resolvedProxyTestAddr1  = "11.11.11.11:7687"
 		resolvedProxyTestAddr2  = "22.22.22.22:7687"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -598,17 +542,7 @@ func (s) TestDelegatingResolverForMultipleProxyAddress(t *testing.T) {
 func (s) TestDelegatingResolverUpdateStateDuringClose(t *testing.T) {
 	const envProxyAddr = "proxytest.com"
 
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		return &url.URL{
-			Scheme: "https",
-			Host:   envProxyAddr,
-		}, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -661,9 +595,13 @@ func (s) TestDelegatingResolverUpdateStateDuringClose(t *testing.T) {
 	// Updating the channel will result in an error being returned. Since the
 	// target resolver's Close method is already called, the delegating resolver
 	// must not call "ResolveNow" on it.
-	go proxyResolver.UpdateState(resolver.State{
-		Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}},
-	})
+	proxyUpdateCh := make(chan struct{})
+	go func() {
+		proxyResolver.UpdateState(resolver.State{
+			Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1"}}}},
+		})
+		close(proxyUpdateCh)
+	}()
 	unblockProxyResolverClose <- struct{}{}
 
 	select {
@@ -671,6 +609,15 @@ func (s) TestDelegatingResolverUpdateStateDuringClose(t *testing.T) {
 		t.Fatalf("targetResolver.ResolveNow() called unexpectedly.")
 	case <-time.After(defaultTestShortTimeout):
 	}
+	// Wait for the proxy update to complete before returning from the test and
+	// before the deferred reassignment of
+	// delegatingresolver.HTTPSProxyFromEnvironment. This ensures that we read
+	// from the function before it is reassigned, preventing a race condition.
+	select {
+	case <-proxyUpdateCh:
+	case <-ctx.Done():
+		t.Fatalf("Context timed out waiting for proxyResolver.UpdateState() to be called.")
+	}
 }
 
 // Tests that calling cc.UpdateState in a blocking manner from a child resolver
@@ -682,17 +629,7 @@ func (s) TestDelegatingResolverUpdateStateDuringClose(t *testing.T) {
 func (s) TestDelegatingResolverUpdateStateFromResolveNow(t *testing.T) {
 	const envProxyAddr = "proxytest.com"
 
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		return &url.URL{
-			Scheme: "https",
-			Host:   envProxyAddr,
-		}, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -746,17 +683,7 @@ func (s) TestDelegatingResolverUpdateStateFromResolveNow(t *testing.T) {
 func (s) TestDelegatingResolverResolveNow(t *testing.T) {
 	const envProxyAddr = "proxytest.com"
 
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		return &url.URL{
-			Scheme: "https",
-			Host:   envProxyAddr,
-		}, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -827,21 +754,7 @@ func (s) TestDelegatingResolverForNonTCPTarget(t *testing.T) {
 		resolvedTargetTestAddr1 = "1.1.1.1:8080"
 		envProxyAddr            = "proxytest.com"
 	)
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
-	}
-	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() {
-		delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe
-	}()
+	overrideTestHTTPSProxy(t, envProxyAddr)
 
 	// Manual resolver to control the target resolution.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -893,7 +806,7 @@ func (s) TestDelegatingResolverForNonTCPTarget(t *testing.T) {
 
 // Tests the scenario where a proxy is configured, and the resolver returns tcp
 // and non-tcp addresses. The test verifies that the delegating resolver doesn't
-// add proxyatrribute to adresses with network type other than tcp , but adds
+// add proxyatrribute to adresses with network type other than tcp, but adds
 // the proxyattribute to addresses with network type tcp.
 func (s) TestDelegatingResolverForMixNetworkType(t *testing.T) {
 	const (
@@ -902,15 +815,80 @@ func (s) TestDelegatingResolverForMixNetworkType(t *testing.T) {
 		resolvedTargetTestAddr2 = "2.2.2.2:8080"
 		envProxyAddr            = "proxytest.com"
 	)
+	overrideTestHTTPSProxy(t, envProxyAddr)
+
+	// Manual resolver to control the target resolution.
+	targetResolver := manual.NewBuilderWithScheme("test")
+	target := targetResolver.Scheme() + ":///" + targetTestAddr
+	// Set up a manual DNS resolver to control the proxy address resolution.
+	proxyResolver, proxyResolverBuilt := setupDNS(t)
+
+	tcc, stateCh, _ := createTestResolverClientConn(t)
+	if _, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false); err != nil {
+		t.Fatalf("Failed to create delegating resolver: %v", err)
+	}
+	// Set network to anything other than tcp.
+	nonTCPAddr := networktype.Set(resolver.Address{Addr: resolvedTargetTestAddr1}, "unix")
+	targetResolver.UpdateState(resolver.State{
+		Addresses:     []resolver.Address{nonTCPAddr, {Addr: resolvedTargetTestAddr2}},
+		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{nonTCPAddr, {Addr: resolvedTargetTestAddr2}}}},
+		ServiceConfig: &serviceconfig.ParseResult{},
+	})
+
+	select {
+	case <-stateCh:
+		t.Fatalf("Delegating resolver invoked UpdateState before both the proxy and target resolvers had updated their states.")
+	case <-time.After(defaultTestShortTimeout):
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+
+	// Wait for the proxy resolver to be built before calling UpdateState.
+	mustBuildResolver(ctx, t, proxyResolverBuilt)
+	proxyResolver.UpdateState(resolver.State{
+		Addresses:     []resolver.Address{{Addr: envProxyAddr}},
+		ServiceConfig: &serviceconfig.ParseResult{},
+	})
+
+	var gotState resolver.State
+	select {
+	case gotState = <-stateCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out when waiting for a state update from the delegating resolver")
+	}
+	wantState := resolver.State{
+		Addresses:     []resolver.Address{nonTCPAddr, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr2)},
+		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{nonTCPAddr, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr2)}}},
+		ServiceConfig: &serviceconfig.ParseResult{},
+	}
+
+	if diff := cmp.Diff(gotState, wantState); diff != "" {
+		t.Fatalf("Unexpected state from delegating resolver. Diff (-got +want):\n%v", diff)
+	}
+}
+
+// Tests the scenario where a proxy is configured but some addresses are
+// excluded (by using the NO_PROXY environment variable). The test verifies that
+// the delegating resolver doesn't add proxyatrribute to adresses excluded, but
+// adds the proxyattribute to all other addresses.
+func (s) TestDelegatingResolverWithNoProxyEnvUsed(t *testing.T) {
+	const (
+		targetTestAddr            = "test.target"
+		noproxyresolvedTargetAddr = "1.1.1.1:8080"
+		resolvedTargetTestAddr    = "2.2.2.2:8080"
+		envProxyAddr              = "proxytest.com"
+	)
 	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == targetTestAddr {
-			return &url.URL{
-				Scheme: "https",
-				Host:   envProxyAddr,
-			}, nil
+		// return nil to mimick the scenario where the address is excluded using
+		// `NO_PROXY` env variable.
+		if req.URL.Host == noproxyresolvedTargetAddr {
+			return nil, nil
 		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, targetTestAddr)
-		return nil, nil
+		return &url.URL{
+			Scheme: "https",
+			Host:   envProxyAddr,
+		}, nil
 	}
 	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
 	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
@@ -928,11 +906,10 @@ func (s) TestDelegatingResolverForMixNetworkType(t *testing.T) {
 	if _, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false); err != nil {
 		t.Fatalf("Failed to create delegating resolver: %v", err)
 	}
-	// Set network to anything other than tcp.
-	nonTCPAddr := networktype.Set(resolver.Address{Addr: resolvedTargetTestAddr1}, "unix")
+
 	targetResolver.UpdateState(resolver.State{
-		Addresses:     []resolver.Address{nonTCPAddr, {Addr: resolvedTargetTestAddr2}},
-		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{nonTCPAddr, {Addr: resolvedTargetTestAddr2}}}},
+		Addresses:     []resolver.Address{{Addr: noproxyresolvedTargetAddr}, {Addr: resolvedTargetTestAddr}},
+		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: noproxyresolvedTargetAddr}, {Addr: resolvedTargetTestAddr}}}},
 		ServiceConfig: &serviceconfig.ParseResult{},
 	})
 
@@ -959,8 +936,8 @@ func (s) TestDelegatingResolverForMixNetworkType(t *testing.T) {
 		t.Fatal("Context timed out when waiting for a state update from the delegating resolver")
 	}
 	wantState := resolver.State{
-		Addresses:     []resolver.Address{nonTCPAddr, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr2)},
-		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{nonTCPAddr, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr2)}}},
+		Addresses:     []resolver.Address{{Addr: noproxyresolvedTargetAddr}, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr)},
+		Endpoints:     []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: noproxyresolvedTargetAddr}, proxyAddressWithTargetAttribute(envProxyAddr, resolvedTargetTestAddr)}}},
 		ServiceConfig: &serviceconfig.ParseResult{},
 	}
 
diff --git a/internal/transport/proxy_ext_test.go b/internal/transport/proxy_ext_test.go
index 4f5ece1e..6359796f 100644
--- a/internal/transport/proxy_ext_test.go
+++ b/internal/transport/proxy_ext_test.go
@@ -71,6 +71,19 @@ func isIPAddr(addr string) bool {
 	return err == nil
 }
 
+func overrideTestHTTPSProxy(t *testing.T, proxyAddr string) {
+	t.Helper()
+	hpfe := func(req *http.Request) (*url.URL, error) {
+		return &url.URL{
+			Scheme: "https",
+			Host:   proxyAddr,
+		}, nil
+	}
+	originalhpfe := delegatingresolver.HTTPSProxyFromEnvironment
+	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
+	t.Cleanup(func() { delegatingresolver.HTTPSProxyFromEnvironment = originalhpfe })
+}
+
 // Tests the scenario where grpc.Dial is performed using a proxy with the
 // default resolver in the target URI. The test verifies that the connection is
 // established to the proxy server, sends the unresolved target URI in the HTTP
@@ -86,7 +99,7 @@ func (s) TestGRPCDialWithProxy(t *testing.T) {
 			t.Error(err)
 		}
 		if got, want := host, "localhost"; got != want {
-			t.Errorf(" Unexpected request host: %s , want = %s ", got, want)
+			t.Errorf(" Unexpected request host: %s, want = %s ", got, want)
 		}
 	}
 	pServer := proxyserver.New(t, reqCheck, false)
@@ -95,20 +108,7 @@ func (s) TestGRPCDialWithProxy(t *testing.T) {
 	// correctly even when unresolved.
 	pAddr := fmt.Sprintf("localhost:%d", testutils.ParsePort(t, pServer.Addr))
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pAddr)
 
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -152,20 +152,7 @@ func (s) TestGRPCDialWithDNSAndProxy(t *testing.T) {
 	}
 	pServer := proxyserver.New(t, reqCheck, false)
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pServer.Addr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pServer.Addr)
 
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -202,7 +189,7 @@ func (s) TestNewClientWithProxy(t *testing.T) {
 			t.Error(err)
 		}
 		if got, want := host, "localhost"; got != want {
-			t.Errorf(" Unexpected request host: %s , want = %s ", got, want)
+			t.Errorf(" Unexpected request host: %s, want = %s ", got, want)
 		}
 	}
 	pServer := proxyserver.New(t, reqCheck, false)
@@ -211,20 +198,7 @@ func (s) TestNewClientWithProxy(t *testing.T) {
 	// correctly even when unresolved.
 	pAddr := fmt.Sprintf("localhost:%d", testutils.ParsePort(t, pServer.Addr))
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pAddr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pAddr)
 
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -265,20 +239,7 @@ func (s) TestNewClientWithProxyAndCustomResolver(t *testing.T) {
 	}
 	pServer := proxyserver.New(t, reqCheck, false)
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pServer.Addr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pServer.Addr)
 
 	// Create and update a custom resolver for target URI.
 	targetResolver := manual.NewBuilderWithScheme("test")
@@ -327,20 +288,7 @@ func (s) TestNewClientWithProxyAndTargetResolutionEnabled(t *testing.T) {
 	}
 	pServer := proxyserver.New(t, reqCheck, false)
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pServer.Addr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pServer.Addr)
 
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -372,20 +320,7 @@ func (s) TestNewClientWithNoProxy(t *testing.T) {
 	reqCheck := func(_ *http.Request) { t.Error("proxy server should not have received a Connect request") }
 	pServer := proxyserver.New(t, reqCheck, false)
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pServer.Addr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pServer.Addr)
 
 	dopts := []grpc.DialOption{
 		grpc.WithTransportCredentials(insecure.NewCredentials()),
@@ -417,20 +352,7 @@ func (s) TestNewClientWithContextDialer(t *testing.T) {
 	reqCheck := func(_ *http.Request) { t.Error("proxy server should not have received a Connect request") }
 	pServer := proxyserver.New(t, reqCheck, false)
 
-	// Overwrite the function in the test and restore them in defer.
-	hpfe := func(req *http.Request) (*url.URL, error) {
-		if req.URL.Host == unresolvedTargetURI {
-			return &url.URL{
-				Scheme: "https",
-				Host:   pServer.Addr,
-			}, nil
-		}
-		t.Errorf("Unexpected request host to proxy: %s want %s", req.URL.Host, unresolvedTargetURI)
-		return nil, nil
-	}
-	orighpfe := delegatingresolver.HTTPSProxyFromEnvironment
-	delegatingresolver.HTTPSProxyFromEnvironment = hpfe
-	defer func() { delegatingresolver.HTTPSProxyFromEnvironment = orighpfe }()
+	overrideTestHTTPSProxy(t, pServer.Addr)
 
 	// Create a custom dialer that directly dials the backend.
 	customDialer := func(_ context.Context, unresolvedTargetURI string) (net.Conn, error) {
@@ -472,7 +394,7 @@ func (s) TestBasicAuthInNewClientWithProxy(t *testing.T) {
 	reqCheck := func(req *http.Request) {
 		proxyCalled = true
 		if got, want := req.URL.Host, "example.test"; got != want {
-			t.Errorf(" Unexpected request host: %s , want = %s ", got, want)
+			t.Errorf(" Unexpected request host: %s, want = %s ", got, want)
 		}
 		wantProxyAuthStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+password))
 		if got := req.Header.Get("Proxy-Authorization"); got != wantProxyAuthStr {