@@ -28,6 +28,8 @@ import (
28
28
29
29
"google.golang.org/grpc/grpclog"
30
30
"google.golang.org/grpc/internal/proxyattributes"
31
+ "google.golang.org/grpc/internal/transport"
32
+ "google.golang.org/grpc/internal/transport/networktype"
31
33
"google.golang.org/grpc/resolver"
32
34
"google.golang.org/grpc/serviceconfig"
33
35
)
@@ -40,14 +42,17 @@ var (
40
42
41
43
// delegatingResolver manages both target URI and proxy address resolution by
42
44
// 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.
44
46
//
45
47
// It implements the [resolver.Resolver] interface.
46
48
type delegatingResolver struct {
47
49
target resolver.Target // parsed target URI to be resolved
48
50
cc resolver.ClientConn // gRPC ClientConn
49
51
proxyURL * url.URL // proxy URL, derived from proxy environment and target
50
52
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.
51
56
mu sync.Mutex // protects all the fields below
52
57
targetResolverState * resolver.State // state of the target resolver
53
58
proxyAddrs []resolver.Address // resolved proxy addresses; empty if no proxy is configured
@@ -66,8 +71,8 @@ func (nopResolver) ResolveNow(resolver.ResolveNowOptions) {}
66
71
67
72
func (nopResolver ) Close () {}
68
73
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:
71
76
// - nil URL, nil error: No proxy is configured or the address is excluded
72
77
// using the `NO_PROXY` environment variable or if req.URL.Host is
73
78
// "localhost" (with or without // a port number)
@@ -86,7 +91,8 @@ func proxyURLForTarget(address string) (*url.URL, error) {
86
91
// resolvers:
87
92
// - one to resolve the proxy address specified using the supported
88
93
// 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.
90
96
// - one to resolve the target URI using the resolver specified by the scheme
91
97
// in the target URI or specified by the user using the WithResolvers dial
92
98
// 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) {
95
101
// resolution is enabled using the dial option.
96
102
func New (target resolver.Target , cc resolver.ClientConn , opts resolver.BuildOptions , targetResolverBuilder resolver.Builder , targetResolutionEnabled bool ) (resolver.Resolver , error ) {
97
103
r := & delegatingResolver {
98
- target : target ,
99
- cc : cc ,
104
+ target : target ,
105
+ cc : cc ,
106
+ proxyResolver : nopResolver {},
107
+ targetResolver : nopResolver {},
100
108
}
101
109
102
110
var err error
@@ -123,37 +131,26 @@ func New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOpti
123
131
// resolution should be handled by the proxy, not the client. Therefore, we
124
132
// bypass the target resolver and store the unresolved target address.
125
133
if target .URL .Scheme == "dns" && ! targetResolutionEnabled {
126
- state := resolver.State {
134
+ r . targetResolverState = & resolver.State {
127
135
Addresses : []resolver.Address {{Addr : target .Endpoint ()}},
128
136
Endpoints : []resolver.Endpoint {{Addresses : []resolver.Address {{Addr : target .Endpoint ()}}}},
129
137
}
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
143
140
}
144
-
145
- if r .targetResolver == nil {
146
- r . targetResolver = nopResolver {}
141
+ wcc := & wrappingClientConn {
142
+ stateListener : r .updateTargetResolverState ,
143
+ parent : r ,
147
144
}
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 )
150
147
}
151
148
return r , nil
152
149
}
153
150
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.
157
154
func (r * delegatingResolver ) proxyURIResolver (opts resolver.BuildOptions ) (resolver.Resolver , error ) {
158
155
proxyBuilder := resolver .Get ("dns" )
159
156
if proxyBuilder == nil {
@@ -189,18 +186,43 @@ func (r *delegatingResolver) Close() {
189
186
r .proxyResolver = nil
190
187
}
191
188
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.
198
221
func (r * delegatingResolver ) updateClientConnStateLocked () error {
199
222
if r .targetResolverState == nil || r .proxyAddrs == nil {
200
223
return nil
201
224
}
202
225
203
- curState := * r .targetResolverState
204
226
// If multiple resolved proxy addresses are present, we send only the
205
227
// unresolved proxy host and let net.Dial handle the proxy host name
206
228
// resolution when creating the transport. Sending all resolved addresses
@@ -218,24 +240,30 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
218
240
}
219
241
var addresses []resolver.Address
220
242
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
+ }
221
248
addresses = append (addresses , proxyattributes .Set (proxyAddr , proxyattributes.Options {
222
249
User : r .proxyURL .User ,
223
250
ConnectAddr : targetAddr .Addr ,
224
251
}))
225
252
}
226
253
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.
234
257
var endpoints []resolver.Endpoint
235
258
for _ , endpt := range (* r .targetResolverState ).Endpoints {
236
259
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 {
239
267
addrs = append (addrs , proxyattributes .Set (proxyAddr , proxyattributes.Options {
240
268
User : r .proxyURL .User ,
241
269
ConnectAddr : targetAddr .Addr ,
@@ -246,8 +274,9 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
246
274
}
247
275
// Use the targetResolverState for its service config and attributes
248
276
// 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
251
280
curState .Addresses = addresses
252
281
curState .Endpoints = endpoints
253
282
return r .cc .UpdateState (curState )
@@ -257,16 +286,17 @@ func (r *delegatingResolver) updateClientConnStateLocked() error {
257
286
// addresses and endpoints, marking the resolver as ready, and triggering a
258
287
// state update if both proxy and target resolvers are ready. If the ClientConn
259
288
// 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.
261
291
func (r * delegatingResolver ) updateProxyResolverState (state resolver.State ) error {
262
292
r .mu .Lock ()
263
293
defer r .mu .Unlock ()
264
294
if logger .V (2 ) {
265
295
logger .Infof ("Addresses received from proxy resolver: %s" , state .Addresses )
266
296
}
267
297
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.
270
300
r .proxyAddrs = make ([]resolver.Address , 0 , len (state .Endpoints ))
271
301
for _ , endpoint := range state .Endpoints {
272
302
r .proxyAddrs = append (r .proxyAddrs , endpoint .Addresses ... )
@@ -294,11 +324,14 @@ func (r *delegatingResolver) updateProxyResolverState(state resolver.State) erro
294
324
return err
295
325
}
296
326
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.
302
335
func (r * delegatingResolver ) updateTargetResolverState (state resolver.State ) error {
303
336
r .mu .Lock ()
304
337
defer r .mu .Unlock ()
@@ -307,6 +340,31 @@ func (r *delegatingResolver) updateTargetResolverState(state resolver.State) err
307
340
logger .Infof ("Addresses received from target resolver: %v" , state .Addresses )
308
341
}
309
342
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
+
310
368
err := r .updateClientConnStateLocked ()
311
369
if err != nil {
312
370
go func () {
@@ -335,7 +393,8 @@ func (wcc *wrappingClientConn) UpdateState(state resolver.State) error {
335
393
return wcc .stateListener (state )
336
394
}
337
395
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.
339
398
func (wcc * wrappingClientConn ) ReportError (err error ) {
340
399
wcc .parent .cc .ReportError (err )
341
400
}
@@ -346,8 +405,8 @@ func (wcc *wrappingClientConn) NewAddress(addrs []resolver.Address) {
346
405
wcc .UpdateState (resolver.State {Addresses : addrs })
347
406
}
348
407
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.
351
410
func (wcc * wrappingClientConn ) ParseServiceConfig (serviceConfigJSON string ) * serviceconfig.ParseResult {
352
411
return wcc .parent .cc .ParseServiceConfig (serviceConfigJSON )
353
412
}
0 commit comments