dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/resource/name.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  /*
    19   *
    20   * Copyright 2021 gRPC authors.
    21   *
    22   */
    23  
    24  package resource
    25  
    26  import (
    27  	"net/url"
    28  	"sort"
    29  	"strings"
    30  )
    31  
    32  import (
    33  	"dubbo.apache.org/dubbo-go/v3/xds/utils/envconfig"
    34  )
    35  
    36  // Name contains the parsed component of an xDS resource name.
    37  //
    38  // An xDS resource name is in the format of
    39  // xdstp://[{authority}]/{resource type}/{id/*}?{context parameters}{#processing directive,*}
    40  //
    41  // See
    42  // https://github.com/cncf/xds/blob/main/proposals/TP1-xds-transport-next.md#uri-based-xds-resource-names
    43  // for details, and examples.
    44  type Name struct {
    45  	Scheme    string
    46  	Authority string
    47  	Type      string
    48  	ID        string
    49  
    50  	ContextParams map[string]string
    51  
    52  	processingDirective string
    53  }
    54  
    55  // ParseName splits the name and returns a struct representation of the Name.
    56  //
    57  // If the name isn't a valid new-style xDS name, field ID is set to the input.
    58  // Note that this is not an error, because we still support the old-style
    59  // resource names (those not starting with "xdstp:").
    60  //
    61  // The caller can tell if the parsing is successful by checking the returned
    62  // Scheme.
    63  func ParseName(name string) *Name {
    64  	if !envconfig.XDSFederation {
    65  		// Return "" scheme to use the default authority for the server.
    66  		return &Name{ID: name}
    67  	}
    68  	if !strings.Contains(name, "://") {
    69  		// Only the long form URL, with ://, is valid.
    70  		return &Name{ID: name}
    71  	}
    72  	parsed, err := url.Parse(name)
    73  	if err != nil {
    74  		return &Name{ID: name}
    75  	}
    76  
    77  	ret := &Name{
    78  		Scheme:    parsed.Scheme,
    79  		Authority: parsed.Host,
    80  	}
    81  	split := strings.SplitN(parsed.Path, "/", 3)
    82  	if len(split) < 3 {
    83  		// Path is in the format of "/type/id". There must be at least 3
    84  		// segments after splitting.
    85  		return &Name{ID: name}
    86  	}
    87  	ret.Type = split[1]
    88  	ret.ID = split[2]
    89  	if len(parsed.Query()) != 0 {
    90  		ret.ContextParams = make(map[string]string)
    91  		for k, vs := range parsed.Query() {
    92  			if len(vs) > 0 {
    93  				// We only keep one value of each key. Behavior for multiple values
    94  				// is undefined.
    95  				ret.ContextParams[k] = vs[0]
    96  			}
    97  		}
    98  	}
    99  	// TODO: processing directive (the part comes after "#" in the URL, stored
   100  	// in parsed.RawFragment) is kept but not processed. Add support for that
   101  	// when it's needed.
   102  	ret.processingDirective = parsed.RawFragment
   103  	return ret
   104  }
   105  
   106  // String returns a canonicalized string of name. The context parameters are
   107  // sorted by the keys.
   108  func (n *Name) String() string {
   109  	if n.Scheme == "" {
   110  		return n.ID
   111  	}
   112  
   113  	// Sort and build query.
   114  	keys := make([]string, 0, len(n.ContextParams))
   115  	for k := range n.ContextParams {
   116  		keys = append(keys, k)
   117  	}
   118  	sort.Strings(keys)
   119  	var pairs []string
   120  	for _, k := range keys {
   121  		pairs = append(pairs, strings.Join([]string{k, n.ContextParams[k]}, "="))
   122  	}
   123  	rawQuery := strings.Join(pairs, "&")
   124  
   125  	path := n.Type
   126  	if n.ID != "" {
   127  		path = path + "/" + n.ID
   128  	}
   129  
   130  	tempURL := &url.URL{
   131  		Scheme:      n.Scheme,
   132  		Host:        n.Authority,
   133  		Path:        path,
   134  		RawQuery:    rawQuery,
   135  		RawFragment: n.processingDirective,
   136  	}
   137  	return tempURL.String()
   138  }