Repository: grpc/grpc-go
File: internal/resolver/delegatingresolver/delegatingresolver.go (Go)

Code before:
	"sync"

	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/proxyattributes"
	"google.golang.org/grpc/internal/transport"
	"google.golang.org/grpc/internal/transport/networktype"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/serviceconfig"
)

var (
	logger = grpclog.Component("delegating-resolver")
	// HTTPSProxyFromEnvironment will be overwritten in the tests
	HTTPSProxyFromEnvironment = http.ProxyFromEnvironment
)

// delegatingResolver manages both target URI and proxy address resolution by
// delegating these tasks to separate child resolvers. Essentially, it acts as
// a intermediary between the gRPC ClientConn and the child resolvers.
//
// It implements the [resolver.Resolver] interface.
type delegatingResolver struct {
	target          resolver.Target     // parsed target URI to be resolved
	cc              resolver.ClientConn // gRPC ClientConn
	proxyURL        *url.URL            // proxy URL, derived from proxy environment and target
	proxyResolverCh chan struct{}       // closed once the proxy resolver has been created

	mu                  sync.Mutex         // protects all the fields below
	targetResolverState *resolver.State    // state of the target resolver
	proxyAddrs          []resolver.Address // resolved proxy addresses; empty if no proxy is configured

	// childMu serializes calls into child resolvers. It also protects access to
	// the following fields.
	childMu        sync.Mutex
	targetResolver resolver.Resolver // resolver for the target URI, based on its scheme
	proxyResolver  resolver.Resolver // resolver for the proxy URI; nil if no proxy is configured
}

// nopResolver is a resolver that does nothing.
type nopResolver struct{}

func (nopResolver) ResolveNow(resolver.ResolveNowOptions) {}

func (nopResolver) Close() {}

// proxyURLForTarget determines the proxy URL for the given address based on
// the environment. It can return the following:
//   - nil URL, nil error: No proxy is configured or the address is excluded
//     using the `NO_PROXY` environment variable or if req.URL.Host is
//     "localhost" (with or without // a port number)
//   - nil URL, non-nil error: An error occurred while retrieving the proxy URL.

Review comment (suggestion):
I don't think we need a channel here, instead we can have a bool named something like `proxyResolverInitialized` and have it protected by `childMu`. We're acquiring `childMu` to access `proxyResolver` anyways, we can rely on the mutex for synchronization.

Code after:
	"sync"

	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/internal/proxyattributes"
	"google.golang.org/grpc/internal/transport"
	"google.golang.org/grpc/internal/transport/networktype"
	"google.golang.org/grpc/resolver"
	"google.golang.org/grpc/serviceconfig"
)

var (
	logger = grpclog.Component("delegating-resolver")
	// HTTPSProxyFromEnvironment will be overwritten in the tests
	HTTPSProxyFromEnvironment = http.ProxyFromEnvironment
)

// 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.
//
// It implements the [resolver.Resolver] interface.
type delegatingResolver struct {
	target   resolver.Target     // parsed target URI to be resolved
	cc       resolver.ClientConn // gRPC ClientConn
	proxyURL *url.URL            // proxy URL, derived from proxy environment and target

	// We do not hold both mu and childMu in the same goroutine. Avoid holding
	// both locks when calling into the child, as the child resolver may
	// synchronously callback into the channel.
	mu                  sync.Mutex         // protects all the fields below
	targetResolverState *resolver.State    // state of the target resolver
	proxyAddrs          []resolver.Address // resolved proxy addresses; empty if no proxy is configured

	// childMu serializes calls into child resolvers. It also protects access to
	// the following fields.
	childMu        sync.Mutex
	targetResolver resolver.Resolver // resolver for the target URI, based on its scheme
	proxyResolver  resolver.Resolver // resolver for the proxy URI; nil if no proxy is configured
}

// nopResolver is a resolver that does nothing.
type nopResolver struct{}

func (nopResolver) ResolveNow(resolver.ResolveNowOptions) {}

func (nopResolver) Close() {}

// proxyURLForTarget determines the proxy URL for the given address based on the
// environment. It can return the following:
//   - nil URL, nil error: No proxy is configured or the address is excluded
//     using the `NO_PROXY` environment variable or if req.URL.Host is