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  }