Repository: grpc/grpc-go

Issue:
HTTPS_PROXY support issues non-compliant CONNECT requests
### What version of gRPC are you using?
`google.golang.org/grpc v1.73.0`

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

### What operating system (Linux, Windows, …) and version?
`Debian GNU/Linux 12 (bookworm) / amd64 `

### What did you do?
When setting `HTTPS_PROXY` env variable, grpc-go issues `CONNECT` requests that lack a port number, when this was omitted in the original target (relying on the `443` default port used elsewhere by gRPC).

This appears to be similar to this other issue that was affecting the C++ gRPC library:
https://github.com/grpc/grpc/pull/26331

I tested this by creating a simple gRPC client example, and using `mitmproxy` to run an HTTPS_PROXY.

```sh
./mitmproxy &
HTTPS_PROXY=127.0.0.1:8080 ./simpleGrpcClient dns:///mytarget.com
```

### What did you expect to see?
simpleGrpcClient should connect to `mytarget` through the `HTTPS_PROXY`, by connecting to the proxy and issuing the following HTTP request:
```
HTTP CONNECT mytarget.com:443
```

### What did you see instead?
simpleGrpcClient connects to the proxy and issues a non-compliant CONNECT requests:
```
HTTP CONNECT mytarget.com
```

This was verified by using `mitmproxy` which printed the following log:
```
Bad HTTP request line: b'CONNECT mytarget.com HTTP/1.1'
```

(while it works correctly if I use an explicit target such as: `dns:///mytarget.com:443`

According to HTTP/1.1 RFC,  in the `CONNECT` method the port information is required as part of the "authority form". See:
- https://datatracker.ietf.org/doc/html/rfc7231#section-4.3.6
- https://datatracker.ietf.org/doc/html/rfc7230#section-5.3.3

A similar issue was present in the C++ gRPC library: https://github.com/grpc/grpc/issues/26227
and was fixed here: https://github.com/grpc/grpc/pull/26331


Discussion:
@eshitachandwani: Hello @dgnemo  , thank you for reporting this. We have a PR out to fix it.

Solution patch:
diff --git a/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go b/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go
index f5f5afec..780cdc4f 100644
--- a/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go
+++ b/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go
@@ -351,6 +351,13 @@ func (b *pickfirstBalancer) ExitIdle() {
 	b.mu.Lock()
 	defer b.mu.Unlock()
 	if b.state == connectivity.Idle {
+		// Move the balancer into CONNECTING state immediately. This is done to
+		// avoid staying in IDLE if a resolver update arrives before the first
+		// SubConn reports CONNECTING.
+		b.updateBalancerState(balancer.State{
+			ConnectivityState: connectivity.Connecting,
+			Picker:            &picker{err: balancer.ErrNoSubConnAvailable},
+		})
 		b.startFirstPassLocked()
 	}
 }
@@ -374,13 +381,14 @@ func (b *pickfirstBalancer) closeSubConnsLocked() {
 
 // deDupAddresses ensures that each address appears only once in the slice.
 func deDupAddresses(addrs []resolver.Address) []resolver.Address {
-	seenAddrs := resolver.NewAddressMapV2[*scData]()
+	seenAddrs := resolver.NewAddressMapV2[bool]()
 	retAddrs := []resolver.Address{}
 
 	for _, addr := range addrs {
 		if _, ok := seenAddrs.Get(addr); ok {
 			continue
 		}
+		seenAddrs.Set(addr, true)
 		retAddrs = append(retAddrs, addr)
 	}
 	return retAddrs
@@ -604,7 +612,7 @@ func (b *pickfirstBalancer) updateSubConnState(sd *scData, newState balancer.Sub
 		if !b.addressList.seekTo(sd.addr) {
 			// This should not fail as we should have only one SubConn after
 			// entering READY. The SubConn should be present in the addressList.
-			b.logger.Errorf("Address %q not found address list in  %v", sd.addr, b.addressList.addresses)
+			b.logger.Errorf("Address %q not found address list in %v", sd.addr, b.addressList.addresses)
 			return
 		}
 		if !b.healthCheckingEnabled {
diff --git a/balancer_wrapper.go b/balancer_wrapper.go
index 948a21ef..2c760e62 100644
--- a/balancer_wrapper.go
+++ b/balancer_wrapper.go
@@ -450,13 +450,14 @@ func (acbw *acBalancerWrapper) healthListenerRegFn() func(context.Context, func(
 	if acbw.ccb.cc.dopts.disableHealthCheck {
 		return noOpRegisterHealthListenerFn
 	}
+	cfg := acbw.ac.cc.healthCheckConfig()
+	if cfg == nil {
+		return noOpRegisterHealthListenerFn
+	}
 	regHealthLisFn := internal.RegisterClientHealthCheckListener
 	if regHealthLisFn == nil {
 		// The health package is not imported.
-		return noOpRegisterHealthListenerFn
-	}
-	cfg := acbw.ac.cc.healthCheckConfig()
-	if cfg == nil {
+		channelz.Error(logger, acbw.ac.channelz, "Health check is requested but health package is not imported.")
 		return noOpRegisterHealthListenerFn
 	}
 	return func(ctx context.Context, listener func(balancer.SubConnState)) func() {
diff --git a/benchmark/benchmain/main.go b/benchmark/benchmain/main.go
index 39a0cc38..443799f5 100644
--- a/benchmark/benchmain/main.go
+++ b/benchmark/benchmain/main.go
@@ -116,7 +116,7 @@ var (
 	serverWriteBufferSize = flags.IntSlice("serverWriteBufferSize", []int{-1}, "Configures the server write buffer size in bytes. If negative, use the default - may be a comma-separated list")
 	sleepBetweenRPCs      = flags.DurationSlice("sleepBetweenRPCs", []time.Duration{0}, "Configures the maximum amount of time the client should sleep between consecutive RPCs - may be a comma-separated list")
 	connections           = flag.Int("connections", 1, "The number of connections. Each connection will handle maxConcurrentCalls RPC streams")
-	recvBufferPool        = flags.StringWithAllowedValues("recvBufferPool", recvBufferPoolNil, "Configures the shared receive buffer pool. One of: nil, simple, all", allRecvBufferPools)
+	recvBufferPool        = flags.StringWithAllowedValues("recvBufferPool", recvBufferPoolSimple, "Configures the shared receive buffer pool. One of: nil, simple, all", allRecvBufferPools)
 	sharedWriteBuffer     = flags.StringWithAllowedValues("sharedWriteBuffer", toggleModeOff,
 		fmt.Sprintf("Configures both client and server to share write buffer - One of: %v", strings.Join(allToggleModes, ", ")), allToggleModes)
 
diff --git a/examples/features/health/README.md b/examples/features/health/README.md
index 1b8f0e89..926b7750 100644
--- a/examples/features/health/README.md
+++ b/examples/features/health/README.md
@@ -1,10 +1,13 @@
 # Health
 
-gRPC provides a health library to communicate a system's health to their clients.
-It works by providing a service definition via the [health/v1](https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto) api.
+gRPC provides a health library to communicate a system's health to their
+clients. It works by providing a service definition via the
+[health/v1](https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto)
+api.
 
-By using the health library, clients can gracefully avoid using servers as they encounter issues.
-Most languages provide an implementation out of box, making it interoperable between systems.
+Clients use the health library to gracefully avoid servers that encounter
+issues. Most languages provide an out-of-box implementation, which makes it
+interoperable between systems.
 
 ## Try it
 
@@ -21,13 +24,17 @@ go run client/main.go
 
 ### Client
 
-Clients have two ways to monitor a servers health.
-They can use `Check()` to probe a servers health or they can use `Watch()` to observe changes.
+Clients have two ways to monitor a server's health. They use `Check()` to probe
+a server's health or `Watch()` to observe changes.
 
-In most cases, clients do not need to directly check backend servers.
-Instead, they can do this transparently when a `healthCheckConfig` is specified in the [service config](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md#service-config-changes) and the [health package](https://pkg.go.dev/google.golang.org/grpc/health) is imported.
-The `serviceName` in `healthCheckConfig` will be used in the health check when connections are established.
-An empty string (`""`) typically indicates the overall health of a server should be reported.
+In most cases, clients do not directly check backend servers. Instead, they do
+this transparently when you specify a `healthCheckConfig` in the [service
+config](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md#service-config-changes)
+and import the [health
+package](https://pkg.go.dev/google.golang.org/grpc/health). The `serviceName` in
+`healthCheckConfig` is used in the health check when connections are
+established. An empty string (`""`) typically indicates the overall health of a
+server.
 
 ```go
 // import grpc/health to enable transparent client side checking
@@ -43,21 +50,25 @@ serviceConfig := grpc.WithDefaultServiceConfig(`{
 conn, err := grpc.NewClient(..., serviceConfig)
 ```
 
-See [A17 - Client-Side Health Checking](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md) for more details.
+See [A17 - Client-Side Health
+Checking](https://github.com/grpc/proposal/blob/master/A17-client-side-health-checking.md)
+for more details.
 
 ### Server
 
-Servers control their serving status.
-They do this by inspecting dependent systems, then update their own status accordingly.
-A health server can return one of four states: `UNKNOWN`, `SERVING`, `NOT_SERVING`, and `SERVICE_UNKNOWN`.
+Servers control their serving status. They do this by inspecting dependent
+systems, then update their status accordingly. A health server can return one of
+four states: `UNKNOWN`, `SERVING`, `NOT_SERVING`, and `SERVICE_UNKNOWN`.
 
-`UNKNOWN` indicates the current state is not yet known.
-This state is often seen at the start up of a server instance.
+`UNKNOWN` indicates the system does not yet know the current state. Servers
+often show this state at startup.
 
 `SERVING` means that the system is healthy and ready to service requests.
-Conversely, `NOT_SERVING` indicates the system is unable to service requests at the time.
+Conversely, `NOT_SERVING` indicates the system is unable to service requests at
+the time.
 
-`SERVICE_UNKNOWN` communicates the `serviceName` requested by the client is not known by the server.
-This status is only reported by the `Watch()` call.
+`SERVICE_UNKNOWN` communicates the `serviceName` requested by the client is not
+known by the server. This status is only reported by the `Watch()` call.
 
-A server may toggle its health using `healthServer.SetServingStatus("serviceName", servingStatus)`.
+A server may toggle its health using
+`healthServer.SetServingStatus("serviceName", servingStatus)`.
diff --git a/examples/features/interceptor/README.md b/examples/features/interceptor/README.md
index 68e6a965..bb9dff71 100644
--- a/examples/features/interceptor/README.md
+++ b/examples/features/interceptor/README.md
@@ -30,11 +30,14 @@ types of interceptors in total.
 
 The type for client-side unary interceptors is
 [`UnaryClientInterceptor`](https://godoc.org/google.golang.org/grpc#UnaryClientInterceptor).
-It is essentially a function type with signature: `func(ctx context.Context,
-method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker,
-opts ...CallOption) error`. Unary interceptor implementations can usually be
-divided into three parts: pre-processing, invoking the RPC method, and
-post-processing.
+It is a function type with signature:
+
+```golang
+func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error
+```
+
+Unary interceptor implementations can usually be divided into three parts:
+pre-processing, invoking the RPC method, and post-processing.
 
 For pre-processing, users can get info about the current RPC call by examining
 the args passed in. The args include the RPC context, method string, request to
@@ -59,22 +62,26 @@ To install a unary interceptor on a ClientConn, configure `Dial` with the
 
 The type for client-side stream interceptors is
 [`StreamClientInterceptor`](https://godoc.org/google.golang.org/grpc#StreamClientInterceptor).
-It is a function type with signature: `func(ctx context.Context, desc
-*StreamDesc, cc *ClientConn, method string, streamer Streamer, opts
-...CallOption) (ClientStream, error)`. An implementation of a stream interceptor
-usually includes pre-processing, and stream operation interception.
+It is a function type with signature:
+
+```golang
+func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)
+```
+
+An implementation of a stream interceptor usually includes pre-processing, and
+stream operation interception.
 
 The pre-processing is similar to unary interceptors.
 
 However, rather than invoking the RPC method followed by post-processing, stream
 interceptors intercept the users' operations on the stream. The interceptor
 first calls the passed-in `streamer` to get a `ClientStream`, and then wraps the
-`ClientStream` while overloading its methods with the interception logic.
+`ClientStream` while overriding its methods with the interception logic.
 Finally, the interceptor returns the wrapped `ClientStream` to user to operate
 on.
 
 In the example, we define a new struct `wrappedStream`, which embeds a
-`ClientStream`. We then implement (overload) the `SendMsg` and `RecvMsg` methods
+`ClientStream`. We then implement (override) the `SendMsg` and `RecvMsg` methods
 on `wrappedStream` to intercept these two operations on the embedded
 `ClientStream`. In the example, we log the message type info and time info for
 interception purpose.
@@ -92,9 +99,11 @@ different information provided as args.
 
 The type for server-side unary interceptors is
 [`UnaryServerInterceptor`](https://godoc.org/google.golang.org/grpc#UnaryServerInterceptor).
-It is a function type with signature: `func(ctx context.Context, req
-interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err
-error)`.
+It is a function type with signature:
+
+```golang
+func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
+```
 
 Refer to the client-side unary interceptor section for a detailed implementation
 and explanation.
@@ -107,8 +116,11 @@ To install a unary interceptor on a Server, configure `NewServer` with the
 
 The type for server-side stream interceptors is
 [`StreamServerInterceptor`](https://godoc.org/google.golang.org/grpc#StreamServerInterceptor).
-It is a function type with the signature: `func(srv interface{}, ss
-ServerStream, info *StreamServerInfo, handler StreamHandler) error`.
+It is a function type with the signature:
+
+```golang
+func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
+```
 
 Refer to the client-side stream interceptor section for a detailed
 implementation and explanation.
@@ -116,4 +128,3 @@ implementation and explanation.
 To install a stream interceptor on a Server, configure `NewServer` with the
 [`StreamInterceptor`](https://godoc.org/google.golang.org/grpc#StreamInterceptor)
 `ServerOption`.
-
diff --git a/experimental/stats/metricregistry.go b/experimental/stats/metricregistry.go
index ad75313a..2b57ba65 100644
--- a/experimental/stats/metricregistry.go
+++ b/experimental/stats/metricregistry.go
@@ -75,6 +75,7 @@ const (
 	MetricTypeIntHisto
 	MetricTypeFloatHisto
 	MetricTypeIntGauge
+	MetricTypeIntUpDownCount
 )
 
 // Int64CountHandle is a typed handle for a int count metric. This handle
@@ -93,6 +94,23 @@ func (h *Int64CountHandle) Record(recorder MetricsRecorder, incr int64, labels .
 	recorder.RecordInt64Count(h, incr, labels...)
 }
 
+// Int64UpDownCountHandle is a typed handle for an int up-down counter metric.
+// This handle is passed at the recording point in order to know which metric
+// to record on.
+type Int64UpDownCountHandle MetricDescriptor
+
+// Descriptor returns the int64 up-down counter handle typecast to a pointer to a
+// MetricDescriptor.
+func (h *Int64UpDownCountHandle) Descriptor() *MetricDescriptor {
+	return (*MetricDescriptor)(h)
+}
+
+// Record records the int64 up-down counter value on the metrics recorder provided.
+// The value 'v' can be positive to increment or negative to decrement.
+func (h *Int64UpDownCountHandle) Record(recorder MetricsRecorder, v int64, labels ...string) {
+	recorder.RecordInt64UpDownCount(h, v, labels...)
+}
+
 // Float64CountHandle is a typed handle for a float count metric. This handle is
 // passed at the recording point in order to know which metric to record on.
 type Float64CountHandle MetricDescriptor
@@ -249,6 +267,21 @@ func RegisterInt64Gauge(descriptor MetricDescriptor) *Int64GaugeHandle {
 	return (*Int64GaugeHandle)(descPtr)
 }
 
+// RegisterInt64UpDownCount registers the metric description onto the global registry.
+// It returns a typed handle to use for recording data.
+//
+// NOTE: this function must only be called during initialization time (i.e. in
+// an init() function), and is not thread-safe. If multiple metrics are
+// registered with the same name, this function will panic.
+func RegisterInt64UpDownCount(descriptor MetricDescriptor) *Int64UpDownCountHandle {
+	registerMetric(descriptor.Name, descriptor.Default)
+	// Set the specific metric type for the up-down counter
+	descriptor.Type = MetricTypeIntUpDownCount
+	descPtr := &descriptor
+	metricsRegistry[descriptor.Name] = descPtr
+	return (*Int64UpDownCountHandle)(descPtr)
+}
+
 // snapshotMetricsRegistryForTesting snapshots the global data of the metrics
 // registry. Returns a cleanup function that sets the metrics registry to its
 // original state.
diff --git a/experimental/stats/metrics.go b/experimental/stats/metrics.go
index ee142360..cb57f1a7 100644
--- a/experimental/stats/metrics.go
+++ b/experimental/stats/metrics.go
@@ -38,6 +38,9 @@ type MetricsRecorder interface {
 	// RecordInt64Gauge records the measurement alongside labels on the int
 	// gauge associated with the provided handle.
 	RecordInt64Gauge(handle *Int64GaugeHandle, incr int64, labels ...string)
+	// RecordInt64UpDownCounter records the measurement alongside labels on the int
+	// count associated with the provided handle.
+	RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string)
 }
 
 // Metrics is an experimental legacy alias of the now-stable stats.MetricSet.
diff --git a/internal/envconfig/envconfig.go b/internal/envconfig/envconfig.go
index 7e060f5e..293a9a40 100644
--- a/internal/envconfig/envconfig.go
+++ b/internal/envconfig/envconfig.go
@@ -75,6 +75,14 @@ var (
 	// ALTSHandshakerKeepaliveParams is set if we should add the
 	// KeepaliveParams when dial the ALTS handshaker service.
 	ALTSHandshakerKeepaliveParams = boolFromEnv("GRPC_EXPERIMENTAL_ALTS_HANDSHAKER_KEEPALIVE_PARAMS", false)
+
+	// EnableDefaultPortForProxyTarget controls whether the resolver adds a default port 443
+	// to a target address that lacks one. This flag only has an effect when all of
+	// the following conditions are met:
+	//   - A connect proxy is being used.
+	//   - Target resolution is disabled.
+	//   - The DNS resolver is being used.
+	EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true)
 )
 
 func boolFromEnv(envVar string, def bool) bool {
diff --git a/internal/resolver/delegatingresolver/delegatingresolver.go b/internal/resolver/delegatingresolver/delegatingresolver.go
index 20b8fb09..5bfa67b7 100644
--- a/internal/resolver/delegatingresolver/delegatingresolver.go
+++ b/internal/resolver/delegatingresolver/delegatingresolver.go
@@ -22,11 +22,13 @@ package delegatingresolver
 
 import (
 	"fmt"
+	"net"
 	"net/http"
 	"net/url"
 	"sync"
 
 	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/internal/envconfig"
 	"google.golang.org/grpc/internal/proxyattributes"
 	"google.golang.org/grpc/internal/transport"
 	"google.golang.org/grpc/internal/transport/networktype"
@@ -40,6 +42,8 @@ var (
 	HTTPSProxyFromEnvironment = http.ProxyFromEnvironment
 )
 
+const defaultPort = "443"
+
 // delegatingResolver manages both target URI and proxy address resolution by
 // delegating these tasks to separate child resolvers. Essentially, it acts as
 // an intermediary between the gRPC ClientConn and the child resolvers.
@@ -107,10 +111,18 @@ func New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOpti
 		targetResolver: nopResolver{},
 	}
 
+	addr := target.Endpoint()
 	var err error
-	r.proxyURL, err = proxyURLForTarget(target.Endpoint())
+	if target.URL.Scheme == "dns" && !targetResolutionEnabled && envconfig.EnableDefaultPortForProxyTarget {
+		addr, err = parseTarget(addr)
+		if err != nil {
+			return nil, fmt.Errorf("delegating_resolver: invalid target address %q: %v", target.Endpoint(), err)
+		}
+	}
+
+	r.proxyURL, err = proxyURLForTarget(addr)
 	if err != nil {
-		return nil, fmt.Errorf("delegating_resolver: failed to determine proxy URL for target %s: %v", target, err)
+		return nil, fmt.Errorf("delegating_resolver: failed to determine proxy URL for target %q: %v", target, err)
 	}
 
 	// proxy is not configured or proxy address excluded using `NO_PROXY` env
@@ -132,8 +144,8 @@ func New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOpti
 	// bypass the target resolver and store the unresolved target address.
 	if target.URL.Scheme == "dns" && !targetResolutionEnabled {
 		r.targetResolverState = &resolver.State{
-			Addresses: []resolver.Address{{Addr: target.Endpoint()}},
-			Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: target.Endpoint()}}}},
+			Addresses: []resolver.Address{{Addr: addr}},
+			Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: addr}}}},
 		}
 		r.updateTargetResolverState(*r.targetResolverState)
 		return r, nil
@@ -202,6 +214,44 @@ func needsProxyResolver(state *resolver.State) bool {
 	return false
 }
 
+// parseTarget takes a target string and ensures it is a valid "host:port" target.
+//
+// It does the following:
+//  1. If the target already has a port (e.g., "host:port", "[ipv6]:port"),
+//     it is returned as is.
+//  2. If the host part is empty (e.g., ":80"), it defaults to "localhost",
+//     returning "localhost:80".
+//  3. If the target is missing a port (e.g., "host", "ipv6"), the defaultPort
+//     is added.
+//
+// An error is returned for empty targets or targets with a trailing colon
+// but no port (e.g., "host:").
+func parseTarget(target string) (string, error) {
+	if target == "" {
+		return "", fmt.Errorf("missing address")
+	}
+
+	host, port, err := net.SplitHostPort(target)
+	if err != nil {
+		// If SplitHostPort fails, it's likely because the port is missing.
+		// We append the default port and return the result.
+		return net.JoinHostPort(target, defaultPort), nil
+	}
+
+	// If SplitHostPort succeeds, we check for edge cases.
+	if port == "" {
+		// A success with an empty port means the target had a trailing colon,
+		// e.g., "host:", which is an error.
+		return "", fmt.Errorf("missing port after port-separator colon")
+	}
+	if host == "" {
+		// A success with an empty host means the target was like ":80".
+		// We default the host to "localhost".
+		host = "localhost"
+	}
+	return net.JoinHostPort(host, port), nil
+}
+
 func skipProxy(address resolver.Address) bool {
 	// Avoid proxy when network is not tcp.
 	networkType, ok := networktype.Get(address)
diff --git a/internal/stats/metrics_recorder_list.go b/internal/stats/metrics_recorder_list.go
index 79044657..d5f7e4d6 100644
--- a/internal/stats/metrics_recorder_list.go
+++ b/internal/stats/metrics_recorder_list.go
@@ -64,6 +64,16 @@ func (l *MetricsRecorderList) RecordInt64Count(handle *estats.Int64CountHandle,
 	}
 }
 
+// RecordInt64UpDownCount records the measurement alongside labels on the int
+// count associated with the provided handle.
+func (l *MetricsRecorderList) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
+	verifyLabels(handle.Descriptor(), labels...)
+
+	for _, metricRecorder := range l.metricsRecorders {
+		metricRecorder.RecordInt64UpDownCount(handle, incr, labels...)
+	}
+}
+
 // RecordFloat64Count records the measurement alongside labels on the float
 // count associated with the provided handle.
 func (l *MetricsRecorderList) RecordFloat64Count(handle *estats.Float64CountHandle, incr float64, labels ...string) {
diff --git a/internal/transport/client_stream.go b/internal/transport/client_stream.go
index ccc0e017..98045251 100644
--- a/internal/transport/client_stream.go
+++ b/internal/transport/client_stream.go
@@ -29,25 +29,27 @@ import (
 
 // ClientStream implements streaming functionality for a gRPC client.
 type ClientStream struct {
-	*Stream // Embed for common stream functionality.
+	Stream // Embed for common stream functionality.
 
 	ct       *http2Client
 	done     chan struct{} // closed at the end of stream to unblock writers.
 	doneFunc func()        // invoked at the end of stream.
 
-	headerChan       chan struct{} // closed to indicate the end of header metadata.
-	headerChanClosed uint32        // set when headerChan is closed. Used to avoid closing headerChan multiple times.
+	headerChan chan struct{} // closed to indicate the end of header metadata.
+	header     metadata.MD   // the received header metadata
+
+	status *status.Status // the status error received from the server
+
+	// Non-pointer fields are at the end to optimize GC allocations.
+
 	// headerValid indicates whether a valid header was received.  Only
 	// meaningful after headerChan is closed (always call waitOnHeader() before
 	// reading its value).
-	headerValid bool
-	header      metadata.MD // the received header metadata
-	noHeaders   bool        // set if the client never received headers (set only after the stream is done).
-
-	bytesReceived atomic.Bool // indicates whether any bytes have been received on this stream
-	unprocessed   atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream
-
-	status *status.Status // the status error received from the server
+	headerValid      bool
+	noHeaders        bool        // set if the client never received headers (set only after the stream is done).
+	headerChanClosed uint32      // set when headerChan is closed. Used to avoid closing headerChan multiple times.
+	bytesReceived    atomic.Bool // indicates whether any bytes have been received on this stream
+	unprocessed      atomic.Bool // set if the server sends a refused stream or GOAWAY including this stream
 }
 
 // Read reads an n byte message from the input stream.
@@ -142,3 +144,11 @@ func (s *ClientStream) TrailersOnly() bool {
 func (s *ClientStream) Status() *status.Status {
 	return s.status
 }
+
+func (s *ClientStream) requestRead(n int) {
+	s.ct.adjustWindow(s, uint32(n))
+}
+
+func (s *ClientStream) updateWindow(n int) {
+	s.ct.updateWindow(s, uint32(n))
+}
diff --git a/internal/transport/flowcontrol.go b/internal/transport/flowcontrol.go
index b4ca86d7..7cfbc963 100644
--- a/internal/transport/flowcontrol.go
+++ b/internal/transport/flowcontrol.go
@@ -28,7 +28,7 @@ import (
 // writeQuota is a soft limit on the amount of data a stream can
 // schedule before some of it is written out.
 type writeQuota struct {
-	quota int32
+	_ noCopy
 	// get waits on read from when quota goes less than or equal to zero.
 	// replenish writes on it when quota goes positive again.
 	ch chan struct{}
@@ -38,16 +38,17 @@ type writeQuota struct {
 	// It is implemented as a field so that it can be updated
 	// by tests.
 	replenish func(n int)
+	quota     int32
 }
 
-func newWriteQuota(sz int32, done <-chan struct{}) *writeQuota {
-	w := &writeQuota{
-		quota: sz,
-		ch:    make(chan struct{}, 1),
-		done:  done,
-	}
+// init allows a writeQuota to be initialized in-place, which is useful for
+// resetting a buffer or for avoiding a heap allocation when the buffer is
+// embedded in another struct.
+func (w *writeQuota) init(sz int32, done <-chan struct{}) {
+	w.quota = sz
+	w.ch = make(chan struct{}, 1)
+	w.done = done
 	w.replenish = w.realReplenish
-	return w
 }
 
 func (w *writeQuota) get(sz int32) error {
diff --git a/internal/transport/handler_server.go b/internal/transport/handler_server.go
index d954a64c..80ef2d00 100644
--- a/internal/transport/handler_server.go
+++ b/internal/transport/handler_server.go
@@ -387,6 +387,12 @@ func (ht *serverHandlerTransport) writeHeader(s *ServerStream, md metadata.MD) e
 	return err
 }
 
+func (ht *serverHandlerTransport) adjustWindow(*ServerStream, uint32) {
+}
+
+func (ht *serverHandlerTransport) updateWindow(*ServerStream, uint32) {
+}
+
 func (ht *serverHandlerTransport) HandleStreams(ctx context.Context, startStream func(*ServerStream)) {
 	// With this transport type there will be exactly 1 stream: this HTTP request.
 	var cancel context.CancelFunc
@@ -411,11 +417,9 @@ func (ht *serverHandlerTransport) HandleStreams(ctx context.Context, startStream
 	ctx = metadata.NewIncomingContext(ctx, ht.headerMD)
 	req := ht.req
 	s := &ServerStream{
-		Stream: &Stream{
+		Stream: Stream{
 			id:             0, // irrelevant
 			ctx:            ctx,
-			requestRead:    func(int) {},
-			buf:            newRecvBuffer(),
 			method:         req.URL.Path,
 			recvCompress:   req.Header.Get("grpc-encoding"),
 			contentSubtype: ht.contentSubtype,
@@ -424,9 +428,11 @@ func (ht *serverHandlerTransport) HandleStreams(ctx context.Context, startStream
 		st:               ht,
 		headerWireLength: 0, // won't have access to header wire length until golang/go#18997.
 	}
-	s.trReader = &transportReader{
-		reader:        &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf},
-		windowHandler: func(int) {},
+	s.Stream.buf.init()
+	s.readRequester = s
+	s.trReader = transportReader{
+		reader:        recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: &s.buf},
+		windowHandler: s,
 	}
 
 	// readerDone is closed when the Body.Read-ing goroutine exits.
diff --git a/internal/transport/http2_client.go b/internal/transport/http2_client.go
index 7cb23879..911d7e1e 100644
--- a/internal/transport/http2_client.go
+++ b/internal/transport/http2_client.go
@@ -481,10 +481,9 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
 func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientStream {
 	// TODO(zhaoq): Handle uint32 overflow of Stream.id.
 	s := &ClientStream{
-		Stream: &Stream{
+		Stream: Stream{
 			method:         callHdr.Method,
 			sendCompress:   callHdr.SendCompress,
-			buf:            newRecvBuffer(),
 			contentSubtype: callHdr.ContentSubtype,
 		},
 		ct:         t,
@@ -492,26 +491,23 @@ func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *ClientSt
 		headerChan: make(chan struct{}),
 		doneFunc:   callHdr.DoneFunc,
 	}
-	s.wq = newWriteQuota(defaultWriteQuota, s.done)
-	s.requestRead = func(n int) {
-		t.adjustWindow(s, uint32(n))
-	}
+	s.Stream.buf.init()
+	s.Stream.wq.init(defaultWriteQuota, s.done)
+	s.readRequester = s
 	// The client side stream context should have exactly the same life cycle with the user provided context.
 	// That means, s.ctx should be read-only. And s.ctx is done iff ctx is done.
 	// So we use the original context here instead of creating a copy.
 	s.ctx = ctx
-	s.trReader = &transportReader{
-		reader: &recvBufferReader{
+	s.trReader = transportReader{
+		reader: recvBufferReader{
 			ctx:     s.ctx,
 			ctxDone: s.ctx.Done(),
-			recv:    s.buf,
+			recv:    &s.buf,
 			closeStream: func(err error) {
 				s.Close(err)
 			},
 		},
-		windowHandler: func(n int) {
-			t.updateWindow(s, uint32(n))
-		},
+		windowHandler: s,
 	}
 	return s
 }
@@ -823,7 +819,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS
 			return nil
 		},
 		onOrphaned: cleanup,
-		wq:         s.wq,
+		wq:         &s.wq,
 	}
 	firstTry := true
 	var ch chan struct{}
@@ -854,7 +850,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*ClientS
 		transportDrainRequired = t.nextID > MaxStreamID
 
 		s.id = hdr.streamID
-		s.fc = &inFlow{limit: uint32(t.initialWindowSize)}
+		s.fc = inFlow{limit: uint32(t.initialWindowSize)}
 		t.activeStreams[s.id] = s
 		t.mu.Unlock()
 
@@ -1002,6 +998,9 @@ func (t *http2Client) closeStream(s *ClientStream, err error, rst bool, rstCode
 // accessed anymore.
 func (t *http2Client) Close(err error) {
 	t.conn.SetWriteDeadline(time.Now().Add(time.Second * 10))
+	// For background on the deadline value chosen here, see
+	// https://github.com/grpc/grpc-go/issues/8425#issuecomment-3057938248 .
+	t.conn.SetReadDeadline(time.Now().Add(time.Second))
 	t.mu.Lock()
 	// Make sure we only close once.
 	if t.state == closing {
@@ -1477,17 +1476,14 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
 		contentTypeErr = "malformed header: missing HTTP content-type"
 		grpcMessage    string
 		recvCompress   string
-		httpStatusCode *int
 		httpStatusErr  string
-		rawStatusCode  = codes.Unknown
+		// the code from the grpc-status header, if present
+		grpcStatusCode = codes.Internal
 		// headerError is set if an error is encountered while parsing the headers
 		headerError string
+		httpStatus  string
 	)
 
-	if initialHeader {
-		httpStatusErr = "malformed header: missing HTTP status"
-	}
-
 	for _, hf := range frame.Fields {
 		switch hf.Name {
 		case "content-type":
@@ -1507,69 +1503,71 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
 				t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
 				return
 			}
-			rawStatusCode = codes.Code(uint32(code))
+			grpcStatusCode = codes.Code(uint32(code))
 		case "grpc-message":
 			grpcMessage = decodeGrpcMessage(hf.Value)
 		case ":status":
-			c, err := strconv.ParseInt(hf.Value, 10, 32)
+			httpStatus = hf.Value
+		default:
+			if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
+				break
+			}
+			v, err := decodeMetadataHeader(hf.Name, hf.Value)
+			if err != nil {
+				headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
+				logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
+				break
+			}
+			mdata[hf.Name] = append(mdata[hf.Name], v)
+		}
+	}
+
+	// If a non-gRPC response is received, then evaluate the HTTP status to
+	// process the response and close the stream.
+	// In case http status doesn't provide any error information (status : 200),
+	// then evalute response code to be Unknown.
+	if !isGRPC {
+		var grpcErrorCode = codes.Internal
+		if httpStatus == "" {
+			httpStatusErr = "malformed header: missing HTTP status"
+		} else {
+			// Parse the status codes (e.g. "200", 404").
+			statusCode, err := strconv.Atoi(httpStatus)
 			if err != nil {
-				se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
+				se := status.New(grpcErrorCode, fmt.Sprintf("transport: malformed http-status: %v", err))
 				t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
 				return
 			}
-			statusCode := int(c)
 			if statusCode >= 100 && statusCode < 200 {
 				if endStream {
 					se := status.New(codes.Internal, fmt.Sprintf(
 						"protocol error: informational header with status code %d must not have END_STREAM set", statusCode))
 					t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
 				}
+				// In case of informational headers, return.
 				return
 			}
-			httpStatusCode = &statusCode
-			if statusCode == 200 {
-				httpStatusErr = ""
-				break
-			}
-
 			httpStatusErr = fmt.Sprintf(
 				"unexpected HTTP status code received from server: %d (%s)",
 				statusCode,
 				http.StatusText(statusCode),
 			)
-		default:
-			if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
-				break
-			}
-			v, err := decodeMetadataHeader(hf.Name, hf.Value)
-			if err != nil {
-				headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
-				logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
-				break
-			}
-			mdata[hf.Name] = append(mdata[hf.Name], v)
-		}
-	}
-
-	if !isGRPC || httpStatusErr != "" {
-		var code = codes.Internal // when header does not include HTTP status, return INTERNAL
-
-		if httpStatusCode != nil {
 			var ok bool
-			code, ok = HTTPStatusConvTab[*httpStatusCode]
+			grpcErrorCode, ok = HTTPStatusConvTab[statusCode]
 			if !ok {
-				code = codes.Unknown
+				grpcErrorCode = codes.Unknown
 			}
 		}
 		var errs []string
 		if httpStatusErr != "" {
 			errs = append(errs, httpStatusErr)
 		}
+
 		if contentTypeErr != "" {
 			errs = append(errs, contentTypeErr)
 		}
-		// Verify the HTTP response is a 200.
-		se := status.New(code, strings.Join(errs, "; "))
+
+		se := status.New(grpcErrorCode, strings.Join(errs, "; "))
 		t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
 		return
 	}
@@ -1623,7 +1621,7 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
 		return
 	}
 
-	status := istatus.NewWithProto(rawStatusCode, grpcMessage, mdata[grpcStatusDetailsBinHeader])
+	status := istatus.NewWithProto(grpcStatusCode, grpcMessage, mdata[grpcStatusDetailsBinHeader])
 
 	// If client received END_STREAM from server while stream was still active,
 	// send RST_STREAM.
diff --git a/internal/transport/http2_server.go b/internal/transport/http2_server.go
index 83cee314..bcedac32 100644
--- a/internal/transport/http2_server.go
+++ b/internal/transport/http2_server.go
@@ -35,6 +35,8 @@ import (
 
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
+	"google.golang.org/protobuf/proto"
+
 	"google.golang.org/grpc/internal"
 	"google.golang.org/grpc/internal/grpclog"
 	"google.golang.org/grpc/internal/grpcutil"
@@ -42,7 +44,6 @@ import (
 	istatus "google.golang.org/grpc/internal/status"
 	"google.golang.org/grpc/internal/syscall"
 	"google.golang.org/grpc/mem"
-	"google.golang.org/protobuf/proto"
 
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
@@ -390,16 +391,15 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade
 	}
 	t.maxStreamID = streamID
 
-	buf := newRecvBuffer()
 	s := &ServerStream{
-		Stream: &Stream{
-			id:  streamID,
-			buf: buf,
-			fc:  &inFlow{limit: uint32(t.initialWindowSize)},
+		Stream: Stream{
+			id: streamID,
+			fc: inFlow{limit: uint32(t.initialWindowSize)},
 		},
 		st:               t,
 		headerWireLength: int(frame.Header().Length),
 	}
+	s.Stream.buf.init()
 	var (
 		// if false, content-type was missing or invalid
 		isGRPC      = false
@@ -640,25 +640,21 @@ func (t *http2Server) operateHeaders(ctx context.Context, frame *http2.MetaHeade
 		t.channelz.SocketMetrics.StreamsStarted.Add(1)
 		t.channelz.SocketMetrics.LastRemoteStreamCreatedTimestamp.Store(time.Now().UnixNano())
 	}
-	s.requestRead = func(n int) {
-		t.adjustWindow(s, uint32(n))
-	}
+	s.readRequester = s
 	s.ctxDone = s.ctx.Done()
-	s.wq = newWriteQuota(defaultWriteQuota, s.ctxDone)
-	s.trReader = &transportReader{
-		reader: &recvBufferReader{
+	s.Stream.wq.init(defaultWriteQuota, s.ctxDone)
+	s.trReader = transportReader{
+		reader: recvBufferReader{
 			ctx:     s.ctx,
 			ctxDone: s.ctxDone,
-			recv:    s.buf,
-		},
-		windowHandler: func(n int) {
-			t.updateWindow(s, uint32(n))
+			recv:    &s.buf,
 		},
+		windowHandler: s,
 	}
 	// Register the stream with loopy.
 	t.controlBuf.put(&registerStream{
 		streamID: s.id,
-		wq:       s.wq,
+		wq:       &s.wq,
 	})
 	handle(s)
 	return nil
@@ -1305,7 +1301,8 @@ func (t *http2Server) Close(err error) {
 // deleteStream deletes the stream s from transport's active streams.
 func (t *http2Server) deleteStream(s *ServerStream, eosReceived bool) {
 	t.mu.Lock()
-	if _, ok := t.activeStreams[s.id]; ok {
+	_, isActive := t.activeStreams[s.id]
+	if isActive {
 		delete(t.activeStreams, s.id)
 		if len(t.activeStreams) == 0 {
 			t.idle = time.Now()
@@ -1313,7 +1310,7 @@ func (t *http2Server) deleteStream(s *ServerStream, eosReceived bool) {
 	}
 	t.mu.Unlock()
 
-	if channelz.IsOn() {
+	if isActive && channelz.IsOn() {
 		if eosReceived {
 			t.channelz.SocketMetrics.StreamsSucceeded.Add(1)
 		} else {
diff --git a/internal/transport/server_stream.go b/internal/transport/server_stream.go
index cf8da0b5..ed6a13b7 100644
--- a/internal/transport/server_stream.go
+++ b/internal/transport/server_stream.go
@@ -32,7 +32,7 @@ import (
 
 // ServerStream implements streaming functionality for a gRPC server.
 type ServerStream struct {
-	*Stream // Embed for common stream functionality.
+	Stream // Embed for common stream functionality.
 
 	st      internalServerTransport
 	ctxDone <-chan struct{} // closed at the end of stream.  Cache of ctx.Done() (for performance)
@@ -43,12 +43,13 @@ type ServerStream struct {
 	// Holds compressor names passed in grpc-accept-encoding metadata from the
 	// client.
 	clientAdvertisedCompressors string
-	headerWireLength            int
 
 	// hdrMu protects outgoing header and trailer metadata.
 	hdrMu      sync.Mutex
 	header     metadata.MD // the outgoing header metadata.  Updated by WriteHeader.
 	headerSent atomic.Bool // atomically set when the headers are sent out.
+
+	headerWireLength int
 }
 
 // Read reads an n byte message from the input stream.
@@ -178,3 +179,11 @@ func (s *ServerStream) SetTrailer(md metadata.MD) error {
 	s.hdrMu.Unlock()
 	return nil
 }
+
+func (s *ServerStream) requestRead(n int) {
+	s.st.adjustWindow(s, uint32(n))
+}
+
+func (s *ServerStream) updateWindow(n int) {
+	s.st.updateWindow(s, uint32(n))
+}
diff --git a/internal/transport/transport.go b/internal/transport/transport.go
index 7dd53e80..9565b400 100644
--- a/internal/transport/transport.go
+++ b/internal/transport/transport.go
@@ -68,11 +68,11 @@ type recvBuffer struct {
 	err     error
 }
 
-func newRecvBuffer() *recvBuffer {
-	b := &recvBuffer{
-		c: make(chan recvMsg, 1),
-	}
-	return b
+// init allows a recvBuffer to be initialized in-place, which is useful
+// for resetting a buffer or for avoiding a heap allocation when the buffer
+// is embedded in another struct.
+func (b *recvBuffer) init() {
+	b.c = make(chan recvMsg, 1)
 }
 
 func (b *recvBuffer) put(r recvMsg) {
@@ -123,6 +123,7 @@ func (b *recvBuffer) get() <-chan recvMsg {
 // recvBufferReader implements io.Reader interface to read the data from
 // recvBuffer.
 type recvBufferReader struct {
+	_           noCopy
 	closeStream func(error) // Closes the client transport stream with the given error and nil trailer metadata.
 	ctx         context.Context
 	ctxDone     <-chan struct{} // cache of ctx.Done() (for performance).
@@ -285,27 +286,32 @@ const (
 
 // Stream represents an RPC in the transport layer.
 type Stream struct {
-	id           uint32
 	ctx          context.Context // the associated context of the stream
 	method       string          // the associated RPC method of the stream
 	recvCompress string
 	sendCompress string
-	buf          *recvBuffer
-	trReader     *transportReader
-	fc           *inFlow
-	wq           *writeQuota
-
-	// Callback to state application's intentions to read data. This
-	// is used to adjust flow control, if needed.
-	requestRead func(int)
 
-	state streamState
+	readRequester readRequester
 
 	// contentSubtype is the content-subtype for requests.
 	// this must be lowercase or the behavior is undefined.
 	contentSubtype string
 
 	trailer metadata.MD // the key-value map of trailer metadata.
+
+	// Non-pointer fields are at the end to optimize GC performance.
+	state    streamState
+	id       uint32
+	buf      recvBuffer
+	trReader transportReader
+	fc       inFlow
+	wq       writeQuota
+}
+
+// readRequester is used to state application's intentions to read data. This
+// is used to adjust flow control, if needed.
+type readRequester interface {
+	requestRead(int)
 }
 
 func (s *Stream) swapState(st streamState) streamState {
@@ -355,7 +361,7 @@ func (s *Stream) ReadMessageHeader(header []byte) (err error) {
 	if er := s.trReader.er; er != nil {
 		return er
 	}
-	s.requestRead(len(header))
+	s.readRequester.requestRead(len(header))
 	for len(header) != 0 {
 		n, err := s.trReader.ReadMessageHeader(header)
 		header = header[n:]
@@ -378,7 +384,7 @@ func (s *Stream) read(n int) (data mem.BufferSlice, err error) {
 	if er := s.trReader.er; er != nil {
 		return nil, er
 	}
-	s.requestRead(n)
+	s.readRequester.requestRead(n)
 	for n != 0 {
 		buf, err := s.trReader.Read(n)
 		var bufLen int
@@ -401,16 +407,34 @@ func (s *Stream) read(n int) (data mem.BufferSlice, err error) {
 	return data, nil
 }
 
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://golang.org/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct {
+}
+
+func (*noCopy) Lock()   {}
+func (*noCopy) Unlock() {}
+
 // transportReader reads all the data available for this Stream from the transport and
 // passes them into the decoder, which converts them into a gRPC message stream.
 // The error is io.EOF when the stream is done or another non-nil error if
 // the stream broke.
 type transportReader struct {
-	reader *recvBufferReader
+	_ noCopy
 	// The handler to control the window update procedure for both this
 	// particular stream and the associated transport.
-	windowHandler func(int)
+	windowHandler windowHandler
 	er            error
+	reader        recvBufferReader
+}
+
+// The handler to control the window update procedure for both this
+// particular stream and the associated transport.
+type windowHandler interface {
+	updateWindow(int)
 }
 
 func (t *transportReader) ReadMessageHeader(header []byte) (int, error) {
@@ -419,7 +443,7 @@ func (t *transportReader) ReadMessageHeader(header []byte) (int, error) {
 		t.er = err
 		return 0, err
 	}
-	t.windowHandler(n)
+	t.windowHandler.updateWindow(n)
 	return n, nil
 }
 
@@ -429,7 +453,7 @@ func (t *transportReader) Read(n int) (mem.Buffer, error) {
 		t.er = err
 		return buf, err
 	}
-	t.windowHandler(buf.Len())
+	t.windowHandler.updateWindow(buf.Len())
 	return buf, nil
 }
 
@@ -615,6 +639,8 @@ type internalServerTransport interface {
 	write(s *ServerStream, hdr []byte, data mem.BufferSlice, opts *WriteOptions) error
 	writeStatus(s *ServerStream, st *status.Status) error
 	incrMsgRecv()
+	adjustWindow(s *ServerStream, n uint32)
+	updateWindow(s *ServerStream, n uint32)
 }
 
 // connectionErrorf creates an ConnectionError with the specified error description.
diff --git a/internal/xds/balancer/clusterimpl/clusterimpl.go b/internal/xds/balancer/clusterimpl/clusterimpl.go
index 72d143a3..cdc7bf2e 100644
--- a/internal/xds/balancer/clusterimpl/clusterimpl.go
+++ b/internal/xds/balancer/clusterimpl/clusterimpl.go
@@ -163,6 +163,7 @@ func (b *clusterImplBalancer) newPickerLocked() *picker {
 		counter:         b.requestCounter,
 		countMax:        b.requestCountMax,
 		telemetryLabels: b.telemetryLabels,
+		clusterName:     b.clusterName,
 	}
 }
 
@@ -414,6 +415,7 @@ func (b *clusterImplBalancer) getClusterName() string {
 // SubConn to the wrapper for this purpose.
 type scWrapper struct {
 	balancer.SubConn
+
 	// locality needs to be atomic because it can be updated while being read by
 	// the picker.
 	locality atomic.Pointer[clients.Locality]
diff --git a/internal/xds/balancer/clusterimpl/picker.go b/internal/xds/balancer/clusterimpl/picker.go
index fab09fa0..0c033261 100644
--- a/internal/xds/balancer/clusterimpl/picker.go
+++ b/internal/xds/balancer/clusterimpl/picker.go
@@ -20,6 +20,7 @@ package clusterimpl
 
 import (
 	"context"
+	"maps"
 
 	v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
 	"google.golang.org/grpc/balancer"
@@ -87,6 +88,7 @@ type picker struct {
 	counter         *xdsclient.ClusterRequestsCounter
 	countMax        uint32
 	telemetryLabels map[string]string
+	clusterName     string
 }
 
 func telemetryLabels(ctx context.Context) map[string]string {
@@ -103,10 +105,9 @@ func telemetryLabels(ctx context.Context) map[string]string {
 func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
 	// Unconditionally set labels if present, even dropped or queued RPC's can
 	// use these labels.
-	if labels := telemetryLabels(info.Ctx); labels != nil {
-		for key, value := range d.telemetryLabels {
-			labels[key] = value
-		}
+	labels := telemetryLabels(info.Ctx)
+	if labels != nil {
+		maps.Copy(labels, d.telemetryLabels)
 	}
 
 	// Don't drop unless the inner picker is READY. Similar to
@@ -154,8 +155,9 @@ func (d *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
 		return pr, err
 	}
 
-	if labels := telemetryLabels(info.Ctx); labels != nil {
+	if labels != nil {
 		labels["grpc.lb.locality"] = xdsinternal.LocalityString(lID)
+		labels["grpc.lb.backend_service"] = d.clusterName
 	}
 
 	if d.loadStore != nil {
diff --git a/internal/xds/clients/xdsclient/ads_stream.go b/internal/xds/clients/xdsclient/ads_stream.go
index 374f137c..3ad62ac1 100644
--- a/internal/xds/clients/xdsclient/ads_stream.go
+++ b/internal/xds/clients/xdsclient/ads_stream.go
@@ -22,7 +22,6 @@ import (
 	"context"
 	"fmt"
 	"sync"
-	"sync/atomic"
 	"time"
 
 	"google.golang.org/grpc/grpclog"
@@ -103,11 +102,11 @@ type adsStreamImpl struct {
 	requestCh    *buffer.Unbounded   // Subscriptions and unsubscriptions are pushed here.
 	runnerDoneCh chan struct{}       // Notify completion of runner goroutine.
 	cancel       context.CancelFunc  // To cancel the context passed to the runner goroutine.
+	fc           *adsFlowControl     // Flow control for ADS stream.
 
 	// Guards access to the below fields (and to the contents of the map).
 	mu                sync.Mutex
 	resourceTypeState map[ResourceType]*resourceTypeState // Map of resource types to their state.
-	fc                *adsFlowControl                     // Flow control for ADS stream.
 	firstRequest      bool                                // False after the first request is sent out.
 }
 
@@ -135,6 +134,7 @@ func newADSStreamImpl(opts adsStreamOpts) *adsStreamImpl {
 		streamCh:          make(chan clients.Stream, 1),
 		requestCh:         buffer.NewUnbounded(),
 		runnerDoneCh:      make(chan struct{}),
+		fc:                newADSFlowControl(),
 		resourceTypeState: make(map[ResourceType]*resourceTypeState),
 	}
 
@@ -150,6 +150,7 @@ func newADSStreamImpl(opts adsStreamOpts) *adsStreamImpl {
 // Stop blocks until the stream is closed and all spawned goroutines exit.
 func (s *adsStreamImpl) Stop() {
 	s.cancel()
+	s.fc.stop()
 	s.requestCh.Close()
 	<-s.runnerDoneCh
 	s.logger.Infof("Shutdown ADS stream")
@@ -240,9 +241,6 @@ func (s *adsStreamImpl) runner(ctx context.Context) {
 		}
 
 		s.mu.Lock()
-		// Flow control is a property of the underlying streaming RPC call and
-		// needs to be initialized everytime a new one is created.
-		s.fc = newADSFlowControl(s.logger)
 		s.firstRequest = true
 		s.mu.Unlock()
 
@@ -256,7 +254,7 @@ func (s *adsStreamImpl) runner(ctx context.Context) {
 
 		// Backoff state is reset upon successful receipt of at least one
 		// message from the server.
-		if s.recv(ctx, stream) {
+		if s.recv(stream) {
 			return backoff.ErrResetBackoff
 		}
 		return nil
@@ -318,11 +316,13 @@ func (s *adsStreamImpl) sendNew(stream clients.Stream, typ ResourceType) error {
 	// This allows us to batch writes for requests which are generated as part
 	// of local processing of a received response.
 	state := s.resourceTypeState[typ]
-	if s.fc.pending.Load() {
+	bufferRequest := func() {
 		select {
 		case state.bufferedRequests <- struct{}{}:
 		default:
 		}
+	}
+	if s.fc.runIfPending(bufferRequest) {
 		return nil
 	}
 
@@ -477,18 +477,19 @@ func (s *adsStreamImpl) sendMessageLocked(stream clients.Stream, names []string,
 //
 // It returns a boolean indicating whether at least one message was received
 // from the server.
-func (s *adsStreamImpl) recv(ctx context.Context, stream clients.Stream) bool {
+func (s *adsStreamImpl) recv(stream clients.Stream) bool {
 	msgReceived := false
 	for {
-		// Wait for ADS stream level flow control to be available, and send out
-		// a request if anything was buffered while we were waiting for local
-		// processing of the previous response to complete.
-		if !s.fc.wait(ctx) {
+		// Wait for ADS stream level flow control to be available.
+		if s.fc.wait() {
 			if s.logger.V(2) {
-				s.logger.Infof("ADS stream context canceled")
+				s.logger.Infof("ADS stream stopped while waiting for flow control")
 			}
 			return msgReceived
 		}
+
+		// Send out a request if anything was buffered while we were waiting for
+		// local processing of the previous response to complete.
 		s.sendBuffered(stream)
 
 		resources, url, version, nonce, err := s.recvMessage(stream)
@@ -508,8 +509,8 @@ func (s *adsStreamImpl) recv(ctx context.Context, stream clients.Stream) bool {
 		}
 		var resourceNames []string
 		var nackErr error
-		s.fc.setPending()
-		resourceNames, nackErr = s.eventHandler.onResponse(resp, s.fc.onDone)
+		s.fc.setPending(true)
+		resourceNames, nackErr = s.eventHandler.onResponse(resp, sync.OnceFunc(func() { s.fc.setPending(false) }))
 		if xdsresource.ErrType(nackErr) == xdsresource.ErrorTypeResourceTypeUnsupported {
 			// A general guiding principle is that if the server sends
 			// something the client didn't actually subscribe to, then the
@@ -707,69 +708,84 @@ func resourceNames(m map[string]*xdsresource.ResourceWatchState) []string {
 	return ret
 }
 
-// adsFlowControl implements ADS stream level flow control that enables the
-// transport to block the reading of the next message off of the stream until
-// the previous update is consumed by all watchers.
+// adsFlowControl implements ADS stream level flow control that enables the ADS
+// stream to block the reading of the next message until the previous update is
+// consumed by all watchers.
 //
-// The lifetime of the flow control is tied to the lifetime of the stream.
+// The lifetime of the flow control is tied to the lifetime of the stream. When
+// the stream is closed, it is the responsibility of the caller to stop the flow
+// control. This ensures that any goroutine blocked on the flow control's wait
+// method is unblocked.
 type adsFlowControl struct {
-	logger *igrpclog.PrefixLogger
-
-	// Whether the most recent update is pending consumption by all watchers.
-	pending atomic.Bool
-	// Channel used to notify when all the watchers have consumed the most
-	// recent update. Wait() blocks on reading a value from this channel.
-	readyCh chan struct{}
+	mu sync.Mutex
+	// cond is used to signal when the most recent update has been consumed, or
+	// the flow control has been stopped (in which case, waiters should be
+	// unblocked as well).
+	cond    *sync.Cond
+	pending bool // indicates if the most recent update is pending consumption
+	stopped bool // indicates if the ADS stream has been stopped
 }
 
 // newADSFlowControl returns a new adsFlowControl.
-func newADSFlowControl(logger *igrpclog.PrefixLogger) *adsFlowControl {
-	return &adsFlowControl{
-		logger:  logger,
-		readyCh: make(chan struct{}, 1),
-	}
+func newADSFlowControl() *adsFlowControl {
+	fc := &adsFlowControl{}
+	fc.cond = sync.NewCond(&fc.mu)
+	return fc
 }
 
-// setPending changes the internal state to indicate that there is an update
-// pending consumption by all watchers.
-func (fc *adsFlowControl) setPending() {
-	fc.pending.Store(true)
+// stop marks the flow control as stopped and signals the condition variable to
+// unblock any goroutine waiting on it.
+func (fc *adsFlowControl) stop() {
+	fc.mu.Lock()
+	defer fc.mu.Unlock()
+
+	fc.stopped = true
+	fc.cond.Broadcast()
 }
 
-// wait blocks until all the watchers have consumed the most recent update and
-// returns true. If the context expires before that, it returns false.
-func (fc *adsFlowControl) wait(ctx context.Context) bool {
-	// If there is no pending update, there is no need to block.
-	if !fc.pending.Load() {
-		// If all watchers finished processing the most recent update before the
-		// `recv` goroutine made the next call to `Wait()`, there would be an
-		// entry in the readyCh channel that needs to be drained to ensure that
-		// the next call to `Wait()` doesn't unblock before it actually should.
-		select {
-		case <-fc.readyCh:
-		default:
-		}
-		return true
+// setPending changes the internal state to indicate whether there is an update
+// pending consumption by all watchers. If there is no longer a pending update,
+// the condition variable is signaled to allow the recv method to proceed.
+func (fc *adsFlowControl) setPending(pending bool) {
+	fc.mu.Lock()
+	defer fc.mu.Unlock()
+
+	if fc.stopped {
+		return
 	}
 
-	select {
-	case <-ctx.Done():
+	fc.pending = pending
+	if !pending {
+		fc.cond.Broadcast()
+	}
+}
+
+func (fc *adsFlowControl) runIfPending(f func()) bool {
+	fc.mu.Lock()
+	defer fc.mu.Unlock()
+
+	if fc.stopped {
 		return false
-	case <-fc.readyCh:
-		return true
 	}
+
+	// If there's a pending update, run the function while still holding the
+	// lock. This ensures that the pending state does not change between the
+	// check and the function call.
+	if fc.pending {
+		f()
+	}
+	return fc.pending
 }
 
-// onDone indicates that all watchers have consumed the most recent update.
-func (fc *adsFlowControl) onDone() {
-	select {
-	// Writes to the readyCh channel should not block ideally. The default
-	// branch here is to appease the paranoid mind.
-	case fc.readyCh <- struct{}{}:
-	default:
-		if fc.logger.V(2) {
-			fc.logger.Infof("ADS stream flow control readyCh is full")
-		}
+// wait blocks until all the watchers have consumed the most recent update.
+// Returns true if the flow control was stopped while waiting, false otherwise.
+func (fc *adsFlowControl) wait() bool {
+	fc.mu.Lock()
+	defer fc.mu.Unlock()
+
+	for fc.pending && !fc.stopped {
+		fc.cond.Wait()
 	}
-	fc.pending.Store(false)
+
+	return fc.stopped
 }
diff --git a/internal/xds/clients/xdsclient/xdsclient.go b/internal/xds/clients/xdsclient/xdsclient.go
index cc7d5c4e..b1c69554 100644
--- a/internal/xds/clients/xdsclient/xdsclient.go
+++ b/internal/xds/clients/xdsclient/xdsclient.go
@@ -319,7 +319,7 @@ func (c *XDSClient) releaseChannel(serverConfig *ServerConfig, state *channelSta
 		c.channelsMu.Lock()
 
 		if c.logger.V(2) {
-			c.logger.Infof("Received request to release a reference to an xdsChannel for server config %q", serverConfig)
+			c.logger.Infof("Received request to release a reference to an xdsChannel for server config %+v", serverConfig)
 		}
 		deInitLocked(state)
 
diff --git a/internal/xds/resolver/serviceconfig.go b/internal/xds/resolver/serviceconfig.go
index 2b4e5a05..f2ceabe7 100644
--- a/internal/xds/resolver/serviceconfig.go
+++ b/internal/xds/resolver/serviceconfig.go
@@ -151,8 +151,11 @@ func (cs *erroringConfigSelector) SelectConfig(iresolver.RPCInfo) (*iresolver.RP
 func (cs *erroringConfigSelector) stop() {}
 
 type configSelector struct {
-	r                *xdsResolver
-	xdsNodeID        string
+	channelID            uint64 // Static hash when hash policy is HashPolicyTypeChannelID
+	xdsNodeID            string // xDS node ID, for annotating errors.
+	sendNewServiceConfig func() // Function to send a new service config to gRPC.
+
+	// Configuration received from the xDS management server.
 	virtualHost      virtualHost
 	routes           []route
 	clusters         map[string]*clusterInfo
@@ -214,9 +217,7 @@ func (cs *configSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*iresolver.RP
 			if v := atomic.AddInt32(ref, -1); v == 0 {
 				// This entry will be removed from activeClusters when
 				// producing the service config for the empty update.
-				cs.r.serializer.TrySchedule(func(context.Context) {
-					cs.r.onClusterRefDownToZero()
-				})
+				cs.sendNewServiceConfig()
 			}
 		},
 		Interceptor: interceptor,
@@ -281,7 +282,7 @@ func (cs *configSelector) generateHash(rpcInfo iresolver.RPCInfo, hashPolicies [
 			generatedPolicyHash = true
 		case xdsresource.HashPolicyTypeChannelID:
 			// Use the static channel ID as the hash for this policy.
-			policyHash = cs.r.channelID
+			policyHash = cs.channelID
 			generatedHash = true
 			generatedPolicyHash = true
 		}
@@ -358,9 +359,7 @@ func (cs *configSelector) stop() {
 	// selector; we need another update to delete clusters from the config (if
 	// we don't have another update pending already).
 	if needUpdate {
-		cs.r.serializer.TrySchedule(func(context.Context) {
-			cs.r.onClusterRefDownToZero()
-		})
+		cs.sendNewServiceConfig()
 	}
 }
 
diff --git a/internal/xds/resolver/xds_resolver.go b/internal/xds/resolver/xds_resolver.go
index 70291f8c..8bb960a6 100644
--- a/internal/xds/resolver/xds_resolver.go
+++ b/internal/xds/resolver/xds_resolver.go
@@ -330,8 +330,13 @@ func (r *xdsResolver) sendNewServiceConfig(cs stoppableConfigSelector) bool {
 // Only executed in the context of a serializer callback.
 func (r *xdsResolver) newConfigSelector() *configSelector {
 	cs := &configSelector{
-		r:         r,
+		channelID: r.channelID,
 		xdsNodeID: r.xdsClient.BootstrapConfig().Node().GetId(),
+		sendNewServiceConfig: func() {
+			r.serializer.TrySchedule(func(context.Context) {
+				r.sendNewServiceConfig(r.curConfigSelector)
+			})
+		},
 		virtualHost: virtualHost{
 			httpFilterConfigOverride: r.currentVirtualHost.HTTPFilterConfigOverride,
 			retryConfig:              r.currentVirtualHost.RetryConfig,
@@ -352,14 +357,14 @@ func (r *xdsResolver) newConfigSelector() *configSelector {
 			ci.cfg = xdsChildConfig{ChildPolicy: balancerConfig(r.currentRouteConfig.ClusterSpecifierPlugins[rt.ClusterSpecifierPlugin])}
 			cs.clusters[clusterName] = ci
 		} else {
-			for cluster, wc := range rt.WeightedClusters {
-				clusterName := clusterPrefix + cluster
+			for _, wc := range rt.WeightedClusters {
+				clusterName := clusterPrefix + wc.Name
 				clusters.Add(&routeCluster{
 					name:                     clusterName,
 					httpFilterConfigOverride: wc.HTTPFilterConfigOverride,
 				}, int64(wc.Weight))
 				ci := r.addOrGetActiveClusterInfo(clusterName)
-				ci.cfg = xdsChildConfig{ChildPolicy: newBalancerConfig(cdsName, cdsBalancerConfig{Cluster: cluster})}
+				ci.cfg = xdsChildConfig{ChildPolicy: newBalancerConfig(cdsName, cdsBalancerConfig{Cluster: wc.Name})}
 				cs.clusters[clusterName] = ci
 			}
 		}
@@ -607,8 +612,3 @@ func (r *xdsResolver) onRouteConfigResourceError(name string, err error) {
 	}
 	r.onResourceError(err)
 }
-
-// Only executed in the context of a serializer callback.
-func (r *xdsResolver) onClusterRefDownToZero() {
-	r.sendNewServiceConfig(r.curConfigSelector)
-}
diff --git a/internal/xds/xdsclient/requests_counter.go b/internal/xds/xdsclient/requests_counter.go
index bd08c7e1..08f38bb1 100644
--- a/internal/xds/xdsclient/requests_counter.go
+++ b/internal/xds/xdsclient/requests_counter.go
@@ -81,27 +81,3 @@ func (c *ClusterRequestsCounter) StartRequest(max uint32) error {
 func (c *ClusterRequestsCounter) EndRequest() {
 	atomic.AddUint32(&c.numRequests, ^uint32(0))
 }
-
-// ClearCounterForTesting clears the counter for the service. Should be only
-// used in tests.
-func ClearCounterForTesting(clusterName, edsServiceName string) {
-	src.mu.Lock()
-	defer src.mu.Unlock()
-	k := clusterNameAndServiceName{
-		clusterName:    clusterName,
-		edsServiceName: edsServiceName,
-	}
-	c, ok := src.clusters[k]
-	if !ok {
-		return
-	}
-	c.numRequests = 0
-}
-
-// ClearAllCountersForTesting clears all the counters. Should be only used in
-// tests.
-func ClearAllCountersForTesting() {
-	src.mu.Lock()
-	defer src.mu.Unlock()
-	src.clusters = make(map[clusterNameAndServiceName]*ClusterRequestsCounter)
-}
diff --git a/internal/xds/xdsclient/xdsresource/type_rds.go b/internal/xds/xdsclient/xdsresource/type_rds.go
index 44bc2789..0cde9738 100644
--- a/internal/xds/xdsclient/xdsresource/type_rds.go
+++ b/internal/xds/xdsclient/xdsresource/type_rds.go
@@ -146,7 +146,7 @@ type Route struct {
 
 	// Only one of the following fields (WeightedClusters or
 	// ClusterSpecifierPlugin) will be set for a route.
-	WeightedClusters map[string]WeightedCluster
+	WeightedClusters []WeightedCluster
 	// ClusterSpecifierPlugin is the name of the Cluster Specifier Plugin that
 	// this Route is linked to, if specified by xDS.
 	ClusterSpecifierPlugin string
@@ -154,6 +154,8 @@ type Route struct {
 
 // WeightedCluster contains settings for an xds ActionType.WeightedCluster.
 type WeightedCluster struct {
+	// Name is the name of the cluster.
+	Name string
 	// Weight is the relative weight of the cluster.  It will never be zero.
 	Weight uint32
 	// HTTPFilterConfigOverride contains any HTTP filter config overrides for
diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_rds.go b/internal/xds/xdsclient/xdsresource/unmarshal_rds.go
index beab03eb..479d023f 100644
--- a/internal/xds/xdsclient/xdsresource/unmarshal_rds.go
+++ b/internal/xds/xdsclient/xdsresource/unmarshal_rds.go
@@ -300,7 +300,6 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif
 
 		switch r.GetAction().(type) {
 		case *v3routepb.Route_Route:
-			route.WeightedClusters = make(map[string]WeightedCluster)
 			action := r.GetRoute()
 
 			// Hash Policies are only applicable for a Ring Hash LB.
@@ -312,7 +311,7 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif
 
 			switch a := action.GetClusterSpecifier().(type) {
 			case *v3routepb.RouteAction_Cluster:
-				route.WeightedClusters[a.Cluster] = WeightedCluster{Weight: 1}
+				route.WeightedClusters = append(route.WeightedClusters, WeightedCluster{Name: a.Cluster, Weight: 1})
 			case *v3routepb.RouteAction_WeightedClusters:
 				wcs := a.WeightedClusters
 				var totalWeight uint64
@@ -325,13 +324,13 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif
 					if totalWeight > math.MaxUint32 {
 						return nil, nil, fmt.Errorf("xds: total weight of clusters exceeds MaxUint32")
 					}
-					wc := WeightedCluster{Weight: w}
+					wc := WeightedCluster{Name: c.GetName(), Weight: w}
 					cfgs, err := processHTTPFilterOverrides(c.GetTypedPerFilterConfig())
 					if err != nil {
 						return nil, nil, fmt.Errorf("route %+v, action %+v: %v", r, a, err)
 					}
 					wc.HTTPFilterConfigOverride = cfgs
-					route.WeightedClusters[c.GetName()] = wc
+					route.WeightedClusters = append(route.WeightedClusters, wc)
 				}
 				if totalWeight == 0 {
 					return nil, nil, fmt.Errorf("route %+v, action %+v, has no valid cluster in WeightedCluster action", r, a)
diff --git a/rpc_util.go b/rpc_util.go
index 47ea09f5..7255585e 100644
--- a/rpc_util.go
+++ b/rpc_util.go
@@ -657,8 +657,20 @@ type streamReader interface {
 	Read(n int) (mem.BufferSlice, error)
 }
 
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://golang.org/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct {
+}
+
+func (*noCopy) Lock()   {}
+func (*noCopy) Unlock() {}
+
 // parser reads complete gRPC messages from the underlying reader.
 type parser struct {
+	_ noCopy
 	// r is the underlying reader.
 	// See the comment on recvMsg for the permissible
 	// error types.
diff --git a/server.go b/server.go
index 1da2a542..ded3c344 100644
--- a/server.go
+++ b/server.go
@@ -1596,7 +1596,7 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv
 	ss := &serverStream{
 		ctx:                   ctx,
 		s:                     stream,
-		p:                     &parser{r: stream, bufferPool: s.opts.bufferPool},
+		p:                     parser{r: stream, bufferPool: s.opts.bufferPool},
 		codec:                 s.getCodec(stream.ContentSubtype()),
 		desc:                  sd,
 		maxReceiveMessageSize: s.opts.maxReceiveMessageSize,
diff --git a/stats/opentelemetry/client_metrics.go b/stats/opentelemetry/client_metrics.go
index d2d80494..c3b77da5 100644
--- a/stats/opentelemetry/client_metrics.go
+++ b/stats/opentelemetry/client_metrics.go
@@ -174,7 +174,8 @@ func (h *clientMetricsHandler) TagRPC(ctx context.Context, info *stats.RPCTagInf
 			// executes on the callpath that this OpenTelemetry component
 			// currently supports.
 			TelemetryLabels: map[string]string{
-				"grpc.lb.locality": "",
+				"grpc.lb.locality":        "",
+				"grpc.lb.backend_service": "",
 			},
 		}
 		ctx = istats.SetLabels(ctx, labels)
diff --git a/stats/opentelemetry/opentelemetry.go b/stats/opentelemetry/opentelemetry.go
index cd01f86c..2a9cb5e5 100644
--- a/stats/opentelemetry/opentelemetry.go
+++ b/stats/opentelemetry/opentelemetry.go
@@ -280,6 +280,18 @@ func createInt64Counter(setOfMetrics map[string]bool, metricName string, meter o
 	return ret
 }
 
+func createInt64UpDownCounter(setOfMetrics map[string]bool, metricName string, meter otelmetric.Meter, options ...otelmetric.Int64UpDownCounterOption) otelmetric.Int64UpDownCounter {
+	if _, ok := setOfMetrics[metricName]; !ok {
+		return noop.Int64UpDownCounter{}
+	}
+	ret, err := meter.Int64UpDownCounter(string(metricName), options...)
+	if err != nil {
+		logger.Errorf("Failed to register metric \"%v\", will not record: %v", metricName, err)
+		return noop.Int64UpDownCounter{}
+	}
+	return ret
+}
+
 func createFloat64Counter(setOfMetrics map[string]bool, metricName string, meter otelmetric.Meter, options ...otelmetric.Float64CounterOption) otelmetric.Float64Counter {
 	if _, ok := setOfMetrics[metricName]; !ok {
 		return noop.Float64Counter{}
@@ -350,11 +362,12 @@ func optionFromLabels(labelKeys []string, optionalLabelKeys []string, optionalLa
 // registryMetrics implements MetricsRecorder for the client and server stats
 // handlers.
 type registryMetrics struct {
-	intCounts   map[*estats.MetricDescriptor]otelmetric.Int64Counter
-	floatCounts map[*estats.MetricDescriptor]otelmetric.Float64Counter
-	intHistos   map[*estats.MetricDescriptor]otelmetric.Int64Histogram
-	floatHistos map[*estats.MetricDescriptor]otelmetric.Float64Histogram
-	intGauges   map[*estats.MetricDescriptor]otelmetric.Int64Gauge
+	intCounts       map[*estats.MetricDescriptor]otelmetric.Int64Counter
+	floatCounts     map[*estats.MetricDescriptor]otelmetric.Float64Counter
+	intHistos       map[*estats.MetricDescriptor]otelmetric.Int64Histogram
+	floatHistos     map[*estats.MetricDescriptor]otelmetric.Float64Histogram
+	intGauges       map[*estats.MetricDescriptor]otelmetric.Int64Gauge
+	intUpDownCounts map[*estats.MetricDescriptor]otelmetric.Int64UpDownCounter
 
 	optionalLabels []string
 }
@@ -365,6 +378,7 @@ func (rm *registryMetrics) registerMetrics(metrics *stats.MetricSet, meter otelm
 	rm.intHistos = make(map[*estats.MetricDescriptor]otelmetric.Int64Histogram)
 	rm.floatHistos = make(map[*estats.MetricDescriptor]otelmetric.Float64Histogram)
 	rm.intGauges = make(map[*estats.MetricDescriptor]otelmetric.Int64Gauge)
+	rm.intUpDownCounts = make(map[*estats.MetricDescriptor]otelmetric.Int64UpDownCounter)
 
 	for metric := range metrics.Metrics() {
 		desc := estats.DescriptorForMetric(metric)
@@ -385,6 +399,8 @@ func (rm *registryMetrics) registerMetrics(metrics *stats.MetricSet, meter otelm
 			rm.floatHistos[desc] = createFloat64Histogram(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description), otelmetric.WithExplicitBucketBoundaries(desc.Bounds...))
 		case estats.MetricTypeIntGauge:
 			rm.intGauges[desc] = createInt64Gauge(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description))
+		case estats.MetricTypeIntUpDownCount:
+			rm.intUpDownCounts[desc] = createInt64UpDownCounter(metrics.Metrics(), desc.Name, meter, otelmetric.WithUnit(desc.Unit), otelmetric.WithDescription(desc.Description))
 		}
 	}
 }
@@ -397,6 +413,14 @@ func (rm *registryMetrics) RecordInt64Count(handle *estats.Int64CountHandle, inc
 	}
 }
 
+func (rm *registryMetrics) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
+	desc := handle.Descriptor()
+	if ic, ok := rm.intUpDownCounts[desc]; ok {
+		ao := optionFromLabels(desc.Labels, desc.OptionalLabels, rm.optionalLabels, labels...)
+		ic.Add(context.TODO(), incr, ao)
+	}
+}
+
 func (rm *registryMetrics) RecordFloat64Count(handle *estats.Float64CountHandle, incr float64, labels ...string) {
 	desc := handle.Descriptor()
 	if fc, ok := rm.floatCounts[desc]; ok {
diff --git a/stream.go b/stream.go
index 0a0af896..f74a6875 100644
--- a/stream.go
+++ b/stream.go
@@ -529,7 +529,7 @@ func (a *csAttempt) newStream() error {
 	}
 	a.transportStream = s
 	a.ctx = s.Context()
-	a.parser = &parser{r: s, bufferPool: a.cs.cc.dopts.copts.BufferPool}
+	a.parser = parser{r: s, bufferPool: a.cs.cc.dopts.copts.BufferPool}
 	return nil
 }
 
@@ -601,7 +601,7 @@ type csAttempt struct {
 	cs              *clientStream
 	transport       transport.ClientTransport
 	transportStream *transport.ClientStream
-	parser          *parser
+	parser          parser
 	pickResult      balancer.PickResult
 
 	finished        bool
@@ -1141,7 +1141,7 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) {
 		// Only initialize this state once per stream.
 		a.decompressorSet = true
 	}
-	if err := recv(a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, payInfo, a.decompressorV1, false); err != nil {
+	if err := recv(&a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, payInfo, a.decompressorV1, false); err != nil {
 		if err == io.EOF {
 			if statusErr := a.transportStream.Status().Err(); statusErr != nil {
 				return statusErr
@@ -1179,7 +1179,7 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) {
 	}
 	// Special handling for non-server-stream rpcs.
 	// This recv expects EOF or errors, so we don't collect inPayload.
-	if err := recv(a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, nil, a.decompressorV1, false); err == io.EOF {
+	if err := recv(&a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, nil, a.decompressorV1, false); err == io.EOF {
 		return a.transportStream.Status().Err() // non-server streaming Recv returns nil on success
 	} else if err != nil {
 		return toRPCErr(err)
@@ -1331,7 +1331,7 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin
 		return nil, err
 	}
 	as.transportStream = s
-	as.parser = &parser{r: s, bufferPool: ac.dopts.copts.BufferPool}
+	as.parser = parser{r: s, bufferPool: ac.dopts.copts.BufferPool}
 	ac.incrCallsStarted()
 	if desc != unaryStreamDesc {
 		// Listen on stream context to cleanup when the stream context is
@@ -1374,7 +1374,7 @@ type addrConnStream struct {
 	decompressorSet  bool
 	decompressorV0   Decompressor
 	decompressorV1   encoding.Compressor
-	parser           *parser
+	parser           parser
 
 	// mu guards finished and is held for the entire finish method.
 	mu       sync.Mutex
@@ -1487,7 +1487,7 @@ func (as *addrConnStream) RecvMsg(m any) (err error) {
 		// Only initialize this state once per stream.
 		as.decompressorSet = true
 	}
-	if err := recv(as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err != nil {
+	if err := recv(&as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err != nil {
 		if err == io.EOF {
 			if statusErr := as.transportStream.Status().Err(); statusErr != nil {
 				return statusErr
@@ -1509,7 +1509,7 @@ func (as *addrConnStream) RecvMsg(m any) (err error) {
 
 	// Special handling for non-server-stream rpcs.
 	// This recv expects EOF or errors, so we don't collect inPayload.
-	if err := recv(as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err == io.EOF {
+	if err := recv(&as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err == io.EOF {
 		return as.transportStream.Status().Err() // non-server streaming Recv returns nil on success
 	} else if err != nil {
 		return toRPCErr(err)
@@ -1597,7 +1597,7 @@ type ServerStream interface {
 type serverStream struct {
 	ctx   context.Context
 	s     *transport.ServerStream
-	p     *parser
+	p     parser
 	codec baseCodec
 	desc  *StreamDesc
 
@@ -1788,7 +1788,7 @@ func (ss *serverStream) RecvMsg(m any) (err error) {
 		payInfo = &payloadInfo{}
 		defer payInfo.free()
 	}
-	if err := recv(ss.p, ss.codec, ss.s, ss.decompressorV0, m, ss.maxReceiveMessageSize, payInfo, ss.decompressorV1, true); err != nil {
+	if err := recv(&ss.p, ss.codec, ss.s, ss.decompressorV0, m, ss.maxReceiveMessageSize, payInfo, ss.decompressorV1, true); err != nil {
 		if err == io.EOF {
 			if len(ss.binlogs) != 0 {
 				chc := &binarylog.ClientHalfClose{}
@@ -1834,7 +1834,7 @@ func (ss *serverStream) RecvMsg(m any) (err error) {
 	}
 	// Special handling for non-client-stream rpcs.
 	// This recv expects EOF or errors, so we don't collect inPayload.
-	if err := recv(ss.p, ss.codec, ss.s, ss.decompressorV0, m, ss.maxReceiveMessageSize, nil, ss.decompressorV1, true); err == io.EOF {
+	if err := recv(&ss.p, ss.codec, ss.s, ss.decompressorV0, m, ss.maxReceiveMessageSize, nil, ss.decompressorV1, true); err == io.EOF {
 		return nil
 	} else if err != nil {
 		return err



Test patch:
diff --git a/balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go b/balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go
index df8c06f4..1c4c96d0 100644
--- a/balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go
+++ b/balancer/pickfirst/pickfirstleaf/pickfirstleaf_ext_test.go
@@ -1152,6 +1152,7 @@ func (s) TestPickFirstLeaf_InterleavingIPv6Preffered(t *testing.T) {
 		ResolverState: resolver.State{
 			Endpoints: []resolver.Endpoint{
 				{Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}},
+				{Addresses: []resolver.Address{{Addr: "[0001:0001:0001:0001:0001:0001:0001:0001]:8080"}}}, // duplicate, should be ignored.
 				{Addresses: []resolver.Address{{Addr: "1.1.1.1:1111"}}},
 				{Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}},
 				{Addresses: []resolver.Address{{Addr: "3.3.3.3:3"}}},
@@ -1504,6 +1505,102 @@ func (s) TestPickFirstLeaf_AddressUpdateWithMetadata(t *testing.T) {
 	}
 }
 
+// Tests the scenario where a connection is established and then breaks, leading
+// to a reconnection attempt. While the reconnection is in progress, a resolver
+// update with a new address is received. The test verifies that the balancer
+// creates a new SubConn for the new address and that the ClientConn eventually
+// becomes READY.
+func (s) TestPickFirstLeaf_Reconnection(t *testing.T) {
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	cc := testutils.NewBalancerClientConn(t)
+	bal := balancer.Get(pickfirstleaf.Name).Build(cc, balancer.BuildOptions{})
+	defer bal.Close()
+	ccState := balancer.ClientConnState{
+		ResolverState: resolver.State{
+			Endpoints: []resolver.Endpoint{
+				{Addresses: []resolver.Address{{Addr: "1.1.1.1:1"}}},
+			},
+		},
+	}
+	if err := bal.UpdateClientConnState(ccState); err != nil {
+		t.Fatalf("UpdateClientConnState(%v) returned error: %v", ccState, err)
+	}
+
+	select {
+	case state := <-cc.NewStateCh:
+		if got, want := state, connectivity.Connecting; got != want {
+			t.Fatalf("Received unexpected ClientConn sate: got %v, want %v", got, want)
+		}
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for ClientConn state update.")
+	}
+
+	sc1 := <-cc.NewSubConnCh
+	select {
+	case <-sc1.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc1.")
+	}
+	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
+	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
+
+	if err := cc.WaitForConnectivityState(ctx, connectivity.Ready); err != nil {
+		t.Fatalf("Context timed out waiting for ClientConn to become READY.")
+	}
+
+	// Simulate a connection breakage, this should result the channel
+	// transitioning to IDLE.
+	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
+	if err := cc.WaitForConnectivityState(ctx, connectivity.Idle); err != nil {
+		t.Fatalf("Context timed out waiting for ClientConn to enter IDLE.")
+	}
+
+	// Calling the idle picker should result in the SubConn being re-connected.
+	picker := <-cc.NewPickerCh
+	if _, err := picker.Pick(balancer.PickInfo{}); err != balancer.ErrNoSubConnAvailable {
+		t.Fatalf("picker.Pick() returned error: %v, want %v", err, balancer.ErrNoSubConnAvailable)
+	}
+
+	select {
+	case <-sc1.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc1.")
+	}
+
+	// Send a resolver update, removing the existing SubConn. Since the balancer
+	// is connecting, it should create a new SubConn for the new backend
+	// address.
+	ccState = balancer.ClientConnState{
+		ResolverState: resolver.State{
+			Endpoints: []resolver.Endpoint{
+				{Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}},
+			},
+		},
+	}
+	if err := bal.UpdateClientConnState(ccState); err != nil {
+		t.Fatalf("UpdateClientConnState(%v) returned error: %v", ccState, err)
+	}
+
+	var sc2 *testutils.TestSubConn
+	select {
+	case sc2 = <-cc.NewSubConnCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for new SubConn to be created.")
+	}
+
+	select {
+	case <-sc2.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc2.")
+	}
+	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
+	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
+	if err := cc.WaitForConnectivityState(ctx, connectivity.Ready); err != nil {
+		t.Fatalf("Context timed out waiting for ClientConn to become READY.")
+	}
+}
+
 // healthListenerCapturingCCWrapper is used to capture the health listener so
 // that health updates can be mocked for testing.
 type healthListenerCapturingCCWrapper struct {
diff --git a/balancer/pickfirst/pickfirstleaf/pickfirstleaf_test.go b/balancer/pickfirst/pickfirstleaf/pickfirstleaf_test.go
index 71984a23..5a73dccd 100644
--- a/balancer/pickfirst/pickfirstleaf/pickfirstleaf_test.go
+++ b/balancer/pickfirst/pickfirstleaf/pickfirstleaf_test.go
@@ -201,7 +201,9 @@ func (s) TestPickFirstLeaf_TFPickerUpdate(t *testing.T) {
 		ResolverState: resolver.State{
 			Endpoints: []resolver.Endpoint{
 				{Addresses: []resolver.Address{{Addr: "1.1.1.1:1"}}},
+				{Addresses: []resolver.Address{{Addr: "1.1.1.1:1"}}}, // duplicate, should be ignored.
 				{Addresses: []resolver.Address{{Addr: "2.2.2.2:2"}}},
+				{Addresses: []resolver.Address{{Addr: "1.1.1.1:1"}}}, // duplicate, should be ignored.
 			},
 		},
 	}
@@ -213,14 +215,35 @@ func (s) TestPickFirstLeaf_TFPickerUpdate(t *testing.T) {
 	// once.
 	tfErr := fmt.Errorf("test err: connection refused")
 	sc1 := <-cc.NewSubConnCh
+	select {
+	case <-sc1.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc1.")
+	}
 	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
 	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure, ConnectionError: tfErr})
 
+	// Move the subconn back to IDLE, it should not be re-connected until the
+	// first pass is complete.
+	shortCtx, shortCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
+	defer shortCancel()
+	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
+	select {
+	case <-sc1.ConnectCh:
+		t.Fatal("Connect() unexpectedly called on sc1.")
+	case <-shortCtx.Done():
+	}
+
 	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
 		t.Fatalf("cc.WaitForPickerWithErr(%v) returned error: %v", balancer.ErrNoSubConnAvailable, err)
 	}
 
 	sc2 := <-cc.NewSubConnCh
+	select {
+	case <-sc2.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc2.")
+	}
 	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
 	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure, ConnectionError: tfErr})
 
@@ -230,6 +253,29 @@ func (s) TestPickFirstLeaf_TFPickerUpdate(t *testing.T) {
 
 	// Subsequent TRANSIENT_FAILUREs should be reported only after seeing "# of SubConns"
 	// TRANSIENT_FAILUREs.
+
+	// Both the subconns should be connected in parallel.
+	select {
+	case <-sc1.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc1.")
+	}
+
+	shortCtx, shortCancel = context.WithTimeout(ctx, defaultTestShortTimeout)
+	defer shortCancel()
+	select {
+	case <-sc2.ConnectCh:
+		t.Fatal("Connect() called on sc2 before it completed backing-off.")
+	case <-shortCtx.Done():
+	}
+
+	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Idle})
+	select {
+	case <-sc2.ConnectCh:
+	case <-ctx.Done():
+		t.Fatal("Context timed out waiting for Connect() to be called on sc2.")
+	}
+
 	newTfErr := fmt.Errorf("test err: unreachable")
 	sc2.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure, ConnectionError: newTfErr})
 	select {
diff --git a/experimental/stats/metricregistry_test.go b/experimental/stats/metricregistry_test.go
index e2da3b5d..5173967d 100644
--- a/experimental/stats/metricregistry_test.go
+++ b/experimental/stats/metricregistry_test.go
@@ -109,6 +109,14 @@ func (s) TestMetricRegistry(t *testing.T) {
 		OptionalLabels: []string{"int gauge optional label"},
 		Default:        false,
 	})
+	intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
+		Name:           "simple up down counter",
+		Description:    "current number of emissions from tests",
+		Unit:           "int",
+		Labels:         []string{"int up down counter label"},
+		OptionalLabels: []string{"int up down counter optional label"},
+		Default:        false,
+	})
 
 	fmr := newFakeMetricsRecorder(t)
 
@@ -120,6 +128,14 @@ func (s) TestMetricRegistry(t *testing.T) {
 		t.Fatalf("fmr.intValues[intCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
 	}
 
+	intUpDownCountHandle1.Record(fmr, 2, []string{"some label value", "some optional label value"}...)
+	// The Metric Descriptor in the handle should be able to identify the metric
+	// information. This is the key passed to metrics recorder to identify
+	// metric.
+	if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 2 {
+		t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 2)
+	}
+
 	floatCountHandle1.Record(fmr, 1.2, []string{"some label value", "some optional label value"}...)
 	if got := fmr.floatValues[floatCountHandle1.Descriptor()]; got != 1.2 {
 		t.Fatalf("fmr.floatValues[floatCountHandle1.MetricDescriptor] got %v, want: %v", got, 1.2)
@@ -141,6 +157,28 @@ func (s) TestMetricRegistry(t *testing.T) {
 	}
 }
 
+func TestUpDownCounts(t *testing.T) {
+	cleanup := snapshotMetricsRegistryForTesting()
+	defer cleanup()
+
+	intUpDownCountHandle1 := RegisterInt64UpDownCount(MetricDescriptor{
+		Name:           "simple up down counter",
+		Description:    "current number of emissions from tests",
+		Unit:           "int",
+		Labels:         []string{"int up down counter label"},
+		OptionalLabels: []string{"int up down counter optional label"},
+		Default:        false,
+	})
+
+	fmr := newFakeMetricsRecorder(t)
+	intUpDownCountHandle1.Record(fmr, 2, []string{"up down value", "some optional label value"}...)
+	intUpDownCountHandle1.Record(fmr, -1, []string{"up down value", "some optional label value"}...)
+
+	if got := fmr.intValues[intUpDownCountHandle1.Descriptor()]; got != 1 {
+		t.Fatalf("fmr.intValues[intUpDownCountHandle1.MetricDescriptor] got %v, want: %v", got, 1)
+	}
+}
+
 // TestNumerousIntCounts tests numerous int count metrics registered onto the
 // metric registry. A component (simulated by test) should be able to record on
 // the different registered int count metrics.
@@ -265,3 +303,8 @@ func (r *fakeMetricsRecorder) RecordInt64Gauge(handle *Int64GaugeHandle, incr in
 	verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
 	r.intValues[handle.Descriptor()] += incr
 }
+
+func (r *fakeMetricsRecorder) RecordInt64UpDownCount(handle *Int64UpDownCountHandle, incr int64, labels ...string) {
+	verifyLabels(r.t, handle.Descriptor().Labels, handle.Descriptor().OptionalLabels, labels)
+	r.intValues[handle.Descriptor()] += incr
+}
diff --git a/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go b/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
index 92ea2ab6..7b02d43b 100644
--- a/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
+++ b/internal/resolver/delegatingresolver/delegatingresolver_ext_test.go
@@ -23,10 +23,12 @@ import (
 	"errors"
 	"net/http"
 	"net/url"
+	"strings"
 	"testing"
 	"time"
 
 	"github.com/google/go-cmp/cmp"
+	"google.golang.org/grpc/internal/envconfig"
 	"google.golang.org/grpc/internal/grpctest"
 	"google.golang.org/grpc/internal/proxyattributes"
 	"google.golang.org/grpc/internal/resolver/delegatingresolver"
@@ -246,54 +248,130 @@ func (s) TestDelegatingResolverwithDNSAndProxyWithTargetResolution(t *testing.T)
 	}
 }
 
-// Tests the scenario where a proxy is configured, the target URI contains the
-// "dns" scheme, and target resolution is disabled(default behavior). The test
-// verifies that the addresses returned by the delegating resolver include the
-// proxy resolver's addresses, with the unresolved target URI as an attribute
-// of the proxy address.
-func (s) TestDelegatingResolverwithDNSAndProxyWithNoTargetResolution(t *testing.T) {
+// Tests the creation of a delegating resolver when a proxy is configured. It
+// verifies successful creation for valid targets and ensures the final address
+// is from the proxy resolver and contains the original, correctly-formatted
+// target address as an attribute.
+func (s) TestDelegatingResolverwithDNSAndProxyWithNoTargetResolutionHappyPaths(t *testing.T) {
 	const (
-		targetTestAddr         = "test.com"
 		envProxyAddr           = "proxytest.com"
 		resolvedProxyTestAddr1 = "11.11.11.11:7687"
 	)
-	overrideTestHTTPSProxy(t, envProxyAddr)
-
-	targetResolver := manual.NewBuilderWithScheme("dns")
-	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)
+	tests := []struct {
+		name               string
+		target             string
+		wantConnectAddress string
+		defaultPortEnvVar  bool
+	}{
+		{
+			name:               "no_port ",
+			target:             "test.com",
+			wantConnectAddress: "test.com:443",
+			defaultPortEnvVar:  true,
+		},
+		{
+			name:               "complete_target",
+			target:             "test.com:8080",
+			wantConnectAddress: "test.com:8080",
+			defaultPortEnvVar:  true,
+		},
+		{
+			name:               "no_port_with_default_port_env_variable_disabled",
+			target:             "test.com",
+			wantConnectAddress: "test.com",
+			defaultPortEnvVar:  false,
+		},
 	}
 
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			testutils.SetEnvConfig(t, &envconfig.EnableDefaultPortForProxyTarget, test.defaultPortEnvVar)
+			overrideTestHTTPSProxy(t, envProxyAddr)
 
-	// Wait for the proxy resolver to be built before calling UpdateState.
-	mustBuildResolver(ctx, t, proxyResolverBuilt)
-	proxyResolver.UpdateState(resolver.State{
-		Addresses: []resolver.Address{
-			{Addr: resolvedProxyTestAddr1},
-		},
-	})
+			targetResolver := manual.NewBuilderWithScheme("dns")
+			target := targetResolver.Scheme() + ":///" + test.target
+			// Set up a manual DNS resolver to control the proxy address resolution.
+			proxyResolver, proxyResolverBuilt := setupDNS(t)
 
-	wantState := resolver.State{
-		Addresses: []resolver.Address{proxyAddressWithTargetAttribute(resolvedProxyTestAddr1, targetTestAddr)},
-		Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{proxyAddressWithTargetAttribute(resolvedProxyTestAddr1, targetTestAddr)}}},
+			tcc, stateCh, _ := createTestResolverClientConn(t)
+			if _, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false); err != nil {
+				t.Fatalf("Delegating resolver creation failed unexpectedly with error: %v", err)
+			}
+			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: resolvedProxyTestAddr1},
+				},
+			})
+
+			wantState := resolver.State{
+				Addresses: []resolver.Address{proxyAddressWithTargetAttribute(resolvedProxyTestAddr1, test.wantConnectAddress)},
+				Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{proxyAddressWithTargetAttribute(resolvedProxyTestAddr1, test.wantConnectAddress)}}},
+			}
+
+			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")
+			}
+
+			if diff := cmp.Diff(gotState, wantState); diff != "" {
+				t.Fatalf("Unexpected state from delegating resolver. Diff (-got +want):\n%v", diff)
+			}
+		})
 	}
+}
 
-	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")
+// Tests the creation of a delegating resolver when a proxy is configured. It
+// verifies correct error handling for invalid targets.
+func (s) TestDelegatingResolverwithUnresolvedErrorTargetWithProxy(t *testing.T) {
+	const (
+		envProxyAddr           = "proxytest.com"
+		resolvedProxyTestAddr1 = "11.11.11.11:7687"
+	)
+	tests := []struct {
+		name               string
+		target             string
+		wantErrorSubstring string
+		defaultPortEnvVar  bool
+	}{
+
+		{
+			name:               "colon_after_host_but_no_port",
+			target:             "test.com:",
+			wantErrorSubstring: "missing port after port-separator colon",
+			defaultPortEnvVar:  true,
+		},
+		{
+			name:               "empty_target",
+			target:             "",
+			wantErrorSubstring: "missing address",
+			defaultPortEnvVar:  true,
+		},
 	}
 
-	if diff := cmp.Diff(gotState, wantState); diff != "" {
-		t.Fatalf("Unexpected state from delegating resolver. Diff (-got +want):\n%v", diff)
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			testutils.SetEnvConfig(t, &envconfig.EnableDefaultPortForProxyTarget, test.defaultPortEnvVar)
+			overrideTestHTTPSProxy(t, envProxyAddr)
+
+			targetResolver := manual.NewBuilderWithScheme("dns")
+			target := targetResolver.Scheme() + ":///" + test.target
+
+			tcc, _, _ := createTestResolverClientConn(t)
+			_, err := delegatingresolver.New(resolver.Target{URL: *testutils.MustParseURL(target)}, tcc, resolver.BuildOptions{}, targetResolver, false)
+			if err == nil {
+				t.Fatalf("Delegating resolver created, want error containing %q", test.wantErrorSubstring)
+			}
+			if !strings.Contains(err.Error(), test.wantErrorSubstring) {
+				t.Fatalf("Delegating resolver failed with error %v, want error containing %v", err.Error(), test.wantErrorSubstring)
+			}
+		})
 	}
 }
 
diff --git a/internal/resolver/delegatingresolver/delegatingresolver_test.go b/internal/resolver/delegatingresolver/delegatingresolver_test.go
index c99afff4..154d7194 100644
--- a/internal/resolver/delegatingresolver/delegatingresolver_test.go
+++ b/internal/resolver/delegatingresolver/delegatingresolver_test.go
@@ -36,10 +36,7 @@ func Test(t *testing.T) {
 	grpctest.RunSubTests(t, s{})
 }
 
-const (
-	targetTestAddr = "test.com"
-	envProxyAddr   = "proxytest.com"
-)
+const targetTestAddr = "test.com"
 
 // overrideHTTPSProxyFromEnvironment function overwrites HTTPSProxyFromEnvironment and
 // returns a function to restore the default values.
diff --git a/internal/testutils/stats/test_metrics_recorder.go b/internal/testutils/stats/test_metrics_recorder.go
index e1a03b8d..be1a0611 100644
--- a/internal/testutils/stats/test_metrics_recorder.go
+++ b/internal/testutils/stats/test_metrics_recorder.go
@@ -35,11 +35,12 @@ import (
 // have taken place. It also persists metrics data keyed on the metrics
 // descriptor.
 type TestMetricsRecorder struct {
-	intCountCh   *testutils.Channel
-	floatCountCh *testutils.Channel
-	intHistoCh   *testutils.Channel
-	floatHistoCh *testutils.Channel
-	intGaugeCh   *testutils.Channel
+	intCountCh       *testutils.Channel
+	floatCountCh     *testutils.Channel
+	intHistoCh       *testutils.Channel
+	floatHistoCh     *testutils.Channel
+	intGaugeCh       *testutils.Channel
+	intUpDownCountCh *testutils.Channel
 
 	// mu protects data.
 	mu sync.Mutex
@@ -50,11 +51,12 @@ type TestMetricsRecorder struct {
 // NewTestMetricsRecorder returns a new TestMetricsRecorder.
 func NewTestMetricsRecorder() *TestMetricsRecorder {
 	return &TestMetricsRecorder{
-		intCountCh:   testutils.NewChannelWithSize(10),
-		floatCountCh: testutils.NewChannelWithSize(10),
-		intHistoCh:   testutils.NewChannelWithSize(10),
-		floatHistoCh: testutils.NewChannelWithSize(10),
-		intGaugeCh:   testutils.NewChannelWithSize(10),
+		intCountCh:       testutils.NewChannelWithSize(10),
+		floatCountCh:     testutils.NewChannelWithSize(10),
+		intHistoCh:       testutils.NewChannelWithSize(10),
+		floatHistoCh:     testutils.NewChannelWithSize(10),
+		intGaugeCh:       testutils.NewChannelWithSize(10),
+		intUpDownCountCh: testutils.NewChannelWithSize(10),
 
 		data: make(map[string]float64),
 	}
@@ -135,6 +137,22 @@ func (r *TestMetricsRecorder) RecordInt64Count(handle *estats.Int64CountHandle,
 	r.data[handle.Name] = float64(incr)
 }
 
+// RecordInt64UpDownCount sends the metrics data to the intUpDownCountCh channel and updates
+// the internal data map with the recorded value.
+func (r *TestMetricsRecorder) RecordInt64UpDownCount(handle *estats.Int64UpDownCountHandle, incr int64, labels ...string) {
+	r.intUpDownCountCh.ReceiveOrFail()
+	r.intUpDownCountCh.Send(MetricsData{
+		Handle:    handle.Descriptor(),
+		IntIncr:   incr,
+		LabelKeys: append(handle.Labels, handle.OptionalLabels...),
+		LabelVals: labels,
+	})
+
+	r.mu.Lock()
+	defer r.mu.Unlock()
+	r.data[handle.Name] = float64(incr)
+}
+
 // WaitForFloat64Count waits for a float count metric to be recorded and
 // verifies that the recorded metrics data matches the expected metricsDataWant.
 // Returns an error if failed to wait or received wrong data.
@@ -294,3 +312,7 @@ func (r *NoopMetricsRecorder) RecordFloat64Histo(*estats.Float64HistoHandle, flo
 
 // RecordInt64Gauge is a noop implementation of RecordInt64Gauge.
 func (r *NoopMetricsRecorder) RecordInt64Gauge(*estats.Int64GaugeHandle, int64, ...string) {}
+
+// RecordInt64UpDownCount is a noop implementation of RecordInt64UpDownCount.
+func (r *NoopMetricsRecorder) RecordInt64UpDownCount(*estats.Int64UpDownCountHandle, int64, ...string) {
+}
diff --git a/internal/testutils/xds/e2e/clientresources.go b/internal/testutils/xds/e2e/clientresources.go
index b8ebf8ba..be375d40 100644
--- a/internal/testutils/xds/e2e/clientresources.go
+++ b/internal/testutils/xds/e2e/clientresources.go
@@ -712,11 +712,7 @@ func EndpointResourceWithOptions(opts EndpointOptions) *v3endpointpb.ClusterLoad
 					},
 				}
 			}
-			metadata, err := structpb.NewStruct(b.Metadata)
-			if err != nil {
-				panic(err)
-			}
-			lbEndpoints = append(lbEndpoints, &v3endpointpb.LbEndpoint{
+			lbEndpoint := &v3endpointpb.LbEndpoint{
 				HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{Endpoint: &v3endpointpb.Endpoint{
 					Address: &v3corepb.Address{Address: &v3corepb.Address_SocketAddress{
 						SocketAddress: &v3corepb.SocketAddress{
@@ -729,12 +725,20 @@ func EndpointResourceWithOptions(opts EndpointOptions) *v3endpointpb.ClusterLoad
 				}},
 				HealthStatus:        b.HealthStatus,
 				LoadBalancingWeight: &wrapperspb.UInt32Value{Value: b.Weight},
-				Metadata: &v3corepb.Metadata{
+			}
+
+			if b.Metadata != nil {
+				metadata, err := structpb.NewStruct(b.Metadata)
+				if err != nil {
+					panic(fmt.Sprintf("failed to marshal metadata: %v", err))
+				}
+				lbEndpoint.Metadata = &v3corepb.Metadata{
 					FilterMetadata: map[string]*structpb.Struct{
 						"envoy.lb": metadata,
 					},
-				},
-			})
+				}
+			}
+			lbEndpoints = append(lbEndpoints, lbEndpoint)
 		}
 
 		l := locality.Locality
diff --git a/internal/transport/proxy_ext_test.go b/internal/transport/proxy_ext_test.go
index 9cd34722..c24f5a70 100644
--- a/internal/transport/proxy_ext_test.go
+++ b/internal/transport/proxy_ext_test.go
@@ -393,7 +393,7 @@ func (s) TestBasicAuthInNewClientWithProxy(t *testing.T) {
 	proxyCalled := false
 	reqCheck := func(req *http.Request) {
 		proxyCalled = true
-		if got, want := req.URL.Host, "example.test"; got != want {
+		if got, want := req.URL.Host, "example.test:443"; got != want {
 			t.Errorf(" Unexpected request host: %s, want = %s ", got, want)
 		}
 		wantProxyAuthStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(user+":"+password))
diff --git a/internal/transport/transport_test.go b/internal/transport/transport_test.go
index d0c8a88d..d704ab8f 100644
--- a/internal/transport/transport_test.go
+++ b/internal/transport/transport_test.go
@@ -39,9 +39,11 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"golang.org/x/net/http2"
 	"golang.org/x/net/http2/hpack"
+
 	"google.golang.org/grpc/attributes"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials"
+	"google.golang.org/grpc/internal"
 	"google.golang.org/grpc/internal/channelz"
 	"google.golang.org/grpc/internal/grpctest"
 	"google.golang.org/grpc/internal/leakcheck"
@@ -88,9 +90,6 @@ func (s *Stream) readTo(p []byte) (int, error) {
 	}
 
 	if data.Len() != len(p) {
-		if err == nil {
-			err = io.ErrUnexpectedEOF
-		}
 		return 0, err
 	}
 
@@ -1856,19 +1855,20 @@ func waitWhileTrue(t *testing.T, condition func() (bool, error)) {
 func (s) TestReadGivesSameErrorAfterAnyErrorOccurs(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
-	testRecvBuffer := newRecvBuffer()
 	s := &Stream{
-		ctx:         ctx,
-		buf:         testRecvBuffer,
-		requestRead: func(int) {},
+		ctx:           ctx,
+		readRequester: &fakeReadRequester{},
 	}
-	s.trReader = &transportReader{
-		reader: &recvBufferReader{
+	s.buf.init()
+	s.trReader = transportReader{
+		reader: recvBufferReader{
 			ctx:     s.ctx,
 			ctxDone: s.ctx.Done(),
-			recv:    s.buf,
+			recv:    &s.buf,
+		},
+		windowHandler: &mockWindowUpdater{
+			f: func(int) {},
 		},
-		windowHandler: func(int) {},
 	}
 	testData := make([]byte, 1)
 	testData[0] = 5
@@ -2588,51 +2588,48 @@ func (s) TestClientHandshakeInfoDialer(t *testing.T) {
 	}
 }
 
-func (s) TestClientDecodeHeaderStatusErr(t *testing.T) {
-	testStream := func() *ClientStream {
-		return &ClientStream{
-			Stream: &Stream{
-				buf: &recvBuffer{
-					c:  make(chan recvMsg),
-					mu: sync.Mutex{},
-				},
+func newTestClientStream() *ClientStream {
+	return &ClientStream{
+		Stream: Stream{
+			buf: recvBuffer{
+				c: make(chan recvMsg),
 			},
-			done:       make(chan struct{}),
-			headerChan: make(chan struct{}),
-		}
+		},
+		done:       make(chan struct{}),
+		headerChan: make(chan struct{}),
 	}
+}
 
-	testClient := func(ts *ClientStream) *http2Client {
-		return &http2Client{
-			mu: sync.Mutex{},
-			activeStreams: map[uint32]*ClientStream{
-				0: ts,
-			},
-			controlBuf: newControlBuffer(make(<-chan struct{})),
-		}
+func newTestHTTP2Client(cs *ClientStream) *http2Client {
+	return &http2Client{
+		activeStreams: map[uint32]*ClientStream{
+			1: cs,
+		},
+		controlBuf: newControlBuffer(make(<-chan struct{})),
 	}
+}
 
-	for _, test := range []struct {
-		name string
-		// input
+// TestClientDecodeHeader validates the handling of initial header frames that
+// do not signal the end of a stream. For all headers that indicate grpc content
+// type, http status will be ignored.
+func (s) TestClientDecodeHeader(t *testing.T) {
+	tests := []struct {
+		name            string
 		metaHeaderFrame *http2.MetaHeadersFrame
-		// output
-		wantStatus *status.Status
+		wantStatus      *status.Status
 	}{
 		{
-			name: "valid header",
+			name: "valid_header",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "content-type", Value: "application/grpc"},
-					{Name: "grpc-status", Value: "0"},
 					{Name: ":status", Value: "200"},
 				},
 			},
-			// no error
 			wantStatus: status.New(codes.OK, ""),
 		},
 		{
-			name: "missing content-type header",
+			name: "missing_content_type_header",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "grpc-status", Value: "0"},
@@ -2641,11 +2638,11 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) {
 			},
 			wantStatus: status.New(
 				codes.Unknown,
-				"malformed header: missing HTTP content-type",
+				"unexpected HTTP status code received from server: 200 (OK); malformed header: missing HTTP content-type",
 			),
 		},
 		{
-			name: "invalid grpc status header field",
+			name: "invalid_grpc_status",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "content-type", Value: "application/grpc"},
@@ -2659,7 +2656,7 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) {
 			),
 		},
 		{
-			name: "invalid http content type",
+			name: "invalid_content_type",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "content-type", Value: "application/json"},
@@ -2671,22 +2668,33 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) {
 			),
 		},
 		{
-			name: "http fallback and invalid http status",
+			name: "invalid_content_type_with_http_status_504",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "content-type", Value: "application/json"},
+					{Name: ":status", Value: "504"},
+				},
+			},
+			wantStatus: status.New(
+				codes.Unavailable,
+				"unexpected HTTP status code received from server: 504 (Gateway Timeout); transport: received unexpected content-type \"application/json\"",
+			),
+		},
+		{
+			name: "http_fallback_and_invalid_http_status",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
-					// No content type provided then fallback into handling http error.
 					{Name: ":status", Value: "xxxx"},
 				},
 			},
 			wantStatus: status.New(
 				codes.Internal,
-				"transport: malformed http-status: strconv.ParseInt: parsing \"xxxx\": invalid syntax",
+				"transport: malformed http-status: strconv.Atoi: parsing \"xxxx\": invalid syntax",
 			),
 		},
 		{
-			name: "http2 frame size exceeds",
+			name: "http2_frame_size_exceeds",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
-				Fields:    nil,
 				Truncated: true,
 			},
 			wantStatus: status.New(
@@ -2695,68 +2703,152 @@ func (s) TestClientDecodeHeaderStatusErr(t *testing.T) {
 			),
 		},
 		{
-			name: "bad status in grpc mode",
+			name: "missing_http_status_and_grpc_status",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "content-type", Value: "application/grpc"},
-					{Name: "grpc-status", Value: "0"},
-					{Name: ":status", Value: "504"},
 				},
 			},
-			wantStatus: status.New(
-				codes.Unavailable,
-				"unexpected HTTP status code received from server: 504 (Gateway Timeout)",
-			),
+			wantStatus: status.New(codes.OK, ""),
 		},
 		{
-			name: "missing http status",
+			name: "ignore_http_status_for_grpc",
 			metaHeaderFrame: &http2.MetaHeadersFrame{
 				Fields: []hpack.HeaderField{
 					{Name: "content-type", Value: "application/grpc"},
+					{Name: ":status", Value: "504"},
 				},
 			},
-			wantStatus: status.New(
-				codes.Internal,
-				"malformed header: missing HTTP status",
-			),
+			wantStatus: status.New(codes.OK, ""),
 		},
-	} {
+	}
 
-		t.Run(test.name, func(t *testing.T) {
-			ts := testStream()
-			s := testClient(ts)
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			cs := newTestClientStream()
+			s := newTestHTTP2Client(cs)
 
-			test.metaHeaderFrame.HeadersFrame = &http2.HeadersFrame{
+			tc.metaHeaderFrame.HeadersFrame = &http2.HeadersFrame{
 				FrameHeader: http2.FrameHeader{
-					StreamID: 0,
+					StreamID: 1,
 				},
 			}
 
-			s.operateHeaders(test.metaHeaderFrame)
-
-			got := ts.status
-			want := test.wantStatus
+			s.operateHeaders(tc.metaHeaderFrame)
+			got := cs.status
+			want := tc.wantStatus
 			if got.Code() != want.Code() || got.Message() != want.Message() {
-				t.Fatalf("operateHeaders(%v); status = \ngot: %s\nwant: %s", test.metaHeaderFrame, got, want)
+				t.Errorf("operateHeaders(%v) got status %q, want %q", tc.metaHeaderFrame, got, want)
 			}
 		})
-		t.Run(fmt.Sprintf("%s-end_stream", test.name), func(t *testing.T) {
-			ts := testStream()
-			s := testClient(ts)
+	}
+}
 
-			test.metaHeaderFrame.HeadersFrame = &http2.HeadersFrame{
+// TestClientDecodeTrailer validates the handling of trailer frames, which may
+// or may not also be the initial header frame (header-only response).
+func (s) TestClientDecodeTrailer(t *testing.T) {
+	tests := []struct {
+		name                string
+		metaHeaderFrame     *http2.MetaHeadersFrame
+		wantEndStreamStatus *status.Status
+	}{
+		{
+			name: "valid_trailer",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "content-type", Value: "application/grpc"},
+					{Name: "grpc-status", Value: "0"},
+					{Name: ":status", Value: "200"},
+				},
+			},
+			wantEndStreamStatus: status.New(codes.OK, ""),
+		},
+		{
+			name: "missing_content_type_in_grpc_mode",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "grpc-status", Value: "0"},
+					{Name: ":status", Value: "200"},
+				},
+			},
+			wantEndStreamStatus: status.New(codes.OK, ""),
+		},
+		{
+			name: "invalid_grpc_status",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "content-type", Value: "application/grpc"},
+					{Name: "grpc-status", Value: "xxxx"},
+					{Name: ":status", Value: "200"},
+				},
+			},
+			wantEndStreamStatus: status.New(
+				codes.Internal,
+				"transport: malformed grpc-status: strconv.ParseInt: parsing \"xxxx\": invalid syntax",
+			),
+		},
+		{
+			name: "missing_grpc_status_in_grpc_mode",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: ":status", Value: "xxxx"},
+				},
+			},
+			wantEndStreamStatus: status.New(codes.Internal, ""),
+		},
+		{
+			name: "http2_frame_size_exceeds",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Truncated: true,
+			},
+			wantEndStreamStatus: status.New(
+				codes.Internal,
+				"peer header list size exceeded limit",
+			),
+		},
+		{
+			name: "missing_grpc_status_in_trailer",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "content-type", Value: "application/grpc"},
+				},
+			},
+			wantEndStreamStatus: status.New(codes.Internal, ""),
+		},
+		{
+			name: "deadline_exceeded_status",
+			metaHeaderFrame: &http2.MetaHeadersFrame{
+				Fields: []hpack.HeaderField{
+					{Name: "content-type", Value: "application/grpc"},
+					{Name: "grpc-status", Value: "4"},
+					{Name: "grpc-message", Value: "Request timed out: Internal error"},
+					{Name: ":status", Value: "200"},
+				},
+			},
+			wantEndStreamStatus: status.New(codes.DeadlineExceeded, "Request timed out: Internal error"),
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			cs := newTestClientStream()
+			// Mark headerChanClosed to indicate trailer frames.
+			cs.headerChanClosed = 1
+			// Simulate the state where the initial headers have already been processed.
+			s := newTestHTTP2Client(cs)
+
+			tc.metaHeaderFrame.HeadersFrame = &http2.HeadersFrame{
 				FrameHeader: http2.FrameHeader{
-					StreamID: 0,
+					StreamID: 1,
 					Flags:    http2.FlagHeadersEndStream,
 				},
 			}
 
-			s.operateHeaders(test.metaHeaderFrame)
-
-			got := ts.status
-			want := test.wantStatus
+			s.operateHeaders(tc.metaHeaderFrame)
+			got := cs.status
+			want := tc.wantEndStreamStatus
 			if got.Code() != want.Code() || got.Message() != want.Message() {
-				t.Fatalf("operateHeaders(%v); status = \ngot: %s\nwant: %s", test.metaHeaderFrame, got, want)
+				t.Errorf("operateHeaders(%v) got status %q, want %q", tc.metaHeaderFrame, got, want)
 			}
 		})
 	}
@@ -3008,27 +3100,89 @@ func (s) TestClientCloseReturnsEarlyWhenGoAwayWriteHangs(t *testing.T) {
 	ct.Close(errors.New("manually closed by client"))
 }
 
+// deadlineTestConn is a net.Conn wrapper used to assert that deadlines are set
+// during http2Client.Close().
+type deadlineTestConn struct {
+	net.Conn
+	// We use atomic.Bool here since there may be more than one call to
+	// http2Client.Close -- which sets these deadlines -- and not all of them
+	// from the same goroutine as our test. In fact we only care about the first
+	// such invocation, which *does* come from the main goroutine of our test,
+	// but the race detector can't know that and complains (understandably)
+	// about writes from those successive calls when these variables are not
+	// atomic.Bool.
+	//
+	// For more detailed background, see
+	// https://github.com/grpc/grpc-go/pull/8534#discussion_r2297717445 .
+	observedReadDeadline  atomic.Bool
+	observedWriteDeadline atomic.Bool
+}
+
+func (c *deadlineTestConn) SetReadDeadline(t time.Time) error {
+	c.observedReadDeadline.Store(true)
+	return c.Conn.SetReadDeadline(t)
+}
+
+func (c *deadlineTestConn) SetWriteDeadline(t time.Time) error {
+	c.observedWriteDeadline.Store(true)
+	return c.Conn.SetWriteDeadline(t)
+}
+
+// Tests that connection read and write deadlines are set as expected during
+// Close().
+func (s) TestCloseSetsConnectionDeadlines(t *testing.T) {
+	dialer := func(_ context.Context, addr string) (net.Conn, error) {
+		conn, err := net.Dial("tcp", addr)
+		if err != nil {
+			return nil, err
+		}
+		return &deadlineTestConn{Conn: conn}, nil
+	}
+	co := ConnectOptions{
+		Dialer: dialer,
+	}
+	server, client, cancel := setUpWithOptions(t, 0, &ServerConfig{}, normal, co)
+	defer cancel()
+	defer server.stop()
+	dConn := client.conn.(*deadlineTestConn)
+	// Set both to false before invoking Close() in case some other code set a
+	// deadline above.
+	dConn.observedReadDeadline.Store(false)
+	dConn.observedWriteDeadline.Store(false)
+	client.Close(fmt.Errorf("closed manually by test"))
+	if !dConn.observedReadDeadline.Load() {
+		t.Errorf("Connection read deadline was never set")
+	}
+	if !dConn.observedWriteDeadline.Load() {
+		t.Errorf("Connection write deadline was never set")
+	}
+}
+
 // TestReadHeaderMultipleBuffers tests the stream when the gRPC headers are
 // split across multiple buffers. It verifies that the reporting of the
 // number of bytes read for flow control is correct.
 func (s) TestReadMessageHeaderMultipleBuffers(t *testing.T) {
 	headerLen := 5
-	recvBuffer := newRecvBuffer()
-	recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, 3)})
-	recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, headerLen-3)})
 	bytesRead := 0
 	s := Stream{
-		requestRead: func(int) {},
-		trReader: &transportReader{
-			reader: &recvBufferReader{
-				recv: recvBuffer,
-			},
-			windowHandler: func(i int) {
+		readRequester: &fakeReadRequester{},
+	}
+	s.buf.init()
+	recvBuffer := &s.buf
+	s.trReader = transportReader{
+		reader: recvBufferReader{
+			recv: recvBuffer,
+		},
+		windowHandler: &mockWindowUpdater{
+			f: func(i int) {
 				bytesRead += i
 			},
 		},
 	}
 
+	recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, 3)})
+	recvBuffer.put(recvMsg{buffer: make(mem.SliceBuffer, headerLen-3)})
+
 	header := make([]byte, headerLen)
 	err := s.ReadMessageHeader(header)
 	if err != nil {
@@ -3131,8 +3285,8 @@ func (s) TestServerSendsRSTAfterDeadlineToMisbehavedClient(t *testing.T) {
 func (s) TestClientTransport_Handle1xxHeaders(t *testing.T) {
 	testStream := func() *ClientStream {
 		return &ClientStream{
-			Stream: &Stream{
-				buf: &recvBuffer{
+			Stream: Stream{
+				buf: recvBuffer{
 					c:  make(chan recvMsg),
 					mu: sync.Mutex{},
 				},
@@ -3204,3 +3358,138 @@ func (s) TestClientTransport_Handle1xxHeaders(t *testing.T) {
 		})
 	}
 }
+
+func (s) TestDeleteStreamMetricsIncrementedOnlyOnce(t *testing.T) {
+	// Enable channelz for metrics collection
+	defer internal.ChannelzTurnOffForTesting()
+	if !channelz.IsOn() {
+		channelz.TurnOn()
+	}
+
+	for _, test := range []struct {
+		name                string
+		eosReceived         bool
+		wantStreamSucceeded int64
+		wantStreamFailed    int64
+	}{
+		{
+			name:                "StreamsSucceeded",
+			eosReceived:         true,
+			wantStreamSucceeded: 1,
+			wantStreamFailed:    0,
+		},
+		{
+			name:                "StreamsFailed",
+			eosReceived:         false,
+			wantStreamSucceeded: 0,
+			wantStreamFailed:    1,
+		},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+			defer cancel()
+
+			// Setup server configuration with channelz support
+			serverConfig := &ServerConfig{
+				ChannelzParent: channelz.RegisterServer(t.Name()),
+			}
+			defer channelz.RemoveEntry(serverConfig.ChannelzParent.ID)
+
+			// Create server and client with normal handler (not notifyCall)
+			server, client, cancel := setUpWithOptions(t, 0, serverConfig, normal, ConnectOptions{})
+			defer func() {
+				client.Close(fmt.Errorf("test cleanup"))
+				server.stop()
+				cancel()
+			}()
+
+			// Wait for connection to be established
+			waitWhileTrue(t, func() (bool, error) {
+				server.mu.Lock()
+				defer server.mu.Unlock()
+				if len(server.conns) == 0 {
+					return true, fmt.Errorf("timed-out while waiting for connection")
+				}
+				return false, nil
+			})
+
+			// Get the server transport
+			server.mu.Lock()
+			var serverTransport *http2Server
+			for st := range server.conns {
+				serverTransport = st.(*http2Server)
+				break
+			}
+			server.mu.Unlock()
+
+			if serverTransport == nil {
+				t.Fatal("Server transport not found")
+			}
+
+			clientStream, err := client.NewStream(ctx, &CallHdr{})
+			if err != nil {
+				t.Fatalf("Failed to create stream: %v", err)
+			}
+
+			// Wait for the stream to be created on the server side
+			var serverStream *ServerStream
+			waitWhileTrue(t, func() (bool, error) {
+				serverTransport.mu.Lock()
+				defer serverTransport.mu.Unlock()
+				for _, v := range serverTransport.activeStreams {
+					if v.id == clientStream.id {
+						serverStream = v
+						return false, nil
+					}
+				}
+				return true, nil
+			})
+
+			if serverStream == nil {
+				t.Fatalf("Server stream not found for client stream ID %d", clientStream.id)
+			}
+
+			// First call to deleteStream should remove the stream from activeStreams and update metrics
+			serverTransport.deleteStream(serverStream, test.eosReceived)
+
+			// Check metrics after first deleteStream call
+			streamsSucceeded := serverTransport.channelz.SocketMetrics.StreamsSucceeded.Load()
+			streamsFailed := serverTransport.channelz.SocketMetrics.StreamsFailed.Load()
+
+			if streamsSucceeded != test.wantStreamSucceeded {
+				t.Errorf("After first deleteStream - StreamsSucceeded: got %d, want %d", streamsSucceeded, test.wantStreamSucceeded)
+			}
+			if streamsFailed != test.wantStreamFailed {
+				t.Errorf("After first deleteStream - StreamsFailed: got %d, want %d", streamsFailed, test.wantStreamFailed)
+			}
+
+			// Additional calls to deleteStream should not change metrics (stream already deleted)
+			serverTransport.deleteStream(serverStream, test.eosReceived)
+			serverTransport.deleteStream(serverStream, test.eosReceived)
+
+			// Verify metrics haven't changed after subsequent calls
+			additionalStreamsSucceeded := serverTransport.channelz.SocketMetrics.StreamsSucceeded.Load()
+			additionalStreamsFailed := serverTransport.channelz.SocketMetrics.StreamsFailed.Load()
+
+			if additionalStreamsSucceeded != test.wantStreamSucceeded {
+				t.Errorf("After multiple deleteStream calls - StreamsSucceeded changed: got %d, want %d", additionalStreamsSucceeded, test.wantStreamSucceeded)
+			}
+			if additionalStreamsFailed != test.wantStreamFailed {
+				t.Errorf("After multiple deleteStream calls - StreamsFailed changed: got %d, want %d", additionalStreamsFailed, test.wantStreamFailed)
+			}
+		})
+	}
+}
+
+type fakeReadRequester struct {
+}
+
+func (f *fakeReadRequester) requestRead(int) {}
+
+type mockWindowUpdater struct {
+	f func(int)
+}
+
+func (m *mockWindowUpdater) updateWindow(n int) {
+	m.f(n)
+}
diff --git a/internal/xds/balancer/cdsbalancer/aggregate_cluster_test.go b/internal/xds/balancer/cdsbalancer/aggregate_cluster_test.go
index 581f511b..7e277f3e 100644
--- a/internal/xds/balancer/cdsbalancer/aggregate_cluster_test.go
+++ b/internal/xds/balancer/cdsbalancer/aggregate_cluster_test.go
@@ -39,9 +39,14 @@ import (
 
 	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
 	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
+	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
 	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	testgrpc "google.golang.org/grpc/interop/grpc_testing"
 	testpb "google.golang.org/grpc/interop/grpc_testing"
+
+	_ "google.golang.org/grpc/internal/xds/httpfilter/router" // Register the router filter.
+	_ "google.golang.org/grpc/internal/xds/resolver"          // Register the xds resolver
 )
 
 // makeAggregateClusterResource returns an aggregate cluster resource with the
@@ -105,13 +110,13 @@ func (s) TestAggregateClusterSuccess_LeafNode(t *testing.T) {
 		},
 		{
 			name:                  "dns",
-			firstClusterResource:  makeLogicalDNSClusterResource(clusterName, "dns_host", uint32(8080)),
-			secondClusterResource: makeLogicalDNSClusterResource(clusterName, "dns_host_new", uint32(8080)),
+			firstClusterResource:  makeLogicalDNSClusterResource(clusterName, "dns_host", uint32(port)),
+			secondClusterResource: makeLogicalDNSClusterResource(clusterName, "dns_host_new", uint32(port)),
 			wantFirstChildCfg: &clusterresolver.LBConfig{
 				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
 					Cluster:          clusterName,
 					Type:             clusterresolver.DiscoveryMechanismTypeLogicalDNS,
-					DNSHostname:      "dns_host:8080",
+					DNSHostname:      fmt.Sprintf("dns_host:%d", port),
 					OutlierDetection: json.RawMessage(`{}`),
 					TelemetryLabels:  xdsinternal.UnknownCSMLabels,
 				}},
@@ -121,7 +126,7 @@ func (s) TestAggregateClusterSuccess_LeafNode(t *testing.T) {
 				DiscoveryMechanisms: []clusterresolver.DiscoveryMechanism{{
 					Cluster:          clusterName,
 					Type:             clusterresolver.DiscoveryMechanismTypeLogicalDNS,
-					DNSHostname:      "dns_host_new:8080",
+					DNSHostname:      fmt.Sprintf("dns_host_new:%d", port),
 					OutlierDetection: json.RawMessage(`{}`),
 					TelemetryLabels:  xdsinternal.UnknownCSMLabels,
 				}},
@@ -133,13 +138,16 @@ func (s) TestAggregateClusterSuccess_LeafNode(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-			mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+			mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 			// Push the first cluster resource through the management server and
 			// verify the configuration pushed to the child policy.
 			resources := e2e.UpdateOptions{
 				NodeID:         nodeID,
+				Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+				Routes:         []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 				Clusters:       []*v3clusterpb.Cluster{test.firstClusterResource},
+				Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 				SkipValidation: true,
 			}
 			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
@@ -154,6 +162,7 @@ func (s) TestAggregateClusterSuccess_LeafNode(t *testing.T) {
 			// Push the second cluster resource through the management server and
 			// verify the configuration pushed to the child policy.
 			resources.Clusters[0] = test.secondClusterResource
+			resources.Endpoints[0] = e2e.DefaultEndpoint(serviceName+"-new", host, []uint32{port})
 			if err := mgmtServer.Update(ctx, resources); err != nil {
 				t.Fatal(err)
 			}
@@ -176,19 +185,21 @@ func (s) TestAggregateClusterSuccess_LeafNode(t *testing.T) {
 // contains the expected discovery mechanisms.
 func (s) TestAggregateClusterSuccess_ThenUpdateChildClusters(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	// Configure the management server with the aggregate cluster resource
 	// pointing to two child clusters, one EDS and one LogicalDNS. Include the
 	// resource corresponding to the EDS cluster here, but don't include
 	// resource corresponding to the LogicalDNS cluster yet.
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{edsClusterName, dnsClusterName}),
 			e2e.DefaultCluster(edsClusterName, serviceName, e2e.SecurityLevelNone),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -237,14 +248,16 @@ func (s) TestAggregateClusterSuccess_ThenUpdateChildClusters(t *testing.T) {
 	const dnsClusterNameNew = dnsClusterName + "-new"
 	const dnsHostNameNew = dnsHostName + "-new"
 	resources = e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{edsClusterName, dnsClusterNameNew}),
 			e2e.DefaultCluster(edsClusterName, serviceName, e2e.SecurityLevelNone),
 			makeLogicalDNSClusterResource(dnsClusterName, dnsHostName, dnsPort),
 			makeLogicalDNSClusterResource(dnsClusterNameNew, dnsHostNameNew, dnsPort),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -283,18 +296,20 @@ func (s) TestAggregateClusterSuccess_ThenUpdateChildClusters(t *testing.T) {
 // policy contains a single discovery mechanism.
 func (s) TestAggregateClusterSuccess_ThenChangeRootToEDS(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	// Configure the management server with the aggregate cluster resource
 	// pointing to two child clusters.
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{edsClusterName, dnsClusterName}),
 			e2e.DefaultCluster(edsClusterName, serviceName, e2e.SecurityLevelNone),
 			makeLogicalDNSClusterResource(dnsClusterName, dnsHostName, dnsPort),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -326,12 +341,14 @@ func (s) TestAggregateClusterSuccess_ThenChangeRootToEDS(t *testing.T) {
 	}
 
 	resources = e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone),
 			makeLogicalDNSClusterResource(dnsClusterName, dnsHostName, dnsPort),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -358,13 +375,15 @@ func (s) TestAggregateClusterSuccess_ThenChangeRootToEDS(t *testing.T) {
 // discovery mechanisms.
 func (s) TestAggregatedClusterSuccess_SwitchBetweenLeafAndAggregate(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	// Start off with the requested cluster being a leaf EDS cluster.
 	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		SkipValidation: true,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
+		Clusters:  []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -388,13 +407,15 @@ func (s) TestAggregatedClusterSuccess_SwitchBetweenLeafAndAggregate(t *testing.T
 	// Switch the requested cluster to be an aggregate cluster pointing to two
 	// child clusters.
 	resources = e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{edsClusterName, dnsClusterName}),
 			e2e.DefaultCluster(edsClusterName, serviceName, e2e.SecurityLevelNone),
 			makeLogicalDNSClusterResource(dnsClusterName, dnsHostName, dnsPort),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -424,9 +445,11 @@ func (s) TestAggregatedClusterSuccess_SwitchBetweenLeafAndAggregate(t *testing.T
 
 	// Switch the cluster back to a leaf EDS cluster.
 	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		SkipValidation: true,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
+		Clusters:  []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -452,10 +475,13 @@ func (s) TestAggregatedClusterSuccess_SwitchBetweenLeafAndAggregate(t *testing.T
 // longer exceed maximum depth, but be at the maximum allowed depth, and
 // verifies that an RPC can be made successfully.
 func (s) TestAggregatedClusterFailure_ExceedsMaxStackDepth(t *testing.T) {
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
+
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{clusterName + "-1"}),
 			makeAggregateClusterResource(clusterName+"-1", []string{clusterName + "-2"}),
@@ -475,7 +501,7 @@ func (s) TestAggregatedClusterFailure_ExceedsMaxStackDepth(t *testing.T) {
 			makeAggregateClusterResource(clusterName+"-15", []string{clusterName + "-16"}),
 			e2e.DefaultCluster(clusterName+"-16", serviceName, e2e.SecurityLevelNone),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -502,7 +528,9 @@ func (s) TestAggregatedClusterFailure_ExceedsMaxStackDepth(t *testing.T) {
 	// Update the aggregate cluster resource to no longer exceed max depth, and
 	// be at the maximum depth allowed.
 	resources = e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterName, []string{clusterName + "-1"}),
 			makeAggregateClusterResource(clusterName+"-1", []string{clusterName + "-2"}),
@@ -521,8 +549,7 @@ func (s) TestAggregatedClusterFailure_ExceedsMaxStackDepth(t *testing.T) {
 			makeAggregateClusterResource(clusterName+"-14", []string{clusterName + "-15"}),
 			e2e.DefaultCluster(clusterName+"-15", serviceName, e2e.SecurityLevelNone),
 		},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, server.Address)})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -540,7 +567,7 @@ func (s) TestAggregatedClusterFailure_ExceedsMaxStackDepth(t *testing.T) {
 // pushed only after all child clusters are resolved.
 func (s) TestAggregatedClusterSuccess_DiamondDependency(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	// Configure the management server with an aggregate cluster resource having
 	// a diamond dependency pattern, (A->[B,C]; B->D; C->D). Includes resources
@@ -554,13 +581,15 @@ func (s) TestAggregatedClusterSuccess_DiamondDependency(t *testing.T) {
 		clusterNameD = clusterName + "-D"
 	)
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterNameA, []string{clusterNameB, clusterNameC}),
 			makeAggregateClusterResource(clusterNameB, []string{clusterNameD}),
 			e2e.DefaultCluster(clusterNameD, serviceName, e2e.SecurityLevelNone),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -607,7 +636,7 @@ func (s) TestAggregatedClusterSuccess_DiamondDependency(t *testing.T) {
 // pushed only after all child clusters are resolved.
 func (s) TestAggregatedClusterSuccess_IgnoreDups(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	// Configure the management server with an aggregate cluster resource that
 	// has duplicates in the graph, (A->[B, C]; B->[C, D]). Include resources
@@ -621,13 +650,15 @@ func (s) TestAggregatedClusterSuccess_IgnoreDups(t *testing.T) {
 		clusterNameD = clusterName + "-D"
 	)
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterNameA, []string{clusterNameB, clusterNameC}),
 			makeAggregateClusterResource(clusterNameB, []string{clusterNameC, clusterNameD}),
 			e2e.DefaultCluster(clusterNameD, serviceName, e2e.SecurityLevelNone),
 		},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -646,7 +677,8 @@ func (s) TestAggregatedClusterSuccess_IgnoreDups(t *testing.T) {
 	// Now configure the resource for cluster C in the management server,
 	// thereby completing the cluster graph. This should result in configuration
 	// being pushed down to the child policy.
-	resources.Clusters = append(resources.Clusters, e2e.DefaultCluster(clusterNameC, serviceName, e2e.SecurityLevelNone))
+	resources.Clusters = append(resources.Clusters, e2e.DefaultCluster(clusterNameC, edsClusterName, e2e.SecurityLevelNone))
+	resources.Endpoints = append(resources.Endpoints, e2e.DefaultEndpoint(edsClusterName, "eshita_eds", []uint32{1234}))
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -656,7 +688,7 @@ func (s) TestAggregatedClusterSuccess_IgnoreDups(t *testing.T) {
 			{
 				Cluster:          clusterNameC,
 				Type:             clusterresolver.DiscoveryMechanismTypeEDS,
-				EDSServiceName:   serviceName,
+				EDSServiceName:   edsClusterName,
 				OutlierDetection: json.RawMessage(`{}`),
 				TelemetryLabels:  xdsinternal.UnknownCSMLabels,
 			},
@@ -685,7 +717,7 @@ func (s) TestAggregatedClusterSuccess_IgnoreDups(t *testing.T) {
 // child policy and that an RPC can be successfully made.
 func (s) TestAggregatedCluster_NodeChildOfItself(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	const (
 		clusterNameA = clusterName // cluster name in cds LB policy config
@@ -695,6 +727,8 @@ func (s) TestAggregatedCluster_NodeChildOfItself(t *testing.T) {
 	// child is itself.
 	resources := e2e.UpdateOptions{
 		NodeID:         nodeID,
+		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:         []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters:       []*v3clusterpb.Cluster{makeAggregateClusterResource(clusterNameA, []string{clusterNameA})},
 		SkipValidation: true,
 	}
@@ -729,13 +763,14 @@ func (s) TestAggregatedCluster_NodeChildOfItself(t *testing.T) {
 
 	// Update the aggregate cluster to point to a leaf EDS cluster.
 	resources = e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterNameA, []string{clusterNameB}),
 			e2e.DefaultCluster(clusterNameB, serviceName, e2e.SecurityLevelNone),
 		},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, server.Address)})},
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
@@ -770,7 +805,7 @@ func (s) TestAggregatedCluster_NodeChildOfItself(t *testing.T) {
 // that the aggregate cluster graph has no leaf clusters.
 func (s) TestAggregatedCluster_CycleWithNoLeafNode(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	const (
 		clusterNameA = clusterName // cluster name in cds LB policy config
@@ -779,7 +814,9 @@ func (s) TestAggregatedCluster_CycleWithNoLeafNode(t *testing.T) {
 	// Configure the management server with an aggregate cluster resource graph
 	// that contains a cycle and no leaf clusters.
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterNameA, []string{clusterNameB}),
 			makeAggregateClusterResource(clusterNameB, []string{clusterNameA}),
@@ -818,7 +855,7 @@ func (s) TestAggregatedCluster_CycleWithNoLeafNode(t *testing.T) {
 // child policy and RPCs should get routed to that leaf cluster.
 func (s) TestAggregatedCluster_CycleWithLeafNode(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	// Start a test service backend.
 	server := stubserver.StartTestService(t, nil)
@@ -832,13 +869,15 @@ func (s) TestAggregatedCluster_CycleWithLeafNode(t *testing.T) {
 	// Configure the management server with an aggregate cluster resource graph
 	// that contains a cycle, but also contains a leaf cluster.
 	resources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterNameA, []string{clusterNameB}),
 			makeAggregateClusterResource(clusterNameB, []string{clusterNameA, clusterNameC}),
 			e2e.DefaultCluster(clusterNameC, serviceName, e2e.SecurityLevelNone),
 		},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
+		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, server.Address)})},
 		SkipValidation: true,
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
@@ -888,7 +927,7 @@ func (s) TestWatchers(t *testing.T) {
 		}
 		return nil
 	}
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, onStreamReq)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, onStreamReq)
 
 	const (
 		clusterA = clusterName
@@ -899,7 +938,9 @@ func (s) TestWatchers(t *testing.T) {
 
 	// Initial CDS resources: A -> B, C
 	initialResources := e2e.UpdateOptions{
-		NodeID: nodeID,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterA)},
 		Clusters: []*v3clusterpb.Cluster{
 			makeAggregateClusterResource(clusterA, []string{clusterB, clusterC}),
 		},
@@ -914,14 +955,10 @@ func (s) TestWatchers(t *testing.T) {
 	}
 
 	// Update the CDS resources to remove cluster C and add cluster D.
-	updatedResources := e2e.UpdateOptions{
-		NodeID: nodeID,
-		Clusters: []*v3clusterpb.Cluster{
-			makeAggregateClusterResource(clusterA, []string{clusterB, clusterD}),
-		},
-		SkipValidation: true,
+	initialResources.Clusters = []*v3clusterpb.Cluster{
+		makeAggregateClusterResource(clusterA, []string{clusterB, clusterD}),
 	}
-	if err := mgmtServer.Update(ctx, updatedResources); err != nil {
+	if err := mgmtServer.Update(ctx, initialResources); err != nil {
 		t.Fatalf("Update failed: %v", err)
 	}
 	wantNames = []string{clusterA, clusterB, clusterD}
diff --git a/internal/xds/balancer/cdsbalancer/cdsbalancer_security_test.go b/internal/xds/balancer/cdsbalancer/cdsbalancer_security_test.go
index 401a16dc..f26571fd 100644
--- a/internal/xds/balancer/cdsbalancer/cdsbalancer_security_test.go
+++ b/internal/xds/balancer/cdsbalancer/cdsbalancer_security_test.go
@@ -25,110 +25,41 @@ import (
 	"os"
 	"strings"
 	"testing"
-	"unsafe"
 
-	"github.com/google/go-cmp/cmp"
 	"github.com/google/uuid"
 	"google.golang.org/grpc"
-	"google.golang.org/grpc/attributes"
-	"google.golang.org/grpc/balancer"
 	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/credentials"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/credentials/tls/certprovider"
 	"google.golang.org/grpc/credentials/xds"
 	"google.golang.org/grpc/internal"
-	"google.golang.org/grpc/internal/balancer/stub"
-	xdscredsinternal "google.golang.org/grpc/internal/credentials/xds"
 	"google.golang.org/grpc/internal/envconfig"
 	"google.golang.org/grpc/internal/stubserver"
 	"google.golang.org/grpc/internal/testutils"
 	"google.golang.org/grpc/internal/testutils/xds/e2e"
 	"google.golang.org/grpc/internal/xds/bootstrap"
-	"google.golang.org/grpc/internal/xds/xdsclient"
 	"google.golang.org/grpc/peer"
 	"google.golang.org/grpc/resolver"
-	"google.golang.org/grpc/resolver/manual"
-	"google.golang.org/grpc/serviceconfig"
 	"google.golang.org/grpc/testdata"
 
 	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
 	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
+	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
 	v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
 	testgrpc "google.golang.org/grpc/interop/grpc_testing"
 	testpb "google.golang.org/grpc/interop/grpc_testing"
 
 	_ "google.golang.org/grpc/credentials/tls/certprovider/pemfile" // Register the file watcher certificate provider plugin.
+	_ "google.golang.org/grpc/internal/xds/httpfilter/router"       // Register the router filter.
+	_ "google.golang.org/grpc/internal/xds/resolver"                // Register the xds resolver
 )
 
-// testCCWrapper wraps a balancer.ClientConn and intercepts NewSubConn and
-// returns the xDS handshake info back to the test for inspection.
-type testCCWrapper struct {
-	balancer.ClientConn
-	handshakeInfoCh chan *xdscredsinternal.HandshakeInfo
-}
-
-// NewSubConn forwards the call to the underlying balancer.ClientConn, but
-// before that, it validates the following:
-//   - there is only one address in the addrs slice
-//   - the single address contains xDS handshake information, which is then
-//     pushed onto the handshakeInfoCh channel
-func (tcc *testCCWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
-	if len(addrs) != 1 {
-		return nil, fmt.Errorf("NewSubConn got %d addresses, want 1", len(addrs))
-	}
-	getHI := internal.GetXDSHandshakeInfoForTesting.(func(attr *attributes.Attributes) *unsafe.Pointer)
-	hi := getHI(addrs[0].Attributes)
-	if hi == nil {
-		return nil, fmt.Errorf("NewSubConn got address without xDS handshake info")
-	}
-
-	sc, err := tcc.ClientConn.NewSubConn(addrs, opts)
-	select {
-	case tcc.handshakeInfoCh <- (*xdscredsinternal.HandshakeInfo)(*hi):
-	default:
-	}
-	return sc, err
-}
-
-// Registers a wrapped cds LB policy for the duration of this test that retains
-// all the functionality of the original cds LB policy, but overrides the
-// NewSubConn method passed to the policy and makes the xDS handshake
-// information passed to NewSubConn available to the test.
-//
-// Accepts as argument a channel onto which xDS handshake information passed to
-// NewSubConn is written to.
-func registerWrappedCDSPolicyWithNewSubConnOverride(t *testing.T, ch chan *xdscredsinternal.HandshakeInfo) {
-	cdsBuilder := balancer.Get(cdsName)
-	internal.BalancerUnregister(cdsBuilder.Name())
-	var ccWrapper *testCCWrapper
-	stub.Register(cdsBuilder.Name(), stub.BalancerFuncs{
-		Init: func(bd *stub.BalancerData) {
-			ccWrapper = &testCCWrapper{
-				ClientConn:      bd.ClientConn,
-				handshakeInfoCh: ch,
-			}
-			bd.ChildBalancer = cdsBuilder.Build(ccWrapper, bd.BuildOptions)
-		},
-		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
-			return cdsBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
-		},
-		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
-			return bd.ChildBalancer.UpdateClientConnState(ccs)
-		},
-		Close: func(bd *stub.BalancerData) {
-			bd.ChildBalancer.Close()
-		},
-	})
-	t.Cleanup(func() { balancer.Register(cdsBuilder) })
-}
-
 // Common setup for security tests:
 //   - creates an xDS client with the specified bootstrap configuration
-//   - creates a manual resolver that specifies cds as the top-level LB policy
-//   - creates a channel that uses the passed in client creds and the manual
-//     resolver
+//   - creates a gRPC channel that uses the passed in client creds
 //   - creates a test server that uses the passed in server creds
 //
 // Returns the following:
@@ -137,32 +68,13 @@ func registerWrappedCDSPolicyWithNewSubConnOverride(t *testing.T, ch chan *xdscr
 func setupForSecurityTests(t *testing.T, bootstrapContents []byte, clientCreds, serverCreds credentials.TransportCredentials) (*grpc.ClientConn, string) {
 	t.Helper()
 
-	config, err := bootstrap.NewConfigFromContents(bootstrapContents)
-	if err != nil {
-		t.Fatalf("Failed to parse bootstrap contents: %s, %v", string(bootstrapContents), err)
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
 	}
-	pool := xdsclient.NewPool(config)
-	xdsClient, xdsClose, err := pool.NewClientForTesting(xdsclient.OptionsForTesting{
-		Name: t.Name(),
-	})
+	r, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bootstrapContents)
 	if err != nil {
-		t.Fatalf("Failed to create xDS client: %v", err)
-	}
-	t.Cleanup(xdsClose)
-
-	// Create a manual resolver that configures the CDS LB policy as the
-	// top-level LB policy on the channel.
-	r := manual.NewBuilderWithScheme("whatever")
-	jsonSC := fmt.Sprintf(`{
-			"loadBalancingConfig":[{
-				"cds_experimental":{
-					"cluster": "%s"
-				}
-			}]
-		}`, clusterName)
-	scpr := internal.ParseServiceConfig.(func(string) *serviceconfig.ParseResult)(jsonSC)
-	state := xdsclient.SetClient(resolver.State{ServiceConfig: scpr}, xdsClient)
-	r.InitialState(state)
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
 
 	// Create a ClientConn with the specified transport credentials.
 	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(r))
@@ -259,14 +171,8 @@ func verifySecurityInformationFromPeer(t *testing.T, pr *peer.Peer, wantSecLevel
 
 // Tests the case where xDS credentials are not in use, but the cds LB policy
 // receives a Cluster update with security configuration. Verifies that the
-// security configuration is not parsed by the cds LB policy by looking at the
-// xDS handshake info passed to NewSubConn.
+// connection between client and server is insecure.
 func (s) TestSecurityConfigWithoutXDSCreds(t *testing.T) {
-	// Register a wrapped cds LB policy for the duration of this test that writes
-	// the xDS handshake info passed to NewSubConn onto the given channel.
-	handshakeInfoCh := make(chan *xdscredsinternal.HandshakeInfo, 1)
-	registerWrappedCDSPolicyWithNewSubConnOverride(t, handshakeInfoCh)
-
 	// Spin up an xDS management server.
 	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
 
@@ -278,14 +184,15 @@ func (s) TestSecurityConfigWithoutXDSCreds(t *testing.T) {
 	// insecure credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, insecure.NewCredentials(), nil)
 
-	// Configure cluster and endpoints resources in the management server. The
+	// Configure default resources in the management server. The
 	// cluster resource is configured to return security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -293,34 +200,19 @@ func (s) TestSecurityConfigWithoutXDSCreds(t *testing.T) {
 	}
 
 	// Verify that a successful RPC can be made.
+	peer := &peer.Peer{}
 	client := testgrpc.NewTestServiceClient(cc)
-	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
 		t.Fatalf("EmptyCall() failed: %v", err)
 	}
 
-	// Ensure that the xDS handshake info passed to NewSubConn is empty.
-	var gotHI *xdscredsinternal.HandshakeInfo
-	select {
-	case gotHI = <-handshakeInfoCh:
-	case <-ctx.Done():
-		t.Fatal("Timeout when waiting to read handshake info passed to NewSubConn")
-	}
-	wantHI := xdscredsinternal.NewHandshakeInfo(nil, nil, nil, false)
-	if !cmp.Equal(gotHI, wantHI) {
-		t.Fatalf("NewSubConn got handshake info %+v, want %+v", gotHI, wantHI)
-	}
+	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelNone)
 }
 
 // Tests the case where xDS credentials are in use, but the cds LB policy
 // receives a Cluster update without security configuration. Verifies that the
-// xDS handshake info passed to NewSubConn specified the use of fallback
-// credentials.
+// connection between client and server is insecure.
 func (s) TestNoSecurityConfigWithXDSCreds(t *testing.T) {
-	// Register a wrapped cds LB policy for the duration of this test that writes
-	// the xDS handshake info passed to NewSubConn onto the given channel.
-	handshakeInfoCh := make(chan *xdscredsinternal.HandshakeInfo, 1)
-	registerWrappedCDSPolicyWithNewSubConnOverride(t, handshakeInfoCh)
-
 	// Spin up an xDS management server.
 	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
 
@@ -332,14 +224,14 @@ func (s) TestNoSecurityConfigWithXDSCreds(t *testing.T) {
 	// insecure credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), nil)
 
-	// Configure cluster and endpoints resources in the management server. The
+	// Configure default resources in the management server. The
 	// cluster resource is not configured to return any security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -347,25 +239,12 @@ func (s) TestNoSecurityConfigWithXDSCreds(t *testing.T) {
 	}
 
 	// Verify that a successful RPC can be made.
+	peer := &peer.Peer{}
 	client := testgrpc.NewTestServiceClient(cc)
-	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true), grpc.Peer(peer)); err != nil {
 		t.Fatalf("EmptyCall() failed: %v", err)
 	}
-
-	// Ensure that the xDS handshake info passed to NewSubConn is empty.
-	var gotHI *xdscredsinternal.HandshakeInfo
-	select {
-	case gotHI = <-handshakeInfoCh:
-	case <-ctx.Done():
-		t.Fatal("Timeout when waiting to read handshake info passed to NewSubConn")
-	}
-	wantHI := xdscredsinternal.NewHandshakeInfo(nil, nil, nil, false)
-	if !cmp.Equal(gotHI, wantHI) {
-		t.Fatalf("NewSubConn got handshake info %+v, want %+v", gotHI, wantHI)
-	}
-	if !gotHI.UseFallbackCreds() {
-		t.Fatal("NewSubConn got handshake info that does not specify the use of fallback creds")
-	}
+	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelNone)
 }
 
 // Tests the case where the security config returned by the management server
@@ -395,11 +274,13 @@ func (s) TestSecurityConfigNotFoundInBootstrap(t *testing.T) {
 
 	// Configure a cluster resource that contains security configuration, in the
 	// management server.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       port,
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -458,15 +339,17 @@ func (s) TestCertproviderStoreError(t *testing.T) {
 	}
 
 	// Create a grpc channel with xDS creds.
-	cc, _ := setupForSecurityTests(t, bootstrapContents, xdsClientCredsWithInsecureFallback(t), nil)
+	cc, serverAddress := setupForSecurityTests(t, bootstrapContents, xdsClientCredsWithInsecureFallback(t), nil)
 
 	// Configure a cluster resource that contains security configuration, in the
 	// management server.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -493,14 +376,15 @@ func (s) TestGoodSecurityConfig(t *testing.T) {
 	// credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
 
-	// Configure cluster and endpoints resources in the management server. The
+	// Configure default resources in the management server. The
 	// cluster resource is configured to return security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -535,8 +419,7 @@ func (s) TestSecurityConfigUpdate_BadToGood(t *testing.T) {
 	// credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
 
-	// Configure cluster and endpoints resources in the management server. The
-	// cluster resource contains security configuration with a certificate
+	// The cluster resource contains security configuration with a certificate
 	// provider instance that is missing in the bootstrap configuration.
 	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
 	cluster.TransportSocket = &v3corepb.TransportSocket{
@@ -554,10 +437,11 @@ func (s) TestSecurityConfigUpdate_BadToGood(t *testing.T) {
 		},
 	}
 	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{cluster},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
+		Clusters:  []*v3clusterpb.Cluster{cluster},
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, serverAddress)})},
 	}
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -572,8 +456,10 @@ func (s) TestSecurityConfigUpdate_BadToGood(t *testing.T) {
 	// configuration.
 	resources = e2e.UpdateOptions{
 		NodeID:         nodeID,
+		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:         []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterName)},
 		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
+		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, serverAddress)})},
 		SkipValidation: true,
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -607,14 +493,15 @@ func (s) TestSecurityConfigUpdate_GoodToFallback(t *testing.T) {
 	// credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
 
-	// Configure cluster and endpoints resources in the management server. The
+	// Configure default resources in the management server. The
 	// cluster resource is configured to return security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -636,12 +523,13 @@ func (s) TestSecurityConfigUpdate_GoodToFallback(t *testing.T) {
 	// Update the resources in the management server to contain no security
 	// configuration. This should result in the use of fallback credentials,
 	// which is insecure in our case.
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, insecureServer.Address)})},
-		SkipValidation: true,
-	}
+	resources = e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, insecureServer.Address),
+		SecLevel:   e2e.SecurityLevelNone,
+	})
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -686,14 +574,15 @@ func (s) TestSecurityConfigUpdate_GoodToBad(t *testing.T) {
 	// credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
 
-	// Configure cluster and endpoints resources in the management server. The
+	// Configure default resources in the management server. The
 	// cluster resource is configured to return security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelMTLS)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelMTLS,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -708,11 +597,10 @@ func (s) TestSecurityConfigUpdate_GoodToBad(t *testing.T) {
 	}
 	verifySecurityInformationFromPeer(t, peer, e2e.SecurityLevelMTLS)
 
-	// Configure cluster and endpoints resources in the management server. The
-	// cluster resource contains security configuration with a certificate
+	// The cluster resource contains security configuration with a certificate
 	// provider instance that is missing in the bootstrap configuration.
-	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
-	cluster.TransportSocket = &v3corepb.TransportSocket{
+	resources.Clusters[0] = e2e.DefaultCluster(resources.Clusters[0].Name, resources.Endpoints[0].ClusterName, e2e.SecurityLevelNone)
+	resources.Clusters[0].TransportSocket = &v3corepb.TransportSocket{
 		Name: "envoy.transport_sockets.tls",
 		ConfigType: &v3corepb.TransportSocket_TypedConfig{
 			TypedConfig: testutils.MarshalAny(t, &v3tlspb.UpstreamTlsContext{
@@ -726,12 +614,6 @@ func (s) TestSecurityConfigUpdate_GoodToBad(t *testing.T) {
 			}),
 		},
 	}
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{cluster},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -791,14 +673,15 @@ func (s) TestSystemRootCertsSecurityConfig(t *testing.T) {
 	// credentials.
 	cc, serverAddress := setupForSecurityTests(t, bc, xdsClientCredsWithInsecureFallback(t), tlsServerCreds(t))
 
-	// Configure cluster and endpoints resources in the management server. The
-	// cluster resource is configured to return security configuration.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelTLSWithSystemRootCerts)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, serverAddress)})},
-		SkipValidation: true,
-	}
+	// Configure default resources in the management server. The cluster
+	// resource is configured to return security configuration.
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, serverAddress),
+		SecLevel:   e2e.SecurityLevelTLSWithSystemRootCerts,
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
diff --git a/internal/xds/balancer/cdsbalancer/cdsbalancer_test.go b/internal/xds/balancer/cdsbalancer/cdsbalancer_test.go
index ca13655d..3bd1a49c 100644
--- a/internal/xds/balancer/cdsbalancer/cdsbalancer_test.go
+++ b/internal/xds/balancer/cdsbalancer/cdsbalancer_test.go
@@ -55,19 +55,27 @@ import (
 	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
 	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
+	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
+	v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
 	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 	testgrpc "google.golang.org/grpc/interop/grpc_testing"
 	testpb "google.golang.org/grpc/interop/grpc_testing"
 
-	_ "google.golang.org/grpc/balancer/ringhash" // Register the ring_hash LB policy
+	_ "google.golang.org/grpc/balancer/ringhash"              // Register the ring_hash LB policy
+	_ "google.golang.org/grpc/internal/xds/httpfilter/router" // Register the router filter.
+	_ "google.golang.org/grpc/internal/xds/resolver"          // Register the xds resolver
 )
 
 const (
+	target                  = "test.service"
+	routeName               = "test_route"
 	clusterName             = "cluster1"
 	edsClusterName          = clusterName + "-eds"
 	dnsClusterName          = clusterName + "-dns"
 	serviceName             = "service1"
 	dnsHostName             = "dns_host"
+	host                    = "localhost"
+	port                    = 8080
 	dnsPort                 = uint32(8080)
 	defaultTestTimeout      = 5 * time.Second
 	defaultTestShortTimeout = 10 * time.Millisecond // For events expected to *not* happen.
@@ -152,38 +160,8 @@ func registerWrappedClusterResolverPolicy(t *testing.T) (chan serviceconfig.Load
 	return lbCfgCh, resolverErrCh, exitIdleCh, closeCh
 }
 
-// Registers a wrapped cds LB policy for the duration of this test that retains
-// all the functionality of the original cds LB policy, but makes the newly
-// built policy available to the test to directly invoke any balancer methods.
-//
-// Returns a channel on which the newly built cds LB policy is written to.
-func registerWrappedCDSPolicy(t *testing.T) chan balancer.Balancer {
-	cdsBuilder := balancer.Get(cdsName)
-	internal.BalancerUnregister(cdsBuilder.Name())
-	cdsBalancerCh := make(chan balancer.Balancer, 1)
-	stub.Register(cdsBuilder.Name(), stub.BalancerFuncs{
-		Init: func(bd *stub.BalancerData) {
-			bal := cdsBuilder.Build(bd.ClientConn, bd.BuildOptions)
-			bd.ChildBalancer = bal
-			cdsBalancerCh <- bal
-		},
-		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
-			return cdsBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
-		},
-		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
-			return bd.ChildBalancer.UpdateClientConnState(ccs)
-		},
-		Close: func(bd *stub.BalancerData) {
-			bd.ChildBalancer.Close()
-		},
-	})
-	t.Cleanup(func() { balancer.Register(cdsBuilder) })
-
-	return cdsBalancerCh
-}
-
 // Performs the following setup required for tests:
-//   - Spins up an xDS management server and and the provided onStreamRequest
+//   - Spins up an xDS management server and the provided onStreamRequest
 //     function is set to be called for every incoming request on the ADS stream.
 //   - Creates an xDS client talking to this management server
 //   - Creates a manual resolver that configures the cds LB policy as the
@@ -196,7 +174,7 @@ func registerWrappedCDSPolicy(t *testing.T) chan balancer.Balancer {
 //   - the grpc channel to the test backend service
 //   - the manual resolver configured on the channel
 //   - the xDS client used the grpc channel
-func setupWithManagementServer(t *testing.T, lis net.Listener, onStreamRequest func(int64, *v3discoverypb.DiscoveryRequest) error) (*e2e.ManagementServer, string, *grpc.ClientConn, *manual.Resolver, xdsclient.XDSClient) {
+func setupWithManagementServerAndManualResolver(t *testing.T, lis net.Listener, onStreamRequest func(int64, *v3discoverypb.DiscoveryRequest) error) (*e2e.ManagementServer, string, *grpc.ClientConn, *manual.Resolver, xdsclient.XDSClient) {
 	t.Helper()
 	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
 		Listener:        lis,
@@ -236,7 +214,7 @@ func setupWithManagementServer(t *testing.T, lis net.Listener, onStreamRequest f
 
 	cc, err := grpc.NewClient(r.Scheme()+":///test.service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
 	if err != nil {
-		t.Fatalf("grpc.NewClient(%q) = %v", lis.Addr().String(), err)
+		t.Fatalf("grpc.NewClient(\"%s:///test.service\") = %v", r.Scheme(), err)
 	}
 	cc.Connect()
 	t.Cleanup(func() { cc.Close() })
@@ -244,6 +222,48 @@ func setupWithManagementServer(t *testing.T, lis net.Listener, onStreamRequest f
 	return mgmtServer, nodeID, cc, r, xdsC
 }
 
+// Performs the following setup required for tests:
+//   - Spins up an xDS management server and the provided onStreamRequest
+//     function is set to be called for every incoming request on the ADS stream.
+//   - Creates an xDS client talking to this management server
+//   - Creates a gRPC channel with grpc.NewClient that create a xds resolver.
+//
+// Returns the following:
+//   - the xDS management server
+//   - the nodeID expected by the management server
+//   - the grpc channel to the test backend service
+//   - the xDS client used the grpc channel
+func setupWithManagementServer(t *testing.T, lis net.Listener, onStreamRequest func(int64, *v3discoverypb.DiscoveryRequest) error) (*e2e.ManagementServer, string, *grpc.ClientConn) {
+	t.Helper()
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
+		Listener:        lis,
+		OnStreamRequest: onStreamRequest,
+		// Required for aggregate clusters as all resources cannot be requested
+		// at once.
+		AllowResourceSubset: true,
+	})
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	r, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", target), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(r))
+	if err != nil {
+		t.Fatalf("grpc.NewClient(\"xds:///%s\") = %v", target, err)
+	}
+	cc.Connect()
+	t.Cleanup(func() { cc.Close() })
+
+	return mgmtServer, nodeID, cc
+}
+
 // Helper function to compare the load balancing configuration received on the
 // channel with the expected one. Both configs are marshalled to JSON and then
 // compared.
@@ -308,7 +328,7 @@ func (s) TestConfigurationUpdate_Success(t *testing.T) {
 		}
 		return nil
 	}
-	_, _, _, r, xdsClient := setupWithManagementServer(t, nil, onStreamReq)
+	_, _, _, r, xdsClient := setupWithManagementServerAndManualResolver(t, nil, onStreamReq)
 
 	// Verify that the specified cluster resource is requested.
 	wantNames := []string{clusterName}
@@ -601,14 +621,16 @@ func (s) TestClusterUpdate_Success(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-			mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+			mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 			ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 			defer cancel()
 			if err := mgmtServer.Update(ctx, e2e.UpdateOptions{
-				NodeID:         nodeID,
-				Clusters:       []*v3clusterpb.Cluster{test.clusterResource},
-				SkipValidation: true,
+				NodeID:    nodeID,
+				Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+				Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, test.clusterResource.Name)},
+				Clusters:  []*v3clusterpb.Cluster{test.clusterResource},
+				Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 			}); err != nil {
 				t.Fatal(err)
 			}
@@ -625,7 +647,7 @@ func (s) TestClusterUpdate_Success(t *testing.T) {
 // balancing configuration pushed to the child is as expected.
 func (s) TestClusterUpdate_SuccessWithLRS(t *testing.T) {
 	lbCfgCh, _, _, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, _, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, _ := setupWithManagementServer(t, nil, nil)
 
 	clusterResource := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
 		ClusterName: clusterName,
@@ -652,9 +674,11 @@ func (s) TestClusterUpdate_SuccessWithLRS(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{clusterResource},
-		SkipValidation: true,
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(target, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, target, clusterResource.Name)},
+		Clusters:  []*v3clusterpb.Cluster{clusterResource},
+		Endpoints: []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{port})},
 	}); err != nil {
 		t.Fatal(err)
 	}
@@ -674,7 +698,7 @@ func (s) TestClusterUpdate_SuccessWithLRS(t *testing.T) {
 //     continue using the previous good update.
 func (s) TestClusterUpdate_Failure(t *testing.T) {
 	_, resolverErrCh, _, _ := registerWrappedClusterResolverPolicy(t)
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
 	defer cancel()
 	cdsResourceCanceledCh := make(chan struct{}, 1)
 	onStreamReq := func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
@@ -688,18 +712,19 @@ func (s) TestClusterUpdate_Failure(t *testing.T) {
 		}
 		return nil
 	}
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, onStreamReq)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, onStreamReq)
 
 	// Configure the management server to return a cluster resource that
 	// contains a config_source_specifier for the `lrs_server` field which is not
 	// set to `self`, and hence is expected to be NACKed by the client.
-	cluster := e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)
-	cluster.LrsServer = &v3corepb.ConfigSource{ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{}}
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{cluster},
-		SkipValidation: true,
-	}
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       port,
+	})
+	resources.Clusters[0].LrsServer = &v3corepb.ConfigSource{ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{}}
+
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -728,13 +753,13 @@ func (s) TestClusterUpdate_Failure(t *testing.T) {
 	server := stubserver.StartTestService(t, nil)
 	t.Cleanup(server.Stop)
 
-	// Configure cluster and endpoints resources in the management server.
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	// Configure correct cluster and endpoints resources in the management server.
+	resources = e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, server.Address),
+	})
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -745,12 +770,8 @@ func (s) TestClusterUpdate_Failure(t *testing.T) {
 	}
 
 	// Send the bad cluster resource again.
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{cluster},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	resources.Clusters[0].LrsServer = &v3corepb.ConfigSource{ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{}}
+
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -818,7 +839,7 @@ func (s) TestResolverError(t *testing.T) {
 		}
 		return nil
 	}
-	mgmtServer, nodeID, cc, r, _ := setupWithManagementServer(t, lis, onStreamReq)
+	mgmtServer, nodeID, cc, r, _ := setupWithManagementServerAndManualResolver(t, lis, onStreamReq)
 
 	// Grab the wrapped connection from the listener wrapper. This will be used
 	// to verify the connection is closed.
@@ -872,7 +893,7 @@ func (s) TestResolverError(t *testing.T) {
 	resources := e2e.UpdateOptions{
 		NodeID:         nodeID,
 		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
+		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, host, []uint32{testutils.ParsePort(t, server.Address)})},
 		SkipValidation: true,
 	}
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -950,6 +971,83 @@ func (s) TestResolverError(t *testing.T) {
 	}
 }
 
+// Tests the scenario for resolver errors: when a resolver resource not found
+// error is received when the LDS resource is removed with a previous good
+// update from the management server, the cds LB policy is expected to push the
+// error down the child policy and put the channel in TRANSIENT_FAILURE. It is
+// also expected to cancel the CDS watch.
+func (s) TestResourceNotFoundResolverError(t *testing.T) {
+	_, _, _, childPolicyCloseCh := registerWrappedClusterResolverPolicy(t)
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	cdsResourceCanceledCh := make(chan struct{}, 1)
+	onStreamReq := func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
+		if req.GetTypeUrl() == version.V3ClusterURL {
+			if len(req.GetResourceNames()) == 0 {
+				select {
+				case cdsResourceCanceledCh <- struct{}{}:
+				case <-ctx.Done():
+				}
+			}
+		}
+		return nil
+	}
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, onStreamReq)
+
+	// Start a test service backend.
+	server := stubserver.StartTestService(t, nil)
+	t.Cleanup(server.Stop)
+
+	// Configure default resources in the management server.
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, server.Address),
+	})
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	// Verify that a successful RPC can be made.
+	client := testgrpc.NewTestServiceClient(cc)
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.WaitForReady(true)); err != nil {
+		t.Fatalf("EmptyCall() failed: %v", err)
+	}
+
+	// Push a resource-not-found type error  by removing the LDS resource.
+	resources.Listeners = nil
+	resources.SkipValidation = true
+	mgmtServer.Update(ctx, resources)
+
+	// Wait for the CDS resource to be not requested anymore.
+	select {
+	case <-ctx.Done():
+		t.Fatal("Timeout when waiting for CDS resource to be not requested")
+	case <-cdsResourceCanceledCh:
+	}
+
+	// Verify that the resolver error is pushed to the child policy.
+	select {
+	case <-childPolicyCloseCh:
+	case <-ctx.Done():
+		t.Fatal("Timeout when waiting for child policy to be closed")
+	}
+
+	testutils.AwaitState(ctx, t, cc, connectivity.TransientFailure)
+
+	// Ensure that the resolver error is propagated to the RPC caller.
+	select {
+	case <-ctx.Done():
+		t.Fatal("Timeout when waiting for error from RPC")
+	default:
+		_, err := client.EmptyCall(ctx, &testpb.Empty{})
+		if err := verifyRPCError(err, codes.Unavailable, "", nodeID); err == nil {
+			return
+		}
+	}
+}
+
 // Tests scenarios involving removal of a cluster resource from the management
 // server.
 //
@@ -973,19 +1071,19 @@ func (s) TestClusterUpdate_ResourceNotFound(t *testing.T) {
 		}
 		return nil
 	}
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, onStreamReq)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, onStreamReq)
 
 	// Start a test service backend.
 	server := stubserver.StartTestService(t, nil)
 	t.Cleanup(server.Stop)
 
-	// Configure cluster and endpoints resources in the management server.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	// Configure default resources in the management server.
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, server.Address),
+	})
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -998,7 +1096,9 @@ func (s) TestClusterUpdate_ResourceNotFound(t *testing.T) {
 
 	// Remove the cluster resource from the management server, triggering a
 	// resource-not-found error.
+	oldClusters := resources.Clusters
 	resources.Clusters = nil
+	resources.SkipValidation = true
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -1016,19 +1116,14 @@ func (s) TestClusterUpdate_ResourceNotFound(t *testing.T) {
 
 	// Ensure RPC fails with Unavailable status code and the error message is
 	// meaningful and contains the xDS node ID.
-	wantErr := fmt.Sprintf("resource %q of type %q has been removed", clusterName, "ClusterResource")
+	wantErr := fmt.Sprintf("resource %q of type %q has been removed", oldClusters[0].Name, "ClusterResource")
 	_, err := client.EmptyCall(ctx, &testpb.Empty{})
 	if err := verifyRPCError(err, codes.Unavailable, wantErr, nodeID); err != nil {
 		t.Fatal(err)
 	}
 
 	// Re-add the cluster resource to the management server.
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	resources.Clusters = oldClusters
 	if err := mgmtServer.Update(ctx, resources); err != nil {
 		t.Fatal(err)
 	}
@@ -1041,22 +1136,21 @@ func (s) TestClusterUpdate_ResourceNotFound(t *testing.T) {
 
 // Tests that closing the cds LB policy results in the the child policy being
 // closed.
-func (s) TestClose(t *testing.T) {
-	cdsBalancerCh := registerWrappedCDSPolicy(t)
+func TestClose(t *testing.T) {
 	_, _, _, childPolicyCloseCh := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	// Start a test service backend.
 	server := stubserver.StartTestService(t, nil)
 	t.Cleanup(server.Stop)
 
-	// Configure cluster and endpoints resources in the management server.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	// Configure resources in the management server.
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, server.Address),
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -1069,14 +1163,8 @@ func (s) TestClose(t *testing.T) {
 		t.Fatalf("EmptyCall() failed: %v", err)
 	}
 
-	// Retrieve the cds LB policy and close it.
-	var cdsBal balancer.Balancer
-	select {
-	case cdsBal = <-cdsBalancerCh:
-	case <-ctx.Done():
-		t.Fatal("Timeout when waiting for cds LB policy to be created")
-	}
-	cdsBal.Close()
+	// Close the gRPC ClientConn, which will close the cds LB policy.
+	cc.Close()
 
 	// Wait for the child policy to be closed.
 	select {
@@ -1089,21 +1177,20 @@ func (s) TestClose(t *testing.T) {
 // Tests that calling ExitIdle on the cds LB policy results in the call being
 // propagated to the child policy.
 func (s) TestExitIdle(t *testing.T) {
-	cdsBalancerCh := registerWrappedCDSPolicy(t)
 	_, _, exitIdleCh, _ := registerWrappedClusterResolverPolicy(t)
-	mgmtServer, nodeID, cc, _, _ := setupWithManagementServer(t, nil, nil)
+	mgmtServer, nodeID, cc := setupWithManagementServer(t, nil, nil)
 
 	// Start a test service backend.
 	server := stubserver.StartTestService(t, nil)
 	t.Cleanup(server.Stop)
 
-	// Configure cluster and endpoints resources in the management server.
-	resources := e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, serviceName, e2e.SecurityLevelNone)},
-		Endpoints:      []*v3endpointpb.ClusterLoadAssignment{e2e.DefaultEndpoint(serviceName, "localhost", []uint32{testutils.ParsePort(t, server.Address)})},
-		SkipValidation: true,
-	}
+	// Configure resources in the management server.
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: target,
+		NodeID:     nodeID,
+		Host:       host,
+		Port:       testutils.ParsePort(t, server.Address),
+	})
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	if err := mgmtServer.Update(ctx, resources); err != nil {
@@ -1116,14 +1203,8 @@ func (s) TestExitIdle(t *testing.T) {
 		t.Fatalf("EmptyCall() failed: %v", err)
 	}
 
-	// Retrieve the cds LB policy and call ExitIdle() on it.
-	var cdsBal balancer.Balancer
-	select {
-	case cdsBal = <-cdsBalancerCh:
-	case <-ctx.Done():
-		t.Fatal("Timeout when waiting for cds LB policy to be created")
-	}
-	cdsBal.ExitIdle()
+	// Connect to gRPC channel that will call ExitIdle on the cds LB policy.
+	cc.Connect()
 
 	// Wait for ExitIdle to be called on the child policy.
 	select {
diff --git a/internal/xds/balancer/clusterimpl/balancer_test.go b/internal/xds/balancer/clusterimpl/balancer_test.go
index 53f2debf..9f55c606 100644
--- a/internal/xds/balancer/clusterimpl/balancer_test.go
+++ b/internal/xds/balancer/clusterimpl/balancer_test.go
@@ -20,12 +20,7 @@ package clusterimpl
 
 import (
 	"context"
-	"encoding/json"
 	"errors"
-	"fmt"
-	"sort"
-	"strings"
-	"sync"
 	"testing"
 	"time"
 
@@ -33,22 +28,14 @@ import (
 	"google.golang.org/grpc/balancer/base"
 	"google.golang.org/grpc/balancer/roundrobin"
 	"google.golang.org/grpc/connectivity"
-	"google.golang.org/grpc/internal"
 	"google.golang.org/grpc/internal/balancer/stub"
 	"google.golang.org/grpc/internal/grpctest"
 	internalserviceconfig "google.golang.org/grpc/internal/serviceconfig"
 	"google.golang.org/grpc/internal/testutils"
 	xdsinternal "google.golang.org/grpc/internal/xds"
-	"google.golang.org/grpc/internal/xds/bootstrap"
-	"google.golang.org/grpc/internal/xds/clients"
 	"google.golang.org/grpc/internal/xds/testutils/fakeclient"
 	"google.golang.org/grpc/internal/xds/xdsclient"
 	"google.golang.org/grpc/resolver"
-	"google.golang.org/grpc/serviceconfig"
-
-	v3orcapb "github.com/cncf/xds/go/xds/data/orca/v3"
-	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
 )
 
 const (
@@ -57,15 +44,10 @@ const (
 
 	testClusterName = "test-cluster"
 	testServiceName = "test-eds-service"
-
-	testNamedMetricsKey1 = "test-named1"
-	testNamedMetricsKey2 = "test-named2"
 )
 
 var (
 	testBackendEndpoints = []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: "1.1.1.1:1"}}}}
-	cmpOpts              = cmp.Options{cmpopts.EquateEmpty(), cmp.AllowUnexported(loadData{}, localityData{}, requestData{}, serverLoadData{}), sortDataSlice}
-	toleranceCmpOpt      = cmp.Options{cmpopts.EquateApprox(0, 1e-5), cmp.AllowUnexported(loadData{}, localityData{}, requestData{}, serverLoadData{})}
 )
 
 type s struct {
@@ -76,556 +58,15 @@ func Test(t *testing.T) {
 	grpctest.RunSubTests(t, s{})
 }
 
-// testLoadReporter records load data pertaining to a single cluster.
-//
-// It implements loadReporter interface for the picker. Tests can use it to
-// override the loadStore in the picker to verify load reporting.
-type testLoadReporter struct {
-	cluster, service string
-
-	mu               sync.Mutex
-	drops            map[string]uint64
-	localityRPCCount map[clients.Locality]*rpcCountData
-}
-
-// CallStarted records a call started for the clients.Locality.
-func (lr *testLoadReporter) CallStarted(locality clients.Locality) {
-	lr.mu.Lock()
-	defer lr.mu.Unlock()
-	if _, ok := lr.localityRPCCount[locality]; !ok {
-		lr.localityRPCCount[locality] = &rpcCountData{}
-	}
-	lr.localityRPCCount[locality].inProgress++
-	lr.localityRPCCount[locality].issued++
-}
-
-// CallFinished records a call finished for the clients.Locality.
-func (lr *testLoadReporter) CallFinished(locality clients.Locality, err error) {
-	lr.mu.Lock()
-	defer lr.mu.Unlock()
-	if lr.localityRPCCount == nil {
-		return
-	}
-	lrc := lr.localityRPCCount[locality]
-	lrc.inProgress--
-	if err == nil {
-		lrc.succeeded++
-	} else {
-		lrc.errored++
-	}
-}
-
-// CallServerLoad records a server load for the clients.Locality.
-func (lr *testLoadReporter) CallServerLoad(locality clients.Locality, name string, val float64) {
-	lr.mu.Lock()
-	defer lr.mu.Unlock()
-	if lr.localityRPCCount == nil {
-		return
-	}
-	lrc, ok := lr.localityRPCCount[locality]
-	if !ok {
-		return
-	}
-	if lrc.serverLoads == nil {
-		lrc.serverLoads = make(map[string]*rpcLoadData)
-	}
-	if _, ok := lrc.serverLoads[name]; !ok {
-		lrc.serverLoads[name] = &rpcLoadData{}
-	}
-	rld := lrc.serverLoads[name]
-	rld.add(val)
-}
-
-// CallDropped records a call dropped for the category.
-func (lr *testLoadReporter) CallDropped(category string) {
-	lr.mu.Lock()
-	defer lr.mu.Unlock()
-	lr.drops[category]++
-}
-
-// stats returns and resets all loads reported for a cluster and service,
-// except inProgress rpc counts.
-//
-// It returns nil if the store doesn't contain any (new) data.
-func (lr *testLoadReporter) stats() *loadData {
-	lr.mu.Lock()
-	defer lr.mu.Unlock()
-
-	sd := newLoadData(lr.cluster, lr.service)
-	for category, val := range lr.drops {
-		if val == 0 {
-			continue
-		}
-		if category != "" {
-			// Skip drops without category. They are counted in total_drops, but
-			// not in per category. One example is drops by circuit breaking.
-			sd.drops[category] = val
-		}
-		sd.totalDrops += val
-		lr.drops[category] = 0 // clear drops for next report
-	}
-	for locality, countData := range lr.localityRPCCount {
-		if countData.succeeded == 0 && countData.errored == 0 && countData.inProgress == 0 && countData.issued == 0 {
-			continue
-		}
-
-		ld := localityData{
-			requestStats: requestData{
-				succeeded:  countData.succeeded,
-				errored:    countData.errored,
-				inProgress: countData.inProgress,
-				issued:     countData.issued,
-			},
-			loadStats: make(map[string]serverLoadData),
-		}
-		// clear localityRPCCount for next report
-		countData.succeeded = 0
-		countData.errored = 0
-		countData.inProgress = 0
-		countData.issued = 0
-		for key, rld := range countData.serverLoads {
-			s, c := rld.loadAndClear() // get and clear serverLoads for next report
-			if c == 0 {
-				continue
-			}
-			ld.loadStats[key] = serverLoadData{sum: s, count: c}
-		}
-		sd.localityStats[locality] = ld
-	}
-	if sd.totalDrops == 0 && len(sd.drops) == 0 && len(sd.localityStats) == 0 {
-		return nil
-	}
-	return sd
-}
-
-// loadData contains all load data reported to the LoadStore since the most recent
-// call to stats().
-type loadData struct {
-	// cluster is the name of the cluster this data is for.
-	cluster string
-	// service is the name of the EDS service this data is for.
-	service string
-	// totalDrops is the total number of dropped requests.
-	totalDrops uint64
-	// drops is the number of dropped requests per category.
-	drops map[string]uint64
-	// localityStats contains load reports per locality.
-	localityStats map[clients.Locality]localityData
-}
-
-// localityData contains load data for a single locality.
-type localityData struct {
-	// requestStats contains counts of requests made to the locality.
-	requestStats requestData
-	// loadStats contains server load data for requests made to the locality,
-	// indexed by the load type.
-	loadStats map[string]serverLoadData
-}
-
-// requestData contains request counts.
-type requestData struct {
-	// succeeded is the number of succeeded requests.
-	succeeded uint64
-	// errored is the number of requests which ran into errors.
-	errored uint64
-	// inProgress is the number of requests in flight.
-	inProgress uint64
-	// issued is the total number requests that were sent.
-	issued uint64
-}
-
-// serverLoadData contains server load data.
-type serverLoadData struct {
-	// count is the number of load reports.
-	count uint64
-	// sum is the total value of all load reports.
-	sum float64
-}
-
-func newLoadData(cluster, service string) *loadData {
-	return &loadData{
-		cluster:       cluster,
-		service:       service,
-		drops:         make(map[string]uint64),
-		localityStats: make(map[clients.Locality]localityData),
-	}
-}
-
-type rpcCountData struct {
-	succeeded   uint64
-	errored     uint64
-	inProgress  uint64
-	issued      uint64
-	serverLoads map[string]*rpcLoadData
-}
-
-type rpcLoadData struct {
-	sum   float64
-	count uint64
-}
-
-func (rld *rpcLoadData) add(v float64) {
-	rld.sum += v
-	rld.count++
-}
-
-func (rld *rpcLoadData) loadAndClear() (s float64, c uint64) {
-	s, rld.sum = rld.sum, 0
-	c, rld.count = rld.count, 0
-	return s, c
-}
-
 func init() {
 	NewRandomWRR = testutils.NewTestWRR
 }
 
-var sortDataSlice = cmp.Transformer("SortDataSlice", func(in []*loadData) []*loadData {
-	out := append([]*loadData(nil), in...) // Copy input to avoid mutating it
-	sort.Slice(out,
-		func(i, j int) bool {
-			if out[i].cluster < out[j].cluster {
-				return true
-			}
-			if out[i].cluster == out[j].cluster {
-				return out[i].service < out[j].service
-			}
-			return false
-		},
-	)
-	return out
-})
-
-func verifyLoadStoreData(wantStoreData, gotStoreData *loadData) error {
-	if diff := cmp.Diff(wantStoreData, gotStoreData, cmpOpts); diff != "" {
-		return fmt.Errorf("store.stats() returned unexpected diff (-want +got):\n%s", diff)
-	}
-	return nil
-}
-
-// TestDropByCategory verifies that the balancer correctly drops the picks, and
-// that the drops are reported.
-func (s) TestDropByCategory(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
-
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	const (
-		dropReason      = "test-dropping-category"
-		dropNumerator   = 1
-		dropDenominator = 2
-	)
-	testLRSServerConfig, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector.googleapis.com:443",
-		ChannelCreds: []bootstrap.ChannelCreds{{Type: "google_default"}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create LRS server config for testing: %v", err)
-	}
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:             testClusterName,
-			EDSServiceName:      testServiceName,
-			LoadReportingServer: testLRSServerConfig,
-			DropCategories: []DropConfig{{
-				Category:           dropReason,
-				RequestsPerMillion: million * dropNumerator / dropDenominator,
-			}},
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	got, err := xdsC.WaitForReportLoad(ctx)
-	if err != nil {
-		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
-	}
-	if got.Server != testLRSServerConfig {
-		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
-	}
-
-	sc1 := <-cc.NewSubConnCh
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
-	// This should get the connecting picker.
-	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
-	// Test pick with one backend.
-
-	testClusterLoadReporter := &testLoadReporter{cluster: testClusterName, service: testServiceName, drops: make(map[string]uint64), localityRPCCount: make(map[clients.Locality]*rpcCountData)}
-
-	const rpcCount = 24
-	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
-		// Override the loadStore in the picker with testClusterLoadReporter.
-		picker := p.(*picker)
-		originalLoadStore := picker.loadStore
-		picker.loadStore = testClusterLoadReporter
-		defer func() { picker.loadStore = originalLoadStore }()
-
-		for i := 0; i < rpcCount; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			// Even RPCs are dropped.
-			if i%2 == 0 {
-				if err == nil || !strings.Contains(err.Error(), "dropped") {
-					return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
-				}
-				continue
-			}
-			if err != nil || gotSCSt.SubConn != sc1 {
-				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
-			}
-			if gotSCSt.Done == nil {
-				continue
-			}
-			// Fail 1/4th of the requests that are not dropped.
-			if i%8 == 1 {
-				gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("test error")})
-			} else {
-				gotSCSt.Done(balancer.DoneInfo{})
-			}
-		}
-		return nil
-	}); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// Dump load data from the store and compare with expected counts.
-	const dropCount = rpcCount * dropNumerator / dropDenominator
-	wantStatsData0 := &loadData{
-		cluster:    testClusterName,
-		service:    testServiceName,
-		totalDrops: dropCount,
-		drops:      map[string]uint64{dropReason: dropCount},
-		localityStats: map[clients.Locality]localityData{
-			{}: {requestStats: requestData{
-				succeeded: (rpcCount - dropCount) * 3 / 4,
-				errored:   (rpcCount - dropCount) / 4,
-				issued:    rpcCount - dropCount,
-			}},
-		},
-	}
-
-	gotStatsData0 := testClusterLoadReporter.stats()
-	if err := verifyLoadStoreData(wantStatsData0, gotStatsData0); err != nil {
-		t.Fatal(err)
-	}
-
-	// Send an update with new drop configs.
-	const (
-		dropReason2      = "test-dropping-category-2"
-		dropNumerator2   = 1
-		dropDenominator2 = 4
-	)
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:             testClusterName,
-			EDSServiceName:      testServiceName,
-			LoadReportingServer: testLRSServerConfig,
-			DropCategories: []DropConfig{{
-				Category:           dropReason2,
-				RequestsPerMillion: million * dropNumerator2 / dropDenominator2,
-			}},
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
-		// Override the loadStore in the picker with testClusterLoadReporter.
-		picker := p.(*picker)
-		originalLoadStore := picker.loadStore
-		picker.loadStore = testClusterLoadReporter
-		defer func() { picker.loadStore = originalLoadStore }()
-		for i := 0; i < rpcCount; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			// Even RPCs are dropped.
-			if i%4 == 0 {
-				if err == nil || !strings.Contains(err.Error(), "dropped") {
-					return fmt.Errorf("pick.Pick, got %v, %v, want error RPC dropped", gotSCSt, err)
-				}
-				continue
-			}
-			if err != nil || gotSCSt.SubConn != sc1 {
-				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
-			}
-			if gotSCSt.Done != nil {
-				gotSCSt.Done(balancer.DoneInfo{})
-			}
-		}
-		return nil
-	}); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	const dropCount2 = rpcCount * dropNumerator2 / dropDenominator2
-	wantStatsData1 := &loadData{
-		cluster:    testClusterName,
-		service:    testServiceName,
-		totalDrops: dropCount2,
-		drops:      map[string]uint64{dropReason2: dropCount2},
-		localityStats: map[clients.Locality]localityData{
-			{}: {requestStats: requestData{
-				succeeded: rpcCount - dropCount2,
-				issued:    rpcCount - dropCount2,
-			}},
-		},
-	}
-
-	gotStatsData1 := testClusterLoadReporter.stats()
-	if err := verifyLoadStoreData(wantStatsData1, gotStatsData1); err != nil {
-		t.Fatal(err)
-	}
-}
-
-// TestDropCircuitBreaking verifies that the balancer correctly drops the picks
-// due to circuit breaking, and that the drops are reported.
-func (s) TestDropCircuitBreaking(t *testing.T) {
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	var maxRequest uint32 = 50
-	testLRSServerConfig, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector.googleapis.com:443",
-		ChannelCreds: []bootstrap.ChannelCreds{{Type: "google_default"}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create LRS server config for testing: %v", err)
-	}
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:               testClusterName,
-			EDSServiceName:        testServiceName,
-			LoadReportingServer:   testLRSServerConfig,
-			MaxConcurrentRequests: &maxRequest,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
-
-	got, err := xdsC.WaitForReportLoad(ctx)
-	if err != nil {
-		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
-	}
-	if got.Server != testLRSServerConfig {
-		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
-	}
-
-	sc1 := <-cc.NewSubConnCh
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
-	// This should get the connecting picker.
-	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
-	// Test pick with one backend.
-	testClusterLoadReporter := &testLoadReporter{cluster: testClusterName, service: testServiceName, drops: make(map[string]uint64), localityRPCCount: make(map[clients.Locality]*rpcCountData)}
-	const rpcCount = 100
-	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
-		dones := []func(){}
-		// Override the loadStore in the picker with testClusterLoadReporter.
-		picker := p.(*picker)
-		originalLoadStore := picker.loadStore
-		picker.loadStore = testClusterLoadReporter
-		defer func() { picker.loadStore = originalLoadStore }()
-
-		for i := 0; i < rpcCount; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			if i < 50 && err != nil {
-				return fmt.Errorf("The first 50%% picks should be non-drops, got error %v", err)
-			} else if i > 50 && err == nil {
-				return fmt.Errorf("The second 50%% picks should be drops, got error <nil>")
-			}
-			dones = append(dones, func() {
-				if gotSCSt.Done != nil {
-					gotSCSt.Done(balancer.DoneInfo{})
-				}
-			})
-		}
-		for _, done := range dones {
-			done()
-		}
-
-		dones = []func(){}
-		// Pick without drops.
-		for i := 0; i < 50; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			if err != nil {
-				t.Errorf("The third 50%% picks should be non-drops, got error %v", err)
-			}
-			dones = append(dones, func() {
-				if gotSCSt.Done != nil {
-					// Fail these requests to test error counts in the load
-					// report.
-					gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("test error")})
-				}
-			})
-		}
-		for _, done := range dones {
-			done()
-		}
-
-		return nil
-	}); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// Dump load data from the store and compare with expected counts.
-	wantStatsData0 := &loadData{
-		cluster:    testClusterName,
-		service:    testServiceName,
-		totalDrops: uint64(maxRequest),
-		localityStats: map[clients.Locality]localityData{
-			{}: {requestStats: requestData{
-				succeeded: uint64(rpcCount - maxRequest),
-				errored:   50,
-				issued:    uint64(rpcCount - maxRequest + 50),
-			}},
-		},
-	}
-
-	gotStatsData0 := testClusterLoadReporter.stats()
-	if err := verifyLoadStoreData(wantStatsData0, gotStatsData0); err != nil {
-		t.Fatal(err)
-	}
-}
-
 // TestPickerUpdateAfterClose covers the case where a child policy sends a
 // picker update after the cluster_impl policy is closed. Because picker updates
 // are handled in the run() goroutine, which exits before Close() returns, we
 // expect the above picker update to be dropped.
 func (s) TestPickerUpdateAfterClose(t *testing.T) {
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
 	builder := balancer.Get(Name)
 	cc := testutils.NewBalancerClientConn(t)
 	b := builder.Build(cc, balancer.BuildOptions{})
@@ -660,6 +101,7 @@ func (s) TestPickerUpdateAfterClose(t *testing.T) {
 	})
 
 	var maxRequest uint32 = 50
+	xdsC := fakeclient.NewClient()
 	if err := b.UpdateClientConnState(balancer.ClientConnState{
 		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
 		BalancerConfig: &LBConfig{
@@ -696,14 +138,12 @@ func (s) TestClusterNameInAddressAttributes(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
 	builder := balancer.Get(Name)
 	cc := testutils.NewBalancerClientConn(t)
 	b := builder.Build(cc, balancer.BuildOptions{})
 	defer b.Close()
 
+	xdsC := fakeclient.NewClient()
 	if err := b.UpdateClientConnState(balancer.ClientConnState{
 		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
 		BalancerConfig: &LBConfig{
@@ -765,421 +205,6 @@ func (s) TestClusterNameInAddressAttributes(t *testing.T) {
 	}
 }
 
-// TestReResolution verifies that when a SubConn turns transient failure,
-// re-resolution is triggered.
-func (s) TestReResolution(t *testing.T) {
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
-
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:        testClusterName,
-			EDSServiceName: testServiceName,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	sc1 := <-cc.NewSubConnCh
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
-	// This should get the connecting picker.
-	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	sc1.UpdateState(balancer.SubConnState{
-		ConnectivityState: connectivity.TransientFailure,
-		ConnectionError:   errors.New("test error"),
-	})
-	// This should get the transient failure picker.
-	if err := cc.WaitForErrPicker(ctx); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// The transient failure should trigger a re-resolution.
-	select {
-	case <-cc.ResolveNowCh:
-	case <-time.After(defaultTestTimeout):
-		t.Fatalf("timeout waiting for ResolveNow()")
-	}
-
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Ready})
-	// Test pick with one backend.
-	if err := cc.WaitForRoundRobinPicker(ctx, sc1); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.TransientFailure})
-	// This should get the transient failure picker.
-	if err := cc.WaitForErrPicker(ctx); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// The transient failure should trigger a re-resolution.
-	select {
-	case <-cc.ResolveNowCh:
-	case <-time.After(defaultTestTimeout):
-		t.Fatalf("timeout waiting for ResolveNow()")
-	}
-}
-
-func (s) TestLoadReporting(t *testing.T) {
-	var testLocality = clients.Locality{
-		Region:  "test-region",
-		Zone:    "test-zone",
-		SubZone: "test-sub-zone",
-	}
-
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	endpoints := make([]resolver.Endpoint, len(testBackendEndpoints))
-	for i, e := range testBackendEndpoints {
-		endpoints[i] = xdsinternal.SetLocalityIDInEndpoint(e, testLocality)
-		for j, a := range e.Addresses {
-			endpoints[i].Addresses[j] = xdsinternal.SetLocalityID(a, testLocality)
-		}
-	}
-	testLRSServerConfig, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector.googleapis.com:443",
-		ChannelCreds: []bootstrap.ChannelCreds{{Type: "google_default"}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create LRS server config for testing: %v", err)
-	}
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: endpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:             testClusterName,
-			EDSServiceName:      testServiceName,
-			LoadReportingServer: testLRSServerConfig,
-			// Locality:                testLocality,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
-
-	got, err := xdsC.WaitForReportLoad(ctx)
-	if err != nil {
-		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
-	}
-	if got.Server != testLRSServerConfig {
-		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
-	}
-
-	sc1 := <-cc.NewSubConnCh
-	sc1.UpdateState(balancer.SubConnState{ConnectivityState: connectivity.Connecting})
-	// This should get the connecting picker.
-	if err := cc.WaitForPickerWithErr(ctx, balancer.ErrNoSubConnAvailable); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	scs := balancer.SubConnState{ConnectivityState: connectivity.Ready}
-	sca := internal.SetConnectedAddress.(func(*balancer.SubConnState, resolver.Address))
-	sca(&scs, endpoints[0].Addresses[0])
-	sc1.UpdateState(scs)
-	// Test pick with one backend.
-	testClusterLoadReporter := &testLoadReporter{cluster: testClusterName, service: testServiceName, drops: make(map[string]uint64), localityRPCCount: make(map[clients.Locality]*rpcCountData)}
-	const successCount = 5
-	const errorCount = 5
-	if err := cc.WaitForPicker(ctx, func(p balancer.Picker) error {
-		// Override the loadStore in the picker with testClusterLoadReporter.
-		picker := p.(*picker)
-		originalLoadStore := picker.loadStore
-		picker.loadStore = testClusterLoadReporter
-		defer func() { picker.loadStore = originalLoadStore }()
-		for i := 0; i < successCount; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			if gotSCSt.SubConn != sc1 {
-				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
-			}
-			lr := &v3orcapb.OrcaLoadReport{
-				NamedMetrics: map[string]float64{testNamedMetricsKey1: 3.14, testNamedMetricsKey2: 2.718},
-			}
-			gotSCSt.Done(balancer.DoneInfo{ServerLoad: lr})
-		}
-		for i := 0; i < errorCount; i++ {
-			gotSCSt, err := p.Pick(balancer.PickInfo{})
-			if gotSCSt.SubConn != sc1 {
-				return fmt.Errorf("picker.Pick, got %v, %v, want SubConn=%v", gotSCSt, err, sc1)
-			}
-			gotSCSt.Done(balancer.DoneInfo{Err: fmt.Errorf("error")})
-		}
-		return nil
-	}); err != nil {
-		t.Fatal(err.Error())
-	}
-
-	// Dump load data from the store and compare with expected counts.
-	sd := testClusterLoadReporter.stats()
-	if sd == nil {
-		t.Fatalf("loads for cluster %v not found in store", testClusterName)
-	}
-	if sd.cluster != testClusterName || sd.service != testServiceName {
-		t.Fatalf("got unexpected load for %q, %q, want %q, %q", sd.cluster, sd.service, testClusterName, testServiceName)
-	}
-	localityData, ok := sd.localityStats[testLocality]
-	if !ok {
-		t.Fatalf("loads for %v not found in store", testLocality)
-	}
-	reqStats := localityData.requestStats
-	if reqStats.succeeded != successCount {
-		t.Errorf("got succeeded %v, want %v", reqStats.succeeded, successCount)
-	}
-	if reqStats.errored != errorCount {
-		t.Errorf("got errord %v, want %v", reqStats.errored, errorCount)
-	}
-	if reqStats.inProgress != 0 {
-		t.Errorf("got inProgress %v, want %v", reqStats.inProgress, 0)
-	}
-	wantLoadStats := map[string]serverLoadData{
-		testNamedMetricsKey1: {count: 5, sum: 15.7},  // aggregation of 5 * 3.14 = 15.7
-		testNamedMetricsKey2: {count: 5, sum: 13.59}, // aggregation of 5 * 2.718 = 13.59
-	}
-	if diff := cmp.Diff(wantLoadStats, localityData.loadStats, toleranceCmpOpt); diff != "" {
-		t.Errorf("localityData.LoadStats returned unexpected diff (-want +got):\n%s", diff)
-	}
-	b.Close()
-	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
-		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
-	}
-}
-
-// TestUpdateLRSServer covers the cases
-// - the init config specifies "" as the LRS server
-// - config modifies LRS server to a different string
-// - config sets LRS server to nil to stop load reporting
-func (s) TestUpdateLRSServer(t *testing.T) {
-	var testLocality = clients.Locality{
-		Region:  "test-region",
-		Zone:    "test-zone",
-		SubZone: "test-sub-zone",
-	}
-
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	endpoints := make([]resolver.Endpoint, len(testBackendEndpoints))
-	for i, e := range testBackendEndpoints {
-		endpoints[i] = xdsinternal.SetLocalityIDInEndpoint(e, testLocality)
-	}
-	testLRSServerConfig, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector.googleapis.com:443",
-		ChannelCreds: []bootstrap.ChannelCreds{{Type: "google_default"}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create LRS server config for testing: %v", err)
-	}
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: endpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:             testClusterName,
-			EDSServiceName:      testServiceName,
-			LoadReportingServer: testLRSServerConfig,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-
-	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
-	defer cancel()
-
-	got, err := xdsC.WaitForReportLoad(ctx)
-	if err != nil {
-		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err)
-	}
-	if got.Server != testLRSServerConfig {
-		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got.Server, testLRSServerConfig)
-	}
-
-	testLRSServerConfig2, err := bootstrap.ServerConfigForTesting(bootstrap.ServerConfigTestingOptions{
-		URI:          "trafficdirector-another.googleapis.com:443",
-		ChannelCreds: []bootstrap.ChannelCreds{{Type: "google_default"}},
-	})
-	if err != nil {
-		t.Fatalf("Failed to create LRS server config for testing: %v", err)
-	}
-
-	// Update LRS server to a different name.
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: endpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:             testClusterName,
-			EDSServiceName:      testServiceName,
-			LoadReportingServer: testLRSServerConfig2,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
-		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
-	}
-	got2, err2 := xdsC.WaitForReportLoad(ctx)
-	if err2 != nil {
-		t.Fatalf("xdsClient.ReportLoad failed with error: %v", err2)
-	}
-	if got2.Server != testLRSServerConfig2 {
-		t.Fatalf("xdsClient.ReportLoad called with {%q}: want {%q}", got2.Server, testLRSServerConfig2)
-	}
-
-	// Update LRS server to nil, to disable LRS.
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: endpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster:        testClusterName,
-			EDSServiceName: testServiceName,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: roundrobin.Name,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("unexpected error from UpdateClientConnState: %v", err)
-	}
-	if err := xdsC.WaitForCancelReportLoad(ctx); err != nil {
-		t.Fatalf("unexpected error waiting form load report to be canceled: %v", err)
-	}
-
-	shortCtx, shortCancel := context.WithTimeout(context.Background(), defaultShortTestTimeout)
-	defer shortCancel()
-	if s, err := xdsC.WaitForReportLoad(shortCtx); err != context.DeadlineExceeded {
-		t.Fatalf("unexpected load report to server: %q", s)
-	}
-}
-
-// Test verifies that child policies was updated on receipt of
-// configuration update.
-func (s) TestChildPolicyUpdatedOnConfigUpdate(t *testing.T) {
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	// Keep track of which child policy was updated
-	updatedChildPolicy := ""
-
-	// Create stub balancers to track config updates
-	const (
-		childPolicyName1 = "stubBalancer1"
-		childPolicyName2 = "stubBalancer2"
-	)
-
-	stub.Register(childPolicyName1, stub.BalancerFuncs{
-		UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
-			updatedChildPolicy = childPolicyName1
-			return nil
-		},
-	})
-
-	stub.Register(childPolicyName2, stub.BalancerFuncs{
-		UpdateClientConnState: func(_ *stub.BalancerData, _ balancer.ClientConnState) error {
-			updatedChildPolicy = childPolicyName2
-			return nil
-		},
-	})
-
-	// Initial config update with childPolicyName1
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster: testClusterName,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: childPolicyName1,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("Error updating the config: %v", err)
-	}
-
-	if updatedChildPolicy != childPolicyName1 {
-		t.Fatal("Child policy 1 was not updated on initial configuration update.")
-	}
-
-	// Second config update with childPolicyName2
-	if err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster: testClusterName,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: childPolicyName2,
-			},
-		},
-	}); err != nil {
-		t.Fatalf("Error updating the config: %v", err)
-	}
-
-	if updatedChildPolicy != childPolicyName2 {
-		t.Fatal("Child policy 2 was not updated after child policy name change.")
-	}
-}
-
-// Test verifies that config update fails if child policy config
-// failed to parse.
-func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
-	xdsC := fakeclient.NewClient()
-
-	builder := balancer.Get(Name)
-	cc := testutils.NewBalancerClientConn(t)
-	b := builder.Build(cc, balancer.BuildOptions{})
-	defer b.Close()
-
-	// Create a stub balancer which fails to ParseConfig.
-	const parseConfigError = "failed to parse config"
-	const childPolicyName = "stubBalancer-FailedToParseChildPolicyConfig"
-	stub.Register(childPolicyName, stub.BalancerFuncs{
-		ParseConfig: func(_ json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
-			return nil, errors.New(parseConfigError)
-		},
-	})
-
-	err := b.UpdateClientConnState(balancer.ClientConnState{
-		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
-		BalancerConfig: &LBConfig{
-			Cluster: testClusterName,
-			ChildPolicy: &internalserviceconfig.BalancerConfig{
-				Name: childPolicyName,
-			},
-		},
-	})
-
-	if err == nil || !strings.Contains(err.Error(), parseConfigError) {
-		t.Fatalf("Got error: %v, want error: %s", err, parseConfigError)
-	}
-}
-
 // Test verify that the case picker is updated synchronously on receipt of
 // configuration update.
 func (s) TestPickerUpdatedSynchronouslyOnConfigUpdate(t *testing.T) {
@@ -1208,9 +233,6 @@ func (s) TestPickerUpdatedSynchronouslyOnConfigUpdate(t *testing.T) {
 	}
 	defer func() { clientConnUpdateHook = origClientConnUpdateHook }()
 
-	defer xdsclient.ClearCounterForTesting(testClusterName, testServiceName)
-	xdsC := fakeclient.NewClient()
-
 	builder := balancer.Get(Name)
 	cc := testutils.NewBalancerClientConn(t)
 	b := builder.Build(cc, balancer.BuildOptions{})
@@ -1228,6 +250,7 @@ func (s) TestPickerUpdatedSynchronouslyOnConfigUpdate(t *testing.T) {
 		},
 	})
 
+	xdsC := fakeclient.NewClient()
 	if err := b.UpdateClientConnState(balancer.ClientConnState{
 		ResolverState: xdsclient.SetClient(resolver.State{Endpoints: testBackendEndpoints}, xdsC),
 		BalancerConfig: &LBConfig{
diff --git a/internal/xds/balancer/clusterimpl/tests/balancer_test.go b/internal/xds/balancer/clusterimpl/tests/balancer_test.go
index 7cfd3992..57b5de5b 100644
--- a/internal/xds/balancer/clusterimpl/tests/balancer_test.go
+++ b/internal/xds/balancer/clusterimpl/tests/balancer_test.go
@@ -20,19 +20,27 @@ package clusterimpl_test
 
 import (
 	"context"
+	"encoding/json"
+	"errors"
 	"fmt"
+	"math"
 	"net"
 	"strconv"
 	"strings"
+	"sync/atomic"
 	"testing"
 	"time"
 
+	"github.com/google/go-cmp/cmp"
 	"github.com/google/uuid"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/balancer"
+	"google.golang.org/grpc/balancer/pickfirst"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/connectivity"
 	"google.golang.org/grpc/credentials/insecure"
 	"google.golang.org/grpc/internal"
+	"google.golang.org/grpc/internal/balancer/stub"
 	"google.golang.org/grpc/internal/grpctest"
 	"google.golang.org/grpc/internal/stubserver"
 	"google.golang.org/grpc/internal/testutils"
@@ -40,7 +48,10 @@ import (
 	"google.golang.org/grpc/internal/testutils/xds/fakeserver"
 	"google.golang.org/grpc/peer"
 	"google.golang.org/grpc/resolver"
+	"google.golang.org/grpc/resolver/manual"
+	"google.golang.org/grpc/serviceconfig"
 	"google.golang.org/grpc/status"
+	"google.golang.org/protobuf/testing/protocmp"
 	"google.golang.org/protobuf/types/known/durationpb"
 	"google.golang.org/protobuf/types/known/wrapperspb"
 
@@ -85,13 +96,12 @@ func (s) TestConfigUpdateWithSameLoadReportingServerConfig(t *testing.T) {
 	testutils.CreateBootstrapFileForTesting(t, bc)
 
 	// Create an xDS resolver with the above bootstrap configuration.
-	var resolverBuilder resolver.Builder
-	var err error
-	if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil {
-		resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc)
-		if err != nil {
-			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
-		}
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
 	}
 
 	// Start a server backend exposing the test service.
@@ -194,13 +204,12 @@ func (s) TestLoadReportingPickFirstMultiLocality(t *testing.T) {
 	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
 
 	// Create an xDS resolver with the above bootstrap configuration.
-	var resolverBuilder resolver.Builder
-	var err error
-	if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil {
-		resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc)
-		if err != nil {
-			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
-		}
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
 	}
 
 	// Start two server backends exposing the test service.
@@ -360,8 +369,8 @@ func waitForSuccessfulLoadReport(ctx context.Context, lrsServer *fakeserver.Serv
 
 // Tests that circuit breaking limits RPCs E2E.
 func (s) TestCircuitBreaking(t *testing.T) {
-	// Create an xDS management server that serves ADS requests.
-	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{})
+	// Create an xDS management server that serves ADS and LRS requests.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{SupportLoadReportingService: true})
 
 	// Create bootstrap configuration pointing to the above management server.
 	nodeID := uuid.New().String()
@@ -369,20 +378,16 @@ func (s) TestCircuitBreaking(t *testing.T) {
 	testutils.CreateBootstrapFileForTesting(t, bc)
 
 	// Create an xDS resolver with the above bootstrap configuration.
-	var resolverBuilder resolver.Builder
-	var err error
-	if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil {
-		resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc)
-		if err != nil {
-			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
-		}
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
 	}
 
 	// Start a server backend exposing the test service.
 	f := &stubserver.StubServer{
-		EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) {
-			return &testpb.Empty{}, nil
-		},
 		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
 			for {
 				if _, err := stream.Recv(); err != nil {
@@ -394,7 +399,8 @@ func (s) TestCircuitBreaking(t *testing.T) {
 	server := stubserver.StartTestService(t, f)
 	defer server.Stop()
 
-	// Configure the xDS management server with default resources.
+	// Configure xDS resources on the management server with a circuit breaking
+	// policy that limits the maximum number of concurrent requests to 3.
 	const serviceName = "my-test-xds-service"
 	const maxRequests = 3
 	resources := e2e.DefaultClientResources(e2e.ResourceParams{
@@ -411,6 +417,11 @@ func (s) TestCircuitBreaking(t *testing.T) {
 			},
 		},
 	}
+	resources.Clusters[0].LrsServer = &v3corepb.ConfigSource{
+		ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
+			Self: &v3corepb.SelfConfigSource{},
+		},
+	}
 
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
@@ -436,21 +447,264 @@ func (s) TestCircuitBreaking(t *testing.T) {
 
 	// Since we are at the max, new streams should fail.  It's possible some are
 	// allowed due to inherent raciness in the tracking, however.
-	for i := 0; i < 100; i++ {
-		stream, err := client.FullDuplexCall(ctx)
-		if status.Code(err) == codes.Unavailable {
-			return
+	const droppedRPCCount = 100
+	for i := 0; i < droppedRPCCount; i++ {
+		if ctx.Err() != nil {
+			t.Fatalf("Context error: %v", ctx.Err())
 		}
-		if err == nil {
-			// Terminate the stream (the server immediately exits upon a client
-			// CloseSend) to ensure we never go over the limit.
+		if _, err := client.FullDuplexCall(ctx); status.Code(err) != codes.Unavailable {
+			t.Fatalf("client.FullDuplexCall() returned status %q, want %q", status.Code(err), codes.Unavailable)
+		}
+	}
+
+	if _, err = mgmtServer.LRSServer.LRSStreamOpenChan.Receive(ctx); err != nil {
+		t.Fatalf("Failure when waiting for an LRS stream to be opened: %v", err)
+	}
+
+	if _, err = mgmtServer.LRSServer.LRSRequestChan.Receive(ctx); err != nil {
+		t.Fatalf("Failure waiting for initial LRS request: %v", err)
+	}
+
+	mgmtServer.LRSServer.LRSResponseChan <- &fakeserver.Response{
+		Resp: &v3lrspb.LoadStatsResponse{
+			SendAllClusters:       true,
+			LoadReportingInterval: durationpb.New(10 * time.Millisecond),
+		},
+	}
+
+	select {
+	case req := <-mgmtServer.LRSServer.LRSRequestChan.C:
+		clusterStats := req.(*fakeserver.Request).Req.(*v3lrspb.LoadStatsRequest).ClusterStats
+		if l := len(clusterStats); l != 1 {
+			t.Fatalf("Received load for %d clusters, want 1", l)
+		}
+		clusterStats[0].LoadReportInterval = nil
+		wantLoad := &v3endpointpb.ClusterStats{
+			ClusterName:        "cluster-my-test-xds-service",
+			ClusterServiceName: "endpoints-my-test-xds-service",
+			UpstreamLocalityStats: []*v3endpointpb.UpstreamLocalityStats{
+				{
+					Locality:                &v3corepb.Locality{Region: "region-1", Zone: "zone-1", SubZone: "subzone-1"},
+					TotalIssuedRequests:     maxRequests,
+					TotalRequestsInProgress: maxRequests,
+				},
+			},
+			TotalDroppedRequests: droppedRPCCount,
+		}
+		if diff := cmp.Diff(wantLoad, clusterStats[0], protocmp.Transform()); diff != "" {
+			t.Errorf("Failed with unexpected diff (-want +got):\n%s", diff)
+		}
+	case <-ctx.Done():
+		t.Fatalf("Timeout while waiting for load report on LRS stream: %v", ctx.Err())
+	}
+}
+
+// ComputeIdealNumRpcs calculates the ideal number of RPCs to send to test
+// probabilistic behaviors.
+//
+// It's based on the idea of a binomial distribution approximated by a normal
+// distribution. The function aims to find a number of RPCs such that
+// the observed probability is within a certain error_tolerance of the expected
+// probability (p).
+func computeIdealNumRpcs(p, errorTolerance float64) uint64 {
+	return uint64(math.Ceil(p * (1 - p) * 5.00 * 5.00 / errorTolerance / errorTolerance))
+}
+
+func verifyDropRateByCategory(ctx context.Context, client testgrpc.TestServiceClient, rpcCount int, lrsServer *fakeserver.Server, wantCluster, wantDropCategory string, wantDropRate, errorTolerance float64) error {
+	for i := 0; i < rpcCount; i++ {
+		if ctx.Err() != nil {
+			return fmt.Errorf("context error: %v", ctx.Err())
+		}
+		stream, err := client.FullDuplexCall(ctx)
+		switch {
+		case err == nil:
+			// Close stream to avoid exceeding limits.
 			stream.CloseSend()
-			stream.Recv()
+		case status.Code(err) == codes.Unavailable && strings.Contains(err.Error(), "RPC is dropped"):
+			continue
+		default:
+			return fmt.Errorf("client.FullDuplexCall(_) failed with unexpected error = %v", err)
 		}
-		time.Sleep(10 * time.Millisecond)
 	}
 
-	t.Fatalf("RPCs unexpectedly allowed beyond circuit breaking maximum")
+	for {
+		if ctx.Err() != nil {
+			return fmt.Errorf("timeout when waiting for load stats")
+		}
+		req, err := lrsServer.LRSRequestChan.Receive(ctx)
+		if err != nil {
+			continue
+		}
+		loadStats := req.(*fakeserver.Request).Req.(*v3lrspb.LoadStatsRequest).ClusterStats
+		if len(loadStats) != 1 || loadStats[0].ClusterName != wantCluster {
+			continue
+		}
+		if loadStats[0].ClusterName != wantCluster {
+			return fmt.Errorf("Received stats for unexpected cluster got: %q, want: %q", loadStats[0].ClusterName, wantCluster)
+		}
+
+		for _, cs := range loadStats {
+			found := false
+			for _, dr := range cs.DroppedRequests {
+				if dr.Category == wantDropCategory {
+					found = true
+					gotRPCDropRate := float64(dr.DroppedCount) / float64(rpcCount)
+					if (math.Trunc(math.Abs(gotRPCDropRate-wantDropRate)*100) / 100) > errorTolerance {
+						return fmt.Errorf("Drop rate goes out of errortolerance got: %v, want: %v, totalDroppedRequest: %v, totalIssuesRequest: %v", (math.Trunc(math.Abs(gotRPCDropRate-wantDropRate)*100) / 100), errorTolerance, cs.TotalDroppedRequests, cs.UpstreamLocalityStats[0].TotalIssuedRequests)
+					}
+				}
+			}
+			if !found {
+				continue
+			}
+		}
+		break
+	}
+	return nil
+}
+
+// TestDropByCategory verifies that the balancer correctly drops the picks, and
+// that the drops are reported.
+func (s) TestDropByCategory(t *testing.T) {
+	// Create an xDS management server that serves ADS and LRS requests.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{SupportLoadReportingService: true})
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+	testutils.CreateBootstrapFileForTesting(t, bc)
+
+	// Create an xDS resolver with the above bootstrap configuration.
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+
+	// Start a server backend exposing the test service.
+	f := &stubserver.StubServer{
+		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
+			for {
+				if _, err := stream.Recv(); err != nil {
+					return err
+				}
+			}
+		},
+	}
+	server := stubserver.StartTestService(t, f)
+	defer server.Stop()
+
+	// Configure xDS resources on the management server with drops
+	// configuration that drops one RPC for every 50 RPCs made.
+	const (
+		dropReason      = "test-dropping-category"
+		dropNumerator   = 1
+		dropDenominator = 50
+		serviceName     = "my-test-xds-service"
+		errorTolerance  = 0.05
+	)
+	wantRPCDropRate := float64(dropNumerator) / float64(dropDenominator)
+	rpcCount := computeIdealNumRpcs(wantRPCDropRate, errorTolerance)
+	t.Logf("About to send %d RPCs to test drop rate of %v", rpcCount, wantRPCDropRate)
+
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: serviceName,
+		NodeID:     nodeID,
+		Host:       "localhost",
+		Port:       testutils.ParsePort(t, server.Address),
+	})
+	resources.Clusters[0].LrsServer = &v3corepb.ConfigSource{
+		ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
+			Self: &v3corepb.SelfConfigSource{},
+		},
+	}
+	resources.Endpoints = []*v3endpointpb.ClusterLoadAssignment{
+		e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
+			ClusterName: "endpoints-" + serviceName,
+			Host:        "localhost",
+			Localities: []e2e.LocalityOptions{
+				{
+					Backends: []e2e.BackendOptions{{Ports: []uint32{testutils.ParsePort(t, server.Address)}}},
+					Weight:   1,
+				},
+			},
+			DropPercents: map[string]int{
+				dropReason: dropNumerator * 100 / dropDenominator,
+			},
+		})}
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a ClientConn and make a successful RPC.
+	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
+	if err != nil {
+		t.Fatalf("failed to dial local test server: %v", err)
+	}
+	defer cc.Close()
+
+	// Ensure the gRPC channel is READY before issuing RPCs to get accurate
+	// drop count.
+	cc.Connect()
+	client := testgrpc.NewTestServiceClient(cc)
+	testutils.AwaitState(ctx, t, cc, connectivity.Ready)
+
+	if _, err = mgmtServer.LRSServer.LRSStreamOpenChan.Receive(ctx); err != nil {
+		t.Fatalf("Failure when waiting for an LRS stream to be opened: %v", err)
+	}
+
+	resp := fakeserver.Response{
+		Resp: &v3lrspb.LoadStatsResponse{
+			Clusters:              []string{resources.Clusters[0].Name},
+			LoadReportingInterval: durationpb.New(50 * time.Millisecond),
+		},
+	}
+	mgmtServer.LRSServer.LRSResponseChan <- &resp
+
+	if err := verifyDropRateByCategory(ctx, client, int(rpcCount), mgmtServer.LRSServer, resources.Clusters[0].Name, dropReason, wantRPCDropRate, errorTolerance); err != nil {
+		t.Fatal(err)
+	}
+
+	// Update the drop configuration to drop 1 out of every 40 RPCs,
+	// and verify that the observed drop rate matches this new config.
+	const (
+		dropReason2      = "test-dropping-category-2"
+		dropNumerator2   = 1
+		dropDenominator2 = 40
+	)
+
+	resources.Endpoints = []*v3endpointpb.ClusterLoadAssignment{
+		e2e.EndpointResourceWithOptions(e2e.EndpointOptions{
+			ClusterName: "endpoints-" + serviceName,
+			Host:        "localhost",
+			Localities: []e2e.LocalityOptions{
+				{
+					Backends: []e2e.BackendOptions{{Ports: []uint32{testutils.ParsePort(t, server.Address)}}},
+					Weight:   1,
+				},
+			},
+			DropPercents: map[string]int{
+				dropReason2: dropNumerator2 * 100 / dropDenominator2, // drops one RPC for every 40 RPCs made
+			},
+		}),
+	}
+
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	wantRPCDropRate = float64(dropNumerator2) / float64(dropDenominator2)
+	rpcCount = computeIdealNumRpcs(wantRPCDropRate, errorTolerance)
+	t.Logf("About to send %d RPCs to test drop rate of %v", rpcCount, wantRPCDropRate)
+
+	if err := verifyDropRateByCategory(ctx, client, int(rpcCount), mgmtServer.LRSServer, resources.Clusters[0].Name, dropReason2, wantRPCDropRate, errorTolerance); err != nil {
+		t.Fatal(err)
+	}
 }
 
 // Tests that circuit breaking limits RPCs in LOGICAL_DNS clusters E2E.
@@ -464,13 +718,12 @@ func (s) TestCircuitBreakingLogicalDNS(t *testing.T) {
 	testutils.CreateBootstrapFileForTesting(t, bc)
 
 	// Create an xDS resolver with the above bootstrap configuration.
-	var resolverBuilder resolver.Builder
-	var err error
-	if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil {
-		resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc)
-		if err != nil {
-			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
-		}
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
 	}
 
 	// Start a server backend exposing the test service.
@@ -540,7 +793,7 @@ func (s) TestCircuitBreakingLogicalDNS(t *testing.T) {
 		}
 	}
 
-	// Since we are at the max, new streams should fail.  It's possible some are
+	// Since we are at the max, new streams should fail. It's possible some are
 	// allowed due to inherent raciness in the tracking, however.
 	for i := 0; i < 100; i++ {
 		stream, err := client.FullDuplexCall(ctx)
@@ -584,13 +837,12 @@ func (s) TestLRSLogicalDNS(t *testing.T) {
 	testutils.CreateBootstrapFileForTesting(t, bc)
 
 	// Create an xDS resolver with the above bootstrap configuration.
-	var resolverBuilder resolver.Builder
-	var err error
-	if newResolver := internal.NewXDSResolverWithConfigForTesting; newResolver != nil {
-		resolverBuilder, err = newResolver.(func([]byte) (resolver.Builder, error))(bc)
-		if err != nil {
-			t.Fatalf("Failed to create xDS resolver for testing: %v", err)
-		}
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
 	}
 
 	// Start a server backend exposing the test service.
@@ -663,3 +915,391 @@ func (s) TestLRSLogicalDNS(t *testing.T) {
 		t.Fatalf("Server did not receive load due to error: %v", err)
 	}
 }
+
+// TestReResolution verifies that when a SubConn turns transient failure,
+// re-resolution is triggered.
+func (s) TestReResolutionAfterTransientFailure(t *testing.T) {
+	// Create an xDS management server.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
+	defer mgmtServer.Stop()
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+	testutils.CreateBootstrapFileForTesting(t, bc)
+
+	// Create an xDS resolver with the above bootstrap configuration.
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+
+	// Create a restartable listener to simulate server being down.
+	l, err := testutils.LocalTCPListener()
+	if err != nil {
+		t.Fatalf("testutils.LocalTCPListener() failed: %v", err)
+	}
+	lis := testutils.NewRestartableListener(l)
+	server := stubserver.StartTestService(t, &stubserver.StubServer{
+		Listener:   lis,
+		EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil },
+	})
+	defer server.Stop()
+	host, port := hostAndPortFromAddress(t, server.Address)
+
+	const (
+		listenerName = "test-listener"
+		routeName    = "test-route"
+		clusterName  = "test-aggregate-cluster"
+		dnsCluster   = "logical-dns-cluster"
+	)
+
+	// Configure xDS resources.
+	ldnsCluster := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
+		Type:        e2e.ClusterTypeLogicalDNS,
+		ClusterName: dnsCluster,
+		DNSHostName: host,
+		DNSPort:     port,
+	})
+	cluster := e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
+		ClusterName: clusterName,
+		Type:        e2e.ClusterTypeAggregate,
+		ChildNames:  []string{dnsCluster},
+	})
+	updateOpts := e2e.UpdateOptions{
+		NodeID:    nodeID,
+		Listeners: []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, routeName)},
+		Routes:    []*v3routepb.RouteConfiguration{e2e.DefaultRouteConfig(routeName, listenerName, clusterName)},
+		Clusters:  []*v3clusterpb.Cluster{cluster, ldnsCluster},
+		Endpoints: nil,
+	}
+
+	// Replace DNS resolver with a wrapped resolver to capture ResolveNow calls.
+	resolveNowCh := make(chan struct{}, 1)
+	dnsR := manual.NewBuilderWithScheme("dns")
+	dnsResolverBuilder := resolver.Get("dns")
+	resolver.Register(dnsR)
+	defer resolver.Register(dnsResolverBuilder)
+	dnsR.ResolveNowCallback = func(resolver.ResolveNowOptions) {
+		close(resolveNowCh)
+	}
+	dnsR.UpdateState(resolver.State{Addresses: []resolver.Address{{Addr: fmt.Sprintf("%s:%d", host, port)}}})
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	if err := mgmtServer.Update(ctx, updateOpts); err != nil {
+		t.Fatalf("Failed to update xDS resources: %v", err)
+	}
+
+	conn, err := grpc.NewClient(fmt.Sprintf("xds:///%s", listenerName), grpc.WithResolvers(resolverBuilder), grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if err != nil {
+		t.Fatalf("Failed to create client: %v", err)
+	}
+	defer conn.Close()
+
+	client := testgrpc.NewTestServiceClient(conn)
+
+	// Verify initial RPC routes correctly to backend.
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
+		t.Fatalf("client.EmptyCall() failed: %v", err)
+	}
+
+	// Stopping the server listener will close the transport on the client,
+	// which will lead to the channel eventually moving to IDLE.
+	lis.Stop()
+	testutils.AwaitState(ctx, t, conn, connectivity.Idle)
+
+	// An RPC at this point is expected to fail with TRANSIENT_FAILURE.
+	if _, err = client.EmptyCall(ctx, &testpb.Empty{}); status.Code(err) != codes.Unavailable {
+		t.Fatalf("EmptyCall RPC succeeded when the channel is in TRANSIENT_FAILURE, got %v want %v", err, codes.Unavailable)
+	}
+
+	// Expect resolver's ResolveNow to be called due to TF state.
+	select {
+	case <-resolveNowCh:
+	case <-ctx.Done():
+		t.Fatalf("Timed out waiting for ResolveNow call after TransientFailure")
+	}
+
+	// Restart the listener and expected to reconnect on its own and come out
+	// of TRANSIENT_FAILURE, even without an RPC attempt.
+	lis.Restart()
+	testutils.AwaitState(ctx, t, conn, connectivity.Ready)
+
+	// An RPC at this point is expected to succeed.
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
+		t.Fatalf("client.EmptyCall() failed: %v", err)
+	}
+}
+
+// TestUpdateLRSServerToNil verifies that updating the cluster's LRS server
+// config from 'Self' to nil correctly closes the LRS stream and ensures no
+// more LRS reports are sent.
+func (s) TestUpdateLRSServerToNil(t *testing.T) {
+	// Create an xDS management server that serves ADS and LRS requests.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{SupportLoadReportingService: true})
+	defer mgmtServer.Stop()
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+	testutils.CreateBootstrapFileForTesting(t, bc)
+
+	// Create an xDS resolver with the above bootstrap configuration.
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+
+	// Start a server backend exposing the test service.
+	server := stubserver.StartTestService(t, nil)
+	defer server.Stop()
+
+	// Configure the xDS management server with default resources.
+	const serviceName = "my-test-xds-service"
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: serviceName,
+		NodeID:     nodeID,
+		Host:       "localhost",
+		Port:       testutils.ParsePort(t, server.Address),
+		SecLevel:   e2e.SecurityLevelNone,
+	})
+	resources.Clusters[0].LrsServer = &v3corepb.ConfigSource{
+		ConfigSourceSpecifier: &v3corepb.ConfigSource_Self{
+			Self: &v3corepb.SelfConfigSource{},
+		},
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	// Create a ClientConn and make a successful RPC.
+	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
+	if err != nil {
+		t.Fatalf("Failed to dial local test server: %v", err)
+	}
+	defer cc.Close()
+
+	client := testgrpc.NewTestServiceClient(cc)
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
+		t.Fatalf("client.EmptyCall() failed: %v", err)
+	}
+
+	// Ensure that an LRS stream is created.
+	if _, err := mgmtServer.LRSServer.LRSStreamOpenChan.Receive(ctx); err != nil {
+		t.Fatalf("Error waiting for initial LRS stream open: %v", err)
+	}
+	if _, err := mgmtServer.LRSServer.LRSRequestChan.Receive(ctx); err != nil {
+		t.Fatalf("Error waiting for initial LRS report: %v", err)
+	}
+
+	// Update LRS Server to nil
+	resources.Clusters[0].LrsServer = nil
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	// Ensure that the old LRS stream is closed.
+	if _, err := mgmtServer.LRSServer.LRSStreamCloseChan.Receive(ctx); err != nil {
+		t.Fatalf("Error waiting for initial LRS stream close : %v", err)
+	}
+
+	// Also ensure that a new LRS stream is not created.
+	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
+	defer sCancel()
+	if _, err := mgmtServer.LRSServer.LRSRequestChan.Receive(sCtx); err != context.DeadlineExceeded {
+		t.Fatalf("Expected no LRS reports after disable, got %v want %v", err, context.DeadlineExceeded)
+	}
+}
+
+// Test verifies that child policy was updated on receipt of
+// configuration update.
+func (s) TestChildPolicyChangeOnConfigUpdate(t *testing.T) {
+	// Create an xDS management server.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
+	defer mgmtServer.Stop()
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+	testutils.CreateBootstrapFileForTesting(t, bc)
+
+	// Create an xDS resolver with the above bootstrap configuration.
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+
+	// Start a server backend exposing the test service.
+	server := stubserver.StartTestService(t, nil)
+	defer server.Stop()
+
+	const serviceName = "test-child-policy"
+
+	// Configure the xDS management server with default resources. Cluster
+	// corresponding to this resource will be configured with "round_robin"
+	// as the endpoint picking policy
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: serviceName,
+		NodeID:     nodeID,
+		Host:       "localhost",
+		Port:       testutils.ParsePort(t, server.Address),
+	})
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatalf("Failed to update xDS resources: %v", err)
+	}
+
+	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithResolvers(resolverBuilder), grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if err != nil {
+		t.Fatalf("Failed to create client: %v", err)
+	}
+	defer cc.Close()
+
+	client := testgrpc.NewTestServiceClient(cc)
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
+		t.Fatalf("client.EmptyCall() failed: %v", err)
+	}
+
+	// Register stub pickfirst LB policy so that we can catch config changes.
+	pfBuilder := balancer.Get(pickfirst.Name)
+	internal.BalancerUnregister(pfBuilder.Name())
+	lbCfgCh := make(chan serviceconfig.LoadBalancingConfig, 1)
+	var updatedChildPolicy atomic.Pointer[string]
+	stub.Register(pfBuilder.Name(), stub.BalancerFuncs{
+		ParseConfig: func(lbCfg json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
+			return pfBuilder.(balancer.ConfigParser).ParseConfig(lbCfg)
+		},
+		Init: func(bd *stub.BalancerData) {
+			bd.ChildBalancer = pfBuilder.Build(bd.ClientConn, bd.BuildOptions)
+		},
+		UpdateClientConnState: func(bd *stub.BalancerData, ccs balancer.ClientConnState) error {
+			name := pfBuilder.Name()
+			updatedChildPolicy.Store(&name)
+			select {
+			case lbCfgCh <- ccs.BalancerConfig:
+			case <-ctx.Done():
+				t.Error("Timed out while waiting for BalancerConfig, context deadline exceeded")
+			}
+			return bd.ChildBalancer.UpdateClientConnState(ccs)
+		},
+		Close: func(bd *stub.BalancerData) {
+			bd.ChildBalancer.Close()
+		},
+	})
+	defer balancer.Register(pfBuilder)
+
+	// Now update the cluster to use "pick_first" as the endpoint picking policy.
+	resources.Clusters[0].LoadBalancingPolicy = &v3clusterpb.LoadBalancingPolicy{
+		Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{{
+			TypedExtensionConfig: &v3corepb.TypedExtensionConfig{
+				TypedConfig: testutils.MarshalAny(t, &v3pickfirstpb.PickFirst{}),
+			},
+		}},
+	}
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatal(err)
+	}
+
+	select {
+	case <-ctx.Done():
+		t.Fatalf("Timeout waiting for pickfirst child policy config")
+	case <-lbCfgCh:
+	}
+
+	if p := updatedChildPolicy.Load(); p == nil || *p != pfBuilder.Name() {
+		var got string
+		if p != nil {
+			got = *p
+		}
+		t.Fatalf("Unexpected child policy after config update, got %q, want %q", got, pfBuilder.Name())
+	}
+
+	// New RPC should still be routed successfully
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
+		t.Errorf("EmptyCall() failed after policy update: %v", err)
+	}
+}
+
+// Test verifies that config update fails if child policy config
+// failed to parse.
+func (s) TestFailedToParseChildPolicyConfig(t *testing.T) {
+	// Create an xDS management server.
+	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{AllowResourceSubset: true})
+	defer mgmtServer.Stop()
+
+	// Create bootstrap configuration pointing to the above management server.
+	nodeID := uuid.New().String()
+	bc := e2e.DefaultBootstrapContents(t, nodeID, mgmtServer.Address)
+	testutils.CreateBootstrapFileForTesting(t, bc)
+
+	// Create an xDS resolver with the above bootstrap configuration.
+	if internal.NewXDSResolverWithConfigForTesting == nil {
+		t.Fatalf("internal.NewXDSResolverWithConfigForTesting is nil")
+	}
+	resolverBuilder, err := internal.NewXDSResolverWithConfigForTesting.(func([]byte) (resolver.Builder, error))(bc)
+	if err != nil {
+		t.Fatalf("Failed to create xDS resolver for testing: %v", err)
+	}
+
+	// Start a server backend exposing the test service.
+	server := stubserver.StartTestService(t, nil)
+	defer server.Stop()
+
+	const serviceName = "test-child-policy"
+	resources := e2e.DefaultClientResources(e2e.ResourceParams{
+		DialTarget: serviceName,
+		NodeID:     nodeID,
+		Host:       "localhost",
+		Port:       testutils.ParsePort(t, server.Address),
+	})
+	resources.Clusters[0].LoadBalancingPolicy = &v3clusterpb.LoadBalancingPolicy{
+		Policies: []*v3clusterpb.LoadBalancingPolicy_Policy{{
+			TypedExtensionConfig: &v3corepb.TypedExtensionConfig{
+				TypedConfig: testutils.MarshalAny(t, &v3pickfirstpb.PickFirst{}),
+			},
+		}},
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
+	defer cancel()
+	if err := mgmtServer.Update(ctx, resources); err != nil {
+		t.Fatalf("Failed to update xDS resources: %v", err)
+	}
+
+	// Register stub pickfirst LB policy so that we can catch parsing errors.
+	pfBuilder := balancer.Get(pickfirst.Name)
+	internal.BalancerUnregister(pfBuilder.Name())
+	const parseConfigError = "failed to parse config"
+	stub.Register(pfBuilder.Name(), stub.BalancerFuncs{
+		ParseConfig: func(_ json.RawMessage) (serviceconfig.LoadBalancingConfig, error) {
+			return nil, errors.New(parseConfigError)
+		},
+	})
+	defer balancer.Register(pfBuilder)
+
+	cc, err := grpc.NewClient(fmt.Sprintf("xds:///%s", serviceName), grpc.WithResolvers(resolverBuilder), grpc.WithTransportCredentials(insecure.NewCredentials()))
+	if err != nil {
+		t.Fatalf("Failed to create client: %v", err)
+	}
+	defer cc.Close()
+
+	client := testgrpc.NewTestServiceClient(cc)
+	if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err == nil || !strings.Contains(err.Error(), parseConfigError) {
+		t.Fatal("EmptyCall RPC succeeded when expected to fail")
+	}
+}
diff --git a/internal/xds/clients/xdsclient/test/ads_stream_flow_control_test.go b/internal/xds/clients/xdsclient/test/ads_stream_flow_control_test.go
index 9c417a14..83155a99 100644
--- a/internal/xds/clients/xdsclient/test/ads_stream_flow_control_test.go
+++ b/internal/xds/clients/xdsclient/test/ads_stream_flow_control_test.go
@@ -20,7 +20,6 @@ package xdsclient_test
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"slices"
 	"sort"
@@ -125,7 +124,6 @@ func (t *transport) NewStream(ctx context.Context, method string) (clients.Strea
 	stream := &stream{
 		stream: s,
 		recvCh: make(chan struct{}, 1),
-		doneCh: make(chan struct{}),
 	}
 	t.adsStreamCh <- stream
 
@@ -138,9 +136,7 @@ func (t *transport) Close() {
 
 type stream struct {
 	stream grpc.ClientStream
-
 	recvCh chan struct{}
-	doneCh <-chan struct{}
 }
 
 func (s *stream) Send(msg []byte) error {
@@ -150,8 +146,8 @@ func (s *stream) Send(msg []byte) error {
 func (s *stream) Recv() ([]byte, error) {
 	select {
 	case s.recvCh <- struct{}{}:
-	case <-s.doneCh:
-		return nil, errors.New("Recv() called after the test has finished")
+	case <-s.stream.Context().Done():
+		// Unblock the recv() once the stream is done.
 	}
 
 	var typedRes []byte
diff --git a/internal/xds/resolver/serviceconfig_test.go b/internal/xds/resolver/serviceconfig_test.go
index 3570ff2e..f05a7d91 100644
--- a/internal/xds/resolver/serviceconfig_test.go
+++ b/internal/xds/resolver/serviceconfig_test.go
@@ -29,7 +29,6 @@ import (
 	"google.golang.org/grpc/internal/grpctest"
 	"google.golang.org/grpc/internal/grpcutil"
 	iresolver "google.golang.org/grpc/internal/resolver"
-	"google.golang.org/grpc/internal/testutils"
 	_ "google.golang.org/grpc/internal/xds/balancer/cdsbalancer" // To parse LB config
 	"google.golang.org/grpc/internal/xds/xdsclient/xdsresource"
 	"google.golang.org/grpc/metadata"
@@ -64,12 +63,8 @@ func (s) TestPruneActiveClusters(t *testing.T) {
 
 func (s) TestGenerateRequestHash(t *testing.T) {
 	const channelID = 12378921
-	cs := &configSelector{
-		r: &xdsResolver{
-			cc:        &testutils.ResolverClientConn{Logger: t},
-			channelID: channelID,
-		},
-	}
+	cs := &configSelector{channelID: channelID}
+
 	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
 	defer cancel()
 	tests := []struct {
diff --git a/internal/xds/xdsclient/tests/ads_stream_restart_test.go b/internal/xds/xdsclient/tests/ads_stream_restart_test.go
index 58fad802..81f3a3c4 100644
--- a/internal/xds/xdsclient/tests/ads_stream_restart_test.go
+++ b/internal/xds/xdsclient/tests/ads_stream_restart_test.go
@@ -20,45 +20,23 @@ package xdsclient_test
 
 import (
 	"context"
-	"fmt"
 	"testing"
-	"time"
 
 	"github.com/google/go-cmp/cmp"
-	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/google/uuid"
+	"google.golang.org/grpc"
 	"google.golang.org/grpc/internal/testutils"
 	"google.golang.org/grpc/internal/testutils/xds/e2e"
 	"google.golang.org/grpc/internal/xds/bootstrap"
 	"google.golang.org/grpc/internal/xds/xdsclient"
 	"google.golang.org/grpc/internal/xds/xdsclient/xdsresource"
-	"google.golang.org/grpc/internal/xds/xdsclient/xdsresource/version"
+	"google.golang.org/protobuf/testing/protocmp"
 
-	v3clusterpb "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
 	v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
 	v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
 	v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
 )
 
-// waitForResourceNames waits for the wantNames to be received on namesCh.
-// Returns a non-nil error if the context expires before that.
-func waitForResourceNames(ctx context.Context, t *testing.T, namesCh chan []string, wantNames []string) error {
-	t.Helper()
-
-	var lastRequestedNames []string
-	for ; ; <-time.After(defaultTestShortTimeout) {
-		select {
-		case <-ctx.Done():
-			return fmt.Errorf("timeout waiting for resources %v to be requested from the management server. Last requested resources: %v", wantNames, lastRequestedNames)
-		case gotNames := <-namesCh:
-			if cmp.Equal(gotNames, wantNames, cmpopts.EquateEmpty(), cmpopts.SortSlices(func(s1, s2 string) bool { return s1 < s2 })) {
-				return nil
-			}
-			lastRequestedNames = gotNames
-		}
-	}
-}
-
 // Tests that an ADS stream is restarted after a connection failure. Also
 // verifies that if there were any watches registered before the connection
 // failed, those resources are re-requested after the stream is restarted.
@@ -74,47 +52,26 @@ func (s) TestADS_ResourcesAreRequestedAfterStreamRestart(t *testing.T) {
 	defer cancel()
 
 	// Start an xDS management server that uses a couple of channels to inform
-	// the test about the specific LDS and CDS resource names being requested.
-	ldsResourcesCh := make(chan []string, 1)
-	cdsResourcesCh := make(chan []string, 1)
-	streamOpened := make(chan struct{}, 1)
-	streamClosed := make(chan struct{}, 1)
+	// the test about the request and response messages being exchanged.
+	streamRequestCh := testutils.NewChannel()
+	streamResponseCh := testutils.NewChannel()
+	streamOpened := testutils.NewChannel()
+	streamClosed := testutils.NewChannel()
 	mgmtServer := e2e.StartManagementServer(t, e2e.ManagementServerOptions{
 		Listener: lis,
 		OnStreamRequest: func(_ int64, req *v3discoverypb.DiscoveryRequest) error {
 			t.Logf("Received request for resources: %v of type %s", req.GetResourceNames(), req.GetTypeUrl())
-
-			// Drain the resource name channels before writing to them to ensure
-			// that the most recently requested names are made available to the
-			// test.
-			switch req.GetTypeUrl() {
-			case version.V3ClusterURL:
-				select {
-				case <-cdsResourcesCh:
-				default:
-				}
-				cdsResourcesCh <- req.GetResourceNames()
-			case version.V3ListenerURL:
-				select {
-				case <-ldsResourcesCh:
-				default:
-				}
-				ldsResourcesCh <- req.GetResourceNames()
-			}
+			streamRequestCh.SendContext(ctx, req)
 			return nil
 		},
+		OnStreamResponse: func(_ context.Context, _ int64, _ *v3discoverypb.DiscoveryRequest, resp *v3discoverypb.DiscoveryResponse) {
+			streamResponseCh.SendContext(ctx, resp)
+		},
 		OnStreamClosed: func(int64, *v3corepb.Node) {
-			select {
-			case streamClosed <- struct{}{}:
-			default:
-			}
-
+			streamClosed.SendContext(ctx, nil)
 		},
 		OnStreamOpen: func(context.Context, int64, string) error {
-			select {
-			case streamOpened <- struct{}{}:
-			default:
-			}
+			streamOpened.SendContext(ctx, nil)
 			return nil
 		},
 	})
@@ -156,93 +113,82 @@ func (s) TestADS_ResourcesAreRequestedAfterStreamRestart(t *testing.T) {
 
 	// Verify that an ADS stream is opened and an LDS request with the above
 	// resource name is sent.
-	select {
-	case <-streamOpened:
-	case <-ctx.Done():
+	if _, err = streamOpened.Receive(ctx); err != nil {
 		t.Fatal("Timeout when waiting for ADS stream to open")
 	}
-	if err := waitForResourceNames(ctx, t, ldsResourcesCh, []string{listenerName}); err != nil {
-		t.Fatal(err)
-	}
 
-	// Verify the update received by the watcher.
-	wantListenerUpdate := listenerUpdateErrTuple{
-		update: xdsresource.ListenerUpdate{
-			RouteConfigName: routeConfigName,
-			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
+	// Verify that the initial discovery request matches expectation.
+	r, err := streamRequestCh.Receive(ctx)
+	if err != nil {
+		t.Fatal("Timeout when waiting for the initial discovery request")
+	}
+	gotReq := r.(*v3discoverypb.DiscoveryRequest)
+	wantReq := &v3discoverypb.DiscoveryRequest{
+		VersionInfo: "",
+		Node: &v3corepb.Node{
+			Id:                   nodeID,
+			UserAgentName:        "gRPC Go",
+			UserAgentVersionType: &v3corepb.Node_UserAgentVersion{UserAgentVersion: grpc.Version},
+			ClientFeatures:       []string{"envoy.lb.does_not_support_overprovisioning", "xds.config.resource-in-sotw"},
 		},
+		ResourceNames: []string{listenerName},
+		TypeUrl:       "type.googleapis.com/envoy.config.listener.v3.Listener",
+		ResponseNonce: "",
 	}
-	if err := verifyListenerUpdate(ctx, lw.updateCh, wantListenerUpdate); err != nil {
-		t.Fatal(err)
+	if diff := cmp.Diff(gotReq, wantReq, protocmp.Transform()); diff != "" {
+		t.Fatalf("Unexpected diff in received discovery request, diff (-got, +want):\n%s", diff)
 	}
 
-	// Create a cluster resource on the management server, in addition to the
-	// existing listener resource.
-	const clusterName = "cluster"
-	resources = e2e.UpdateOptions{
-		NodeID:         nodeID,
-		Listeners:      []*v3listenerpb.Listener{e2e.DefaultClientListener(listenerName, routeConfigName)},
-		Clusters:       []*v3clusterpb.Cluster{e2e.DefaultCluster(clusterName, clusterName, e2e.SecurityLevelNone)},
-		SkipValidation: true,
-	}
-	if err := mgmtServer.Update(ctx, resources); err != nil {
-		t.Fatal(err)
+	// Capture the version and nonce from the response.
+	r, err = streamResponseCh.Receive(ctx)
+	if err != nil {
+		t.Fatal("Timeout when waiting for a discovery response from the server")
 	}
+	gotResp := r.(*v3discoverypb.DiscoveryResponse)
 
-	// Register a watch for a cluster resource, and verify that a CDS request
-	// with the above resource name is sent.
-	cw := newClusterWatcher()
-	cdsCancel := xdsresource.WatchCluster(client, clusterName, cw)
-	if err := waitForResourceNames(ctx, t, cdsResourcesCh, []string{clusterName}); err != nil {
-		t.Fatal(err)
+	// Verify that the ACK contains the appropriate version and nonce.
+	r, err = streamRequestCh.Receive(ctx)
+	if err != nil {
+		t.Fatal("Timeout when waiting for ACK")
+	}
+	gotReq = r.(*v3discoverypb.DiscoveryRequest)
+	wantReq.VersionInfo = gotResp.GetVersionInfo()
+	wantReq.ResponseNonce = gotResp.GetNonce()
+	if diff := cmp.Diff(gotReq, wantReq, protocmp.Transform()); diff != "" {
+		t.Fatalf("Unexpected diff in received discovery request, diff (-got, +want):\n%s", diff)
 	}
 
 	// Verify the update received by the watcher.
-	wantClusterUpdate := clusterUpdateErrTuple{
-		update: xdsresource.ClusterUpdate{
-			ClusterName:    clusterName,
-			EDSServiceName: clusterName,
+	wantListenerUpdate := listenerUpdateErrTuple{
+		update: xdsresource.ListenerUpdate{
+			RouteConfigName: routeConfigName,
+			HTTPFilters:     []xdsresource.HTTPFilter{{Name: "router"}},
 		},
 	}
-	if err := verifyClusterUpdate(ctx, cw.updateCh, wantClusterUpdate); err != nil {
-		t.Fatal(err)
-	}
-
-	// Cancel the watch for the above cluster resource, and verify that a CDS
-	// request with no resource names is sent.
-	cdsCancel()
-	if err := waitForResourceNames(ctx, t, cdsResourcesCh, []string{}); err != nil {
+	if err := verifyListenerUpdate(ctx, lw.updateCh, wantListenerUpdate); err != nil {
 		t.Fatal(err)
 	}
 
 	// Stop the restartable listener and wait for the stream to close.
 	lis.Stop()
-	select {
-	case <-streamClosed:
-	case <-ctx.Done():
+	if _, err = streamClosed.Receive(ctx); err != nil {
 		t.Fatal("Timeout when waiting for ADS stream to close")
 	}
 
 	// Restart the restartable listener and wait for the stream to open.
 	lis.Restart()
-	select {
-	case <-streamOpened:
-	case <-ctx.Done():
+	if _, err = streamOpened.Receive(ctx); err != nil {
 		t.Fatal("Timeout when waiting for ADS stream to open")
 	}
 
 	// Verify that the listener resource is requested again.
-	if err := waitForResourceNames(ctx, t, ldsResourcesCh, []string{listenerName}); err != nil {
-		t.Fatal(err)
+	r, err = streamRequestCh.Receive(ctx)
+	if err != nil {
+		t.Fatal("Timeout when waiting for the initial discovery request")
 	}
-
-	// Wait for a short duration and verify that no CDS request is sent, since
-	// there are no resources being watched.
-	sCtx, sCancel := context.WithTimeout(ctx, defaultTestShortTimeout)
-	defer sCancel()
-	select {
-	case <-sCtx.Done():
-	case names := <-cdsResourcesCh:
-		t.Fatalf("CDS request sent for resource names %v, when expecting no request", names)
+	gotReq = r.(*v3discoverypb.DiscoveryRequest)
+	wantReq.ResponseNonce = ""
+	if diff := cmp.Diff(gotReq, wantReq, protocmp.Transform()); diff != "" {
+		t.Fatalf("Unexpected diff in received discovery request, diff (-got, +want):\n%s", diff)
 	}
 }
diff --git a/internal/xds/xdsclient/tests/dump_test.go b/internal/xds/xdsclient/tests/dump_test.go
index a476c29b..11a43c12 100644
--- a/internal/xds/xdsclient/tests/dump_test.go
+++ b/internal/xds/xdsclient/tests/dump_test.go
@@ -63,7 +63,7 @@ func makeGenericXdsConfig(typeURL, name, version string, status v3adminpb.Client
 }
 
 func checkResourceDump(ctx context.Context, want *v3statuspb.ClientStatusResponse, pool *xdsclient.Pool) error {
-	var cmpOpts = cmp.Options{
+	cmpOpts := cmp.Options{
 		protocmp.Transform(),
 		protocmp.IgnoreFields((*v3statuspb.ClientConfig_GenericXdsConfig)(nil), "last_updated"),
 		protocmp.IgnoreFields((*v3adminpb.UpdateFailureState)(nil), "last_update_attempt", "details"),
@@ -89,7 +89,7 @@ func checkResourceDump(ctx context.Context, want *v3statuspb.ClientStatusRespons
 		if diff == "" {
 			return nil
 		}
-		lastErr = fmt.Errorf("received unexpected resource dump, diff (-got, +want):\n%s, got: %s\n want:%s", diff, pretty.ToJSON(got), pretty.ToJSON(want))
+		lastErr = fmt.Errorf("received unexpected resource dump, diff (-want, +got):\n%s, got: %s\n want:%s", diff, pretty.ToJSON(got), pretty.ToJSON(want))
 	}
 	return fmt.Errorf("timeout when waiting for resource dump to reach expected state: %v", lastErr)
 }
diff --git a/internal/xds/xdsclient/tests/federation_watchers_test.go b/internal/xds/xdsclient/tests/federation_watchers_test.go
index 1bf0dc55..2e844d50 100644
--- a/internal/xds/xdsclient/tests/federation_watchers_test.go
+++ b/internal/xds/xdsclient/tests/federation_watchers_test.go
@@ -182,7 +182,7 @@ func (s) TestFederation_RouteConfigResourceContextParamOrder(t *testing.T) {
 						{
 							Prefix:           newStringP("/"),
 							ActionType:       xdsresource.RouteActionRoute,
-							WeightedClusters: map[string]xdsresource.WeightedCluster{"cluster-resource": {Weight: 100}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: "cluster-resource", Weight: 100}},
 						},
 					},
 				},
diff --git a/internal/xds/xdsclient/tests/rds_watchers_test.go b/internal/xds/xdsclient/tests/rds_watchers_test.go
index 3ec333ad..e71dfbd3 100644
--- a/internal/xds/xdsclient/tests/rds_watchers_test.go
+++ b/internal/xds/xdsclient/tests/rds_watchers_test.go
@@ -183,7 +183,7 @@ func (s) TestRDSWatch(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 								},
 							},
 						},
@@ -206,7 +206,7 @@ func (s) TestRDSWatch(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{cdsNameNewStyle: {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsNameNewStyle, Weight: 100}},
 								},
 							},
 						},
@@ -345,7 +345,7 @@ func (s) TestRDSWatch_TwoWatchesForSameResourceName(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 								},
 							},
 						},
@@ -361,7 +361,7 @@ func (s) TestRDSWatch_TwoWatchesForSameResourceName(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{"new-cds-resource": {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: "new-cds-resource", Weight: 100}},
 								},
 							},
 						},
@@ -383,7 +383,7 @@ func (s) TestRDSWatch_TwoWatchesForSameResourceName(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{cdsNameNewStyle: {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsNameNewStyle, Weight: 100}},
 								},
 							},
 						},
@@ -399,7 +399,7 @@ func (s) TestRDSWatch_TwoWatchesForSameResourceName(t *testing.T) {
 								{
 									Prefix:           newStringP("/"),
 									ActionType:       xdsresource.RouteActionRoute,
-									WeightedClusters: map[string]xdsresource.WeightedCluster{"new-cds-resource": {Weight: 100}},
+									WeightedClusters: []xdsresource.WeightedCluster{{Name: "new-cds-resource", Weight: 100}},
 								},
 							},
 						},
@@ -597,7 +597,7 @@ func (s) TestRDSWatch_ThreeWatchesForDifferentResourceNames(t *testing.T) {
 						{
 							Prefix:           newStringP("/"),
 							ActionType:       xdsresource.RouteActionRoute,
-							WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 						},
 					},
 				},
@@ -688,7 +688,7 @@ func (s) TestRDSWatch_ResourceCaching(t *testing.T) {
 						{
 							Prefix:           newStringP("/"),
 							ActionType:       xdsresource.RouteActionRoute,
-							WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 						},
 					},
 				},
@@ -819,7 +819,7 @@ func (s) TestRDSWatch_ValidResponseCancelsExpiryTimerBehavior(t *testing.T) {
 						{
 							Prefix:           newStringP("/"),
 							ActionType:       xdsresource.RouteActionRoute,
-							WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 						},
 					},
 				},
@@ -983,7 +983,7 @@ func (s) TestRDSWatch_PartialValid(t *testing.T) {
 						{
 							Prefix:           newStringP("/"),
 							ActionType:       xdsresource.RouteActionRoute,
-							WeightedClusters: map[string]xdsresource.WeightedCluster{cdsName: {Weight: 100}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: cdsName, Weight: 100}},
 						},
 					},
 				},
diff --git a/internal/xds/xdsclient/tests/resource_update_test.go b/internal/xds/xdsclient/tests/resource_update_test.go
index 90517a90..298b152b 100644
--- a/internal/xds/xdsclient/tests/resource_update_test.go
+++ b/internal/xds/xdsclient/tests/resource_update_test.go
@@ -510,7 +510,7 @@ func (s) TestHandleRouteConfigResponseFromManagementServer(t *testing.T) {
 					{
 						Domains: []string{"lds-target-name"},
 						Routes: []*xdsresource.Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]xdsresource.WeightedCluster{"cluster-name": {Weight: 1}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: "cluster-name", Weight: 1}},
 							ActionType:       xdsresource.RouteActionRoute}},
 					},
 				},
@@ -538,7 +538,7 @@ func (s) TestHandleRouteConfigResponseFromManagementServer(t *testing.T) {
 					{
 						Domains: []string{"lds-target-name"},
 						Routes: []*xdsresource.Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]xdsresource.WeightedCluster{"cluster-name": {Weight: 1}},
+							WeightedClusters: []xdsresource.WeightedCluster{{Name: "cluster-name", Weight: 1}},
 							ActionType:       xdsresource.RouteActionRoute}},
 					},
 				},
diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go
index b652c722..2560365d 100644
--- a/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go
+++ b/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go
@@ -565,7 +565,11 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) {
 				InlineRouteConfig: &RouteConfigUpdate{
 					VirtualHosts: []*VirtualHost{{
 						Domains: []string{v3LDSTarget},
-						Routes:  []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, ActionType: RouteActionRoute}},
+						Routes: []*Route{{
+							Prefix:           newStringP("/"),
+							WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
+							ActionType:       RouteActionRoute,
+						}},
 					}}},
 				MaxStreamDuration: time.Second,
 				Raw:               v3LisWithInlineRoute,
diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go
index 622ef7df..90d80c8b 100644
--- a/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go
+++ b/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go
@@ -140,7 +140,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 					Domains: []string{ldsTarget},
 					Routes: []*Route{{
 						Prefix:           newStringP("/"),
-						WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+						WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 						ActionType:       RouteActionRoute,
 					}},
 					HTTPFilterConfigOverride: cfgs,
@@ -153,7 +153,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 					Domains: []string{ldsTarget},
 					Routes: []*Route{{Prefix: newStringP("/"),
 						CaseInsensitive:  true,
-						WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+						WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 						ActionType:       RouteActionRoute}},
 				},
 			},
@@ -204,7 +204,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 					Domains: []string{ldsTarget},
 					Routes: []*Route{{
 						Prefix:           newStringP("/"),
-						WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+						WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 						ActionType:       RouteActionRoute,
 						RetryConfig:      rrc,
 					}},
@@ -312,7 +312,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{Prefix: newStringP("/"),
 							CaseInsensitive:  true,
-							WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 				},
@@ -356,13 +356,13 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 					{
 						Domains: []string{uninterestingDomain},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: uninterestingClusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 					{
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 				},
@@ -394,7 +394,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 					{
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{Prefix: newStringP("/"),
-							WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: clusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 				},
@@ -434,10 +434,10 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{
 							Prefix: newStringP("/"),
-							WeightedClusters: map[string]WeightedCluster{
-								"a": {Weight: 2},
-								"b": {Weight: 3},
-								"c": {Weight: 5},
+							WeightedClusters: []WeightedCluster{
+								{Name: "a", Weight: 2},
+								{Name: "b", Weight: 3},
+								{Name: "c", Weight: 5},
 							},
 							ActionType: RouteActionRoute,
 						}},
@@ -472,7 +472,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{
 							Prefix:            newStringP("/"),
-							WeightedClusters:  map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters:  []WeightedCluster{{Name: clusterName, Weight: 1}},
 							MaxStreamDuration: newDurationP(time.Second),
 							ActionType:        RouteActionRoute,
 						}},
@@ -507,7 +507,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{
 							Prefix:            newStringP("/"),
-							WeightedClusters:  map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters:  []WeightedCluster{{Name: clusterName, Weight: 1}},
 							MaxStreamDuration: newDurationP(time.Second),
 							ActionType:        RouteActionRoute,
 						}},
@@ -542,7 +542,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) {
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{
 							Prefix:            newStringP("/"),
-							WeightedClusters:  map[string]WeightedCluster{clusterName: {Weight: 1}},
+							WeightedClusters:  []WeightedCluster{{Name: clusterName, Weight: 1}},
 							MaxStreamDuration: newDurationP(0),
 							ActionType:        RouteActionRoute,
 						}},
@@ -836,13 +836,13 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
 					{
 						Domains: []string{uninterestingDomain},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: uninterestingClusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 					{
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: v3ClusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 				},
@@ -858,13 +858,13 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) {
 					{
 						Domains: []string{uninterestingDomain},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{uninterestingClusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: uninterestingClusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 					{
 						Domains: []string{ldsTarget},
 						Routes: []*Route{{Prefix: newStringP(""),
-							WeightedClusters: map[string]WeightedCluster{v3ClusterName: {Weight: 1}},
+							WeightedClusters: []WeightedCluster{{Name: v3ClusterName, Weight: 1}},
 							ActionType:       RouteActionRoute}},
 					},
 				},
@@ -913,9 +913,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 		goodUpdateWithFilterConfigs = func(cfgs map[string]httpfilter.FilterConfig) []*Route {
 			// Sets per-filter config in cluster "B" and in the route.
 			return []*Route{{
-				Prefix:                   newStringP("/"),
-				CaseInsensitive:          true,
-				WeightedClusters:         map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60, HTTPFilterConfigOverride: cfgs}},
+				Prefix:          newStringP("/"),
+				CaseInsensitive: true,
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60, HTTPFilterConfigOverride: cfgs},
+					{Name: "A", Weight: 40},
+				},
 				HTTPFilterConfigOverride: cfgs,
 				ActionType:               RouteActionRoute,
 			}}
@@ -953,10 +956,13 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 							}}}},
 			}},
 			wantRoutes: []*Route{{
-				Prefix:           newStringP("/"),
-				CaseInsensitive:  true,
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Prefix:          newStringP("/"),
+				CaseInsensitive: true,
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 		},
 		{
@@ -1001,9 +1007,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 						PrefixMatch: newStringP("tv"),
 					},
 				},
-				Fraction:         newUInt32P(10000),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Fraction: newUInt32P(10000),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1046,9 +1055,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 						RegexMatch:  func() *regexp.Regexp { return regexp.MustCompile("tv") }(),
 					},
 				},
-				Fraction:         newUInt32P(10000),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Fraction: newUInt32P(10000),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1091,9 +1103,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 						StringMatch: &sm,
 					},
 				},
-				Fraction:         newUInt32P(10000),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Fraction: newUInt32P(10000),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1125,9 +1140,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 			// Only one route in the result, because the second one with query
 			// parameters is ignored.
 			wantRoutes: []*Route{{
-				Prefix:           newStringP("/a/"),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Prefix: newStringP("/a/"),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1285,9 +1303,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 				},
 			},
 			wantRoutes: []*Route{{
-				Prefix:           newStringP("/a/"),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
-				ActionType:       RouteActionRoute,
+				Prefix: newStringP("/a/"),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1310,9 +1331,12 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 				},
 			},
 			wantRoutes: []*Route{{
-				Prefix:           newStringP("/a/"),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 20}, "B": {Weight: 30}},
-				ActionType:       RouteActionRoute,
+				Prefix: newStringP("/a/"),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 30},
+					{Name: "A", Weight: 20},
+				},
+				ActionType: RouteActionRoute,
 			}},
 			wantErr: false,
 		},
@@ -1362,8 +1386,11 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 						PrefixMatch: newStringP("tv"),
 					},
 				},
-				Fraction:         newUInt32P(10000),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
+				Fraction: newUInt32P(10000),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
 				HashPolicies: []*HashPolicy{
 					{HashPolicyType: HashPolicyTypeChannelID},
 				},
@@ -1419,8 +1446,11 @@ func (s) TestRoutesProtoToSlice(t *testing.T) {
 						PrefixMatch: newStringP("tv"),
 					},
 				},
-				Fraction:         newUInt32P(10000),
-				WeightedClusters: map[string]WeightedCluster{"A": {Weight: 40}, "B": {Weight: 60}},
+				Fraction: newUInt32P(10000),
+				WeightedClusters: []WeightedCluster{
+					{Name: "B", Weight: 60},
+					{Name: "A", Weight: 40},
+				},
 				HashPolicies: []*HashPolicy{
 					{HashPolicyType: HashPolicyTypeHeader,
 						HeaderName: ":path"},
diff --git a/test/http_header_end2end_test.go b/test/http_header_end2end_test.go
index 0044a9f7..9ad33ef3 100644
--- a/test/http_header_end2end_test.go
+++ b/test/http_header_end2end_test.go
@@ -103,10 +103,9 @@ func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) {
 		errCode codes.Code
 	}{
 		{
-			name: "missing gRPC status",
+			name: "missing gRPC content type",
 			header: []string{
 				":status", "403",
-				"content-type", "application/grpc",
 			},
 			errCode: codes.PermissionDenied,
 		},
@@ -120,23 +119,23 @@ func (s) TestHTTPHeaderFrameErrorHandlingInitialHeader(t *testing.T) {
 			errCode: codes.Internal,
 		},
 		{
-			name: "Malformed grpc-tags-bin field",
+			name: "Malformed grpc-tags-bin field ignores http status",
 			header: []string{
 				":status", "502",
 				"content-type", "application/grpc",
 				"grpc-status", "0",
 				"grpc-tags-bin", "???",
 			},
-			errCode: codes.Unavailable,
+			errCode: codes.Internal,
 		},
 		{
-			name: "gRPC status error",
+			name: "gRPC status error ignoring http status",
 			header: []string{
 				":status", "502",
 				"content-type", "application/grpc",
 				"grpc-status", "3",
 			},
-			errCode: codes.Unavailable,
+			errCode: codes.InvalidArgument,
 		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
@@ -161,7 +160,7 @@ func (s) TestHTTPHeaderFrameErrorHandlingNormalTrailer(t *testing.T) {
 		errCode        codes.Code
 	}{
 		{
-			name: "trailer missing grpc-status",
+			name: "trailer missing grpc-status to ignore http status",
 			responseHeader: []string{
 				":status", "200",
 				"content-type", "application/grpc",
@@ -170,10 +169,10 @@ func (s) TestHTTPHeaderFrameErrorHandlingNormalTrailer(t *testing.T) {
 				// trailer missing grpc-status
 				":status", "502",
 			},
-			errCode: codes.Unavailable,
+			errCode: codes.Internal,
 		},
 		{
-			name: "malformed grpc-status-details-bin field with status 404",
+			name: "malformed grpc-status-details-bin field with status 404 to be ignored due to content type",
 			responseHeader: []string{
 				":status", "404",
 				"content-type", "application/grpc",
@@ -183,20 +182,19 @@ func (s) TestHTTPHeaderFrameErrorHandlingNormalTrailer(t *testing.T) {
 				"grpc-status", "0",
 				"grpc-status-details-bin", "????",
 			},
-			errCode: codes.Unimplemented,
+			errCode: codes.Internal,
 		},
 		{
-			name: "malformed grpc-status-details-bin field with status 200",
+			name: "malformed grpc-status-details-bin field with status 404 and no content type",
 			responseHeader: []string{
-				":status", "200",
-				"content-type", "application/grpc",
+				":status", "404",
 			},
 			trailer: []string{
 				// malformed grpc-status-details-bin field
 				"grpc-status", "0",
 				"grpc-status-details-bin", "????",
 			},
-			errCode: codes.Internal,
+			errCode: codes.Unimplemented,
 		},
 	}
 	for _, test := range tests {
diff --git a/test/xds/xds_telemetry_labels_test.go b/test/xds/xds_telemetry_labels_test.go
index 3ed589e5..36187419 100644
--- a/test/xds/xds_telemetry_labels_test.go
+++ b/test/xds/xds_telemetry_labels_test.go
@@ -44,7 +44,8 @@ const serviceNamespaceKey = "service_namespace"
 const serviceNamespaceKeyCSM = "csm.service_namespace_name"
 const serviceNameValue = "grpc-service"
 const serviceNamespaceValue = "grpc-service-namespace"
-
+const backendServiceKey = "grpc.lb.backend_service"
+const backendServiceValue = "cluster-my-service-client-side-xds"
 const localityKey = "grpc.lb.locality"
 const localityValue = `{region="region-1", zone="zone-1", sub_zone="subzone-1"}`
 
@@ -124,23 +125,23 @@ func (fsh *fakeStatsHandler) TagRPC(ctx context.Context, _ *stats.RPCTagInfo) co
 
 func (fsh *fakeStatsHandler) HandleRPC(_ context.Context, rs stats.RPCStats) {
 	switch rs.(type) {
-	// stats.Begin won't get Telemetry Labels because happens after picker
-	// picks.
-
-	// These three stats callouts trigger all metrics for OpenTelemetry that
-	// aren't started. All of these should have access to the desired telemetry
+	// stats.Begin is called before the picker runs, so it won't have telemetry
 	// labels.
+	// The following three stats callouts trigger OpenTelemetry metrics and are
+	// guaranteed to run after the picker has selected a subchannel. Therefore,
+	// they should have access to the desired telemetry labels.
 	case *stats.OutPayload, *stats.InPayload, *stats.End:
 		want := map[string]string{
 			serviceNameKeyCSM:      serviceNameValue,
 			serviceNamespaceKeyCSM: serviceNamespaceValue,
 			localityKey:            localityValue,
+			backendServiceKey:      backendServiceValue,
 		}
 		if diff := cmp.Diff(fsh.labels.TelemetryLabels, want); diff != "" {
 			fsh.t.Fatalf("fsh.labels.TelemetryLabels (-got +want): %v", diff)
 		}
 	default:
 		// Nothing to assert for the other stats.Handler callouts.
-	}
 
+	}
 }
diff --git a/testdata/spiffe/spiffebundle_wrong_kty.json b/testdata/spiffe/spiffebundle_wrong_kty.json
index cd69722d..a7863553 100644
--- a/testdata/spiffe/spiffebundle_wrong_kty.json
+++ b/testdata/spiffe/spiffebundle_wrong_kty.json
@@ -4,7 +4,7 @@
       "spiffe_sequence": 12035488,
       "keys": [
         {
-          "kty": "EC",
+          "kty": "DSA",
           "use": "x509-svid",
           "x5c": [
             "MIIFsjCCA5qgAwIBAgIURygVMMzdr+Q7rsUaz189JozyHMwwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQwwCgYDVQQHDANTVkwxDTALBgNVBAoMBGdSUEMxFTATBgNVBAMMDHRlc3QtY2xpZW50MTAeFw0yMTEyMjMxODQyNTJaFw0zMTEyMjExODQyNTJaME4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEMMAoGA1UEBwwDU1ZMMQ0wCwYDVQQKDARnUlBDMRUwEwYDVQQDDAx0ZXN0LWNsaWVudDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ4AqpGetyVSqGUuBJLVFla+7bEfca7UYzfVSSZLZ/X+JDmWIVN8UIPuFib5jhMEc3XaUnFXUmM7zEtz/ZG5hapwLwOb2C3ZxOP6PQjYCJxbkLie+b43UQrFu1xxd3vMhVJgcj/AIxEpmszuqOa6kUrkYifjJADQ+64kZgl66bsTdXMCzpxyFl9xUfff59L8OX+HUfAcoZz3emjg3ZJPYURQEmjdZTOau1EjFilwHgd989Jt7NKgx30NXoHmw7nusVBIY94fL2VKN3f1XVm0dHu5NI279Q6zr0ZBU7k5T3IeHnzsUesQS4NGlklDWoVTKk73Uv9Pna8yQsSW757PEbHOGp9Knu4bnoGPOlsG81yIPipO6hTgGFK24pF97M9kpGbWqYX4+2vLlrCAfcmsHqaUPmQlYeRVTT6vw7ctYo2kyUYGtnODXk76LqewRBVvkzx75QUhfjAyb740YcDmIenc56Tq6gebJHjhEmVSehR6xIpXP7SVeurTyhPsEQnpJHtgs4dcwWOZp7BvPNzHXmJqfr7vsshie3vS5kQ0u1e1yqAqXgyDjqKXOkx+dpgUTehSJHhPNHvTc5LXRsvvXKYz6FrwR/DZ8t7BNEvPeLjFgxpH7QVJFLCvCbXs5K6yYbsnLfxFIBPRnrbJkIsK+sQwnRdnsiUdPsTkG5B2lQfQIDAQABo4GHMIGEMB0GA1UdDgQWBBQ2lBp0PiRHHvQ5IRURm8aHsj4RETAfBgNVHSMEGDAWgBQ2lBp0PiRHHvQ5IRURm8aHsj4RETAPBgNVHRMBAf8EBTADAQH/MDEGA1UdEQQqMCiGJnNwaWZmZTovL2Zvby5iYXIuY29tL2NsaWVudC93b3JrbG9hZC8xMA0GCSqGSIb3DQEBCwUAA4ICAQA1mSkgRclAl+E/aS9zJ7t8+Y4n3T24nOKKveSIjxXm/zjhWqVsLYBI6kglWtih2+PELvU8JdPqNZK34Kl0Q6FWpVSGDdWN1i6NyORt2ocggL3ke3iXxRk3UpUKJmqwz81VhA2KUHnMlyE0IufFfZNwNWWHBv13uJfRbjeQpKPhU+yf4DeXrsWcvrZlGvAET+mcplafUzCp7Iv+PcISJtUerbxbVtuHVeZCLlgDXWkLAWJN8rf0dIG4x060LJ+j6j9uRVhb9sZn1HJV+j4XdIYm1VKilluhOtNwP2d3Ox/JuTBxf7hFHXZPfMagQE5k5PzmxRaCAEMJ1l2DvUbZw+shJfSNoWcBo2qadnUaWT3BmmJRBDh7ZReib/RQ1Rd4ygOyzP3E0vkV4/gqyjLdApXh5PZP8KLQZ+1JN/sdWt7VfIt9wYOpkIqujdll51ESHzwQeAK9WVCB4UvVz6zdhItB9CRbXPreWC+wCB1xDovIzFKOVsLs5+Gqs1m7VinG2LxbDqaKyo/FB0Hxx0acBNzezLWoDwXYQrN0T0S4pnqhKD1CYPpdArBkNezUYAjS725FkApuK+mnBX3U0msBffEaUEOkcyar1EW2m/33vpetD/k3eQQkmvQf4Hbiu9AF+9cNDm/hMuXEw5EXGA91fn0891b5eEW8BJHXX0jri0aN8g=="