github.com/kaydxh/golang@v0.0.131/go/net/resolver/target.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package resolver 23 24 import ( 25 "net/url" 26 "strings" 27 ) 28 29 // Target represents a target for gRPC, as specified in: 30 // https://github.com/grpc/grpc/blob/master/doc/naming.md. 31 // It is parsed from the target string that gets passed into Dial or DialContext by the user. And 32 // grpc passes it to the resolver and the balancer. 33 // 34 // If the target follows the naming spec, and the parsed scheme is registered with grpc, we will 35 // parse the target string according to the spec. e.g. "dns://some_authority/foo.bar" will be parsed 36 // into &Target{Scheme: "dns", Authority: "some_authority", Endpoint: "foo.bar"} 37 // 38 // If the target does not contain a scheme, we will apply the default scheme, and set the Target to 39 // be the full target string. e.g. "foo.bar" will be parsed into 40 // &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "foo.bar"}. 41 // 42 // If the parsed scheme is not registered (i.e. no corresponding resolver available to resolve the 43 // endpoint), we set the Scheme to be the default scheme, and set the Endpoint to be the full target 44 // string. e.g. target string "unknown_scheme://authority/endpoint" will be parsed into 45 // &Target{Scheme: resolver.GetDefaultScheme(), Endpoint: "unknown_scheme://authority/endpoint"}. 46 type Target struct { 47 Scheme string 48 Authority string 49 Endpoint string 50 URL url.URL 51 } 52 53 // parseTarget uses RFC 3986 semantics to parse the given target into a 54 // resolver.Target struct containing scheme, authority and endpoint. Query 55 // params are stripped from the endpoint. 56 // use URL first 57 func ParseTarget(target string) (Target, error) { 58 u, err := url.Parse(target) 59 if err != nil { 60 return Target{}, err 61 } 62 // For targets of the form "[scheme]://[authority]/endpoint, the endpoint 63 // value returned from url.Parse() contains a leading "/". Although this is 64 // in accordance with RFC 3986, we do not want to break existing resolver 65 // implementations which expect the endpoint without the leading "/". So, we 66 // end up stripping the leading "/" here. But this will result in an 67 // incorrect parsing for something like "unix:///path/to/socket". Since we 68 // own the "unix" resolver, we can workaround in the unix resolver by using 69 // the `URL` field instead of the `Endpoint` field. 70 endpoint := u.Path 71 if endpoint == "" { 72 endpoint = u.Opaque 73 } 74 75 endpoint = strings.TrimPrefix(endpoint, "/") 76 return Target{ 77 Scheme: u.Scheme, 78 Authority: u.Host, 79 Endpoint: endpoint, 80 URL: *u, 81 }, nil 82 } 83 84 // ParseTarget splits target into a resolver.Target struct containing scheme, 85 // authority and endpoint. skipUnixColonParsing indicates that the parse should 86 // not parse "unix:[path]" cases. This should be true in cases where a custom 87 // dialer is present, to prevent a behavior change. 88 // 89 // If target is not a valid scheme://authority/endpoint as specified in 90 // https://github.com/grpc/grpc/blob/master/doc/naming.md, 91 // it returns {Endpoint: target}. 92 /* 93 func ParseTarget(target string, skipUnixColonParsing bool) (ret Target) { 94 var ok bool 95 if strings.HasPrefix(target, "unix-abstract:") { 96 if strings.HasPrefix(target, "unix-abstract://") { 97 // Maybe, with Authority specified, try to parse it 98 var remain string 99 ret.Scheme, remain, _ = strings_.Split2(target, "://") 100 ret.Authority, ret.Endpoint, ok = strings_.Split2(remain, "/") 101 if !ok { 102 // No Authority, add the "//" back 103 ret.Endpoint = "//" + remain 104 } else { 105 // Found Authority, add the "/" back 106 ret.Endpoint = "/" + ret.Endpoint 107 } 108 } else { 109 // Without Authority specified, split target on ":" 110 ret.Scheme, ret.Endpoint, _ = strings_.Split2(target, ":") 111 } 112 return ret 113 } 114 ret.Scheme, ret.Endpoint, ok = strings_.Split2(target, "://") 115 if !ok { 116 if strings.HasPrefix(target, "unix:") && !skipUnixColonParsing { 117 // Handle the "unix:[local/path]" and "unix:[/absolute/path]" cases, 118 // because splitting on :// only handles the 119 // "unix://[/absolute/path]" case. Only handle if the dialer is nil, 120 // to avoid a behavior change with custom dialers. 121 return Target{Scheme: "unix", Endpoint: target[len("unix:"):]} 122 } 123 return Target{Endpoint: target} 124 } 125 ret.Authority, ret.Endpoint, ok = strings_.Split2(ret.Endpoint, "/") 126 if !ok { 127 return Target{Endpoint: target} 128 } 129 if ret.Scheme == "unix" { 130 // Add the "/" back in the unix case, so the unix resolver receives the 131 // actual endpoint in the "unix://[/absolute/path]" case. 132 ret.Endpoint = "/" + ret.Endpoint 133 } 134 return ret 135 } 136 */