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  */