github.com/searKing/golang/go@v1.2.74/net/resolver/target.go (about) 1 // Copyright 2021 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package resolver 6 7 import ( 8 "fmt" 9 "net/url" 10 "strings" 11 ) 12 13 // Target represents a target for gRPC, as specified in: 14 // https://github.com/grpc/grpc/blob/master/doc/naming.md. 15 // It is parsed from the target string that gets passed into Dial or DialContext 16 // by the user. And gRPC passes it to the resolver and the balancer. 17 // 18 // If the target follows the naming spec, and the parsed scheme is registered 19 // with gRPC, we will parse the target string according to the spec. If the 20 // target does not contain a scheme or if the parsed scheme is not registered 21 // (i.e. no corresponding resolver available to resolve the endpoint), we will 22 // apply the default scheme, and will attempt to reparse it. 23 // 24 // Examples: 25 // 26 // - "dns://some_authority/foo.bar" 27 // Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"} 28 // - "foo.bar" 29 // Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"} 30 // - "unknown_scheme://authority/endpoint" 31 // Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"} 32 // 33 // If the target does not contain a scheme, we will apply the default scheme, and set the Target to 34 // be the full target string. e.g. "foo.bar" will be parsed into 35 // &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}. 36 // 37 // If the parsed scheme is not registered (i.e. no corresponding resolver available to resolve the 38 // endpoint), we set the Scheme to be the default scheme, and set the Endpoint to be the full target 39 // string. e.g. target string "unknown_scheme://authority/endpoint" will be parsed into 40 // &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}. 41 42 type Target struct { 43 // Deprecated: use URL.Scheme instead. 44 Scheme string 45 // Deprecated: use URL.Host instead. 46 Authority string 47 // Deprecated: use URL.Path or URL.Opaque instead. The latter is set when 48 // the former is empty. 49 Endpoint string 50 // URL contains the parsed dial target with an optional default scheme added 51 // to it if the original dial target contained no scheme or contained an 52 // unregistered scheme. Any query params specified in the original dial 53 // target can be accessed from here. 54 URL url.URL 55 } 56 57 func (t *Target) key() targetKey { 58 return targetKey{ 59 Scheme: t.Scheme, 60 Authority: t.Authority, 61 Endpoint: t.Endpoint, 62 } 63 } 64 65 type targetKey Target 66 67 func (k targetKey) String() string { 68 // Only used by tests. 69 return fmt.Sprintf("%s|%s|%s", k.Scheme, k.Authority, k.Endpoint) 70 } 71 72 // ParseTarget uses RFC 3986 semantics to parse the given target into a 73 // resolver.Target struct containing scheme, authority and endpoint. Query 74 // params are stripped from the endpoint. 75 // 76 // If target is not a valid scheme://authority/endpoint as specified in 77 // https://github.com/grpc/grpc/blob/master/doc/naming.md, 78 // it returns {Endpoint: target}. 79 // Code borrowed from https://github.com/grpc/grpc-go/blob/v1.48.0/clientconn.go#L1619 80 // See https://github.com/grpc/grpc-go/pull/4817 81 func ParseTarget(target string) Target { 82 parsedTarget, err := parseTarget(target) 83 if err == nil && parsedTarget.Scheme != "" { 84 return parsedTarget 85 } 86 87 // We are here because the user's dial target did not contain a scheme or 88 // specified an unregistered scheme. We should fallback to the default 89 // scheme, except when a custom dialer is specified in which case, we should 90 // always use passthrough scheme. 91 defScheme := GetDefaultScheme() 92 canonicalTarget := defScheme + ":///" + target 93 94 parsedTarget, err = parseTarget(canonicalTarget) 95 if err != nil { 96 return Target{ 97 Endpoint: target, 98 URL: url.URL{ 99 Path: target, 100 }, 101 } 102 } 103 parsedTarget.Scheme = "" // trim scheme 104 parsedTarget.URL.Scheme = "" 105 return parsedTarget 106 } 107 108 // parseTarget uses RFC 3986 semantics to parse the given target into a 109 // resolver.Target struct containing scheme, authority and endpoint. Query 110 // params are stripped from the endpoint. 111 func parseTarget(target string) (Target, error) { 112 u, err := url.Parse(target) 113 if err != nil { 114 return Target{}, err 115 } 116 // For targets of the form "[scheme]://[authority]/endpoint, the endpoint 117 // value returned from url.Parse() contains a leading "/". Although this is 118 // in accordance with RFC 3986, we do not want to break existing resolver 119 // implementations which expect the endpoint without the leading "/". So, we 120 // end up stripping the leading "/" here. But this will result in an 121 // incorrect parsing for something like "unix:///path/to/socket". Since we 122 // own the "unix" resolver, we can workaround in the unix resolver by using 123 // the `URL` field instead of the `Endpoint` field. 124 endpoint := u.Path 125 if endpoint == "" { 126 endpoint = u.Opaque 127 } 128 endpoint = strings.TrimPrefix(endpoint, "/") 129 return Target{ 130 Scheme: u.Scheme, 131 Authority: u.Host, 132 Endpoint: endpoint, 133 URL: *u, 134 }, nil 135 }