dubbo.apache.org/dubbo-go/v3@v3.1.1/xds/client/resource/unmarshal_lds.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 "errors" 28 "fmt" 29 "strconv" 30 ) 31 32 import ( 33 v1udpatypepb "github.com/cncf/udpa/go/udpa/type/v1" 34 35 v3cncftypepb "github.com/cncf/xds/go/xds/type/v3" 36 37 dubbogoLogger "github.com/dubbogo/gost/log/logger" 38 39 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 40 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 41 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 42 43 "github.com/golang/protobuf/proto" 44 "github.com/golang/protobuf/ptypes" 45 46 "google.golang.org/protobuf/types/known/anypb" 47 ) 48 49 import ( 50 "dubbo.apache.org/dubbo-go/v3/xds/client/resource/version" 51 "dubbo.apache.org/dubbo-go/v3/xds/httpfilter" 52 "dubbo.apache.org/dubbo-go/v3/xds/utils/pretty" 53 ) 54 55 // UnmarshalListener processes resources received in an LDS response, validates 56 // them, and transforms them into a native struct which contains only fields we 57 // are interested in. 58 func UnmarshalListener(opts *UnmarshalOptions) (map[string]ListenerUpdateErrTuple, UpdateMetadata, error) { 59 update := make(map[string]ListenerUpdateErrTuple) 60 md, err := processAllResources(opts, update) 61 return update, md, err 62 } 63 64 func unmarshalListenerResource(r *anypb.Any, f UpdateValidatorFunc, logger dubbogoLogger.Logger) (string, ListenerUpdate, error) { 65 if !IsListenerResource(r.GetTypeUrl()) { 66 return "", ListenerUpdate{}, fmt.Errorf("unexpected resource type: %q ", r.GetTypeUrl()) 67 } 68 // TODO: Pass version.TransportAPI instead of relying upon the type URL 69 v2 := r.GetTypeUrl() == version.V2ListenerURL 70 lis := &v3listenerpb.Listener{} 71 if err := proto.Unmarshal(r.GetValue(), lis); err != nil { 72 return "", ListenerUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err) 73 } 74 dubbogoLogger.Debugf("Resource with name: %v, type: %T, contains: %v", lis.GetName(), lis, pretty.ToJSON(lis)) 75 76 lu, err := processListener(lis, logger, v2) 77 if err != nil { 78 return lis.GetName(), ListenerUpdate{}, err 79 } 80 if f != nil { 81 if err := f(*lu); err != nil { 82 return lis.GetName(), ListenerUpdate{}, err 83 } 84 } 85 lu.Raw = r 86 return lis.GetName(), *lu, nil 87 } 88 89 func processListener(lis *v3listenerpb.Listener, logger dubbogoLogger.Logger, v2 bool) (*ListenerUpdate, error) { 90 if lis.GetApiListener() != nil { 91 return processClientSideListener(lis, logger, v2) 92 } 93 return processServerSideListener(lis, logger) 94 } 95 96 // processClientSideListener checks if the provided Listener proto meets 97 // the expected criteria. If so, it returns a non-empty routeConfigName. 98 func processClientSideListener(lis *v3listenerpb.Listener, logger dubbogoLogger.Logger, v2 bool) (*ListenerUpdate, error) { 99 update := &ListenerUpdate{} 100 101 apiLisAny := lis.GetApiListener().GetApiListener() 102 if !IsHTTPConnManagerResource(apiLisAny.GetTypeUrl()) { 103 return nil, fmt.Errorf("unexpected resource type: %q", apiLisAny.GetTypeUrl()) 104 } 105 apiLis := &v3httppb.HttpConnectionManager{} 106 if err := proto.Unmarshal(apiLisAny.GetValue(), apiLis); err != nil { 107 return nil, fmt.Errorf("failed to unmarshal api_listner: %v", err) 108 } 109 // "HttpConnectionManager.xff_num_trusted_hops must be unset or zero and 110 // HttpConnectionManager.original_ip_detection_extensions must be empty. If 111 // either field has an incorrect value, the Listener must be NACKed." - A41 112 if apiLis.XffNumTrustedHops != 0 { 113 return nil, fmt.Errorf("xff_num_trusted_hops must be unset or zero %+v", apiLis) 114 } 115 if len(apiLis.OriginalIpDetectionExtensions) != 0 { 116 return nil, fmt.Errorf("original_ip_detection_extensions must be empty %+v", apiLis) 117 } 118 119 switch apiLis.RouteSpecifier.(type) { 120 case *v3httppb.HttpConnectionManager_Rds: 121 if apiLis.GetRds().GetConfigSource().GetAds() == nil { 122 return nil, fmt.Errorf("ConfigSource is not ADS: %+v", lis) 123 } 124 name := apiLis.GetRds().GetRouteConfigName() 125 if name == "" { 126 return nil, fmt.Errorf("empty route_config_name: %+v", lis) 127 } 128 update.RouteConfigName = name 129 case *v3httppb.HttpConnectionManager_RouteConfig: 130 routeU, err := generateRDSUpdateFromRouteConfiguration(apiLis.GetRouteConfig(), logger, v2) 131 if err != nil { 132 return nil, fmt.Errorf("failed to parse inline RDS resp: %v", err) 133 } 134 update.InlineRouteConfig = &routeU 135 case nil: 136 return nil, fmt.Errorf("no RouteSpecifier: %+v", apiLis) 137 default: 138 return nil, fmt.Errorf("unsupported type %T for RouteSpecifier", apiLis.RouteSpecifier) 139 } 140 141 if v2 { 142 return update, nil 143 } 144 145 // The following checks and fields only apply to xDS protocol versions v3+. 146 147 update.MaxStreamDuration = apiLis.GetCommonHttpProtocolOptions().GetMaxStreamDuration().AsDuration() 148 149 var err error 150 if update.HTTPFilters, err = processHTTPFilters(apiLis.GetHttpFilters(), false); err != nil { 151 return nil, err 152 } 153 154 return update, nil 155 } 156 157 func unwrapHTTPFilterConfig(config *anypb.Any) (proto.Message, string, error) { 158 switch { 159 case ptypes.Is(config, &v3cncftypepb.TypedStruct{}): 160 // The real type name is inside the new TypedStruct message. 161 s := new(v3cncftypepb.TypedStruct) 162 if err := ptypes.UnmarshalAny(config, s); err != nil { 163 return nil, "", fmt.Errorf("error unmarshaling TypedStruct filter config: %v", err) 164 } 165 return s, s.GetTypeUrl(), nil 166 case ptypes.Is(config, &v1udpatypepb.TypedStruct{}): 167 // The real type name is inside the old TypedStruct message. 168 s := new(v1udpatypepb.TypedStruct) 169 if err := ptypes.UnmarshalAny(config, s); err != nil { 170 return nil, "", fmt.Errorf("error unmarshaling TypedStruct filter config: %v", err) 171 } 172 return s, s.GetTypeUrl(), nil 173 default: 174 return config, config.GetTypeUrl(), nil 175 } 176 } 177 178 func validateHTTPFilterConfig(cfg *anypb.Any, lds, optional bool) (httpfilter.Filter, httpfilter.FilterConfig, error) { 179 config, typeURL, err := unwrapHTTPFilterConfig(cfg) 180 if err != nil { 181 return nil, nil, err 182 } 183 filterBuilder := httpfilter.Get(typeURL) 184 if filterBuilder == nil { 185 if optional { 186 return nil, nil, nil 187 } 188 return nil, nil, fmt.Errorf("no filter implementation found for %q", typeURL) 189 } 190 parseFunc := filterBuilder.ParseFilterConfig 191 if !lds { 192 parseFunc = filterBuilder.ParseFilterConfigOverride 193 } 194 filterConfig, err := parseFunc(config) 195 if err != nil { 196 return nil, nil, fmt.Errorf("error parsing config for filter %q: %v", typeURL, err) 197 } 198 return filterBuilder, filterConfig, nil 199 } 200 201 func processHTTPFilterOverrides(cfgs map[string]*anypb.Any) (map[string]httpfilter.FilterConfig, error) { 202 if len(cfgs) == 0 { 203 return nil, nil 204 } 205 m := make(map[string]httpfilter.FilterConfig) 206 for name, cfg := range cfgs { 207 optional := false 208 s := new(v3routepb.FilterConfig) 209 if ptypes.Is(cfg, s) { 210 if err := ptypes.UnmarshalAny(cfg, s); err != nil { 211 return nil, fmt.Errorf("filter override %q: error unmarshaling FilterConfig: %v", name, err) 212 } 213 cfg = s.GetConfig() 214 optional = s.GetIsOptional() 215 } 216 217 httpFilter, config, err := validateHTTPFilterConfig(cfg, false, optional) 218 if err != nil { 219 return nil, fmt.Errorf("filter override %q: %v", name, err) 220 } 221 if httpFilter == nil { 222 // Optional configs are ignored. 223 continue 224 } 225 m[name] = config 226 } 227 return m, nil 228 } 229 230 func processHTTPFilters(filters []*v3httppb.HttpFilter, server bool) ([]HTTPFilter, error) { 231 ret := make([]HTTPFilter, 0, len(filters)) 232 seenNames := make(map[string]bool, len(filters)) 233 for _, filter := range filters { 234 name := filter.GetName() 235 if name == "" { 236 return nil, errors.New("filter missing name field") 237 } 238 if seenNames[name] { 239 return nil, fmt.Errorf("duplicate filter name %q", name) 240 } 241 seenNames[name] = true 242 243 httpFilter, config, err := validateHTTPFilterConfig(filter.GetTypedConfig(), true, filter.GetIsOptional()) 244 if err != nil { 245 return nil, err 246 } 247 if httpFilter == nil { 248 // Optional configs are ignored. 249 continue 250 } 251 if server { 252 if _, ok := httpFilter.(httpfilter.ServerInterceptorBuilder); !ok { 253 if filter.GetIsOptional() { 254 continue 255 } 256 return nil, fmt.Errorf("HTTP filter %q not supported server-side", name) 257 } 258 } else if _, ok := httpFilter.(httpfilter.ClientInterceptorBuilder); !ok { 259 if filter.GetIsOptional() { 260 continue 261 } 262 return nil, fmt.Errorf("HTTP filter %q not supported client-side", name) 263 } 264 265 // Save name/config 266 ret = append(ret, HTTPFilter{Name: name, Filter: httpFilter, Config: config}) 267 } 268 // "Validation will fail if a terminal filter is not the last filter in the 269 // chain or if a non-terminal filter is the last filter in the chain." - A39 270 if len(ret) == 0 { 271 return nil, fmt.Errorf("http filters list is empty") 272 } 273 var i int 274 for ; i < len(ret)-1; i++ { 275 if ret[i].Filter.IsTerminal() { 276 return nil, fmt.Errorf("http filter %q is a terminal filter but it is not last in the filter chain", ret[i].Name) 277 } 278 } 279 if !ret[i].Filter.IsTerminal() { 280 return nil, fmt.Errorf("http filter %q is not a terminal filter", ret[len(ret)-1].Name) 281 } 282 return ret, nil 283 } 284 285 func processServerSideListener(lis *v3listenerpb.Listener, logger dubbogoLogger.Logger) (*ListenerUpdate, error) { 286 if n := len(lis.ListenerFilters); n != 0 { 287 return nil, fmt.Errorf("unsupported field 'listener_filters' contains %d entries", n) 288 } 289 if useOrigDst := lis.GetUseOriginalDst(); useOrigDst != nil && useOrigDst.GetValue() { 290 return nil, errors.New("unsupported field 'use_original_dst' is present and set to true") 291 } 292 addr := lis.GetAddress() 293 if addr == nil { 294 return nil, fmt.Errorf("no address field in LDS response: %+v", lis) 295 } 296 sockAddr := addr.GetSocketAddress() 297 if sockAddr == nil { 298 return nil, fmt.Errorf("no socket_address field in LDS response: %+v", lis) 299 } 300 lu := &ListenerUpdate{ 301 InboundListenerCfg: &InboundListenerConfig{ 302 Address: sockAddr.GetAddress(), 303 Port: strconv.Itoa(int(sockAddr.GetPortValue())), 304 }, 305 } 306 307 //fcMgr, err := NewFilterChainManager(lis, logger) 308 //if err != nil { 309 // return nil, err 310 //} 311 //lu.InboundListenerCfg.FilterChains = fcMgr 312 return lu, nil 313 }