@@ -334,41 +334,32 @@ static FilterChain parseFilterChain(
334334 TlsContextManager tlsContextManager , FilterRegistry filterRegistry ,
335335 Set <FilterChainMatch > uniqueSet , Set <String > certProviderInstances , boolean parseHttpFilters )
336336 throws ResourceInvalidException {
337- io .grpc .xds .HttpConnectionManager httpConnectionManager = null ;
338- HashSet <String > uniqueNames = new HashSet <>();
339- for (io .envoyproxy .envoy .config .listener .v3 .Filter filter : proto .getFiltersList ()) {
340- if (!uniqueNames .add (filter .getName ())) {
341- throw new ResourceInvalidException (
342- "FilterChain " + proto .getName () + " with duplicated filter: " + filter .getName ());
343- }
344- if (!filter .hasTypedConfig ()) {
345- throw new ResourceInvalidException (
346- "FilterChain " + proto .getName () + " contains filter " + filter .getName ()
347- + " without typed_config" );
348- }
349- Any any = filter .getTypedConfig ();
350- // HttpConnectionManager is the only supported network filter at the moment.
351- if (!any .getTypeUrl ().equals (TYPE_URL_HTTP_CONNECTION_MANAGER )) {
352- throw new ResourceInvalidException (
353- "FilterChain " + proto .getName () + " contains filter " + filter .getName ()
354- + " with unsupported typed_config type " + any .getTypeUrl ());
355- }
356- if (httpConnectionManager == null ) {
357- HttpConnectionManager hcmProto ;
358- try {
359- hcmProto = any .unpack (HttpConnectionManager .class );
360- } catch (InvalidProtocolBufferException e ) {
361- throw new ResourceInvalidException ("FilterChain " + proto .getName () + " with filter "
362- + filter .getName () + " failed to unpack message" , e );
363- }
364- httpConnectionManager = parseHttpConnectionManager (
365- hcmProto , rdsResources , filterRegistry , parseHttpFilters , false /* isForClient */ );
366- }
367- }
368- if (httpConnectionManager == null ) {
337+ if (proto .getFiltersCount () != 1 ) {
369338 throw new ResourceInvalidException ("FilterChain " + proto .getName ()
370- + " missing required HttpConnectionManager filter" );
339+ + " should contain exact one HttpConnectionManager filter" );
371340 }
341+ io .envoyproxy .envoy .config .listener .v3 .Filter filter = proto .getFiltersList ().get (0 );
342+ if (!filter .hasTypedConfig ()) {
343+ throw new ResourceInvalidException (
344+ "FilterChain " + proto .getName () + " contains filter " + filter .getName ()
345+ + " without typed_config" );
346+ }
347+ Any any = filter .getTypedConfig ();
348+ // HttpConnectionManager is the only supported network filter at the moment.
349+ if (!any .getTypeUrl ().equals (TYPE_URL_HTTP_CONNECTION_MANAGER )) {
350+ throw new ResourceInvalidException (
351+ "FilterChain " + proto .getName () + " contains filter " + filter .getName ()
352+ + " with unsupported typed_config type " + any .getTypeUrl ());
353+ }
354+ HttpConnectionManager hcmProto ;
355+ try {
356+ hcmProto = any .unpack (HttpConnectionManager .class );
357+ } catch (InvalidProtocolBufferException e ) {
358+ throw new ResourceInvalidException ("FilterChain " + proto .getName () + " with filter "
359+ + filter .getName () + " failed to unpack message" , e );
360+ }
361+ io .grpc .xds .HttpConnectionManager httpConnectionManager = parseHttpConnectionManager (
362+ hcmProto , rdsResources , filterRegistry , parseHttpFilters , false /* isForClient */ );
372363
373364 EnvoyServerProtoData .DownstreamTlsContext downstreamTlsContext = null ;
374365 if (proto .hasTransportSocket ()) {
@@ -762,17 +753,26 @@ static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
762753 // Parse http filters.
763754 List <NamedFilterConfig > filterConfigs = null ;
764755 if (parseHttpFilter ) {
756+ if (proto .getHttpFiltersList ().isEmpty ()) {
757+ throw new ResourceInvalidException ("Missing HttpFilter in HttpConnectionManager." );
758+ }
765759 filterConfigs = new ArrayList <>();
766760 Set <String > names = new HashSet <>();
767- for (io .envoyproxy .envoy .extensions .filters .network .http_connection_manager .v3 .HttpFilter
768- httpFilter : proto .getHttpFiltersList ()) {
761+ for (int i = 0 ; i < proto .getHttpFiltersCount (); i ++) {
762+ io .envoyproxy .envoy .extensions .filters .network .http_connection_manager .v3 .HttpFilter
763+ httpFilter = proto .getHttpFiltersList ().get (i );
769764 String filterName = httpFilter .getName ();
770765 if (!names .add (filterName )) {
771766 throw new ResourceInvalidException (
772767 "HttpConnectionManager contains duplicate HttpFilter: " + filterName );
773768 }
774769 StructOrError <FilterConfig > filterConfig =
775770 parseHttpFilter (httpFilter , filterRegistry , isForClient );
771+ if ((i == proto .getHttpFiltersCount () - 1 )
772+ && (filterConfig == null || !isTerminalFilter (filterConfig .struct ))) {
773+ throw new ResourceInvalidException ("The last HttpFilter must be a terminal filter: "
774+ + filterName );
775+ }
776776 if (filterConfig == null ) {
777777 continue ;
778778 }
@@ -781,6 +781,10 @@ static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
781781 "HttpConnectionManager contains invalid HttpFilter: "
782782 + filterConfig .getErrorDetail ());
783783 }
784+ if ((i < proto .getHttpFiltersCount () - 1 ) && isTerminalFilter (filterConfig .getStruct ())) {
785+ throw new ResourceInvalidException ("A terminal HttpFilter must be the last filter: "
786+ + filterName );
787+ }
784788 filterConfigs .add (new NamedFilterConfig (filterName , filterConfig .struct ));
785789 }
786790 }
@@ -821,6 +825,11 @@ static io.grpc.xds.HttpConnectionManager parseHttpConnectionManager(
821825 "HttpConnectionManager neither has inlined route_config nor RDS" );
822826 }
823827
828+ // hard-coded: currently router config is the only terminal filter.
829+ private static boolean isTerminalFilter (FilterConfig filterConfig ) {
830+ return RouterFilter .ROUTER_CONFIG .equals (filterConfig );
831+ }
832+
824833 @ VisibleForTesting
825834 @ Nullable // Returns null if the filter is optional but not supported.
826835 static StructOrError <FilterConfig > parseHttpFilter (
0 commit comments