@@ -28,6 +28,8 @@ import (
2828
2929 "google.golang.org/grpc/grpclog"
3030 "google.golang.org/grpc/internal/proxyattributes"
31+ "google.golang.org/grpc/internal/transport"
32+ "google.golang.org/grpc/internal/transport/networktype"
3133 "google.golang.org/grpc/resolver"
3234 "google.golang.org/grpc/serviceconfig"
3335)
@@ -40,14 +42,17 @@ var (
4042
4143// delegatingResolver manages both target URI and proxy address resolution by
4244// delegating these tasks to separate child resolvers. Essentially, it acts as
43- // a intermediary between the gRPC ClientConn and the child resolvers.
45+ // an intermediary between the gRPC ClientConn and the child resolvers.
4446//
4547// It implements the [resolver.Resolver] interface.
4648type delegatingResolver struct {
4749 target resolver.Target // parsed target URI to be resolved
4850 cc resolver.ClientConn // gRPC ClientConn
4951 proxyURL * url.URL // proxy URL, derived from proxy environment and target
5052
53+ // We do not hold both mu and childMu in the same goroutine. Avoid holding
54+ // both locks when calling into the child, as the child resolver may
55+ // synchronously callback into the channel.
5156 mu sync.Mutex // protects all the fields below
5257 targetResolverState * resolver.State // state of the target resolver
5358 proxyAddrs []resolver.Address // resolved proxy addresses; empty if no proxy is configured
@@ -66,8 +71,8 @@ func (nopResolver) ResolveNow(resolver.ResolveNowOptions) {}
6671
6772func (nopResolver ) Close () {}
6873
69- // proxyURLForTarget determines the proxy URL for the given address based on
70- // the environment. It can return the following:
74+ // proxyURLForTarget determines the proxy URL for the given address based on the
75+ // environment. It can return the following:
7176// - nil URL, nil error: No proxy is configured or the address is excluded
7277// using the `NO_PROXY` environment variable or if req.URL.Host is
7378// "localhost" (with or without // a port number)
@@ -86,7 +91,8 @@ func proxyURLForTarget(address string) (*url.URL, error) {
8691// resolvers:
8792// - one to resolve the proxy address specified using the supported
8893// environment variables. This uses the registered resolver for the "dns"
89- // scheme.
94+ // scheme. It is lazily built when a target resolver update contains at least
95+ // one TCP address.
9096// - one to resolve the target URI using the resolver specified by the scheme
9197// in the target URI or specified by the user using the WithResolvers dial
9298// option. As a special case, if the target URI's scheme is "dns" and a
@@ -95,8 +101,10 @@ func proxyURLForTarget(address string) (*url.URL, error) {
95101// resolution is enabled using the dial option.
96102func New (target resolver.Target , cc resolver.ClientConn , opts resolver.BuildOptions , targetResolverBuilder resolver.Builder , targetResolutionEnabled bool ) (resolver.Resolver , error ) {
97103 r := & delegatingResolver {
98- target : target ,
99- cc : cc ,
104+ target : target ,
105+ cc : cc ,
106+ proxyResolver : nopResolver {},
107+ targetResolver : nopResolver {},
100108 }
101109
102110 var err error
@@ -123,37 +131,26 @@ func New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOpti
123131 // resolution should be handled by the proxy, not the client. Therefore, we
124132 // bypass the target resolver and store the unresolved target address.
125133 if target .URL .Scheme == "dns" && ! targetResolutionEnabled {
126- state := resolver.State {
134+ r . targetResolverState = & resolver.State {
127135 Addresses : []resolver.Address {{Addr : target .Endpoint ()}},
128136 Endpoints : []resolver.Endpoint {{Addresses : []resolver.Address {{Addr : target .Endpoint ()}}}},
129137 }
130- r .targetResolverState = & state
131- } else {
132- wcc := & wrappingClientConn {
133- stateListener : r .updateTargetResolverState ,
134- parent : r ,
135- }
136- if r .targetResolver , err = targetResolverBuilder .Build (target , wcc , opts ); err != nil {
137- return nil , fmt .Errorf ("delegating_resolver: unable to build the resolver for target %s: %v" , target , err )
138- }
139- }
140-
141- if r .proxyResolver , err = r .proxyURIResolver (opts ); err != nil {
142- return nil , fmt .Errorf ("delegating_resolver: failed to build resolver for proxy URL %q: %v" , r .proxyURL , err )
138+ r .updateTargetResolverState (* r .targetResolverState )
139+ return r , nil
143140 }
144-
145- if r .targetResolver == nil {
146- r . targetResolver = nopResolver {}
141+ wcc := & wrappingClientConn {
142+ stateListener : r .updateTargetResolverState ,
143+ parent : r ,
147144 }
148- if r .proxyResolver = = nil {
149- r . proxyResolver = nopResolver {}
145+ if r .targetResolver , err = targetResolverBuilder . Build ( target , wcc , opts ); err ! = nil {
146+ return nil , fmt . Errorf ( "delegating_resolver: unable to build the resolver for target %s: %v" , target , err )
150147 }
151148 return r , nil
152149}
153150
154- // proxyURIResolver creates a resolver for resolving proxy URIs using the
155- // "dns" scheme. It adjusts the proxyURL to conform to the "dns:///" format and
156- // builds a resolver with a wrappingClientConn to capture resolved addresses.
151+ // proxyURIResolver creates a resolver for resolving proxy URIs using the "dns"
152+ // scheme. It adjusts the proxyURL to conform to the "dns:///" format and builds
153+ // a resolver with a wrappingClientConn to capture resolved addresses.
157154func (r * delegatingResolver ) proxyURIResolver (opts resolver.BuildOptions ) (resolver.Resolver , error ) {
158155 proxyBuilder := resolver .Get ("dns" )
159156 if proxyBuilder == nil {
@@ -189,18 +186,43 @@ func (r *delegatingResolver) Close() {
189186 r .proxyResolver = nil
190187}
191188
192- // updateClientConnStateLocked creates a list of combined addresses by
193- // pairing each proxy address with every target address. For each pair, it
194- // generates a new [resolver.Address] using the proxy address, and adding the
195- // target address as the attribute along with user info. It returns nil if
196- // either resolver has not sent update even once and returns the error from
197- // ClientConn update once both resolvers have sent update atleast once.
189+ func networkTypeFromAddr (addr resolver.Address ) string {
190+ networkType , ok := networktype .Get (addr )
191+ if ! ok {
192+ networkType , _ = transport .ParseDialTarget (addr .Addr )
193+ }
194+ return networkType
195+ }
196+
197+ func isTCPAddressPresent (state * resolver.State ) bool {
198+ for _ , addr := range state .Addresses {
199+ if networkType := networkTypeFromAddr (addr ); networkType == "tcp" {
200+ return true
201+ }
202+ }
203+ for _ , endpoint := range state .Endpoints {
204+ for _ , addr := range endpoint .Addresses {
205+ if networktype := networkTypeFromAddr (addr ); networktype == "tcp" {
206+ return true
207+ }
208+ }
209+ }
210+ return false
211+ }
212+
213+ // updateClientConnStateLocked constructs a combined list of addresses by
214+ // pairing each proxy address with every target address of type TCP. For each
215+ // pair, it creates a new [resolver.Address] using the proxy address and
216+ // attaches the corresponding target address and user info as attributes. Target
217+ // addresses that are not of type TCP are appended to the list as-is. The
218+ // function returns nil if either resolver has not yet provided an update, and
219+ // returns the result of ClientConn.UpdateState once both resolvers have
220+ // provided at least one update.
198221func (r * delegatingResolver ) updateClientConnStateLocked () error {
199222 if r .targetResolverState == nil || r .proxyAddrs == nil {
200223 return nil
201224 }
202225
203- curState := * r .targetResolverState
204226 // If multiple resolved proxy addresses are present, we send only the
205227 // unresolved proxy host and let net.Dial handle the proxy host name
206228 // resolution when creating the transport. Sending all resolved addresses
@@ -218,24 +240,30 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
218240 }
219241 var addresses []resolver.Address
220242 for _ , targetAddr := range (* r .targetResolverState ).Addresses {
243+ // Avoid proxy when network is not tcp.
244+ if networkType := networkTypeFromAddr (targetAddr ); networkType != "tcp" {
245+ addresses = append (addresses , targetAddr )
246+ continue
247+ }
221248 addresses = append (addresses , proxyattributes .Set (proxyAddr , proxyattributes.Options {
222249 User : r .proxyURL .User ,
223250 ConnectAddr : targetAddr .Addr ,
224251 }))
225252 }
226253
227- // Create a list of combined endpoints by pairing all proxy endpoints
228- // with every target endpoint. Each time, it constructs a new
229- // [resolver.Endpoint] using the all addresses from all the proxy endpoint
230- // and the target addresses from one endpoint. The target address and user
231- // information from the proxy URL are added as attributes to the proxy
232- // address.The resulting list of addresses is then grouped into endpoints,
233- // covering all combinations of proxy and target endpoints.
254+ // For each target endpoint, construct a new [resolver.Endpoint] that
255+ // includes all addresses from all proxy endpoints and the addresses from
256+ // that target endpoint, preserving the number of target endpoints.
234257 var endpoints []resolver.Endpoint
235258 for _ , endpt := range (* r .targetResolverState ).Endpoints {
236259 var addrs []resolver.Address
237- for _ , proxyAddr := range r .proxyAddrs {
238- for _ , targetAddr := range endpt .Addresses {
260+ for _ , targetAddr := range endpt .Addresses {
261+ // Avoid proxy when network is not tcp.
262+ if networkType := networkTypeFromAddr (targetAddr ); networkType != "tcp" {
263+ addrs = append (addrs , targetAddr )
264+ continue
265+ }
266+ for _ , proxyAddr := range r .proxyAddrs {
239267 addrs = append (addrs , proxyattributes .Set (proxyAddr , proxyattributes.Options {
240268 User : r .proxyURL .User ,
241269 ConnectAddr : targetAddr .Addr ,
@@ -246,8 +274,9 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
246274 }
247275 // Use the targetResolverState for its service config and attributes
248276 // contents. The state update is only sent after both the target and proxy
249- // resolvers have sent their updates, and curState has been updated with
250- // the combined addresses.
277+ // resolvers have sent their updates, and curState has been updated with the
278+ // combined addresses.
279+ curState := * r .targetResolverState
251280 curState .Addresses = addresses
252281 curState .Endpoints = endpoints
253282 return r .cc .UpdateState (curState )
@@ -257,16 +286,17 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
257286// addresses and endpoints, marking the resolver as ready, and triggering a
258287// state update if both proxy and target resolvers are ready. If the ClientConn
259288// returns a non-nil error, it calls `ResolveNow()` on the target resolver. It
260- // is a StateListener function of wrappingClientConn passed to the proxy resolver.
289+ // is a StateListener function of wrappingClientConn passed to the proxy
290+ // resolver.
261291func (r * delegatingResolver ) updateProxyResolverState (state resolver.State ) error {
262292 r .mu .Lock ()
263293 defer r .mu .Unlock ()
264294 if logger .V (2 ) {
265295 logger .Infof ("Addresses received from proxy resolver: %s" , state .Addresses )
266296 }
267297 if len (state .Endpoints ) > 0 {
268- // We expect exactly one address per endpoint because the proxy
269- // resolver uses "dns" resolution.
298+ // We expect exactly one address per endpoint because the proxy resolver
299+ // uses "dns" resolution.
270300 r .proxyAddrs = make ([]resolver.Address , 0 , len (state .Endpoints ))
271301 for _ , endpoint := range state .Endpoints {
272302 r .proxyAddrs = append (r .proxyAddrs , endpoint .Addresses ... )
@@ -294,11 +324,14 @@ func (r *delegatingResolver) updateProxyResolverState(state resolver.State) erro
294324 return err
295325}
296326
297- // updateTargetResolverState updates the target resolver state by storing target
298- // addresses, endpoints, and service config, marking the resolver as ready, and
299- // triggering a state update if both resolvers are ready. If the ClientConn
300- // returns a non-nil error, it calls `ResolveNow()` on the proxy resolver. It
301- // is a StateListener function of wrappingClientConn passed to the target resolver.
327+ // updateTargetResolverState is the StateListener function provided to the
328+ // target resolver via wrappingClientConn. It updates the resolver state and
329+ // marks the target resolver as ready. If the update includes at least one TCP
330+ // address and the proxy resolver has not yet been constructed, it initializes
331+ // the proxy resolver. A combined state update is triggered once both resolvers
332+ // are ready. If all addresses are non-TCP, it proceeds without waiting for the
333+ // proxy resolver. If ClientConn.UpdateState returns a non-nil error,
334+ // ResolveNow() is called on the proxy resolver.
302335func (r * delegatingResolver ) updateTargetResolverState (state resolver.State ) error {
303336 r .mu .Lock ()
304337 defer r .mu .Unlock ()
@@ -307,6 +340,31 @@ func (r *delegatingResolver) updateTargetResolverState(state resolver.State) err
307340 logger .Infof ("Addresses received from target resolver: %v" , state .Addresses )
308341 }
309342 r .targetResolverState = & state
343+ // If no addresses returned by resolver have network type as tcp , do not
344+ // wait for proxy update.
345+ if ! isTCPAddressPresent (r .targetResolverState ) {
346+ return r .cc .UpdateState (* r .targetResolverState )
347+ }
348+
349+ // The proxy resolver may be rebuilt multiple times, specifically each time
350+ // the target resolver sends an update, even if the target resolver is built
351+ // successfully but building the proxy resolver fails.
352+ if len (r .proxyAddrs ) == 0 {
353+ go func () {
354+ r .childMu .Lock ()
355+ defer r .childMu .Unlock ()
356+ if _ , ok := r .proxyResolver .(nopResolver ); ! ok {
357+ return
358+ }
359+ proxyResolver , err := r .proxyURIResolver (resolver.BuildOptions {})
360+ if err != nil {
361+ r .cc .ReportError (fmt .Errorf ("delegating_resolver: unable to build the proxy resolver: %v" , err ))
362+ return
363+ }
364+ r .proxyResolver = proxyResolver
365+ }()
366+ }
367+
310368 err := r .updateClientConnStateLocked ()
311369 if err != nil {
312370 go func () {
@@ -335,7 +393,8 @@ func (wcc *wrappingClientConn) UpdateState(state resolver.State) error {
335393 return wcc .stateListener (state )
336394}
337395
338- // ReportError intercepts errors from the child resolvers and passes them to ClientConn.
396+ // ReportError intercepts errors from the child resolvers and passes them to
397+ // ClientConn.
339398func (wcc * wrappingClientConn ) ReportError (err error ) {
340399 wcc .parent .cc .ReportError (err )
341400}
@@ -346,8 +405,8 @@ func (wcc *wrappingClientConn) NewAddress(addrs []resolver.Address) {
346405 wcc .UpdateState (resolver.State {Addresses : addrs })
347406}
348407
349- // ParseServiceConfig parses the provided service config and returns an
350- // object that provides the parsed config.
408+ // ParseServiceConfig parses the provided service config and returns an object
409+ // that provides the parsed config.
351410func (wcc * wrappingClientConn ) ParseServiceConfig (serviceConfigJSON string ) * serviceconfig.ParseResult {
352411 return wcc .parent .cc .ParseServiceConfig (serviceConfigJSON )
353412}
0 commit comments