1/* 2 * 3 * Copyright 2020 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 19package xdsclient 20 21import ( 22 "fmt" 23 "strings" 24 "testing" 25 "time" 26 27 v1typepb "github.com/cncf/udpa/go/udpa/type/v1" 28 v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" 29 "github.com/golang/protobuf/proto" 30 spb "github.com/golang/protobuf/ptypes/struct" 31 "github.com/google/go-cmp/cmp" 32 "github.com/google/go-cmp/cmp/cmpopts" 33 "google.golang.org/protobuf/types/known/durationpb" 34 35 "google.golang.org/grpc/internal/testutils" 36 "google.golang.org/grpc/internal/xds/env" 37 "google.golang.org/grpc/xds/internal/httpfilter" 38 _ "google.golang.org/grpc/xds/internal/httpfilter/router" 39 "google.golang.org/grpc/xds/internal/testutils/e2e" 40 "google.golang.org/grpc/xds/internal/version" 41 42 v2xdspb "github.com/envoyproxy/go-control-plane/envoy/api/v2" 43 v2corepb "github.com/envoyproxy/go-control-plane/envoy/api/v2/core" 44 v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" 45 v2httppb "github.com/envoyproxy/go-control-plane/envoy/config/filter/network/http_connection_manager/v2" 46 v2listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v2" 47 v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 48 v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 49 v3tlspb "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" 50 anypb "github.com/golang/protobuf/ptypes/any" 51 wrapperspb "github.com/golang/protobuf/ptypes/wrappers" 52) 53 54func (s) TestUnmarshalListener_ClientSide(t *testing.T) { 55 const ( 56 v2LDSTarget = "lds.target.good:2222" 57 v3LDSTarget = "lds.target.good:3333" 58 v2RouteConfigName = "v2RouteConfig" 59 v3RouteConfigName = "v3RouteConfig" 60 routeName = "routeName" 61 testVersion = "test-version-lds-client" 62 ) 63 64 var ( 65 v2Lis = testutils.MarshalAny(&v2xdspb.Listener{ 66 Name: v2LDSTarget, 67 ApiListener: &v2listenerpb.ApiListener{ 68 ApiListener: testutils.MarshalAny(&v2httppb.HttpConnectionManager{ 69 RouteSpecifier: &v2httppb.HttpConnectionManager_Rds{ 70 Rds: &v2httppb.Rds{ 71 ConfigSource: &v2corepb.ConfigSource{ 72 ConfigSourceSpecifier: &v2corepb.ConfigSource_Ads{Ads: &v2corepb.AggregatedConfigSource{}}, 73 }, 74 RouteConfigName: v2RouteConfigName, 75 }, 76 }, 77 }), 78 }, 79 }) 80 customFilter = &v3httppb.HttpFilter{ 81 Name: "customFilter", 82 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig}, 83 } 84 typedStructFilter = &v3httppb.HttpFilter{ 85 Name: "customFilter", 86 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: wrappedCustomFilterTypedStructConfig}, 87 } 88 customOptionalFilter = &v3httppb.HttpFilter{ 89 Name: "customFilter", 90 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig}, 91 IsOptional: true, 92 } 93 customFilter2 = &v3httppb.HttpFilter{ 94 Name: "customFilter2", 95 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: customFilterConfig}, 96 } 97 errFilter = &v3httppb.HttpFilter{ 98 Name: "errFilter", 99 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig}, 100 } 101 errOptionalFilter = &v3httppb.HttpFilter{ 102 Name: "errFilter", 103 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: errFilterConfig}, 104 IsOptional: true, 105 } 106 clientOnlyCustomFilter = &v3httppb.HttpFilter{ 107 Name: "clientOnlyCustomFilter", 108 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: clientOnlyCustomFilterConfig}, 109 } 110 serverOnlyCustomFilter = &v3httppb.HttpFilter{ 111 Name: "serverOnlyCustomFilter", 112 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig}, 113 } 114 serverOnlyOptionalCustomFilter = &v3httppb.HttpFilter{ 115 Name: "serverOnlyOptionalCustomFilter", 116 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig}, 117 IsOptional: true, 118 } 119 unknownFilter = &v3httppb.HttpFilter{ 120 Name: "unknownFilter", 121 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig}, 122 } 123 unknownOptionalFilter = &v3httppb.HttpFilter{ 124 Name: "unknownFilter", 125 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: unknownFilterConfig}, 126 IsOptional: true, 127 } 128 v3LisWithInlineRoute = testutils.MarshalAny(&v3listenerpb.Listener{ 129 Name: v3LDSTarget, 130 ApiListener: &v3listenerpb.ApiListener{ 131 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 132 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 133 RouteConfig: &v3routepb.RouteConfiguration{ 134 Name: routeName, 135 VirtualHosts: []*v3routepb.VirtualHost{{ 136 Domains: []string{v3LDSTarget}, 137 Routes: []*v3routepb.Route{{ 138 Match: &v3routepb.RouteMatch{ 139 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 140 }, 141 Action: &v3routepb.Route_Route{ 142 Route: &v3routepb.RouteAction{ 143 ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName}, 144 }}}}}}}, 145 }, 146 HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter}, 147 CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{ 148 MaxStreamDuration: durationpb.New(time.Second), 149 }, 150 }), 151 }, 152 }) 153 v3LisWithFilters = func(fs ...*v3httppb.HttpFilter) *anypb.Any { 154 fs = append(fs, emptyRouterFilter) 155 return testutils.MarshalAny(&v3listenerpb.Listener{ 156 Name: v3LDSTarget, 157 ApiListener: &v3listenerpb.ApiListener{ 158 ApiListener: testutils.MarshalAny( 159 &v3httppb.HttpConnectionManager{ 160 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 161 Rds: &v3httppb.Rds{ 162 ConfigSource: &v3corepb.ConfigSource{ 163 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}}, 164 }, 165 RouteConfigName: v3RouteConfigName, 166 }, 167 }, 168 CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{ 169 MaxStreamDuration: durationpb.New(time.Second), 170 }, 171 HttpFilters: fs, 172 }), 173 }, 174 }) 175 } 176 errMD = UpdateMetadata{ 177 Status: ServiceStatusNACKed, 178 Version: testVersion, 179 ErrState: &UpdateErrorMetadata{ 180 Version: testVersion, 181 Err: cmpopts.AnyError, 182 }, 183 } 184 ) 185 186 tests := []struct { 187 name string 188 resources []*anypb.Any 189 wantUpdate map[string]ListenerUpdateErrTuple 190 wantMD UpdateMetadata 191 wantErr bool 192 }{ 193 { 194 name: "non-listener resource", 195 resources: []*anypb.Any{{TypeUrl: version.V3HTTPConnManagerURL}}, 196 wantMD: errMD, 197 wantErr: true, 198 }, 199 { 200 name: "badly marshaled listener resource", 201 resources: []*anypb.Any{ 202 { 203 TypeUrl: version.V3ListenerURL, 204 Value: func() []byte { 205 lis := &v3listenerpb.Listener{ 206 Name: v3LDSTarget, 207 ApiListener: &v3listenerpb.ApiListener{ 208 ApiListener: &anypb.Any{ 209 TypeUrl: version.V3HTTPConnManagerURL, 210 Value: []byte{1, 2, 3, 4}, 211 }, 212 }, 213 } 214 mLis, _ := proto.Marshal(lis) 215 return mLis 216 }(), 217 }, 218 }, 219 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 220 wantMD: errMD, 221 wantErr: true, 222 }, 223 { 224 name: "wrong type in apiListener", 225 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 226 Name: v3LDSTarget, 227 ApiListener: &v3listenerpb.ApiListener{ 228 ApiListener: testutils.MarshalAny(&v2xdspb.Listener{}), 229 }, 230 })}, 231 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 232 wantMD: errMD, 233 wantErr: true, 234 }, 235 { 236 name: "empty httpConnMgr in apiListener", 237 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 238 Name: v3LDSTarget, 239 ApiListener: &v3listenerpb.ApiListener{ 240 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 241 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 242 Rds: &v3httppb.Rds{}, 243 }, 244 }), 245 }, 246 })}, 247 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 248 wantMD: errMD, 249 wantErr: true, 250 }, 251 { 252 name: "scopedRoutes routeConfig in apiListener", 253 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 254 Name: v3LDSTarget, 255 ApiListener: &v3listenerpb.ApiListener{ 256 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 257 RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{}, 258 }), 259 }, 260 })}, 261 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 262 wantMD: errMD, 263 wantErr: true, 264 }, 265 { 266 name: "rds.ConfigSource in apiListener is not ADS", 267 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 268 Name: v3LDSTarget, 269 ApiListener: &v3listenerpb.ApiListener{ 270 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 271 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 272 Rds: &v3httppb.Rds{ 273 ConfigSource: &v3corepb.ConfigSource{ 274 ConfigSourceSpecifier: &v3corepb.ConfigSource_Path{ 275 Path: "/some/path", 276 }, 277 }, 278 RouteConfigName: v3RouteConfigName, 279 }, 280 }, 281 }), 282 }, 283 })}, 284 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 285 wantMD: errMD, 286 wantErr: true, 287 }, 288 { 289 name: "empty resource list", 290 wantMD: UpdateMetadata{ 291 Status: ServiceStatusACKed, 292 Version: testVersion, 293 }, 294 }, 295 { 296 name: "v3 with no filters", 297 resources: []*anypb.Any{v3LisWithFilters()}, 298 wantUpdate: map[string]ListenerUpdateErrTuple{ 299 v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList, Raw: v3LisWithFilters()}}, 300 }, 301 wantMD: UpdateMetadata{ 302 Status: ServiceStatusACKed, 303 Version: testVersion, 304 }, 305 }, 306 { 307 name: "v3 no terminal filter", 308 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 309 Name: v3LDSTarget, 310 ApiListener: &v3listenerpb.ApiListener{ 311 ApiListener: testutils.MarshalAny( 312 &v3httppb.HttpConnectionManager{ 313 RouteSpecifier: &v3httppb.HttpConnectionManager_Rds{ 314 Rds: &v3httppb.Rds{ 315 ConfigSource: &v3corepb.ConfigSource{ 316 ConfigSourceSpecifier: &v3corepb.ConfigSource_Ads{Ads: &v3corepb.AggregatedConfigSource{}}, 317 }, 318 RouteConfigName: v3RouteConfigName, 319 }, 320 }, 321 CommonHttpProtocolOptions: &v3corepb.HttpProtocolOptions{ 322 MaxStreamDuration: durationpb.New(time.Second), 323 }, 324 }), 325 }, 326 })}, 327 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 328 wantMD: errMD, 329 wantErr: true, 330 }, 331 { 332 name: "v3 with custom filter", 333 resources: []*anypb.Any{v3LisWithFilters(customFilter)}, 334 wantUpdate: map[string]ListenerUpdateErrTuple{ 335 v3LDSTarget: {Update: ListenerUpdate{ 336 RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, 337 HTTPFilters: []HTTPFilter{ 338 { 339 Name: "customFilter", 340 Filter: httpFilter{}, 341 Config: filterConfig{Cfg: customFilterConfig}, 342 }, 343 routerFilter, 344 }, 345 Raw: v3LisWithFilters(customFilter), 346 }}, 347 }, 348 wantMD: UpdateMetadata{ 349 Status: ServiceStatusACKed, 350 Version: testVersion, 351 }, 352 }, 353 { 354 name: "v3 with custom filter in typed struct", 355 resources: []*anypb.Any{v3LisWithFilters(typedStructFilter)}, 356 wantUpdate: map[string]ListenerUpdateErrTuple{ 357 v3LDSTarget: {Update: ListenerUpdate{ 358 RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, 359 HTTPFilters: []HTTPFilter{ 360 { 361 Name: "customFilter", 362 Filter: httpFilter{}, 363 Config: filterConfig{Cfg: customFilterTypedStructConfig}, 364 }, 365 routerFilter, 366 }, 367 Raw: v3LisWithFilters(typedStructFilter), 368 }}, 369 }, 370 wantMD: UpdateMetadata{ 371 Status: ServiceStatusACKed, 372 Version: testVersion, 373 }, 374 }, 375 { 376 name: "v3 with optional custom filter", 377 resources: []*anypb.Any{v3LisWithFilters(customOptionalFilter)}, 378 wantUpdate: map[string]ListenerUpdateErrTuple{ 379 v3LDSTarget: {Update: ListenerUpdate{ 380 RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, 381 HTTPFilters: []HTTPFilter{ 382 { 383 Name: "customFilter", 384 Filter: httpFilter{}, 385 Config: filterConfig{Cfg: customFilterConfig}, 386 }, 387 routerFilter, 388 }, 389 Raw: v3LisWithFilters(customOptionalFilter), 390 }}, 391 }, 392 wantMD: UpdateMetadata{ 393 Status: ServiceStatusACKed, 394 Version: testVersion, 395 }, 396 }, 397 { 398 name: "v3 with two filters with same name", 399 resources: []*anypb.Any{v3LisWithFilters(customFilter, customFilter)}, 400 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 401 wantMD: errMD, 402 wantErr: true, 403 }, 404 { 405 name: "v3 with two filters - same type different name", 406 resources: []*anypb.Any{v3LisWithFilters(customFilter, customFilter2)}, 407 wantUpdate: map[string]ListenerUpdateErrTuple{ 408 v3LDSTarget: {Update: ListenerUpdate{ 409 RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, 410 HTTPFilters: []HTTPFilter{{ 411 Name: "customFilter", 412 Filter: httpFilter{}, 413 Config: filterConfig{Cfg: customFilterConfig}, 414 }, { 415 Name: "customFilter2", 416 Filter: httpFilter{}, 417 Config: filterConfig{Cfg: customFilterConfig}, 418 }, 419 routerFilter, 420 }, 421 Raw: v3LisWithFilters(customFilter, customFilter2), 422 }}, 423 }, 424 wantMD: UpdateMetadata{ 425 Status: ServiceStatusACKed, 426 Version: testVersion, 427 }, 428 }, 429 { 430 name: "v3 with server-only filter", 431 resources: []*anypb.Any{v3LisWithFilters(serverOnlyCustomFilter)}, 432 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 433 wantMD: errMD, 434 wantErr: true, 435 }, 436 { 437 name: "v3 with optional server-only filter", 438 resources: []*anypb.Any{v3LisWithFilters(serverOnlyOptionalCustomFilter)}, 439 wantUpdate: map[string]ListenerUpdateErrTuple{ 440 v3LDSTarget: {Update: ListenerUpdate{ 441 RouteConfigName: v3RouteConfigName, 442 MaxStreamDuration: time.Second, 443 Raw: v3LisWithFilters(serverOnlyOptionalCustomFilter), 444 HTTPFilters: routerFilterList, 445 }}, 446 }, 447 wantMD: UpdateMetadata{ 448 Status: ServiceStatusACKed, 449 Version: testVersion, 450 }, 451 }, 452 { 453 name: "v3 with client-only filter", 454 resources: []*anypb.Any{v3LisWithFilters(clientOnlyCustomFilter)}, 455 wantUpdate: map[string]ListenerUpdateErrTuple{ 456 v3LDSTarget: {Update: ListenerUpdate{ 457 RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, 458 HTTPFilters: []HTTPFilter{ 459 { 460 Name: "clientOnlyCustomFilter", 461 Filter: clientOnlyHTTPFilter{}, 462 Config: filterConfig{Cfg: clientOnlyCustomFilterConfig}, 463 }, 464 routerFilter}, 465 Raw: v3LisWithFilters(clientOnlyCustomFilter), 466 }}, 467 }, 468 wantMD: UpdateMetadata{ 469 Status: ServiceStatusACKed, 470 Version: testVersion, 471 }, 472 }, 473 { 474 name: "v3 with err filter", 475 resources: []*anypb.Any{v3LisWithFilters(errFilter)}, 476 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 477 wantMD: errMD, 478 wantErr: true, 479 }, 480 { 481 name: "v3 with optional err filter", 482 resources: []*anypb.Any{v3LisWithFilters(errOptionalFilter)}, 483 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 484 wantMD: errMD, 485 wantErr: true, 486 }, 487 { 488 name: "v3 with unknown filter", 489 resources: []*anypb.Any{v3LisWithFilters(unknownFilter)}, 490 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 491 wantMD: errMD, 492 wantErr: true, 493 }, 494 { 495 name: "v3 with unknown filter (optional)", 496 resources: []*anypb.Any{v3LisWithFilters(unknownOptionalFilter)}, 497 wantUpdate: map[string]ListenerUpdateErrTuple{ 498 v3LDSTarget: {Update: ListenerUpdate{ 499 RouteConfigName: v3RouteConfigName, 500 MaxStreamDuration: time.Second, 501 HTTPFilters: routerFilterList, 502 Raw: v3LisWithFilters(unknownOptionalFilter), 503 }}, 504 }, 505 wantMD: UpdateMetadata{ 506 Status: ServiceStatusACKed, 507 Version: testVersion, 508 }, 509 }, 510 { 511 name: "v2 listener resource", 512 resources: []*anypb.Any{v2Lis}, 513 wantUpdate: map[string]ListenerUpdateErrTuple{ 514 v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}}, 515 }, 516 wantMD: UpdateMetadata{ 517 Status: ServiceStatusACKed, 518 Version: testVersion, 519 }, 520 }, 521 { 522 name: "v3 listener resource", 523 resources: []*anypb.Any{v3LisWithFilters()}, 524 wantUpdate: map[string]ListenerUpdateErrTuple{ 525 v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, HTTPFilters: routerFilterList, Raw: v3LisWithFilters()}}, 526 }, 527 wantMD: UpdateMetadata{ 528 Status: ServiceStatusACKed, 529 Version: testVersion, 530 }, 531 }, 532 { 533 name: "v3 listener with inline route configuration", 534 resources: []*anypb.Any{v3LisWithInlineRoute}, 535 wantUpdate: map[string]ListenerUpdateErrTuple{ 536 v3LDSTarget: {Update: ListenerUpdate{ 537 InlineRouteConfig: &RouteConfigUpdate{ 538 VirtualHosts: []*VirtualHost{{ 539 Domains: []string{v3LDSTarget}, 540 Routes: []*Route{{Prefix: newStringP("/"), WeightedClusters: map[string]WeightedCluster{clusterName: {Weight: 1}}, RouteAction: RouteActionRoute}}, 541 }}}, 542 MaxStreamDuration: time.Second, 543 Raw: v3LisWithInlineRoute, 544 HTTPFilters: routerFilterList, 545 }}, 546 }, 547 wantMD: UpdateMetadata{ 548 Status: ServiceStatusACKed, 549 Version: testVersion, 550 }, 551 }, 552 { 553 name: "multiple listener resources", 554 resources: []*anypb.Any{v2Lis, v3LisWithFilters()}, 555 wantUpdate: map[string]ListenerUpdateErrTuple{ 556 v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}}, 557 v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters(), HTTPFilters: routerFilterList}}, 558 }, 559 wantMD: UpdateMetadata{ 560 Status: ServiceStatusACKed, 561 Version: testVersion, 562 }, 563 }, 564 { 565 // To test that unmarshal keeps processing on errors. 566 name: "good and bad listener resources", 567 resources: []*anypb.Any{ 568 v2Lis, 569 testutils.MarshalAny(&v3listenerpb.Listener{ 570 Name: "bad", 571 ApiListener: &v3listenerpb.ApiListener{ 572 ApiListener: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 573 RouteSpecifier: &v3httppb.HttpConnectionManager_ScopedRoutes{}, 574 }), 575 }}), 576 v3LisWithFilters(), 577 }, 578 wantUpdate: map[string]ListenerUpdateErrTuple{ 579 v2LDSTarget: {Update: ListenerUpdate{RouteConfigName: v2RouteConfigName, Raw: v2Lis}}, 580 v3LDSTarget: {Update: ListenerUpdate{RouteConfigName: v3RouteConfigName, MaxStreamDuration: time.Second, Raw: v3LisWithFilters(), HTTPFilters: routerFilterList}}, 581 "bad": {Err: cmpopts.AnyError}, 582 }, 583 wantMD: errMD, 584 wantErr: true, 585 }, 586 } 587 588 for _, test := range tests { 589 t.Run(test.name, func(t *testing.T) { 590 update, md, err := UnmarshalListener(testVersion, test.resources, nil) 591 if (err != nil) != test.wantErr { 592 t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr) 593 } 594 if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" { 595 t.Errorf("got unexpected update, diff (-got +want): %v", diff) 596 } 597 if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" { 598 t.Errorf("got unexpected metadata, diff (-got +want): %v", diff) 599 } 600 }) 601 } 602} 603 604func (s) TestUnmarshalListener_ServerSide(t *testing.T) { 605 oldRBAC := env.RBACSupport 606 env.RBACSupport = true 607 defer func() { 608 env.RBACSupport = oldRBAC 609 }() 610 const ( 611 v3LDSTarget = "grpc/server?xds.resource.listening_address=0.0.0.0:9999" 612 testVersion = "test-version-lds-server" 613 ) 614 615 var ( 616 serverOnlyCustomFilter = &v3httppb.HttpFilter{ 617 Name: "serverOnlyCustomFilter", 618 ConfigType: &v3httppb.HttpFilter_TypedConfig{TypedConfig: serverOnlyCustomFilterConfig}, 619 } 620 routeConfig = &v3routepb.RouteConfiguration{ 621 Name: "routeName", 622 VirtualHosts: []*v3routepb.VirtualHost{{ 623 Domains: []string{"lds.target.good:3333"}, 624 Routes: []*v3routepb.Route{{ 625 Match: &v3routepb.RouteMatch{ 626 PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}, 627 }, 628 Action: &v3routepb.Route_NonForwardingAction{}, 629 }}}}} 630 inlineRouteConfig = &RouteConfigUpdate{ 631 VirtualHosts: []*VirtualHost{{ 632 Domains: []string{"lds.target.good:3333"}, 633 Routes: []*Route{{Prefix: newStringP("/"), RouteAction: RouteActionNonForwardingAction}}, 634 }}} 635 emptyValidNetworkFilters = []*v3listenerpb.Filter{ 636 { 637 Name: "filter-1", 638 ConfigType: &v3listenerpb.Filter_TypedConfig{ 639 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 640 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 641 RouteConfig: routeConfig, 642 }, 643 HttpFilters: []*v3httppb.HttpFilter{e2e.RouterHTTPFilter}, 644 }), 645 }, 646 }, 647 } 648 localSocketAddress = &v3corepb.Address{ 649 Address: &v3corepb.Address_SocketAddress{ 650 SocketAddress: &v3corepb.SocketAddress{ 651 Address: "0.0.0.0", 652 PortSpecifier: &v3corepb.SocketAddress_PortValue{ 653 PortValue: 9999, 654 }, 655 }, 656 }, 657 } 658 listenerEmptyTransportSocket = testutils.MarshalAny(&v3listenerpb.Listener{ 659 Name: v3LDSTarget, 660 Address: localSocketAddress, 661 FilterChains: []*v3listenerpb.FilterChain{ 662 { 663 Name: "filter-chain-1", 664 Filters: emptyValidNetworkFilters, 665 }, 666 }, 667 }) 668 listenerNoValidationContextDeprecatedFields = testutils.MarshalAny(&v3listenerpb.Listener{ 669 Name: v3LDSTarget, 670 Address: localSocketAddress, 671 FilterChains: []*v3listenerpb.FilterChain{ 672 { 673 Name: "filter-chain-1", 674 Filters: emptyValidNetworkFilters, 675 TransportSocket: &v3corepb.TransportSocket{ 676 Name: "envoy.transport_sockets.tls", 677 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 678 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 679 CommonTlsContext: &v3tlspb.CommonTlsContext{ 680 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 681 InstanceName: "identityPluginInstance", 682 CertificateName: "identityCertName", 683 }, 684 }, 685 }), 686 }, 687 }, 688 }, 689 }, 690 DefaultFilterChain: &v3listenerpb.FilterChain{ 691 Name: "default-filter-chain-1", 692 Filters: emptyValidNetworkFilters, 693 TransportSocket: &v3corepb.TransportSocket{ 694 Name: "envoy.transport_sockets.tls", 695 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 696 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 697 CommonTlsContext: &v3tlspb.CommonTlsContext{ 698 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 699 InstanceName: "defaultIdentityPluginInstance", 700 CertificateName: "defaultIdentityCertName", 701 }, 702 }, 703 }), 704 }, 705 }, 706 }, 707 }) 708 listenerNoValidationContextNewFields = testutils.MarshalAny(&v3listenerpb.Listener{ 709 Name: v3LDSTarget, 710 Address: localSocketAddress, 711 FilterChains: []*v3listenerpb.FilterChain{ 712 { 713 Name: "filter-chain-1", 714 Filters: emptyValidNetworkFilters, 715 TransportSocket: &v3corepb.TransportSocket{ 716 Name: "envoy.transport_sockets.tls", 717 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 718 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 719 CommonTlsContext: &v3tlspb.CommonTlsContext{ 720 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 721 InstanceName: "identityPluginInstance", 722 CertificateName: "identityCertName", 723 }, 724 }, 725 }), 726 }, 727 }, 728 }, 729 }, 730 DefaultFilterChain: &v3listenerpb.FilterChain{ 731 Name: "default-filter-chain-1", 732 Filters: emptyValidNetworkFilters, 733 TransportSocket: &v3corepb.TransportSocket{ 734 Name: "envoy.transport_sockets.tls", 735 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 736 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 737 CommonTlsContext: &v3tlspb.CommonTlsContext{ 738 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 739 InstanceName: "defaultIdentityPluginInstance", 740 CertificateName: "defaultIdentityCertName", 741 }, 742 }, 743 }), 744 }, 745 }, 746 }, 747 }) 748 listenerWithValidationContextDeprecatedFields = testutils.MarshalAny(&v3listenerpb.Listener{ 749 Name: v3LDSTarget, 750 Address: localSocketAddress, 751 FilterChains: []*v3listenerpb.FilterChain{ 752 { 753 Name: "filter-chain-1", 754 Filters: emptyValidNetworkFilters, 755 TransportSocket: &v3corepb.TransportSocket{ 756 Name: "envoy.transport_sockets.tls", 757 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 758 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 759 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 760 CommonTlsContext: &v3tlspb.CommonTlsContext{ 761 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 762 InstanceName: "identityPluginInstance", 763 CertificateName: "identityCertName", 764 }, 765 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 766 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 767 InstanceName: "rootPluginInstance", 768 CertificateName: "rootCertName", 769 }, 770 }, 771 }, 772 }), 773 }, 774 }, 775 }, 776 }, 777 DefaultFilterChain: &v3listenerpb.FilterChain{ 778 Name: "default-filter-chain-1", 779 Filters: emptyValidNetworkFilters, 780 TransportSocket: &v3corepb.TransportSocket{ 781 Name: "envoy.transport_sockets.tls", 782 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 783 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 784 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 785 CommonTlsContext: &v3tlspb.CommonTlsContext{ 786 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 787 InstanceName: "defaultIdentityPluginInstance", 788 CertificateName: "defaultIdentityCertName", 789 }, 790 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextCertificateProviderInstance{ 791 ValidationContextCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 792 InstanceName: "defaultRootPluginInstance", 793 CertificateName: "defaultRootCertName", 794 }, 795 }, 796 }, 797 }), 798 }, 799 }, 800 }, 801 }) 802 listenerWithValidationContextNewFields = testutils.MarshalAny(&v3listenerpb.Listener{ 803 Name: v3LDSTarget, 804 Address: localSocketAddress, 805 FilterChains: []*v3listenerpb.FilterChain{ 806 { 807 Name: "filter-chain-1", 808 Filters: emptyValidNetworkFilters, 809 TransportSocket: &v3corepb.TransportSocket{ 810 Name: "envoy.transport_sockets.tls", 811 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 812 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 813 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 814 CommonTlsContext: &v3tlspb.CommonTlsContext{ 815 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 816 InstanceName: "identityPluginInstance", 817 CertificateName: "identityCertName", 818 }, 819 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContext{ 820 ValidationContext: &v3tlspb.CertificateValidationContext{ 821 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 822 InstanceName: "rootPluginInstance", 823 CertificateName: "rootCertName", 824 }, 825 }, 826 }, 827 }, 828 }), 829 }, 830 }, 831 }, 832 }, 833 DefaultFilterChain: &v3listenerpb.FilterChain{ 834 Name: "default-filter-chain-1", 835 Filters: emptyValidNetworkFilters, 836 TransportSocket: &v3corepb.TransportSocket{ 837 Name: "envoy.transport_sockets.tls", 838 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 839 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 840 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 841 CommonTlsContext: &v3tlspb.CommonTlsContext{ 842 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 843 InstanceName: "defaultIdentityPluginInstance", 844 CertificateName: "defaultIdentityCertName", 845 }, 846 ValidationContextType: &v3tlspb.CommonTlsContext_CombinedValidationContext{ 847 CombinedValidationContext: &v3tlspb.CommonTlsContext_CombinedCertificateValidationContext{ 848 DefaultValidationContext: &v3tlspb.CertificateValidationContext{ 849 CaCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 850 InstanceName: "defaultRootPluginInstance", 851 CertificateName: "defaultRootCertName", 852 }, 853 }, 854 }, 855 }, 856 }, 857 }), 858 }, 859 }, 860 }, 861 }) 862 errMD = UpdateMetadata{ 863 Status: ServiceStatusNACKed, 864 Version: testVersion, 865 ErrState: &UpdateErrorMetadata{ 866 Version: testVersion, 867 Err: cmpopts.AnyError, 868 }, 869 } 870 ) 871 872 tests := []struct { 873 name string 874 resources []*anypb.Any 875 wantUpdate map[string]ListenerUpdateErrTuple 876 wantMD UpdateMetadata 877 wantErr string 878 }{ 879 { 880 name: "non-empty listener filters", 881 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 882 Name: v3LDSTarget, 883 ListenerFilters: []*v3listenerpb.ListenerFilter{ 884 {Name: "listener-filter-1"}, 885 }, 886 })}, 887 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 888 wantMD: errMD, 889 wantErr: "unsupported field 'listener_filters'", 890 }, 891 { 892 name: "use_original_dst is set", 893 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 894 Name: v3LDSTarget, 895 UseOriginalDst: &wrapperspb.BoolValue{Value: true}, 896 })}, 897 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 898 wantMD: errMD, 899 wantErr: "unsupported field 'use_original_dst'", 900 }, 901 { 902 name: "no address field", 903 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{Name: v3LDSTarget})}, 904 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 905 wantMD: errMD, 906 wantErr: "no address field in LDS response", 907 }, 908 { 909 name: "no socket address field", 910 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 911 Name: v3LDSTarget, 912 Address: &v3corepb.Address{}, 913 })}, 914 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 915 wantMD: errMD, 916 wantErr: "no socket_address field in LDS response", 917 }, 918 { 919 name: "no filter chains and no default filter chain", 920 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 921 Name: v3LDSTarget, 922 Address: localSocketAddress, 923 FilterChains: []*v3listenerpb.FilterChain{ 924 { 925 FilterChainMatch: &v3listenerpb.FilterChainMatch{DestinationPort: &wrapperspb.UInt32Value{Value: 666}}, 926 Filters: emptyValidNetworkFilters, 927 }, 928 }, 929 })}, 930 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 931 wantMD: errMD, 932 wantErr: "no supported filter chains and no default filter chain", 933 }, 934 { 935 name: "missing http connection manager network filter", 936 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 937 Name: v3LDSTarget, 938 Address: localSocketAddress, 939 FilterChains: []*v3listenerpb.FilterChain{ 940 { 941 Name: "filter-chain-1", 942 }, 943 }, 944 })}, 945 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 946 wantMD: errMD, 947 wantErr: "missing HttpConnectionManager filter", 948 }, 949 { 950 name: "missing filter name in http filter", 951 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 952 Name: v3LDSTarget, 953 Address: localSocketAddress, 954 FilterChains: []*v3listenerpb.FilterChain{ 955 { 956 Name: "filter-chain-1", 957 Filters: []*v3listenerpb.Filter{ 958 { 959 ConfigType: &v3listenerpb.Filter_TypedConfig{ 960 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{}), 961 }, 962 }, 963 }, 964 }, 965 }, 966 })}, 967 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 968 wantMD: errMD, 969 wantErr: "missing name field in filter", 970 }, 971 { 972 name: "duplicate filter names in http filter", 973 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 974 Name: v3LDSTarget, 975 Address: localSocketAddress, 976 FilterChains: []*v3listenerpb.FilterChain{ 977 { 978 Name: "filter-chain-1", 979 Filters: []*v3listenerpb.Filter{ 980 { 981 Name: "name", 982 ConfigType: &v3listenerpb.Filter_TypedConfig{ 983 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 984 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 985 RouteConfig: routeConfig, 986 }, 987 HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter}, 988 }), 989 }, 990 }, 991 { 992 Name: "name", 993 ConfigType: &v3listenerpb.Filter_TypedConfig{ 994 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 995 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 996 RouteConfig: routeConfig, 997 }, 998 HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter}, 999 }), 1000 }, 1001 }, 1002 }, 1003 }, 1004 }, 1005 })}, 1006 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1007 wantMD: errMD, 1008 wantErr: "duplicate filter name", 1009 }, 1010 { 1011 name: "no terminal filter", 1012 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1013 Name: v3LDSTarget, 1014 Address: localSocketAddress, 1015 FilterChains: []*v3listenerpb.FilterChain{ 1016 { 1017 Name: "filter-chain-1", 1018 Filters: []*v3listenerpb.Filter{ 1019 { 1020 Name: "name", 1021 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1022 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 1023 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 1024 RouteConfig: routeConfig, 1025 }, 1026 }), 1027 }, 1028 }, 1029 }, 1030 }, 1031 }, 1032 })}, 1033 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1034 wantMD: errMD, 1035 wantErr: "http filters list is empty", 1036 }, 1037 { 1038 name: "terminal filter not last", 1039 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1040 Name: v3LDSTarget, 1041 Address: localSocketAddress, 1042 FilterChains: []*v3listenerpb.FilterChain{ 1043 { 1044 Name: "filter-chain-1", 1045 Filters: []*v3listenerpb.Filter{ 1046 { 1047 Name: "name", 1048 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1049 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 1050 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 1051 RouteConfig: routeConfig, 1052 }, 1053 HttpFilters: []*v3httppb.HttpFilter{emptyRouterFilter, serverOnlyCustomFilter}, 1054 }), 1055 }, 1056 }, 1057 }, 1058 }, 1059 }, 1060 })}, 1061 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1062 wantMD: errMD, 1063 wantErr: "is a terminal filter but it is not last in the filter chain", 1064 }, 1065 { 1066 name: "last not terminal filter", 1067 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1068 Name: v3LDSTarget, 1069 Address: localSocketAddress, 1070 FilterChains: []*v3listenerpb.FilterChain{ 1071 { 1072 Name: "filter-chain-1", 1073 Filters: []*v3listenerpb.Filter{ 1074 { 1075 Name: "name", 1076 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1077 TypedConfig: testutils.MarshalAny(&v3httppb.HttpConnectionManager{ 1078 RouteSpecifier: &v3httppb.HttpConnectionManager_RouteConfig{ 1079 RouteConfig: routeConfig, 1080 }, 1081 HttpFilters: []*v3httppb.HttpFilter{serverOnlyCustomFilter}, 1082 }), 1083 }, 1084 }, 1085 }, 1086 }, 1087 }, 1088 })}, 1089 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1090 wantMD: errMD, 1091 wantErr: "is not a terminal filter", 1092 }, 1093 { 1094 name: "unsupported oneof in typed config of http filter", 1095 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1096 Name: v3LDSTarget, 1097 Address: localSocketAddress, 1098 FilterChains: []*v3listenerpb.FilterChain{ 1099 { 1100 Name: "filter-chain-1", 1101 Filters: []*v3listenerpb.Filter{ 1102 { 1103 Name: "name", 1104 ConfigType: &v3listenerpb.Filter_ConfigDiscovery{}, 1105 }, 1106 }, 1107 }, 1108 }, 1109 })}, 1110 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1111 wantMD: errMD, 1112 wantErr: "unsupported config_type", 1113 }, 1114 { 1115 name: "overlapping filter chain match criteria", 1116 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1117 Name: v3LDSTarget, 1118 Address: localSocketAddress, 1119 FilterChains: []*v3listenerpb.FilterChain{ 1120 { 1121 FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{1, 2, 3, 4, 5}}, 1122 Filters: emptyValidNetworkFilters, 1123 }, 1124 { 1125 FilterChainMatch: &v3listenerpb.FilterChainMatch{}, 1126 Filters: emptyValidNetworkFilters, 1127 }, 1128 { 1129 FilterChainMatch: &v3listenerpb.FilterChainMatch{SourcePorts: []uint32{5, 6, 7}}, 1130 Filters: emptyValidNetworkFilters, 1131 }, 1132 }, 1133 })}, 1134 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1135 wantMD: errMD, 1136 wantErr: "multiple filter chains with overlapping matching rules are defined", 1137 }, 1138 { 1139 name: "unsupported network filter", 1140 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1141 Name: v3LDSTarget, 1142 Address: localSocketAddress, 1143 FilterChains: []*v3listenerpb.FilterChain{ 1144 { 1145 Name: "filter-chain-1", 1146 Filters: []*v3listenerpb.Filter{ 1147 { 1148 Name: "name", 1149 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1150 TypedConfig: testutils.MarshalAny(&v3httppb.LocalReplyConfig{}), 1151 }, 1152 }, 1153 }, 1154 }, 1155 }, 1156 })}, 1157 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1158 wantMD: errMD, 1159 wantErr: "unsupported network filter", 1160 }, 1161 { 1162 name: "badly marshaled network filter", 1163 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1164 Name: v3LDSTarget, 1165 Address: localSocketAddress, 1166 FilterChains: []*v3listenerpb.FilterChain{ 1167 { 1168 Name: "filter-chain-1", 1169 Filters: []*v3listenerpb.Filter{ 1170 { 1171 Name: "name", 1172 ConfigType: &v3listenerpb.Filter_TypedConfig{ 1173 TypedConfig: &anypb.Any{ 1174 TypeUrl: version.V3HTTPConnManagerURL, 1175 Value: []byte{1, 2, 3, 4}, 1176 }, 1177 }, 1178 }, 1179 }, 1180 }, 1181 }, 1182 })}, 1183 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1184 wantMD: errMD, 1185 wantErr: "failed unmarshaling of network filter", 1186 }, 1187 { 1188 name: "unexpected transport socket name", 1189 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1190 Name: v3LDSTarget, 1191 Address: localSocketAddress, 1192 FilterChains: []*v3listenerpb.FilterChain{ 1193 { 1194 Name: "filter-chain-1", 1195 Filters: emptyValidNetworkFilters, 1196 TransportSocket: &v3corepb.TransportSocket{ 1197 Name: "unsupported-transport-socket-name", 1198 }, 1199 }, 1200 }, 1201 })}, 1202 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1203 wantMD: errMD, 1204 wantErr: "transport_socket field has unexpected name", 1205 }, 1206 { 1207 name: "unexpected transport socket typedConfig URL", 1208 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1209 Name: v3LDSTarget, 1210 Address: localSocketAddress, 1211 FilterChains: []*v3listenerpb.FilterChain{ 1212 { 1213 Name: "filter-chain-1", 1214 Filters: emptyValidNetworkFilters, 1215 TransportSocket: &v3corepb.TransportSocket{ 1216 Name: "envoy.transport_sockets.tls", 1217 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1218 TypedConfig: testutils.MarshalAny(&v3tlspb.UpstreamTlsContext{}), 1219 }, 1220 }, 1221 }, 1222 }, 1223 })}, 1224 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1225 wantMD: errMD, 1226 wantErr: "transport_socket field has unexpected typeURL", 1227 }, 1228 { 1229 name: "badly marshaled transport socket", 1230 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1231 Name: v3LDSTarget, 1232 Address: localSocketAddress, 1233 FilterChains: []*v3listenerpb.FilterChain{ 1234 { 1235 Name: "filter-chain-1", 1236 Filters: emptyValidNetworkFilters, 1237 TransportSocket: &v3corepb.TransportSocket{ 1238 Name: "envoy.transport_sockets.tls", 1239 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1240 TypedConfig: &anypb.Any{ 1241 TypeUrl: version.V3DownstreamTLSContextURL, 1242 Value: []byte{1, 2, 3, 4}, 1243 }, 1244 }, 1245 }, 1246 }, 1247 }, 1248 })}, 1249 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1250 wantMD: errMD, 1251 wantErr: "failed to unmarshal DownstreamTlsContext in LDS response", 1252 }, 1253 { 1254 name: "missing CommonTlsContext", 1255 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1256 Name: v3LDSTarget, 1257 Address: localSocketAddress, 1258 FilterChains: []*v3listenerpb.FilterChain{ 1259 { 1260 Name: "filter-chain-1", 1261 Filters: emptyValidNetworkFilters, 1262 TransportSocket: &v3corepb.TransportSocket{ 1263 Name: "envoy.transport_sockets.tls", 1264 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1265 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{}), 1266 }, 1267 }, 1268 }, 1269 }, 1270 })}, 1271 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1272 wantMD: errMD, 1273 wantErr: "DownstreamTlsContext in LDS response does not contain a CommonTlsContext", 1274 }, 1275 { 1276 name: "unsupported validation context in transport socket", 1277 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1278 Name: v3LDSTarget, 1279 Address: localSocketAddress, 1280 FilterChains: []*v3listenerpb.FilterChain{ 1281 { 1282 Name: "filter-chain-1", 1283 Filters: emptyValidNetworkFilters, 1284 TransportSocket: &v3corepb.TransportSocket{ 1285 Name: "envoy.transport_sockets.tls", 1286 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1287 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1288 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1289 ValidationContextType: &v3tlspb.CommonTlsContext_ValidationContextSdsSecretConfig{ 1290 ValidationContextSdsSecretConfig: &v3tlspb.SdsSecretConfig{ 1291 Name: "foo-sds-secret", 1292 }, 1293 }, 1294 }, 1295 }), 1296 }, 1297 }, 1298 }, 1299 }, 1300 })}, 1301 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1302 wantMD: errMD, 1303 wantErr: "validation context contains unexpected type", 1304 }, 1305 { 1306 name: "empty transport socket", 1307 resources: []*anypb.Any{listenerEmptyTransportSocket}, 1308 wantUpdate: map[string]ListenerUpdateErrTuple{ 1309 v3LDSTarget: {Update: ListenerUpdate{ 1310 InboundListenerCfg: &InboundListenerConfig{ 1311 Address: "0.0.0.0", 1312 Port: "9999", 1313 FilterChains: &FilterChainManager{ 1314 dstPrefixMap: map[string]*destPrefixEntry{ 1315 unspecifiedPrefixMapKey: { 1316 srcTypeArr: [3]*sourcePrefixes{ 1317 { 1318 srcPrefixMap: map[string]*sourcePrefixEntry{ 1319 unspecifiedPrefixMapKey: { 1320 srcPortMap: map[int]*FilterChain{ 1321 0: { 1322 InlineRouteConfig: inlineRouteConfig, 1323 HTTPFilters: routerFilterList, 1324 }, 1325 }, 1326 }, 1327 }, 1328 }, 1329 }, 1330 }, 1331 }, 1332 }, 1333 }, 1334 Raw: listenerEmptyTransportSocket, 1335 }}, 1336 }, 1337 wantMD: UpdateMetadata{ 1338 Status: ServiceStatusACKed, 1339 Version: testVersion, 1340 }, 1341 }, 1342 { 1343 name: "no identity and root certificate providers using deprecated fields", 1344 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1345 Name: v3LDSTarget, 1346 Address: localSocketAddress, 1347 FilterChains: []*v3listenerpb.FilterChain{ 1348 { 1349 Name: "filter-chain-1", 1350 Filters: emptyValidNetworkFilters, 1351 TransportSocket: &v3corepb.TransportSocket{ 1352 Name: "envoy.transport_sockets.tls", 1353 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1354 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1355 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 1356 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1357 TlsCertificateCertificateProviderInstance: &v3tlspb.CommonTlsContext_CertificateProviderInstance{ 1358 InstanceName: "identityPluginInstance", 1359 CertificateName: "identityCertName", 1360 }, 1361 }, 1362 }), 1363 }, 1364 }, 1365 }, 1366 }, 1367 })}, 1368 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1369 wantMD: errMD, 1370 wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set", 1371 }, 1372 { 1373 name: "no identity and root certificate providers using new fields", 1374 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1375 Name: v3LDSTarget, 1376 Address: localSocketAddress, 1377 FilterChains: []*v3listenerpb.FilterChain{ 1378 { 1379 Name: "filter-chain-1", 1380 Filters: emptyValidNetworkFilters, 1381 TransportSocket: &v3corepb.TransportSocket{ 1382 Name: "envoy.transport_sockets.tls", 1383 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1384 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1385 RequireClientCertificate: &wrapperspb.BoolValue{Value: true}, 1386 CommonTlsContext: &v3tlspb.CommonTlsContext{ 1387 TlsCertificateProviderInstance: &v3tlspb.CertificateProviderPluginInstance{ 1388 InstanceName: "identityPluginInstance", 1389 CertificateName: "identityCertName", 1390 }, 1391 }, 1392 }), 1393 }, 1394 }, 1395 }, 1396 }, 1397 })}, 1398 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1399 wantMD: errMD, 1400 wantErr: "security configuration on the server-side does not contain root certificate provider instance name, but require_client_cert field is set", 1401 }, 1402 { 1403 name: "no identity certificate provider with require_client_cert", 1404 resources: []*anypb.Any{testutils.MarshalAny(&v3listenerpb.Listener{ 1405 Name: v3LDSTarget, 1406 Address: localSocketAddress, 1407 FilterChains: []*v3listenerpb.FilterChain{ 1408 { 1409 Name: "filter-chain-1", 1410 Filters: emptyValidNetworkFilters, 1411 TransportSocket: &v3corepb.TransportSocket{ 1412 Name: "envoy.transport_sockets.tls", 1413 ConfigType: &v3corepb.TransportSocket_TypedConfig{ 1414 TypedConfig: testutils.MarshalAny(&v3tlspb.DownstreamTlsContext{ 1415 CommonTlsContext: &v3tlspb.CommonTlsContext{}, 1416 }), 1417 }, 1418 }, 1419 }, 1420 }, 1421 })}, 1422 wantUpdate: map[string]ListenerUpdateErrTuple{v3LDSTarget: {Err: cmpopts.AnyError}}, 1423 wantMD: errMD, 1424 wantErr: "security configuration on the server-side does not contain identity certificate provider instance name", 1425 }, 1426 { 1427 name: "happy case with no validation context using deprecated fields", 1428 resources: []*anypb.Any{listenerNoValidationContextDeprecatedFields}, 1429 wantUpdate: map[string]ListenerUpdateErrTuple{ 1430 v3LDSTarget: {Update: ListenerUpdate{ 1431 InboundListenerCfg: &InboundListenerConfig{ 1432 Address: "0.0.0.0", 1433 Port: "9999", 1434 FilterChains: &FilterChainManager{ 1435 dstPrefixMap: map[string]*destPrefixEntry{ 1436 unspecifiedPrefixMapKey: { 1437 srcTypeArr: [3]*sourcePrefixes{ 1438 { 1439 srcPrefixMap: map[string]*sourcePrefixEntry{ 1440 unspecifiedPrefixMapKey: { 1441 srcPortMap: map[int]*FilterChain{ 1442 0: { 1443 SecurityCfg: &SecurityConfig{ 1444 IdentityInstanceName: "identityPluginInstance", 1445 IdentityCertName: "identityCertName", 1446 }, 1447 InlineRouteConfig: inlineRouteConfig, 1448 HTTPFilters: routerFilterList, 1449 }, 1450 }, 1451 }, 1452 }, 1453 }, 1454 }, 1455 }, 1456 }, 1457 def: &FilterChain{ 1458 SecurityCfg: &SecurityConfig{ 1459 IdentityInstanceName: "defaultIdentityPluginInstance", 1460 IdentityCertName: "defaultIdentityCertName", 1461 }, 1462 InlineRouteConfig: inlineRouteConfig, 1463 HTTPFilters: routerFilterList, 1464 }, 1465 }, 1466 }, 1467 Raw: listenerNoValidationContextDeprecatedFields, 1468 }}, 1469 }, 1470 wantMD: UpdateMetadata{ 1471 Status: ServiceStatusACKed, 1472 Version: testVersion, 1473 }, 1474 }, 1475 { 1476 name: "happy case with no validation context using new fields", 1477 resources: []*anypb.Any{listenerNoValidationContextNewFields}, 1478 wantUpdate: map[string]ListenerUpdateErrTuple{ 1479 v3LDSTarget: {Update: ListenerUpdate{ 1480 InboundListenerCfg: &InboundListenerConfig{ 1481 Address: "0.0.0.0", 1482 Port: "9999", 1483 FilterChains: &FilterChainManager{ 1484 dstPrefixMap: map[string]*destPrefixEntry{ 1485 unspecifiedPrefixMapKey: { 1486 srcTypeArr: [3]*sourcePrefixes{ 1487 { 1488 srcPrefixMap: map[string]*sourcePrefixEntry{ 1489 unspecifiedPrefixMapKey: { 1490 srcPortMap: map[int]*FilterChain{ 1491 0: { 1492 SecurityCfg: &SecurityConfig{ 1493 IdentityInstanceName: "identityPluginInstance", 1494 IdentityCertName: "identityCertName", 1495 }, 1496 InlineRouteConfig: inlineRouteConfig, 1497 HTTPFilters: routerFilterList, 1498 }, 1499 }, 1500 }, 1501 }, 1502 }, 1503 }, 1504 }, 1505 }, 1506 def: &FilterChain{ 1507 SecurityCfg: &SecurityConfig{ 1508 IdentityInstanceName: "defaultIdentityPluginInstance", 1509 IdentityCertName: "defaultIdentityCertName", 1510 }, 1511 InlineRouteConfig: inlineRouteConfig, 1512 HTTPFilters: routerFilterList, 1513 }, 1514 }, 1515 }, 1516 Raw: listenerNoValidationContextNewFields, 1517 }}, 1518 }, 1519 wantMD: UpdateMetadata{ 1520 Status: ServiceStatusACKed, 1521 Version: testVersion, 1522 }, 1523 }, 1524 { 1525 name: "happy case with validation context provider instance with deprecated fields", 1526 resources: []*anypb.Any{listenerWithValidationContextDeprecatedFields}, 1527 wantUpdate: map[string]ListenerUpdateErrTuple{ 1528 v3LDSTarget: {Update: ListenerUpdate{ 1529 InboundListenerCfg: &InboundListenerConfig{ 1530 Address: "0.0.0.0", 1531 Port: "9999", 1532 FilterChains: &FilterChainManager{ 1533 dstPrefixMap: map[string]*destPrefixEntry{ 1534 unspecifiedPrefixMapKey: { 1535 srcTypeArr: [3]*sourcePrefixes{ 1536 { 1537 srcPrefixMap: map[string]*sourcePrefixEntry{ 1538 unspecifiedPrefixMapKey: { 1539 srcPortMap: map[int]*FilterChain{ 1540 0: { 1541 SecurityCfg: &SecurityConfig{ 1542 RootInstanceName: "rootPluginInstance", 1543 RootCertName: "rootCertName", 1544 IdentityInstanceName: "identityPluginInstance", 1545 IdentityCertName: "identityCertName", 1546 RequireClientCert: true, 1547 }, 1548 InlineRouteConfig: inlineRouteConfig, 1549 HTTPFilters: routerFilterList, 1550 }, 1551 }, 1552 }, 1553 }, 1554 }, 1555 }, 1556 }, 1557 }, 1558 def: &FilterChain{ 1559 SecurityCfg: &SecurityConfig{ 1560 RootInstanceName: "defaultRootPluginInstance", 1561 RootCertName: "defaultRootCertName", 1562 IdentityInstanceName: "defaultIdentityPluginInstance", 1563 IdentityCertName: "defaultIdentityCertName", 1564 RequireClientCert: true, 1565 }, 1566 InlineRouteConfig: inlineRouteConfig, 1567 HTTPFilters: routerFilterList, 1568 }, 1569 }, 1570 }, 1571 Raw: listenerWithValidationContextDeprecatedFields, 1572 }}, 1573 }, 1574 wantMD: UpdateMetadata{ 1575 Status: ServiceStatusACKed, 1576 Version: testVersion, 1577 }, 1578 }, 1579 { 1580 name: "happy case with validation context provider instance with new fields", 1581 resources: []*anypb.Any{listenerWithValidationContextNewFields}, 1582 wantUpdate: map[string]ListenerUpdateErrTuple{ 1583 v3LDSTarget: {Update: ListenerUpdate{ 1584 InboundListenerCfg: &InboundListenerConfig{ 1585 Address: "0.0.0.0", 1586 Port: "9999", 1587 FilterChains: &FilterChainManager{ 1588 dstPrefixMap: map[string]*destPrefixEntry{ 1589 unspecifiedPrefixMapKey: { 1590 srcTypeArr: [3]*sourcePrefixes{ 1591 { 1592 srcPrefixMap: map[string]*sourcePrefixEntry{ 1593 unspecifiedPrefixMapKey: { 1594 srcPortMap: map[int]*FilterChain{ 1595 0: { 1596 SecurityCfg: &SecurityConfig{ 1597 RootInstanceName: "rootPluginInstance", 1598 RootCertName: "rootCertName", 1599 IdentityInstanceName: "identityPluginInstance", 1600 IdentityCertName: "identityCertName", 1601 RequireClientCert: true, 1602 }, 1603 InlineRouteConfig: inlineRouteConfig, 1604 HTTPFilters: routerFilterList, 1605 }, 1606 }, 1607 }, 1608 }, 1609 }, 1610 }, 1611 }, 1612 }, 1613 def: &FilterChain{ 1614 SecurityCfg: &SecurityConfig{ 1615 RootInstanceName: "defaultRootPluginInstance", 1616 RootCertName: "defaultRootCertName", 1617 IdentityInstanceName: "defaultIdentityPluginInstance", 1618 IdentityCertName: "defaultIdentityCertName", 1619 RequireClientCert: true, 1620 }, 1621 InlineRouteConfig: inlineRouteConfig, 1622 HTTPFilters: routerFilterList, 1623 }, 1624 }, 1625 }, 1626 Raw: listenerWithValidationContextNewFields, 1627 }}, 1628 }, 1629 wantMD: UpdateMetadata{ 1630 Status: ServiceStatusACKed, 1631 Version: testVersion, 1632 }, 1633 }, 1634 } 1635 1636 for _, test := range tests { 1637 t.Run(test.name, func(t *testing.T) { 1638 gotUpdate, md, err := UnmarshalListener(testVersion, test.resources, nil) 1639 if (err != nil) != (test.wantErr != "") { 1640 t.Fatalf("UnmarshalListener(), got err: %v, wantErr: %v", err, test.wantErr) 1641 } 1642 if err != nil && !strings.Contains(err.Error(), test.wantErr) { 1643 t.Fatalf("UnmarshalListener() = %v wantErr: %q", err, test.wantErr) 1644 } 1645 if diff := cmp.Diff(gotUpdate, test.wantUpdate, cmpOpts); diff != "" { 1646 t.Errorf("got unexpected update, diff (-got +want): %v", diff) 1647 } 1648 if diff := cmp.Diff(md, test.wantMD, cmpOptsIgnoreDetails); diff != "" { 1649 t.Errorf("got unexpected metadata, diff (-got +want): %v", diff) 1650 } 1651 }) 1652 } 1653} 1654 1655type filterConfig struct { 1656 httpfilter.FilterConfig 1657 Cfg proto.Message 1658 Override proto.Message 1659} 1660 1661// httpFilter allows testing the http filter registry and parsing functionality. 1662type httpFilter struct { 1663 httpfilter.ClientInterceptorBuilder 1664 httpfilter.ServerInterceptorBuilder 1665} 1666 1667func (httpFilter) TypeURLs() []string { return []string{"custom.filter"} } 1668 1669func (httpFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1670 return filterConfig{Cfg: cfg}, nil 1671} 1672 1673func (httpFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1674 return filterConfig{Override: override}, nil 1675} 1676 1677func (httpFilter) IsTerminal() bool { 1678 return false 1679} 1680 1681// errHTTPFilter returns errors no matter what is passed to ParseFilterConfig. 1682type errHTTPFilter struct { 1683 httpfilter.ClientInterceptorBuilder 1684} 1685 1686func (errHTTPFilter) TypeURLs() []string { return []string{"err.custom.filter"} } 1687 1688func (errHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1689 return nil, fmt.Errorf("error from ParseFilterConfig") 1690} 1691 1692func (errHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1693 return nil, fmt.Errorf("error from ParseFilterConfigOverride") 1694} 1695 1696func (errHTTPFilter) IsTerminal() bool { 1697 return false 1698} 1699 1700func init() { 1701 httpfilter.Register(httpFilter{}) 1702 httpfilter.Register(errHTTPFilter{}) 1703 httpfilter.Register(serverOnlyHTTPFilter{}) 1704 httpfilter.Register(clientOnlyHTTPFilter{}) 1705} 1706 1707// serverOnlyHTTPFilter does not implement ClientInterceptorBuilder 1708type serverOnlyHTTPFilter struct { 1709 httpfilter.ServerInterceptorBuilder 1710} 1711 1712func (serverOnlyHTTPFilter) TypeURLs() []string { return []string{"serverOnly.custom.filter"} } 1713 1714func (serverOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1715 return filterConfig{Cfg: cfg}, nil 1716} 1717 1718func (serverOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1719 return filterConfig{Override: override}, nil 1720} 1721 1722func (serverOnlyHTTPFilter) IsTerminal() bool { 1723 return false 1724} 1725 1726// clientOnlyHTTPFilter does not implement ServerInterceptorBuilder 1727type clientOnlyHTTPFilter struct { 1728 httpfilter.ClientInterceptorBuilder 1729} 1730 1731func (clientOnlyHTTPFilter) TypeURLs() []string { return []string{"clientOnly.custom.filter"} } 1732 1733func (clientOnlyHTTPFilter) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) { 1734 return filterConfig{Cfg: cfg}, nil 1735} 1736 1737func (clientOnlyHTTPFilter) ParseFilterConfigOverride(override proto.Message) (httpfilter.FilterConfig, error) { 1738 return filterConfig{Override: override}, nil 1739} 1740 1741func (clientOnlyHTTPFilter) IsTerminal() bool { 1742 return false 1743} 1744 1745var customFilterConfig = &anypb.Any{ 1746 TypeUrl: "custom.filter", 1747 Value: []byte{1, 2, 3}, 1748} 1749 1750var errFilterConfig = &anypb.Any{ 1751 TypeUrl: "err.custom.filter", 1752 Value: []byte{1, 2, 3}, 1753} 1754 1755var serverOnlyCustomFilterConfig = &anypb.Any{ 1756 TypeUrl: "serverOnly.custom.filter", 1757 Value: []byte{1, 2, 3}, 1758} 1759 1760var clientOnlyCustomFilterConfig = &anypb.Any{ 1761 TypeUrl: "clientOnly.custom.filter", 1762 Value: []byte{1, 2, 3}, 1763} 1764 1765var customFilterTypedStructConfig = &v1typepb.TypedStruct{ 1766 TypeUrl: "custom.filter", 1767 Value: &spb.Struct{ 1768 Fields: map[string]*spb.Value{ 1769 "foo": {Kind: &spb.Value_StringValue{StringValue: "bar"}}, 1770 }, 1771 }, 1772} 1773var wrappedCustomFilterTypedStructConfig *anypb.Any 1774 1775func init() { 1776 wrappedCustomFilterTypedStructConfig = testutils.MarshalAny(customFilterTypedStructConfig) 1777} 1778 1779var unknownFilterConfig = &anypb.Any{ 1780 TypeUrl: "unknown.custom.filter", 1781 Value: []byte{1, 2, 3}, 1782} 1783 1784func wrappedOptionalFilter(name string) *anypb.Any { 1785 return testutils.MarshalAny(&v3routepb.FilterConfig{ 1786 IsOptional: true, 1787 Config: &anypb.Any{ 1788 TypeUrl: name, 1789 Value: []byte{1, 2, 3}, 1790 }, 1791 }) 1792} 1793