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