...

Source file src/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/name.go

Documentation: google.golang.org/grpc/xds/internal/xdsclient/xdsresource

     1  /*
     2   *
     3   * Copyright 2021 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * 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  package xdsresource
    19  
    20  import (
    21  	"net/url"
    22  	"sort"
    23  	"strings"
    24  )
    25  
    26  // FederationScheme is the scheme of a federation resource name.
    27  const FederationScheme = "xdstp"
    28  
    29  // Name contains the parsed component of an xDS resource name.
    30  //
    31  // An xDS resource name is in the format of
    32  // xdstp://[{authority}]/{resource type}/{id/*}?{context parameters}{#processing directive,*}
    33  //
    34  // See
    35  // https://github.com/cncf/xds/blob/main/proposals/TP1-xds-transport-next.md#uri-based-xds-resource-names
    36  // for details, and examples.
    37  type Name struct {
    38  	Scheme    string
    39  	Authority string
    40  	Type      string
    41  	ID        string
    42  
    43  	ContextParams map[string]string
    44  
    45  	processingDirective string
    46  }
    47  
    48  // ParseName splits the name and returns a struct representation of the Name.
    49  //
    50  // If the name isn't a valid new-style xDS name, field ID is set to the input.
    51  // Note that this is not an error, because we still support the old-style
    52  // resource names (those not starting with "xdstp:").
    53  //
    54  // The caller can tell if the parsing is successful by checking the returned
    55  // Scheme.
    56  func ParseName(name string) *Name {
    57  	if !strings.Contains(name, "://") {
    58  		// Only the long form URL, with ://, is valid.
    59  		return &Name{ID: name}
    60  	}
    61  	parsed, err := url.Parse(name)
    62  	if err != nil {
    63  		return &Name{ID: name}
    64  	}
    65  
    66  	ret := &Name{
    67  		Scheme:    parsed.Scheme,
    68  		Authority: parsed.Host,
    69  	}
    70  	split := strings.SplitN(parsed.Path, "/", 3)
    71  	if len(split) < 3 {
    72  		// Path is in the format of "/type/id". There must be at least 3
    73  		// segments after splitting.
    74  		return &Name{ID: name}
    75  	}
    76  	ret.Type = split[1]
    77  	ret.ID = split[2]
    78  	if len(parsed.Query()) != 0 {
    79  		ret.ContextParams = make(map[string]string)
    80  		for k, vs := range parsed.Query() {
    81  			if len(vs) > 0 {
    82  				// We only keep one value of each key. Behavior for multiple values
    83  				// is undefined.
    84  				ret.ContextParams[k] = vs[0]
    85  			}
    86  		}
    87  	}
    88  	// TODO: processing directive (the part comes after "#" in the URL, stored
    89  	// in parsed.RawFragment) is kept but not processed. Add support for that
    90  	// when it's needed.
    91  	ret.processingDirective = parsed.RawFragment
    92  	return ret
    93  }
    94  
    95  // String returns a canonicalized string of name. The context parameters are
    96  // sorted by the keys.
    97  func (n *Name) String() string {
    98  	if n.Scheme == "" {
    99  		return n.ID
   100  	}
   101  
   102  	// Sort and build query.
   103  	keys := make([]string, 0, len(n.ContextParams))
   104  	for k := range n.ContextParams {
   105  		keys = append(keys, k)
   106  	}
   107  	sort.Strings(keys)
   108  	var pairs []string
   109  	for _, k := range keys {
   110  		pairs = append(pairs, strings.Join([]string{k, n.ContextParams[k]}, "="))
   111  	}
   112  	rawQuery := strings.Join(pairs, "&")
   113  
   114  	path := n.Type
   115  	if n.ID != "" {
   116  		path = "/" + path + "/" + n.ID
   117  	}
   118  
   119  	tempURL := &url.URL{
   120  		Scheme:      n.Scheme,
   121  		Host:        n.Authority,
   122  		Path:        path,
   123  		RawQuery:    rawQuery,
   124  		RawFragment: n.processingDirective,
   125  	}
   126  	return tempURL.String()
   127  }
   128  

View as plain text