@@ -23,6 +23,7 @@ import (
23
23
"errors"
24
24
"fmt"
25
25
"math"
26
+ "net/url"
26
27
"reflect"
27
28
"strings"
28
29
"sync"
@@ -37,7 +38,6 @@ import (
37
38
"google.golang.org/grpc/internal/backoff"
38
39
"google.golang.org/grpc/internal/channelz"
39
40
"google.golang.org/grpc/internal/grpcsync"
40
- "google.golang.org/grpc/internal/grpcutil"
41
41
iresolver "google.golang.org/grpc/internal/resolver"
42
42
"google.golang.org/grpc/internal/transport"
43
43
"google.golang.org/grpc/keepalive"
@@ -248,38 +248,15 @@ func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *
248
248
}
249
249
250
250
// Determine the resolver to use.
251
- cc .parsedTarget = grpcutil .ParseTarget (cc .target , cc .dopts .copts .Dialer != nil )
252
- channelz .Infof (logger , cc .channelzID , "parsed scheme: %q" , cc .parsedTarget .Scheme )
253
- resolverBuilder := cc .getResolver (cc .parsedTarget .Scheme )
254
- if resolverBuilder == nil {
255
- // If resolver builder is still nil, the parsed target's scheme is
256
- // not registered. Fallback to default resolver and set Endpoint to
257
- // the original target.
258
- channelz .Infof (logger , cc .channelzID , "scheme %q not registered, fallback to default scheme" , cc .parsedTarget .Scheme )
259
- cc .parsedTarget = resolver.Target {
260
- Scheme : resolver .GetDefaultScheme (),
261
- Endpoint : target ,
262
- }
263
- resolverBuilder = cc .getResolver (cc .parsedTarget .Scheme )
264
- if resolverBuilder == nil {
265
- return nil , fmt .Errorf ("could not get resolver for default scheme: %q" , cc .parsedTarget .Scheme )
266
- }
251
+ resolverBuilder , err := cc .parseTargetAndFindResolver ()
252
+ if err != nil {
253
+ return nil , err
267
254
}
268
-
269
- creds := cc .dopts .copts .TransportCredentials
270
- if creds != nil && creds .Info ().ServerName != "" {
271
- cc .authority = creds .Info ().ServerName
272
- } else if cc .dopts .insecure && cc .dopts .authority != "" {
273
- cc .authority = cc .dopts .authority
274
- } else if strings .HasPrefix (cc .target , "unix:" ) || strings .HasPrefix (cc .target , "unix-abstract:" ) {
275
- cc .authority = "localhost"
276
- } else if strings .HasPrefix (cc .parsedTarget .Endpoint , ":" ) {
277
- cc .authority = "localhost" + cc .parsedTarget .Endpoint
278
- } else {
279
- // Use endpoint from "scheme://authority/endpoint" as the default
280
- // authority for ClientConn.
281
- cc .authority = cc .parsedTarget .Endpoint
255
+ cc .authority , err = determineAuthority (cc .parsedTarget .Endpoint , cc .target , cc .dopts )
256
+ if err != nil {
257
+ return nil , err
282
258
}
259
+ channelz .Infof (logger , cc .channelzID , "Channel authority set to %q" , cc .authority )
283
260
284
261
if cc .dopts .scChan != nil && ! scSet {
285
262
// Blocking wait for the initial service config.
@@ -902,10 +879,7 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
902
879
// ac.state is Ready, try to find the connected address.
903
880
var curAddrFound bool
904
881
for _ , a := range addrs {
905
- // a.ServerName takes precedent over ClientConn authority, if present.
906
- if a .ServerName == "" {
907
- a .ServerName = ac .cc .authority
908
- }
882
+ a .ServerName = ac .cc .getServerName (a )
909
883
if reflect .DeepEqual (ac .curAddr , a ) {
910
884
curAddrFound = true
911
885
break
@@ -919,6 +893,26 @@ func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool {
919
893
return curAddrFound
920
894
}
921
895
896
+ // getServerName determines the serverName to be used in the connection
897
+ // handshake. The default value for the serverName is the authority on the
898
+ // ClientConn, which either comes from the user's dial target or through an
899
+ // authority override specified using the WithAuthority dial option. Name
900
+ // resolvers can specify a per-address override for the serverName through the
901
+ // resolver.Address.ServerName field which is used only if the WithAuthority
902
+ // dial option was not used. The rationale is that per-address authority
903
+ // overrides specified by the name resolver can represent a security risk, while
904
+ // an override specified by the user is more dependable since they probably know
905
+ // what they are doing.
906
+ func (cc * ClientConn ) getServerName (addr resolver.Address ) string {
907
+ if cc .dopts .authority != "" {
908
+ return cc .dopts .authority
909
+ }
910
+ if addr .ServerName != "" {
911
+ return addr .ServerName
912
+ }
913
+ return cc .authority
914
+ }
915
+
922
916
func getMethodConfig (sc * ServiceConfig , method string ) MethodConfig {
923
917
if sc == nil {
924
918
return MethodConfig {}
@@ -1275,11 +1269,7 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
1275
1269
prefaceReceived := grpcsync .NewEvent ()
1276
1270
connClosed := grpcsync .NewEvent ()
1277
1271
1278
- // addr.ServerName takes precedent over ClientConn authority, if present.
1279
- if addr .ServerName == "" {
1280
- addr .ServerName = ac .cc .authority
1281
- }
1282
-
1272
+ addr .ServerName = ac .cc .getServerName (addr )
1283
1273
hctx , hcancel := context .WithCancel (ac .ctx )
1284
1274
hcStarted := false // protected by ac.mu
1285
1275
@@ -1621,3 +1611,117 @@ func (cc *ClientConn) connectionError() error {
1621
1611
defer cc .lceMu .Unlock ()
1622
1612
return cc .lastConnectionError
1623
1613
}
1614
+
1615
+ func (cc * ClientConn ) parseTargetAndFindResolver () (resolver.Builder , error ) {
1616
+ channelz .Infof (logger , cc .channelzID , "original dial target is: %q" , cc .target )
1617
+
1618
+ var rb resolver.Builder
1619
+ parsedTarget , err := parseTarget (cc .target )
1620
+ if err != nil {
1621
+ channelz .Infof (logger , cc .channelzID , "dial target %q parse failed: %v" , cc .target , err )
1622
+ } else {
1623
+ channelz .Infof (logger , cc .channelzID , "parsed dial target is: %+v" , parsedTarget )
1624
+ rb = cc .getResolver (parsedTarget .Scheme )
1625
+ if rb != nil {
1626
+ cc .parsedTarget = parsedTarget
1627
+ return rb , nil
1628
+ }
1629
+ }
1630
+
1631
+ // We are here because the user's dial target did not contain a scheme or
1632
+ // specified an unregistered scheme. We should fallback to the default
1633
+ // scheme, except when a custom dialer is specified in which case, we should
1634
+ // always use passthrough scheme.
1635
+ defScheme := resolver .GetDefaultScheme ()
1636
+ if cc .dopts .copts .Dialer != nil {
1637
+ defScheme = "passthrough"
1638
+ }
1639
+ channelz .Infof (logger , cc .channelzID , "fallback to scheme %q" , defScheme )
1640
+ canonicalTarget := defScheme + ":///" + cc .target
1641
+
1642
+ parsedTarget , err = parseTarget (canonicalTarget )
1643
+ if err != nil {
1644
+ channelz .Infof (logger , cc .channelzID , "dial target %q parse failed: %v" , canonicalTarget , err )
1645
+ return nil , err
1646
+ }
1647
+ channelz .Infof (logger , cc .channelzID , "parsed dial target is: %+v" , parsedTarget )
1648
+ rb = cc .getResolver (parsedTarget .Scheme )
1649
+ if rb == nil {
1650
+ return nil , fmt .Errorf ("could not get resolver for default scheme: %q" , parsedTarget .Scheme )
1651
+ }
1652
+ cc .parsedTarget = parsedTarget
1653
+ return rb , nil
1654
+ }
1655
+
1656
+ // parseTarget uses RFC 3986 semantics to parse the given target into a
1657
+ // resolver.Target struct containing scheme, authority and endpoint. Query
1658
+ // params are stripped from the endpoint.
1659
+ func parseTarget (target string ) (resolver.Target , error ) {
1660
+ u , err := url .Parse (target )
1661
+ if err != nil {
1662
+ return resolver.Target {}, err
1663
+ }
1664
+ // For targets of the form "[scheme]://[authority]/endpoint, the endpoint
1665
+ // value returned from url.Parse() contains a leading "/". Although this is
1666
+ // in accordance with RFC 3986, we do not want to break existing resolver
1667
+ // implementations which expect the endpoint without the leading "/". So, we
1668
+ // end up stripping the leading "/" here. But this will result in an
1669
+ // incorrect parsing for something like "unix:///path/to/socket". Since we
1670
+ // own the "unix" resolver, we can workaround in the unix resolver by using
1671
+ // the `URL` field instead of the `Endpoint` field.
1672
+ endpoint := u .Path
1673
+ if endpoint == "" {
1674
+ endpoint = u .Opaque
1675
+ }
1676
+ endpoint = strings .TrimPrefix (endpoint , "/" )
1677
+ return resolver.Target {
1678
+ Scheme : u .Scheme ,
1679
+ Authority : u .Host ,
1680
+ Endpoint : endpoint ,
1681
+ URL : * u ,
1682
+ }, nil
1683
+ }
1684
+
1685
+ // Determine channel authority. The order of precedence is as follows:
1686
+ // - user specified authority override using `WithAuthority` dial option
1687
+ // - creds' notion of server name for the authentication handshake
1688
+ // - endpoint from dial target of the form "scheme://[authority]/endpoint"
1689
+ func determineAuthority (endpoint , target string , dopts dialOptions ) (string , error ) {
1690
+ // Historically, we had two options for users to specify the serverName or
1691
+ // authority for a channel. One was through the transport credentials
1692
+ // (either in its constructor, or through the OverrideServerName() method).
1693
+ // The other option (for cases where WithInsecure() dial option was used)
1694
+ // was to use the WithAuthority() dial option.
1695
+ //
1696
+ // A few things have changed since:
1697
+ // - `insecure` package with an implementation of the `TransportCredentials`
1698
+ // interface for the insecure case
1699
+ // - WithAuthority() dial option support for secure credentials
1700
+ authorityFromCreds := ""
1701
+ if creds := dopts .copts .TransportCredentials ; creds != nil && creds .Info ().ServerName != "" {
1702
+ authorityFromCreds = creds .Info ().ServerName
1703
+ }
1704
+ authorityFromDialOption := dopts .authority
1705
+ if (authorityFromCreds != "" && authorityFromDialOption != "" ) && authorityFromCreds != authorityFromDialOption {
1706
+ return "" , fmt .Errorf ("ClientConn's authority from transport creds %q and dial option %q don't match" , authorityFromCreds , authorityFromDialOption )
1707
+ }
1708
+
1709
+ switch {
1710
+ case authorityFromDialOption != "" :
1711
+ return authorityFromDialOption , nil
1712
+ case authorityFromCreds != "" :
1713
+ return authorityFromCreds , nil
1714
+ case strings .HasPrefix (target , "unix:" ) || strings .HasPrefix (target , "unix-abstract:" ):
1715
+ // TODO: remove when the unix resolver implements optional interface to
1716
+ // return channel authority.
1717
+ return "localhost" , nil
1718
+ case strings .HasPrefix (endpoint , ":" ):
1719
+ return "localhost" + endpoint , nil
1720
+ default :
1721
+ // TODO: Define an optional interface on the resolver builder to return
1722
+ // the channel authority given the user's dial target. For resolvers
1723
+ // which don't implement this interface, we will use the endpoint from
1724
+ // "scheme://authority/endpoint" as the default authority.
1725
+ return endpoint , nil
1726
+ }
1727
+ }
0 commit comments